diff options
142 files changed, 2514 insertions, 7111 deletions
diff --git a/CPPLINT.cfg b/CPPLINT.cfg new file mode 100644 index 0000000000..83288421e2 --- /dev/null +++ b/CPPLINT.cfg @@ -0,0 +1,33 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Don't search for additional CPPLINT.cfg in parent directories. +set noparent + +# Use 'ART_' as the cpp header guard prefix (e.g. #ifndef ART_PATH_TO_FILE_H_). +root=.. + +# Limit line length. +linelength=100 + +# Ignore the following categories of errors, as specified by the filter: +# (the filter settings are concatenated together) +filter=-build/c++11 +filter=-build/include +filter=-readability/function,-readability/streams,-readability/todo +filter=-runtime/printf,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn +# TODO: this should be re-enabled. +filter=-whitespace/line_length diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 8a8df36101..7e492c7453 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,4 +1,10 @@ [Hook Scripts] check_generated_files_up_to_date = tools/cpp-define-generator/presubmit-check-files-up-to-date check_generated_tests_up_to_date = tools/test_presubmit.py -check_cpplint_on_changed_files = tools/cpplint_presubmit.py + +[Builtin Hooks] +cpplint = true + +[Builtin Hooks Options] +# Cpplint prints nothing unless there were errors. +cpplint = --quiet ${PREUPLOAD_FILES} diff --git a/build/Android.cpplint.mk b/build/Android.cpplint.mk index 247f4e3470..964a4c896f 100644 --- a/build/Android.cpplint.mk +++ b/build/Android.cpplint.mk @@ -16,42 +16,46 @@ include art/build/Android.common_build.mk -ART_CPPLINT := $(LOCAL_PATH)/tools/cpplint.py -ART_CPPLINT_FILTER := --filter=-whitespace/line_length,-build/include,-readability/function,-readability/streams,-readability/todo,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn,-runtime/printf -# Use `pwd` instead of $TOP for root, $TOP is always . and --root doesn't seem -# to work with a relative path (b/34787652). -ART_CPPLINT_FLAGS := --root=`pwd` +# Use upstream cpplint (toolpath from .repo/manifests/GLOBAL-PREUPLOAD.cfg). +ART_CPPLINT := external/google-styleguide/cpplint/cpplint.py + +# This file previously configured many cpplint settings. +# Everything that could be moved to CPPLINT.cfg has moved there. +# Please add new settings to CPPLINT.cfg over adding new flags in this file. + +ART_CPPLINT_FLAGS := +# No output when there are no errors. ART_CPPLINT_QUIET := --quiet -ART_CPPLINT_INGORED := \ - runtime/elf.h \ - openjdkjvmti/include/jvmti.h -# This: -# 1) Gets a list of all .h & .cc files in the art directory. +# 1) Get list of all .h & .cc files in the art directory. +# 2) Prepends 'art/' to each of them to make the full name. +ART_CPPLINT_SRC := $(addprefix $(LOCAL_PATH)/, $(call all-subdir-named-files,*.h) $(call all-subdir-named-files,*$(ART_CPP_EXTENSION))) + +# 1) Get list of all CPPLINT.cfg files in the art directory. # 2) Prepends 'art/' to each of them to make the full name. -# 3) removes art/runtime/elf.h from the list. -ART_CPPLINT_SRC := $(filter-out $(patsubst %,$(LOCAL_PATH)/%,$(ART_CPPLINT_INGORED)), $(addprefix $(LOCAL_PATH)/, $(call all-subdir-named-files,*.h) $(call all-subdir-named-files,*$(ART_CPP_EXTENSION)))) +ART_CPPLINT_CFG := $(addprefix $(LOCAL_PATH)/, $(call all-subdir-named-files,CPPLINT.cfg)) # "mm cpplint-art" to verify we aren't regressing +# - files not touched since the last build are skipped (quite fast). .PHONY: cpplint-art -cpplint-art: - $(ART_CPPLINT) $(ART_CPPLINT_FLAGS) $(ART_CPPLINT_FILTER) $(ART_CPPLINT_SRC) +cpplint-art: cpplint-art-phony -# "mm cpplint-art-all" to see all warnings +# "mm cpplint-art-all" to manually execute cpplint.py on all files (very slow). .PHONY: cpplint-art-all cpplint-art-all: $(ART_CPPLINT) $(ART_CPPLINT_FLAGS) $(ART_CPPLINT_SRC) OUT_CPPLINT := $(TARGET_COMMON_OUT_ROOT)/cpplint +# Build up the list of all targets for linting the ART source files. ART_CPPLINT_TARGETS := define declare-art-cpplint-target art_cpplint_file := $(1) art_cpplint_touch := $$(OUT_CPPLINT)/$$(subst /,__,$$(art_cpplint_file)) -$$(art_cpplint_touch): $$(art_cpplint_file) $(ART_CPPLINT) art/build/Android.cpplint.mk - $(hide) $(ART_CPPLINT) $(ART_CPPLINT_QUIET) $(ART_CPPLINT_FLAGS) $(ART_CPPLINT_FILTER) $$< +$$(art_cpplint_touch): $$(art_cpplint_file) $(ART_CPPLINT) $(ART_CPPLINT_CFG) art/build/Android.cpplint.mk + $(hide) $(ART_CPPLINT) $(ART_CPPLINT_QUIET) $(ART_CPPLINT_FLAGS) $$< $(hide) mkdir -p $$(dir $$@) $(hide) touch $$@ diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 42d0ba57cf..3c8eade773 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -608,7 +608,7 @@ define define-test-art-gtest-combination endif .PHONY: $$(rule_name) -$$(rule_name): $$(dependencies) dx d8 +$$(rule_name): $$(dependencies) dx d8-compat-dx $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) # Clear locally defined variables. diff --git a/cmdline/cmdline_parser.h b/cmdline/cmdline_parser.h index fef39ad930..82c04e70f5 100644 --- a/cmdline/cmdline_parser.h +++ b/cmdline/cmdline_parser.h @@ -340,7 +340,7 @@ struct CmdlineParser { typename std::enable_if<std::is_same<TArg, Unit>::value>::type InitializeTypedBuilder(ArgumentBuilder<TArg>* arg_builder) { // Every Unit argument implicitly maps to a runtime value of Unit{} - std::vector<Unit> values(names_.size(), Unit{}); // NOLINT [whitespace/braces] [5] + std::vector<Unit> values(names_.size(), Unit{}); arg_builder->SetValuesInternal(std::move(values)); } diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index 34aea55f5b..1536339515 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -190,7 +190,7 @@ class CmdlineParserTest : public ::testing::Test { #define EXPECT_SINGLE_PARSE_VALUE(expected, argv, key) \ _EXPECT_SINGLE_PARSE_EXISTS(argv, key); \ EXPECT_KEY_VALUE(args, key, expected); \ - } while (false) // NOLINT [readability/namespace] [5] + } while (false) #define EXPECT_SINGLE_PARSE_VALUE_STR(expected, argv, key) \ EXPECT_SINGLE_PARSE_VALUE(std::string(expected), argv, key) @@ -318,7 +318,7 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { * Test success */ { - XGcOption option_all_true{}; // NOLINT [readability/braces] [4] + XGcOption option_all_true{}; option_all_true.collector_type_ = gc::CollectorType::kCollectorTypeCMS; option_all_true.verify_pre_gc_heap_ = true; option_all_true.verify_pre_sweeping_heap_ = true; @@ -335,7 +335,7 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { EXPECT_SINGLE_PARSE_VALUE(option_all_true, xgc_args_all_true, M::GcOption); - XGcOption option_all_false{}; // NOLINT [readability/braces] [4] + XGcOption option_all_false{}; option_all_false.collector_type_ = gc::CollectorType::kCollectorTypeMS; option_all_false.verify_pre_gc_heap_ = false; option_all_false.verify_pre_sweeping_heap_ = false; @@ -350,7 +350,7 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { EXPECT_SINGLE_PARSE_VALUE(option_all_false, xgc_args_all_false, M::GcOption); - XGcOption option_all_default{}; // NOLINT [readability/braces] [4] + XGcOption option_all_default{}; const char* xgc_args_blank = "-Xgc:"; EXPECT_SINGLE_PARSE_VALUE(option_all_default, xgc_args_blank, M::GcOption); @@ -566,10 +566,10 @@ TEST_F(CmdlineParserTest, MultipleArguments) { auto&& map = parser_->ReleaseArgumentsMap(); EXPECT_EQ(5u, map.Size()); - EXPECT_KEY_VALUE(map, M::Help, Unit{}); // NOLINT [whitespace/braces] [5] + EXPECT_KEY_VALUE(map, M::Help, Unit{}); EXPECT_KEY_VALUE(map, M::ForegroundHeapGrowthMultiplier, 0.5); EXPECT_KEY_VALUE(map, M::Dex2Oat, false); - EXPECT_KEY_VALUE(map, M::MethodTrace, Unit{}); // NOLINT [whitespace/braces] [5] + EXPECT_KEY_VALUE(map, M::MethodTrace, Unit{}); EXPECT_KEY_VALUE(map, M::LargeObjectSpace, gc::space::LargeObjectSpaceType::kMap); } // TEST_F } // namespace art diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 87bf1c4d43..37bdcdc5e2 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -57,7 +57,7 @@ template <> struct CmdlineType<Unit> : CmdlineTypeParser<Unit> { Result Parse(const std::string& args) { if (args == "") { - return Result::Success(Unit{}); // NOLINT [whitespace/braces] [5] + return Result::Success(Unit{}); } return Result::Failure("Unexpected extra characters " + args); } @@ -532,7 +532,7 @@ struct XGcOption { template <> struct CmdlineType<XGcOption> : CmdlineTypeParser<XGcOption> { Result Parse(const std::string& option) { // -Xgc: already stripped - XGcOption xgc{}; // NOLINT [readability/braces] [4] + XGcOption xgc{}; std::vector<std::string> gc_options; Split(option, ',', &gc_options); diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc index fc6a717aa6..e41371855d 100644 --- a/compiler/compiled_method.cc +++ b/compiler/compiled_method.cc @@ -26,8 +26,8 @@ CompiledCode::CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set, const ArrayRef<const uint8_t>& quick_code) : compiler_driver_(compiler_driver), - instruction_set_(instruction_set), - quick_code_(compiler_driver_->GetCompiledMethodStorage()->DeduplicateCode(quick_code)) { + quick_code_(compiler_driver_->GetCompiledMethodStorage()->DeduplicateCode(quick_code)), + packed_fields_(InstructionSetField::Encode(instruction_set)) { } CompiledCode::~CompiledCode() { @@ -48,7 +48,7 @@ bool CompiledCode::operator==(const CompiledCode& rhs) const { } size_t CompiledCode::AlignCode(size_t offset) const { - return AlignCode(offset, instruction_set_); + return AlignCode(offset, GetInstructionSet()); } size_t CompiledCode::AlignCode(size_t offset, InstructionSet instruction_set) { @@ -56,7 +56,7 @@ size_t CompiledCode::AlignCode(size_t offset, InstructionSet instruction_set) { } size_t CompiledCode::CodeDelta() const { - return CodeDelta(instruction_set_); + return CodeDelta(GetInstructionSet()); } size_t CompiledCode::CodeDelta(InstructionSet instruction_set) { diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index 892bc592db..acdce260e5 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -22,6 +22,8 @@ #include <vector> #include "arch/instruction_set.h" +#include "base/bit_field.h" +#include "base/bit_utils.h" namespace art { @@ -44,7 +46,7 @@ class CompiledCode { virtual ~CompiledCode(); InstructionSet GetInstructionSet() const { - return instruction_set_; + return GetPackedField<InstructionSetField>(); } ArrayRef<const uint8_t> GetQuickCode() const; @@ -68,6 +70,11 @@ class CompiledCode { static const void* CodePointer(const void* code_pointer, InstructionSet instruction_set); protected: + static constexpr size_t kInstructionSetFieldSize = + MinimumBitsToStore(static_cast<size_t>(InstructionSet::kLast)); + static constexpr size_t kNumberOfCompiledCodePackedBits = kInstructionSetFieldSize; + static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte; + template <typename T> static ArrayRef<const T> GetArray(const LengthPrefixedArray<T>* array); @@ -75,13 +82,26 @@ class CompiledCode { return compiler_driver_; } + template <typename BitFieldType> + typename BitFieldType::value_type GetPackedField() const { + return BitFieldType::Decode(packed_fields_); + } + + template <typename BitFieldType> + void SetPackedField(typename BitFieldType::value_type value) { + DCHECK(IsUint<BitFieldType::size>(static_cast<uintptr_t>(value))); + packed_fields_ = BitFieldType::Update(value, packed_fields_); + } + private: - CompilerDriver* const compiler_driver_; + using InstructionSetField = BitField<InstructionSet, 0u, kInstructionSetFieldSize>; - const InstructionSet instruction_set_; + CompilerDriver* const compiler_driver_; - // Used to store the PIC code for Quick. + // Used to store the compiled code. const LengthPrefixedArray<uint8_t>* const quick_code_; + + uint32_t packed_fields_; }; class CompiledMethod FINAL : public CompiledCode { @@ -116,6 +136,18 @@ class CompiledMethod FINAL : public CompiledCode { static void ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m); + bool IsIntrinsic() const { + return GetPackedField<IsIntrinsicField>(); + } + + // Marks the compiled method as being generated using an intrinsic codegen. + // Such methods have no relationships to their code items. + // This affects debug information generated at link time. + void MarkAsIntrinsic() { + DCHECK(!IsIntrinsic()); + SetPackedField<IsIntrinsicField>(/* value */ true); + } + size_t GetFrameSizeInBytes() const { return frame_size_in_bytes_; } @@ -137,6 +169,14 @@ class CompiledMethod FINAL : public CompiledCode { ArrayRef<const linker::LinkerPatch> GetPatches() const; private: + static constexpr size_t kIsIntrinsicLsb = kNumberOfCompiledCodePackedBits; + static constexpr size_t kIsIntrinsicSize = 1u; + static constexpr size_t kNumberOfCompiledMethodPackedBits = kIsIntrinsicLsb + kIsIntrinsicSize; + static_assert(kNumberOfCompiledMethodPackedBits <= CompiledCode::kMaxNumberOfPackedBits, + "Too many packed fields."); + + using IsIntrinsicField = BitField<bool, kIsIntrinsicLsb, kIsIntrinsicSize>; + // For quick code, the size of the activation used by the code. const size_t frame_size_in_bytes_; // For quick code, a bit mask describing spilled GPR callee-save registers. diff --git a/compiler/compiler.h b/compiler/compiler.h index 3aa84f8e2b..85abd6654c 100644 --- a/compiler/compiler.h +++ b/compiler/compiler.h @@ -65,7 +65,8 @@ class Compiler { virtual CompiledMethod* JniCompile(uint32_t access_flags, uint32_t method_idx, - const DexFile& dex_file) const = 0; + const DexFile& dex_file, + Handle<mirror::DexCache> dex_cache) const = 0; virtual bool JitCompile(Thread* self ATTRIBUTE_UNUSED, jit::JitCodeCache* code_cache ATTRIBUTE_UNUSED, diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc index 866bf4394d..933034f593 100644 --- a/compiler/debug/dwarf/dwarf_test.cc +++ b/compiler/debug/dwarf/dwarf_test.cc @@ -125,7 +125,7 @@ TEST_F(DwarfTest, DebugFrame) { WriteCIE(is64bit, Reg(is64bit ? 16 : 8), initial_opcodes, kCFIFormat, &debug_frame_data_); std::vector<uintptr_t> debug_frame_patches; - std::vector<uintptr_t> expected_patches { 28 }; // NOLINT + std::vector<uintptr_t> expected_patches = { 28 }; WriteFDE(is64bit, 0, 0, 0x01000000, 0x01000000, ArrayRef<const uint8_t>(*opcodes.data()), kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches); @@ -140,7 +140,7 @@ TEST_F(DwarfTest, DebugFrame64) { initial_opcodes, kCFIFormat, &debug_frame_data_); DebugFrameOpCodeWriter<> opcodes; std::vector<uintptr_t> debug_frame_patches; - std::vector<uintptr_t> expected_patches { 32 }; // NOLINT + std::vector<uintptr_t> expected_patches = { 32 }; WriteFDE(is64bit, 0, 0, 0x0100000000000000, 0x0200000000000000, ArrayRef<const uint8_t>(*opcodes.data()), kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches); @@ -237,7 +237,7 @@ TEST_F(DwarfTest, DebugLine) { DW_CHECK_NEXT("1\t0\t1000\t2000\tfile.c"); std::vector<uintptr_t> debug_line_patches; - std::vector<uintptr_t> expected_patches { 87 }; // NOLINT + std::vector<uintptr_t> expected_patches = { 87 }; WriteDebugLineTable(include_directories, files, opcodes, 0, &debug_line_data_, &debug_line_patches); @@ -275,7 +275,7 @@ TEST_F(DwarfTest, DebugLineSpecialOpcodes) { EXPECT_LT(opcodes.data()->size(), num_rows * 3); std::vector<std::string> directories; - std::vector<FileEntry> files { { "file.c", 0, 1000, 2000 } }; // NOLINT + std::vector<FileEntry> files = { { "file.c", 0, 1000, 2000 } }; std::vector<uintptr_t> debug_line_patches; WriteDebugLineTable(directories, files, opcodes, 0, &debug_line_data_, &debug_line_patches); @@ -333,7 +333,7 @@ TEST_F(DwarfTest, DebugInfo) { DW_CHECK("3 DW_TAG_compile_unit [no children]"); std::vector<uintptr_t> debug_info_patches; - std::vector<uintptr_t> expected_patches { 16, 20, 29, 33, 42, 46 }; // NOLINT + std::vector<uintptr_t> expected_patches = { 16, 20, 29, 33, 42, 46 }; dwarf::WriteDebugInfoCU(0 /* debug_abbrev_offset */, info, 0, &debug_info_data_, &debug_info_patches); diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index 2b617273b5..37c2d32091 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -202,7 +202,7 @@ class ElfCompilationUnitWriter { // Decode dex register locations for all stack maps. // It might be expensive, so do it just once and reuse the result. std::vector<DexRegisterMap> dex_reg_maps; - if (mi->code_info != nullptr) { + if (dex_code != nullptr && mi->code_info != nullptr) { const CodeInfo code_info(mi->code_info); CodeInfoEncoding encoding = code_info.ExtractEncoding(); for (size_t s = 0; s < code_info.GetNumberOfStackMaps(encoding); ++s) { diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index f4700d4040..726401d09e 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -516,7 +516,8 @@ static void CompileMethod(Thread* self, access_flags |= annotations::GetNativeMethodAnnotationAccessFlags( dex_file, dex_file.GetClassDef(class_def_idx), method_idx); - compiled_method = driver->GetCompiler()->JniCompile(access_flags, method_idx, dex_file); + compiled_method = driver->GetCompiler()->JniCompile( + access_flags, method_idx, dex_file, dex_cache); CHECK(compiled_method != nullptr); } } else if ((access_flags & kAccAbstract) != 0) { diff --git a/compiler/driver/compiler_options_map-storage.h b/compiler/driver/compiler_options_map-storage.h index 756598de05..01f32e0c9c 100644 --- a/compiler/driver/compiler_options_map-storage.h +++ b/compiler/driver/compiler_options_map-storage.h @@ -36,7 +36,7 @@ #define COMPILER_OPTIONS_KEY(Type, Name, ...) \ template <typename Base, template <typename TV> class KeyType> \ - const KeyType<Type> CompilerOptionsMap<Base, KeyType>::Name {__VA_ARGS__}; // NOLINT [readability/braces] [4] + const KeyType<Type> CompilerOptionsMap<Base, KeyType>::Name {__VA_ARGS__}; #include <driver/compiler_options_map.def> template struct CompilerOptionsMap<COMPILER_OPTIONS_MAP_TYPE, COMPILER_OPTIONS_MAP_KEY_TYPE>; diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc index 2432f13044..a6687fe258 100644 --- a/compiler/optimizing/block_builder.cc +++ b/compiler/optimizing/block_builder.cc @@ -40,20 +40,20 @@ bool HBasicBlockBuilder::CreateBranchTargets() { // Create the first block for the dex instructions, single successor of the entry block. MaybeCreateBlockAt(0u); - if (code_item_.tries_size_ != 0) { + if (code_item_->tries_size_ != 0) { // Create branch targets at the start/end of the TryItem range. These are // places where the program might fall through into/out of the a block and // where TryBoundary instructions will be inserted later. Other edges which // enter/exit the try blocks are a result of branches/switches. - for (size_t idx = 0; idx < code_item_.tries_size_; ++idx) { - const DexFile::TryItem* try_item = DexFile::GetTryItems(code_item_, idx); + for (size_t idx = 0; idx < code_item_->tries_size_; ++idx) { + const DexFile::TryItem* try_item = DexFile::GetTryItems(*code_item_, idx); uint32_t dex_pc_start = try_item->start_addr_; uint32_t dex_pc_end = dex_pc_start + try_item->insn_count_; MaybeCreateBlockAt(dex_pc_start); - if (dex_pc_end < code_item_.insns_size_in_code_units_) { + if (dex_pc_end < code_item_->insns_size_in_code_units_) { // TODO: Do not create block if the last instruction cannot fall through. MaybeCreateBlockAt(dex_pc_end); - } else if (dex_pc_end == code_item_.insns_size_in_code_units_) { + } else if (dex_pc_end == code_item_->insns_size_in_code_units_) { // The TryItem spans until the very end of the CodeItem and therefore // cannot have any code afterwards. } else { @@ -63,7 +63,7 @@ bool HBasicBlockBuilder::CreateBranchTargets() { } // Create branch targets for exception handlers. - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(code_item_, 0); + const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0); uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); for (uint32_t idx = 0; idx < handlers_size; ++idx) { CatchHandlerIterator iterator(handlers_ptr); @@ -76,7 +76,7 @@ bool HBasicBlockBuilder::CreateBranchTargets() { // Iterate over all instructions and find branching instructions. Create blocks for // the locations these instructions branch to. - IterationRange<DexInstructionIterator> instructions = code_item_.Instructions(); + IterationRange<DexInstructionIterator> instructions = code_item_->Instructions(); for (const DexInstructionPcPair& pair : instructions) { const uint32_t dex_pc = pair.DexPc(); const Instruction& instruction = pair.Inst(); @@ -127,7 +127,7 @@ void HBasicBlockBuilder::ConnectBasicBlocks() { bool is_throwing_block = false; // Calculate the qucikening index here instead of CreateBranchTargets since it's easier to // calculate in dex_pc order. - for (const DexInstructionPcPair& pair : code_item_.Instructions()) { + for (const DexInstructionPcPair& pair : code_item_->Instructions()) { const uint32_t dex_pc = pair.DexPc(); const Instruction& instruction = pair.Inst(); @@ -229,7 +229,7 @@ bool HBasicBlockBuilder::MightHaveLiveNormalPredecessors(HBasicBlock* catch_bloc } } - const Instruction& first = code_item_.InstructionAt(catch_block->GetDexPc()); + const Instruction& first = code_item_->InstructionAt(catch_block->GetDexPc()); if (first.Opcode() == Instruction::MOVE_EXCEPTION) { // Verifier guarantees that if a catch block begins with MOVE_EXCEPTION then // it has no live normal predecessors. @@ -247,7 +247,7 @@ bool HBasicBlockBuilder::MightHaveLiveNormalPredecessors(HBasicBlock* catch_bloc } void HBasicBlockBuilder::InsertTryBoundaryBlocks() { - if (code_item_.tries_size_ == 0) { + if (code_item_->tries_size_ == 0) { return; } @@ -269,12 +269,12 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { // loop for synchronized blocks. if (ContainsElement(throwing_blocks_, block)) { // Try to find a TryItem covering the block. - const int32_t try_item_idx = DexFile::FindTryItem(DexFile::GetTryItems(code_item_, 0u), - code_item_.tries_size_, + const int32_t try_item_idx = DexFile::FindTryItem(DexFile::GetTryItems(*code_item_, 0u), + code_item_->tries_size_, block->GetDexPc()); if (try_item_idx != -1) { // Block throwing and in a TryItem. Store the try block information. - try_block_info.Put(block->GetBlockId(), DexFile::GetTryItems(code_item_, try_item_idx)); + try_block_info.Put(block->GetBlockId(), DexFile::GetTryItems(*code_item_, try_item_idx)); } } } @@ -285,7 +285,7 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { // Iterate over catch blocks, create artifical landing pads if necessary to // simplify the CFG, and set metadata. - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(code_item_, 0); + const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0); uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); for (uint32_t idx = 0; idx < handlers_size; ++idx) { CatchHandlerIterator iterator(handlers_ptr); @@ -333,7 +333,7 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { HTryBoundary* try_entry = new (allocator_) HTryBoundary( HTryBoundary::BoundaryKind::kEntry, try_block->GetDexPc()); try_block->CreateImmediateDominator()->AddInstruction(try_entry); - LinkToCatchBlocks(try_entry, code_item_, try_item, catch_blocks); + LinkToCatchBlocks(try_entry, *code_item_, try_item, catch_blocks); break; } } @@ -361,12 +361,13 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { HTryBoundary* try_exit = new (allocator_) HTryBoundary(HTryBoundary::BoundaryKind::kExit, successor->GetDexPc()); graph_->SplitEdge(try_block, successor)->AddInstruction(try_exit); - LinkToCatchBlocks(try_exit, code_item_, try_item, catch_blocks); + LinkToCatchBlocks(try_exit, *code_item_, try_item, catch_blocks); } } } bool HBasicBlockBuilder::Build() { + DCHECK(code_item_ != nullptr); DCHECK(graph_->GetBlocks().empty()); graph_->SetEntryBlock(new (allocator_) HBasicBlock(graph_, kNoDexPc)); @@ -383,6 +384,27 @@ bool HBasicBlockBuilder::Build() { return true; } +void HBasicBlockBuilder::BuildIntrinsic() { + DCHECK(code_item_ == nullptr); + DCHECK(graph_->GetBlocks().empty()); + + // Create blocks. + HBasicBlock* entry_block = new (allocator_) HBasicBlock(graph_, kNoDexPc); + HBasicBlock* exit_block = new (allocator_) HBasicBlock(graph_, kNoDexPc); + HBasicBlock* body = MaybeCreateBlockAt(/* semantic_dex_pc */ kNoDexPc, /* store_dex_pc */ 0u); + + // Add blocks to the graph. + graph_->AddBlock(entry_block); + graph_->AddBlock(body); + graph_->AddBlock(exit_block); + graph_->SetEntryBlock(entry_block); + graph_->SetExitBlock(exit_block); + + // Connect blocks. + entry_block->AddSuccessor(body); + body->AddSuccessor(exit_block); +} + size_t HBasicBlockBuilder::GetQuickenIndex(uint32_t dex_pc) const { return quicken_index_for_dex_pc_.Get(dex_pc); } diff --git a/compiler/optimizing/block_builder.h b/compiler/optimizing/block_builder.h index 79f7a7bc81..7d0f56db34 100644 --- a/compiler/optimizing/block_builder.h +++ b/compiler/optimizing/block_builder.h @@ -28,14 +28,15 @@ class HBasicBlockBuilder : public ValueObject { public: HBasicBlockBuilder(HGraph* graph, const DexFile* const dex_file, - const DexFile::CodeItem& code_item, + const DexFile::CodeItem* code_item, ScopedArenaAllocator* local_allocator) : allocator_(graph->GetAllocator()), graph_(graph), dex_file_(dex_file), code_item_(code_item), local_allocator_(local_allocator), - branch_targets_(code_item.insns_size_in_code_units_, + branch_targets_(code_item != nullptr ? code_item->insns_size_in_code_units_ + : /* fake dex_pc=0 for intrinsic graph */ 1u, nullptr, local_allocator->Adapter(kArenaAllocGraphBuilder)), throwing_blocks_(kDefaultNumberOfThrowingBlocks, @@ -50,6 +51,9 @@ class HBasicBlockBuilder : public ValueObject { // exits a try block. bool Build(); + // Creates basic blocks in `graph_` for compiling an intrinsic. + void BuildIntrinsic(); + size_t GetNumberOfBranches() const { return number_of_branches_; } HBasicBlock* GetBlockAt(uint32_t dex_pc) const { return branch_targets_[dex_pc]; } @@ -79,7 +83,7 @@ class HBasicBlockBuilder : public ValueObject { HGraph* const graph_; const DexFile* const dex_file_; - const DexFile::CodeItem& code_item_; + const DexFile::CodeItem* const code_item_; // null for intrinsic graph. ScopedArenaAllocator* const local_allocator_; ScopedArenaVector<HBasicBlock*> branch_targets_; diff --git a/compiler/optimizing/bounds_check_elimination.h b/compiler/optimizing/bounds_check_elimination.h index 6dc53207ea..79c67a8c7a 100644 --- a/compiler/optimizing/bounds_check_elimination.h +++ b/compiler/optimizing/bounds_check_elimination.h @@ -28,8 +28,9 @@ class BoundsCheckElimination : public HOptimization { public: BoundsCheckElimination(HGraph* graph, const SideEffectsAnalysis& side_effects, - HInductionVarAnalysis* induction_analysis) - : HOptimization(graph, kBoundsCheckEliminationPassName), + HInductionVarAnalysis* induction_analysis, + const char* name = kBoundsCheckEliminationPassName) + : HOptimization(graph, name), side_effects_(side_effects), induction_analysis_(induction_analysis) {} diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 4ed1612220..d73ef1f3a1 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -24,6 +24,7 @@ #include "data_type-inl.h" #include "dex/verified_method.h" #include "driver/compiler_options.h" +#include "driver/dex_compilation_unit.h" #include "instruction_builder.h" #include "mirror/class_loader.h" #include "mirror/dex_cache.h" @@ -36,6 +37,7 @@ namespace art { HGraphBuilder::HGraphBuilder(HGraph* graph, + const DexFile::CodeItem* code_item, const DexCompilationUnit* dex_compilation_unit, const DexCompilationUnit* outer_compilation_unit, CompilerDriver* driver, @@ -45,7 +47,7 @@ HGraphBuilder::HGraphBuilder(HGraph* graph, VariableSizedHandleScope* handles) : graph_(graph), dex_file_(&graph->GetDexFile()), - code_item_(*dex_compilation_unit->GetCodeItem()), + code_item_(code_item), dex_compilation_unit_(dex_compilation_unit), outer_compilation_unit_(outer_compilation_unit), compiler_driver_(driver), @@ -67,23 +69,21 @@ bool HGraphBuilder::SkipCompilation(size_t number_of_branches) { return false; } - if (compiler_options.IsHugeMethod(code_item_.insns_size_in_code_units_)) { + if (compiler_options.IsHugeMethod(code_item_->insns_size_in_code_units_)) { VLOG(compiler) << "Skip compilation of huge method " << dex_file_->PrettyMethod(dex_compilation_unit_->GetDexMethodIndex()) - << ": " << code_item_.insns_size_in_code_units_ << " code units"; - MaybeRecordStat(compilation_stats_, - MethodCompilationStat::kNotCompiledHugeMethod); + << ": " << code_item_->insns_size_in_code_units_ << " code units"; + MaybeRecordStat(compilation_stats_, MethodCompilationStat::kNotCompiledHugeMethod); return true; } // If it's large and contains no branches, it's likely to be machine generated initialization. - if (compiler_options.IsLargeMethod(code_item_.insns_size_in_code_units_) + if (compiler_options.IsLargeMethod(code_item_->insns_size_in_code_units_) && (number_of_branches == 0)) { VLOG(compiler) << "Skip compilation of large method with no branch " << dex_file_->PrettyMethod(dex_compilation_unit_->GetDexMethodIndex()) - << ": " << code_item_.insns_size_in_code_units_ << " code units"; - MaybeRecordStat(compilation_stats_, - MethodCompilationStat::kNotCompiledLargeMethodNoBranches); + << ": " << code_item_->insns_size_in_code_units_ << " code units"; + MaybeRecordStat(compilation_stats_, MethodCompilationStat::kNotCompiledLargeMethodNoBranches); return true; } @@ -91,12 +91,13 @@ bool HGraphBuilder::SkipCompilation(size_t number_of_branches) { } GraphAnalysisResult HGraphBuilder::BuildGraph() { + DCHECK(code_item_ != nullptr); DCHECK(graph_->GetBlocks().empty()); - graph_->SetNumberOfVRegs(code_item_.registers_size_); - graph_->SetNumberOfInVRegs(code_item_.ins_size_); - graph_->SetMaximumNumberOfOutVRegs(code_item_.outs_size_); - graph_->SetHasTryCatch(code_item_.tries_size_ != 0); + graph_->SetNumberOfVRegs(code_item_->registers_size_); + graph_->SetNumberOfInVRegs(code_item_->ins_size_); + graph_->SetMaximumNumberOfOutVRegs(code_item_->outs_size_); + graph_->SetHasTryCatch(code_item_->tries_size_ != 0); // Use ScopedArenaAllocator for all local allocations. ScopedArenaAllocator local_allocator(graph_->GetArenaStack()); @@ -148,4 +149,61 @@ GraphAnalysisResult HGraphBuilder::BuildGraph() { return ssa_builder.BuildSsa(); } +void HGraphBuilder::BuildIntrinsicGraph(ArtMethod* method) { + DCHECK(code_item_ == nullptr); + DCHECK(graph_->GetBlocks().empty()); + + // Determine the number of arguments and associated vregs. + uint32_t method_idx = dex_compilation_unit_->GetDexMethodIndex(); + const char* shorty = dex_file_->GetMethodShorty(dex_file_->GetMethodId(method_idx)); + size_t num_args = strlen(shorty + 1); + size_t num_wide_args = std::count(shorty + 1, shorty + 1 + num_args, 'J') + + std::count(shorty + 1, shorty + 1 + num_args, 'D'); + size_t num_arg_vregs = num_args + num_wide_args + (dex_compilation_unit_->IsStatic() ? 0u : 1u); + + // For simplicity, reserve 2 vregs (the maximum) for return value regardless of the return type. + size_t return_vregs = 2u; + graph_->SetNumberOfVRegs(return_vregs + num_arg_vregs); + graph_->SetNumberOfInVRegs(num_arg_vregs); + graph_->SetMaximumNumberOfOutVRegs(num_arg_vregs); + graph_->SetHasTryCatch(false); + + // Use ScopedArenaAllocator for all local allocations. + ScopedArenaAllocator local_allocator(graph_->GetArenaStack()); + HBasicBlockBuilder block_builder(graph_, dex_file_, /* code_item */ nullptr, &local_allocator); + SsaBuilder ssa_builder(graph_, + dex_compilation_unit_->GetClassLoader(), + dex_compilation_unit_->GetDexCache(), + handles_, + &local_allocator); + HInstructionBuilder instruction_builder(graph_, + &block_builder, + &ssa_builder, + dex_file_, + /* code_item */ nullptr, + return_type_, + dex_compilation_unit_, + outer_compilation_unit_, + compiler_driver_, + code_generator_, + interpreter_metadata_, + compilation_stats_, + handles_, + &local_allocator); + + // 1) Create basic blocks for the intrinsic and link them together. + block_builder.BuildIntrinsic(); + + // 2) Build the trivial dominator tree. + GraphAnalysisResult bdt_result = graph_->BuildDominatorTree(); + DCHECK_EQ(bdt_result, kAnalysisSuccess); + + // 3) Populate basic blocks with instructions for the intrinsic. + instruction_builder.BuildIntrinsic(method); + + // 4) Type the graph (no dead/redundant phis to eliminate). + GraphAnalysisResult build_ssa_result = ssa_builder.BuildSsa(); + DCHECK_EQ(build_ssa_result, kAnalysisSuccess); +} + } // namespace art diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 5a860f1e43..0bb3a051f7 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -21,17 +21,19 @@ #include "dex_file-inl.h" #include "dex_file.h" #include "driver/compiler_driver.h" -#include "driver/dex_compilation_unit.h" #include "nodes.h" namespace art { +class ArtMethod; class CodeGenerator; +class DexCompilationUnit; class OptimizingCompilerStats; class HGraphBuilder : public ValueObject { public: HGraphBuilder(HGraph* graph, + const DexFile::CodeItem* code_item, const DexCompilationUnit* dex_compilation_unit, const DexCompilationUnit* outer_compilation_unit, CompilerDriver* driver, @@ -47,8 +49,8 @@ class HGraphBuilder : public ValueObject { VariableSizedHandleScope* handles, DataType::Type return_type = DataType::Type::kInt32) : graph_(graph), - dex_file_(dex_compilation_unit->GetDexFile()), - code_item_(code_item), + dex_file_(&graph->GetDexFile()), + code_item_(&code_item), dex_compilation_unit_(dex_compilation_unit), outer_compilation_unit_(nullptr), compiler_driver_(nullptr), @@ -59,6 +61,7 @@ class HGraphBuilder : public ValueObject { return_type_(return_type) {} GraphAnalysisResult BuildGraph(); + void BuildIntrinsicGraph(ArtMethod* method); static constexpr const char* kBuilderPassName = "builder"; @@ -67,7 +70,7 @@ class HGraphBuilder : public ValueObject { HGraph* const graph_; const DexFile* const dex_file_; - const DexFile::CodeItem& code_item_; + const DexFile::CodeItem* const code_item_; // null for intrinsic graph. // The compilation unit of the current method being compiled. Note that // it can be an inlined method. diff --git a/compiler/optimizing/cha_guard_optimization.h b/compiler/optimizing/cha_guard_optimization.h index ba0cdb81fd..f14e07bd6c 100644 --- a/compiler/optimizing/cha_guard_optimization.h +++ b/compiler/optimizing/cha_guard_optimization.h @@ -26,8 +26,9 @@ namespace art { */ class CHAGuardOptimization : public HOptimization { public: - explicit CHAGuardOptimization(HGraph* graph) - : HOptimization(graph, kCHAGuardOptimizationPassName) {} + explicit CHAGuardOptimization(HGraph* graph, + const char* name = kCHAGuardOptimizationPassName) + : HOptimization(graph, name) {} void Run() OVERRIDE; diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 015a6a04d3..0bd3ce937a 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -946,12 +946,12 @@ static void CheckLoopEntriesCanBeUsedForOsr(const HGraph& graph, void CodeGenerator::BuildStackMaps(MemoryRegion stack_map_region, MemoryRegion method_info_region, - const DexFile::CodeItem& code_item) { + const DexFile::CodeItem* code_item_for_osr_check) { StackMapStream* stack_map_stream = GetStackMapStream(); stack_map_stream->FillInCodeInfo(stack_map_region); stack_map_stream->FillInMethodInfo(method_info_region); - if (kIsDebugBuild) { - CheckLoopEntriesCanBeUsedForOsr(*graph_, CodeInfo(stack_map_region), code_item); + if (kIsDebugBuild && code_item_for_osr_check != nullptr) { + CheckLoopEntriesCanBeUsedForOsr(*graph_, CodeInfo(stack_map_region), *code_item_for_osr_check); } } diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 18ad60db87..08e4462356 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -346,7 +346,7 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { void BuildStackMaps(MemoryRegion stack_map_region, MemoryRegion method_info_region, - const DexFile::CodeItem& code_item); + const DexFile::CodeItem* code_item_for_osr_check); void ComputeStackMapAndMethodInfoSize(size_t* stack_map_size, size_t* method_info_size); size_t GetNumberOfJitRoots() const; diff --git a/compiler/optimizing/code_sinking.h b/compiler/optimizing/code_sinking.h index 59cda52a8c..836d9d4f67 100644 --- a/compiler/optimizing/code_sinking.h +++ b/compiler/optimizing/code_sinking.h @@ -28,8 +28,10 @@ namespace art { */ class CodeSinking : public HOptimization { public: - CodeSinking(HGraph* graph, OptimizingCompilerStats* stats) - : HOptimization(graph, kCodeSinkingPassName, stats) {} + CodeSinking(HGraph* graph, + OptimizingCompilerStats* stats, + const char* name = kCodeSinkingPassName) + : HOptimization(graph, name, stats) {} void Run() OVERRIDE; diff --git a/compiler/optimizing/constructor_fence_redundancy_elimination.h b/compiler/optimizing/constructor_fence_redundancy_elimination.h index d89210cd1c..f4b06d5544 100644 --- a/compiler/optimizing/constructor_fence_redundancy_elimination.h +++ b/compiler/optimizing/constructor_fence_redundancy_elimination.h @@ -48,12 +48,13 @@ namespace art { class ConstructorFenceRedundancyElimination : public HOptimization { public: ConstructorFenceRedundancyElimination(HGraph* graph, - OptimizingCompilerStats* stats) - : HOptimization(graph, kPassName, stats) {} + OptimizingCompilerStats* stats, + const char* name = kCFREPassName) + : HOptimization(graph, name, stats) {} void Run() OVERRIDE; - static constexpr const char* kPassName = "constructor_fence_redundancy_elimination"; + static constexpr const char* kCFREPassName = "constructor_fence_redundancy_elimination"; private: DISALLOW_COPY_AND_ASSIGN(ConstructorFenceRedundancyElimination); diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index e2747afd85..ad29ba56ab 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -97,8 +97,8 @@ static DataType::Type ImplicitConversion(DataType::Type type) { // Class methods. // -HInductionVarAnalysis::HInductionVarAnalysis(HGraph* graph) - : HOptimization(graph, kInductionPassName), +HInductionVarAnalysis::HInductionVarAnalysis(HGraph* graph, const char* name) + : HOptimization(graph, name), global_depth_(0), stack_(graph->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)), map_(std::less<HInstruction*>(), diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h index a2d302ae81..8737b890d9 100644 --- a/compiler/optimizing/induction_var_analysis.h +++ b/compiler/optimizing/induction_var_analysis.h @@ -35,7 +35,7 @@ namespace art { */ class HInductionVarAnalysis : public HOptimization { public: - explicit HInductionVarAnalysis(HGraph* graph); + explicit HInductionVarAnalysis(HGraph* graph, const char* name = kInductionPassName); void Run() OVERRIDE; diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc index f87b46d4ee..4c11ad4643 100644 --- a/compiler/optimizing/induction_var_analysis_test.cc +++ b/compiler/optimizing/induction_var_analysis_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <regex> // NOLINT [build/c++11] [5] +#include <regex> #include "base/arena_allocator.h" #include "builder.h" diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 7adb196d14..3f4a3d8b8e 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -1667,6 +1667,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, } } HGraphBuilder builder(callee_graph, + code_item, &dex_compilation_unit, &outer_compilation_unit_, compiler_driver_, diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index c4b3a32d91..042eee3204 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -44,8 +44,9 @@ class HInliner : public HOptimization { size_t total_number_of_dex_registers, size_t total_number_of_instructions, HInliner* parent, - size_t depth = 0) - : HOptimization(outer_graph, kInlinerPassName, stats), + size_t depth = 0, + const char* name = kInlinerPassName) + : HOptimization(outer_graph, name, stats), outermost_graph_(outermost_graph), outer_compilation_unit_(outer_compilation_unit), caller_compilation_unit_(caller_compilation_unit), diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 8e9b818722..61840cc20f 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -272,6 +272,7 @@ static bool IsBlockPopulated(HBasicBlock* block) { } bool HInstructionBuilder::Build() { + DCHECK(code_item_ != nullptr); locals_for_.resize( graph_->GetBlocks().size(), ScopedArenaVector<HInstruction*>(local_allocator_->Adapter(kArenaAllocGraphBuilder))); @@ -321,7 +322,7 @@ bool HInstructionBuilder::Build() { quicken_index = block_builder_->GetQuickenIndex(block_dex_pc); } - for (const DexInstructionPcPair& pair : code_item_.Instructions(block_dex_pc)) { + for (const DexInstructionPcPair& pair : code_item_->Instructions(block_dex_pc)) { if (current_block_ == nullptr) { // The previous instruction ended this block. break; @@ -364,6 +365,73 @@ bool HInstructionBuilder::Build() { return true; } +void HInstructionBuilder::BuildIntrinsic(ArtMethod* method) { + DCHECK(code_item_ == nullptr); + DCHECK(method->IsIntrinsic()); + + locals_for_.resize( + graph_->GetBlocks().size(), + ScopedArenaVector<HInstruction*>(local_allocator_->Adapter(kArenaAllocGraphBuilder))); + + // Fill the entry block. Do not add suspend check, we do not want a suspend + // check in intrinsics; intrinsic methods are supposed to be fast. + current_block_ = graph_->GetEntryBlock(); + InitializeBlockLocals(); + InitializeParameters(); + AppendInstruction(new (allocator_) HGoto(0u)); + + // Fill the body. + current_block_ = current_block_->GetSingleSuccessor(); + InitializeBlockLocals(); + DCHECK(!IsBlockPopulated(current_block_)); + + // Add the invoke and return instruction. Use HInvokeStaticOrDirect even + // for methods that would normally use an HInvokeVirtual (sharpen the call). + size_t in_vregs = graph_->GetNumberOfInVRegs(); + size_t number_of_arguments = + in_vregs - std::count(current_locals_->end() - in_vregs, current_locals_->end(), nullptr); + uint32_t method_idx = dex_compilation_unit_->GetDexMethodIndex(); + MethodReference target_method(dex_file_, method_idx); + HInvokeStaticOrDirect::DispatchInfo dispatch_info = { + HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall, + HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, + /* method_load_data */ 0u + }; + InvokeType invoke_type = dex_compilation_unit_->IsStatic() ? kStatic : kDirect; + HInvokeStaticOrDirect* invoke = new (allocator_) HInvokeStaticOrDirect( + allocator_, + number_of_arguments, + return_type_, + kNoDexPc, + method_idx, + method, + dispatch_info, + invoke_type, + target_method, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HandleInvoke(invoke, + in_vregs, + /* args */ nullptr, + graph_->GetNumberOfVRegs() - in_vregs, + /* is_range */ true, + dex_file_->GetMethodShorty(method_idx), + /* clinit_check */ nullptr, + /* is_unresolved */ false); + + // Add the return instruction. + if (return_type_ == DataType::Type::kVoid) { + AppendInstruction(new (allocator_) HReturnVoid()); + } else { + AppendInstruction(new (allocator_) HReturn(invoke)); + } + + // Fill the exit block. + DCHECK_EQ(current_block_->GetSingleSuccessor(), graph_->GetExitBlock()); + current_block_ = graph_->GetExitBlock(); + InitializeBlockLocals(); + AppendInstruction(new (allocator_) HExit()); +} + ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() { // The callback gets called when the line number changes. // In other words, it marks the start of new java statement. @@ -373,15 +441,15 @@ ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() { return false; } }; - const uint32_t num_instructions = code_item_.insns_size_in_code_units_; + const uint32_t num_instructions = code_item_->insns_size_in_code_units_; ArenaBitVector* locations = ArenaBitVector::Create(local_allocator_, num_instructions, /* expandable */ false, kArenaAllocGraphBuilder); locations->ClearAllBits(); - dex_file_->DecodeDebugPositionInfo(&code_item_, Callback::Position, locations); + dex_file_->DecodeDebugPositionInfo(code_item_, Callback::Position, locations); // Instruction-specific tweaks. - IterationRange<DexInstructionIterator> instructions = code_item_.Instructions(); + IterationRange<DexInstructionIterator> instructions = code_item_->Instructions(); for (const DexInstructionPcPair& inst : instructions) { switch (inst->Opcode()) { case Instruction::MOVE_EXCEPTION: { @@ -1641,7 +1709,7 @@ void HInstructionBuilder::BuildFillArrayData(const Instruction& instruction, uin int32_t payload_offset = instruction.VRegB_31t() + dex_pc; const Instruction::ArrayDataPayload* payload = - reinterpret_cast<const Instruction::ArrayDataPayload*>(code_item_.insns_ + payload_offset); + reinterpret_cast<const Instruction::ArrayDataPayload*>(code_item_->insns_ + payload_offset); const uint8_t* data = payload->data; uint32_t element_count = payload->element_count; diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 058b711687..f551ac4280 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -50,7 +50,7 @@ class HInstructionBuilder : public ValueObject { HBasicBlockBuilder* block_builder, SsaBuilder* ssa_builder, const DexFile* dex_file, - const DexFile::CodeItem& code_item, + const DexFile::CodeItem* code_item, DataType::Type return_type, const DexCompilationUnit* dex_compilation_unit, const DexCompilationUnit* outer_compilation_unit, @@ -85,6 +85,7 @@ class HInstructionBuilder : public ValueObject { } bool Build(); + void BuildIntrinsic(ArtMethod* method); private: void InitializeBlockLocals(); @@ -327,7 +328,7 @@ class HInstructionBuilder : public ValueObject { // The dex file where the method being compiled is, and the bytecode data. const DexFile* const dex_file_; - const DexFile::CodeItem& code_item_; + const DexFile::CodeItem* const code_item_; // null for intrinsic graph. // The return type of the method being compiled. const DataType::Type return_type_; diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 707ff3408e..818d7f63a3 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -37,8 +37,10 @@ static constexpr uint64_t kNanDouble = 0x7ff8000000000000; // Recognize intrinsics from HInvoke nodes. class IntrinsicsRecognizer : public HOptimization { public: - IntrinsicsRecognizer(HGraph* graph, OptimizingCompilerStats* stats) - : HOptimization(graph, kIntrinsicsRecognizerPassName, stats) {} + IntrinsicsRecognizer(HGraph* graph, + OptimizingCompilerStats* stats, + const char* name = kIntrinsicsRecognizerPassName) + : HOptimization(graph, name, stats) {} void Run() OVERRIDE; diff --git a/compiler/optimizing/licm.h b/compiler/optimizing/licm.h index bf56f53d46..ee567aeb20 100644 --- a/compiler/optimizing/licm.h +++ b/compiler/optimizing/licm.h @@ -26,8 +26,11 @@ class SideEffectsAnalysis; class LICM : public HOptimization { public: - LICM(HGraph* graph, const SideEffectsAnalysis& side_effects, OptimizingCompilerStats* stats) - : HOptimization(graph, kLoopInvariantCodeMotionPassName, stats), + LICM(HGraph* graph, + const SideEffectsAnalysis& side_effects, + OptimizingCompilerStats* stats, + const char* name = kLoopInvariantCodeMotionPassName) + : HOptimization(graph, name, stats), side_effects_(side_effects) {} void Run() OVERRIDE; diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h index aa8b5bbdc9..437e6be418 100644 --- a/compiler/optimizing/load_store_analysis.h +++ b/compiler/optimizing/load_store_analysis.h @@ -564,8 +564,8 @@ class HeapLocationCollector : public HGraphVisitor { class LoadStoreAnalysis : public HOptimization { public: - explicit LoadStoreAnalysis(HGraph* graph) - : HOptimization(graph, kLoadStoreAnalysisPassName), + explicit LoadStoreAnalysis(HGraph* graph, const char* name = kLoadStoreAnalysisPassName) + : HOptimization(graph, name), heap_location_collector_(graph) {} const HeapLocationCollector& GetHeapLocationCollector() const { diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 605fdae0f8..89ad85e0b4 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -199,6 +199,12 @@ class LSEVisitor : public HGraphDelegateVisitor { if (predecessors.size() == 0) { return; } + if (block->IsExitBlock()) { + // Exit block doesn't really merge values since the control flow ends in + // its predecessors. Each predecessor needs to make sure stores are kept + // if necessary. + return; + } ScopedArenaVector<HInstruction*>& heap_values = heap_values_for_[block->GetBlockId()]; for (size_t i = 0; i < heap_values.size(); i++) { @@ -233,15 +239,23 @@ class LSEVisitor : public HGraphDelegateVisitor { } } - if (merged_value == kUnknownHeapValue || ref_info->IsSingletonAndNonRemovable()) { - // There are conflicting heap values from different predecessors, - // or the heap value may be needed after method return or deoptimization. - // Keep the last store in each predecessor since future loads cannot be eliminated. - for (HBasicBlock* predecessor : predecessors) { - ScopedArenaVector<HInstruction*>& pred_values = - heap_values_for_[predecessor->GetBlockId()]; - KeepIfIsStore(pred_values[i]); + if (ref_info->IsSingleton()) { + if (ref_info->IsSingletonAndNonRemovable() || + (merged_value == kUnknownHeapValue && + !block->IsSingleReturnOrReturnVoidAllowingPhis())) { + // The heap value may be needed after method return or deoptimization, + // or there are conflicting heap values from different predecessors and + // this block is not a single return, + // keep the last store in each predecessor since future loads may not + // be eliminated. + for (HBasicBlock* predecessor : predecessors) { + ScopedArenaVector<HInstruction*>& pred_values = + heap_values_for_[predecessor->GetBlockId()]; + KeepIfIsStore(pred_values[i]); + } } + } else { + // Currenctly we don't eliminate stores to non-singletons. } if ((merged_value == nullptr) || !from_all_predecessors) { @@ -549,6 +563,31 @@ class LSEVisitor : public HGraphDelegateVisitor { } } + // Keep necessary stores before exiting a method via return/throw. + void HandleExit(HBasicBlock* block) { + const ScopedArenaVector<HInstruction*>& heap_values = + heap_values_for_[block->GetBlockId()]; + for (size_t i = 0; i < heap_values.size(); i++) { + HInstruction* heap_value = heap_values[i]; + ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo(); + if (!ref_info->IsSingletonAndRemovable()) { + KeepIfIsStore(heap_value); + } + } + } + + void VisitReturn(HReturn* instruction) OVERRIDE { + HandleExit(instruction->GetBlock()); + } + + void VisitReturnVoid(HReturnVoid* return_void) OVERRIDE { + HandleExit(return_void->GetBlock()); + } + + void VisitThrow(HThrow* throw_instruction) OVERRIDE { + HandleExit(throw_instruction->GetBlock()); + } + void HandleInvoke(HInstruction* instruction) { SideEffects side_effects = instruction->GetSideEffects(); ScopedArenaVector<HInstruction*>& heap_values = diff --git a/compiler/optimizing/load_store_elimination.h b/compiler/optimizing/load_store_elimination.h index 20a8a769c0..7153541baf 100644 --- a/compiler/optimizing/load_store_elimination.h +++ b/compiler/optimizing/load_store_elimination.h @@ -29,8 +29,9 @@ class LoadStoreElimination : public HOptimization { LoadStoreElimination(HGraph* graph, const SideEffectsAnalysis& side_effects, const LoadStoreAnalysis& lsa, - OptimizingCompilerStats* stats) - : HOptimization(graph, kLoadStoreEliminationPassName, stats), + OptimizingCompilerStats* stats, + const char* name = kLoadStoreEliminationPassName) + : HOptimization(graph, name, stats), side_effects_(side_effects), lsa_(lsa) {} diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index fcc59ea3f9..1ca096035e 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -448,8 +448,9 @@ static bool CheckInductionSetFullyRemoved(ScopedArenaSet<HInstruction*>* iset) { HLoopOptimization::HLoopOptimization(HGraph* graph, CompilerDriver* compiler_driver, HInductionVarAnalysis* induction_analysis, - OptimizingCompilerStats* stats) - : HOptimization(graph, kLoopOptimizationPassName, stats), + OptimizingCompilerStats* stats, + const char* name) + : HOptimization(graph, name, stats), compiler_driver_(compiler_driver), induction_range_(induction_analysis), loop_allocator_(nullptr), diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h index 51e0a986b8..a707ad1358 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -37,7 +37,8 @@ class HLoopOptimization : public HOptimization { HLoopOptimization(HGraph* graph, CompilerDriver* compiler_driver, HInductionVarAnalysis* induction_analysis, - OptimizingCompilerStats* stats); + OptimizingCompilerStats* stats, + const char* name = kLoopOptimizationPassName); void Run() OVERRIDE; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index f4f6434678..fff61f5727 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1810,6 +1810,11 @@ bool HBasicBlock::IsSingleReturn() const { return HasOnlyOneInstruction(*this) && GetLastInstruction()->IsReturn(); } +bool HBasicBlock::IsSingleReturnOrReturnVoidAllowingPhis() const { + return (GetFirstInstruction() == GetLastInstruction()) && + (GetLastInstruction()->IsReturn() || GetLastInstruction()->IsReturnVoid()); +} + bool HBasicBlock::IsSingleTryBoundary() const { return HasOnlyOneInstruction(*this) && GetLastInstruction()->IsTryBoundary(); } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 29c78a1e34..6672901781 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -968,6 +968,7 @@ class HBasicBlock : public ArenaObject<kArenaAllocBasicBlock> { bool IsSingleGoto() const; bool IsSingleReturn() const; + bool IsSingleReturnOrReturnVoidAllowingPhis() const; bool IsSingleTryBoundary() const; // Returns true if this block emits nothing but a jump. diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc index 1e68ca2802..7edb642c5b 100644 --- a/compiler/optimizing/optimization.cc +++ b/compiler/optimizing/optimization.cc @@ -16,5 +16,317 @@ #include "optimization.h" +#ifdef ART_ENABLE_CODEGEN_arm +#include "instruction_simplifier_arm.h" +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 +#include "instruction_simplifier_arm64.h" +#endif +#ifdef ART_ENABLE_CODEGEN_mips +#include "instruction_simplifier_mips.h" +#include "pc_relative_fixups_mips.h" +#endif +#ifdef ART_ENABLE_CODEGEN_x86 +#include "pc_relative_fixups_x86.h" +#endif +#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) +#include "x86_memory_gen.h" +#endif + +#include "bounds_check_elimination.h" +#include "cha_guard_optimization.h" +#include "code_sinking.h" +#include "constant_folding.h" +#include "constructor_fence_redundancy_elimination.h" +#include "dead_code_elimination.h" +#include "driver/dex_compilation_unit.h" +#include "gvn.h" +#include "induction_var_analysis.h" +#include "inliner.h" +#include "instruction_simplifier.h" +#include "intrinsics.h" +#include "licm.h" +#include "load_store_analysis.h" +#include "load_store_elimination.h" +#include "loop_optimization.h" +#include "scheduler.h" +#include "select_generator.h" +#include "sharpening.h" +#include "side_effects_analysis.h" + +// Decide between default or alternative pass name. + namespace art { + +const char* OptimizationPassName(OptimizationPass pass) { + switch (pass) { + case OptimizationPass::kSideEffectsAnalysis: + return SideEffectsAnalysis::kSideEffectsAnalysisPassName; + case OptimizationPass::kInductionVarAnalysis: + return HInductionVarAnalysis::kInductionPassName; + case OptimizationPass::kLoadStoreAnalysis: + return LoadStoreAnalysis::kLoadStoreAnalysisPassName; + case OptimizationPass::kGlobalValueNumbering: + return GVNOptimization::kGlobalValueNumberingPassName; + case OptimizationPass::kInvariantCodeMotion: + return LICM::kLoopInvariantCodeMotionPassName; + case OptimizationPass::kLoopOptimization: + return HLoopOptimization::kLoopOptimizationPassName; + case OptimizationPass::kBoundsCheckElimination: + return BoundsCheckElimination::kBoundsCheckEliminationPassName; + case OptimizationPass::kLoadStoreElimination: + return LoadStoreElimination::kLoadStoreEliminationPassName; + case OptimizationPass::kConstantFolding: + return HConstantFolding::kConstantFoldingPassName; + case OptimizationPass::kDeadCodeElimination: + return HDeadCodeElimination::kDeadCodeEliminationPassName; + case OptimizationPass::kInliner: + return HInliner::kInlinerPassName; + case OptimizationPass::kSharpening: + return HSharpening::kSharpeningPassName; + case OptimizationPass::kSelectGenerator: + return HSelectGenerator::kSelectGeneratorPassName; + case OptimizationPass::kInstructionSimplifier: + return InstructionSimplifier::kInstructionSimplifierPassName; + case OptimizationPass::kIntrinsicsRecognizer: + return IntrinsicsRecognizer::kIntrinsicsRecognizerPassName; + case OptimizationPass::kCHAGuardOptimization: + return CHAGuardOptimization::kCHAGuardOptimizationPassName; + case OptimizationPass::kCodeSinking: + return CodeSinking::kCodeSinkingPassName; + case OptimizationPass::kConstructorFenceRedundancyElimination: + return ConstructorFenceRedundancyElimination::kCFREPassName; + case OptimizationPass::kScheduling: + return HInstructionScheduling::kInstructionSchedulingPassName; +#ifdef ART_ENABLE_CODEGEN_arm + case OptimizationPass::kInstructionSimplifierArm: + return arm::InstructionSimplifierArm::kInstructionSimplifierArmPassName; +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 + case OptimizationPass::kInstructionSimplifierArm64: + return arm64::InstructionSimplifierArm64::kInstructionSimplifierArm64PassName; +#endif +#ifdef ART_ENABLE_CODEGEN_mips + case OptimizationPass::kPcRelativeFixupsMips: + return mips::PcRelativeFixups::kPcRelativeFixupsMipsPassName; + case OptimizationPass::kInstructionSimplifierMips: + return mips::InstructionSimplifierMips::kInstructionSimplifierMipsPassName; +#endif +#ifdef ART_ENABLE_CODEGEN_x86 + case OptimizationPass::kPcRelativeFixupsX86: + return x86::PcRelativeFixups::kPcRelativeFixupsX86PassName; +#endif +#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) + case OptimizationPass::kX86MemoryOperandGeneration: + return x86::X86MemoryOperandGeneration::kX86MemoryOperandGenerationPassName; +#endif + } +} + +#define X(x) if (name == OptimizationPassName((x))) return (x) + +OptimizationPass OptimizationPassByName(const std::string& name) { + X(OptimizationPass::kBoundsCheckElimination); + X(OptimizationPass::kCHAGuardOptimization); + X(OptimizationPass::kCodeSinking); + X(OptimizationPass::kConstantFolding); + X(OptimizationPass::kConstructorFenceRedundancyElimination); + X(OptimizationPass::kDeadCodeElimination); + X(OptimizationPass::kGlobalValueNumbering); + X(OptimizationPass::kInductionVarAnalysis); + X(OptimizationPass::kInliner); + X(OptimizationPass::kInstructionSimplifier); + X(OptimizationPass::kIntrinsicsRecognizer); + X(OptimizationPass::kInvariantCodeMotion); + X(OptimizationPass::kLoadStoreAnalysis); + X(OptimizationPass::kLoadStoreElimination); + X(OptimizationPass::kLoopOptimization); + X(OptimizationPass::kScheduling); + X(OptimizationPass::kSelectGenerator); + X(OptimizationPass::kSharpening); + X(OptimizationPass::kSideEffectsAnalysis); +#ifdef ART_ENABLE_CODEGEN_arm + X(OptimizationPass::kInstructionSimplifierArm); +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 + X(OptimizationPass::kInstructionSimplifierArm64); +#endif +#ifdef ART_ENABLE_CODEGEN_mips + X(OptimizationPass::kPcRelativeFixupsMips); + X(OptimizationPass::kInstructionSimplifierMips); +#endif +#ifdef ART_ENABLE_CODEGEN_x86 + X(OptimizationPass::kPcRelativeFixupsX86); + X(OptimizationPass::kX86MemoryOperandGeneration); +#endif + LOG(FATAL) << "Cannot find optimization " << name; + UNREACHABLE(); +} + +#undef X + +ArenaVector<HOptimization*> ConstructOptimizations( + const OptimizationDef definitions[], + size_t length, + ArenaAllocator* allocator, + HGraph* graph, + OptimizingCompilerStats* stats, + CodeGenerator* codegen, + CompilerDriver* driver, + const DexCompilationUnit& dex_compilation_unit, + VariableSizedHandleScope* handles) { + ArenaVector<HOptimization*> optimizations(allocator->Adapter()); + + // Some optimizations require SideEffectsAnalysis or HInductionVarAnalysis + // instances. This method uses the nearest instance preceeding it in the pass + // name list or fails fatally if no such analysis can be found. + SideEffectsAnalysis* most_recent_side_effects = nullptr; + HInductionVarAnalysis* most_recent_induction = nullptr; + LoadStoreAnalysis* most_recent_lsa = nullptr; + + // Loop over the requested optimizations. + for (size_t i = 0; i < length; i++) { + OptimizationPass pass = definitions[i].first; + const char* alt_name = definitions[i].second; + const char* name = alt_name != nullptr + ? alt_name + : OptimizationPassName(pass); + HOptimization* opt = nullptr; + + switch (pass) { + // + // Analysis passes (kept in most recent for subsequent passes). + // + case OptimizationPass::kSideEffectsAnalysis: + opt = most_recent_side_effects = new (allocator) SideEffectsAnalysis(graph, name); + break; + case OptimizationPass::kInductionVarAnalysis: + opt = most_recent_induction = new (allocator) HInductionVarAnalysis(graph, name); + break; + case OptimizationPass::kLoadStoreAnalysis: + opt = most_recent_lsa = new (allocator) LoadStoreAnalysis(graph, name); + break; + // + // Passes that need prior analysis. + // + case OptimizationPass::kGlobalValueNumbering: + CHECK(most_recent_side_effects != nullptr); + opt = new (allocator) GVNOptimization(graph, *most_recent_side_effects, name); + break; + case OptimizationPass::kInvariantCodeMotion: + CHECK(most_recent_side_effects != nullptr); + opt = new (allocator) LICM(graph, *most_recent_side_effects, stats, name); + break; + case OptimizationPass::kLoopOptimization: + CHECK(most_recent_induction != nullptr); + opt = new (allocator) HLoopOptimization(graph, driver, most_recent_induction, stats, name); + break; + case OptimizationPass::kBoundsCheckElimination: + CHECK(most_recent_side_effects != nullptr && most_recent_induction != nullptr); + opt = new (allocator) BoundsCheckElimination( + graph, *most_recent_side_effects, most_recent_induction, name); + break; + case OptimizationPass::kLoadStoreElimination: + CHECK(most_recent_side_effects != nullptr && most_recent_induction != nullptr); + opt = new (allocator) LoadStoreElimination( + graph, *most_recent_side_effects, *most_recent_lsa, stats, name); + break; + // + // Regular passes. + // + case OptimizationPass::kConstantFolding: + opt = new (allocator) HConstantFolding(graph, name); + break; + case OptimizationPass::kDeadCodeElimination: + opt = new (allocator) HDeadCodeElimination(graph, stats, name); + break; + case OptimizationPass::kInliner: { + size_t number_of_dex_registers = dex_compilation_unit.GetCodeItem()->registers_size_; + opt = new (allocator) HInliner(graph, // outer_graph + graph, // outermost_graph + codegen, + dex_compilation_unit, // outer_compilation_unit + dex_compilation_unit, // outermost_compilation_unit + driver, + handles, + stats, + number_of_dex_registers, + /* total_number_of_instructions */ 0, + /* parent */ nullptr, + /* depth */ 0, + name); + break; + } + case OptimizationPass::kSharpening: + opt = new (allocator) HSharpening( + graph, codegen, dex_compilation_unit, driver, handles, name); + break; + case OptimizationPass::kSelectGenerator: + opt = new (allocator) HSelectGenerator(graph, handles, stats, name); + break; + case OptimizationPass::kInstructionSimplifier: + opt = new (allocator) InstructionSimplifier(graph, codegen, driver, stats, name); + break; + case OptimizationPass::kIntrinsicsRecognizer: + opt = new (allocator) IntrinsicsRecognizer(graph, stats, name); + break; + case OptimizationPass::kCHAGuardOptimization: + opt = new (allocator) CHAGuardOptimization(graph, name); + break; + case OptimizationPass::kCodeSinking: + opt = new (allocator) CodeSinking(graph, stats, name); + break; + case OptimizationPass::kConstructorFenceRedundancyElimination: + opt = new (allocator) ConstructorFenceRedundancyElimination(graph, stats, name); + break; + case OptimizationPass::kScheduling: + opt = new (allocator) HInstructionScheduling( + graph, driver->GetInstructionSet(), codegen, name); + break; + // + // Arch-specific passes. + // +#ifdef ART_ENABLE_CODEGEN_arm + case OptimizationPass::kInstructionSimplifierArm: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) arm::InstructionSimplifierArm(graph, stats); + break; +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 + case OptimizationPass::kInstructionSimplifierArm64: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) arm64::InstructionSimplifierArm64(graph, stats); + break; +#endif +#ifdef ART_ENABLE_CODEGEN_mips + case OptimizationPass::kPcRelativeFixupsMips: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) mips::PcRelativeFixups(graph, codegen, stats); + break; + case OptimizationPass::kInstructionSimplifierMips: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) mips::InstructionSimplifierMips(graph, codegen, stats); + break; +#endif +#ifdef ART_ENABLE_CODEGEN_x86 + case OptimizationPass::kPcRelativeFixupsX86: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) x86::PcRelativeFixups(graph, codegen, stats); + break; + case OptimizationPass::kX86MemoryOperandGeneration: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats); + break; +#endif + } // switch + + // Add each next optimization to result vector. + CHECK(opt != nullptr); + DCHECK_STREQ(name, opt->GetPassName()); // sanity + optimizations.push_back(opt); + } + + return optimizations; +} + } // namespace art diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h index ce41a2e512..c170f155fa 100644 --- a/compiler/optimizing/optimization.h +++ b/compiler/optimizing/optimization.h @@ -23,6 +23,10 @@ namespace art { +class CodeGenerator; +class CompilerDriver; +class DexCompilationUnit; + /** * Abstraction to implement an optimization pass. */ @@ -58,6 +62,81 @@ class HOptimization : public ArenaObject<kArenaAllocOptimization> { DISALLOW_COPY_AND_ASSIGN(HOptimization); }; +// Optimization passes that can be constructed by the helper method below. An enum +// field is preferred over a string lookup at places where performance matters. +// TODO: generate this table and lookup methods below automatically? +enum class OptimizationPass { + kBoundsCheckElimination, + kCHAGuardOptimization, + kCodeSinking, + kConstantFolding, + kConstructorFenceRedundancyElimination, + kDeadCodeElimination, + kGlobalValueNumbering, + kInductionVarAnalysis, + kInliner, + kInstructionSimplifier, + kIntrinsicsRecognizer, + kInvariantCodeMotion, + kLoadStoreAnalysis, + kLoadStoreElimination, + kLoopOptimization, + kScheduling, + kSelectGenerator, + kSharpening, + kSideEffectsAnalysis, +#ifdef ART_ENABLE_CODEGEN_arm + kInstructionSimplifierArm, +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 + kInstructionSimplifierArm64, +#endif +#ifdef ART_ENABLE_CODEGEN_mips + kPcRelativeFixupsMips, + kInstructionSimplifierMips, +#endif +#ifdef ART_ENABLE_CODEGEN_x86 + kPcRelativeFixupsX86, +#endif +#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) + kX86MemoryOperandGeneration, +#endif +}; + +// Lookup name of optimization pass. +const char* OptimizationPassName(OptimizationPass pass); + +// Lookup optimization pass by name. +OptimizationPass OptimizationPassByName(const std::string& name); + +// Optimization definition consisting of an optimization pass +// and an optional alternative name (nullptr denotes default). +typedef std::pair<OptimizationPass, const char*> OptimizationDef; + +// Helper method for optimization definition array entries. +inline OptimizationDef OptDef(OptimizationPass pass, const char* name = nullptr) { + return std::make_pair(pass, name); +} + +// Helper method to construct series of optimization passes. +// The array should consist of the requested optimizations +// and optional alternative names for repeated passes. +// Example: +// { OptPass(kConstantFolding), +// OptPass(Inliner), +// OptPass(kConstantFolding, "constant_folding$after_inlining") +// } +ArenaVector<HOptimization*> ConstructOptimizations( + const OptimizationDef definitions[], + size_t length, + ArenaAllocator* allocator, + HGraph* graph, + OptimizingCompilerStats* stats, + CodeGenerator* codegen, + CompilerDriver* driver, + const DexCompilationUnit& dex_compilation_unit, + VariableSizedHandleScope* handles); + } // namespace art #endif // ART_COMPILER_OPTIMIZING_OPTIMIZATION_H_ diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 2bba985c34..53f9ec413b 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -22,23 +22,6 @@ #include <stdint.h> -#ifdef ART_ENABLE_CODEGEN_arm64 -#include "instruction_simplifier_arm64.h" -#endif - -#ifdef ART_ENABLE_CODEGEN_mips -#include "instruction_simplifier_mips.h" -#include "pc_relative_fixups_mips.h" -#endif - -#ifdef ART_ENABLE_CODEGEN_x86 -#include "pc_relative_fixups_x86.h" -#endif - -#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) -#include "x86_memory_gen.h" -#endif - #include "art_method-inl.h" #include "base/arena_allocator.h" #include "base/arena_containers.h" @@ -47,16 +30,10 @@ #include "base/mutex.h" #include "base/scoped_arena_allocator.h" #include "base/timing_logger.h" -#include "bounds_check_elimination.h" #include "builder.h" -#include "cha_guard_optimization.h" #include "code_generator.h" -#include "code_sinking.h" #include "compiled_method.h" #include "compiler.h" -#include "constant_folding.h" -#include "constructor_fence_redundancy_elimination.h" -#include "dead_code_elimination.h" #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" #include "dex/verification_results.h" @@ -67,31 +44,19 @@ #include "driver/dex_compilation_unit.h" #include "graph_checker.h" #include "graph_visualizer.h" -#include "gvn.h" -#include "induction_var_analysis.h" #include "inliner.h" -#include "instruction_simplifier.h" -#include "instruction_simplifier_arm.h" -#include "intrinsics.h" #include "jit/debugger_interface.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "jit/jit_logger.h" #include "jni/quick/jni_compiler.h" -#include "licm.h" #include "linker/linker_patch.h" -#include "load_store_analysis.h" -#include "load_store_elimination.h" -#include "loop_optimization.h" #include "nodes.h" #include "oat_quick_method_header.h" #include "prepare_for_register_allocation.h" #include "reference_type_propagation.h" #include "register_allocator_linear_scan.h" -#include "scheduler.h" #include "select_generator.h" -#include "sharpening.h" -#include "side_effects_analysis.h" #include "ssa_builder.h" #include "ssa_liveness_analysis.h" #include "ssa_phi_elimination.h" @@ -311,12 +276,8 @@ class OptimizingCompiler FINAL : public Compiler { CompiledMethod* JniCompile(uint32_t access_flags, uint32_t method_idx, - const DexFile& dex_file) const OVERRIDE { - return ArtQuickJniCompileMethod(GetCompilerDriver(), - access_flags, - method_idx, - dex_file); - } + const DexFile& dex_file, + Handle<mirror::DexCache> dex_cache) const OVERRIDE; uintptr_t GetEntryPointOf(ArtMethod* method) const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { @@ -339,21 +300,52 @@ class OptimizingCompiler FINAL : public Compiler { private: void RunOptimizations(HGraph* graph, CodeGenerator* codegen, - CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, - VariableSizedHandleScope* handles) const; + VariableSizedHandleScope* handles, + const OptimizationDef definitions[], + size_t length) const { + // Convert definitions to optimization passes. + ArenaVector<HOptimization*> optimizations = ConstructOptimizations( + definitions, + length, + graph->GetAllocator(), + graph, + compilation_stats_.get(), + codegen, + GetCompilerDriver(), + dex_compilation_unit, + handles); + DCHECK_EQ(length, optimizations.size()); + // Run the optimization passes one by one. + for (size_t i = 0; i < length; ++i) { + PassScope scope(optimizations[i]->GetPassName(), pass_observer); + optimizations[i]->Run(); + } + } + + template <size_t length> void RunOptimizations( + HGraph* graph, + CodeGenerator* codegen, + const DexCompilationUnit& dex_compilation_unit, + PassObserver* pass_observer, + VariableSizedHandleScope* handles, + const OptimizationDef (&definitions)[length]) const { + RunOptimizations( + graph, codegen, dex_compilation_unit, pass_observer, handles, definitions, length); + } - void RunOptimizations(HOptimization* optimizations[], - size_t length, - PassObserver* pass_observer) const; + void RunOptimizations(HGraph* graph, + CodeGenerator* codegen, + const DexCompilationUnit& dex_compilation_unit, + PassObserver* pass_observer, + VariableSizedHandleScope* handles) const; private: // Create a 'CompiledMethod' for an optimized graph. CompiledMethod* Emit(ArenaAllocator* allocator, CodeVectorAllocator* code_allocator, CodeGenerator* codegen, - CompilerDriver* driver, const DexFile::CodeItem* item) const; // Try compiling a method and return the code generator used for @@ -366,29 +358,29 @@ class OptimizingCompiler FINAL : public Compiler { CodeGenerator* TryCompile(ArenaAllocator* allocator, ArenaStack* arena_stack, CodeVectorAllocator* code_allocator, - const DexFile::CodeItem* code_item, - uint32_t access_flags, - InvokeType invoke_type, - uint16_t class_def_idx, - uint32_t method_idx, - Handle<mirror::ClassLoader> class_loader, - const DexFile& dex_file, - Handle<mirror::DexCache> dex_cache, + const DexCompilationUnit& dex_compilation_unit, ArtMethod* method, bool osr, VariableSizedHandleScope* handles) const; + CodeGenerator* TryCompileIntrinsic(ArenaAllocator* allocator, + ArenaStack* arena_stack, + CodeVectorAllocator* code_allocator, + const DexCompilationUnit& dex_compilation_unit, + ArtMethod* method, + VariableSizedHandleScope* handles) const; + void MaybeRunInliner(HGraph* graph, CodeGenerator* codegen, - CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, VariableSizedHandleScope* handles) const; - void RunArchOptimizations(InstructionSet instruction_set, - HGraph* graph, + void RunArchOptimizations(HGraph* graph, CodeGenerator* codegen, - PassObserver* pass_observer) const; + const DexCompilationUnit& dex_compilation_unit, + PassObserver* pass_observer, + VariableSizedHandleScope* handles) const; std::unique_ptr<OptimizingCompilerStats> compilation_stats_; @@ -444,299 +436,130 @@ static bool IsInstructionSetSupported(InstructionSet instruction_set) { || instruction_set == InstructionSet::kX86_64; } -// Strip pass name suffix to get optimization name. -static std::string ConvertPassNameToOptimizationName(const std::string& pass_name) { - size_t pos = pass_name.find(kPassNameSeparator); - return pos == std::string::npos ? pass_name : pass_name.substr(0, pos); -} - -static HOptimization* BuildOptimization( - const std::string& pass_name, - ArenaAllocator* allocator, - HGraph* graph, - OptimizingCompilerStats* stats, - CodeGenerator* codegen, - CompilerDriver* driver, - const DexCompilationUnit& dex_compilation_unit, - VariableSizedHandleScope* handles, - SideEffectsAnalysis* most_recent_side_effects, - HInductionVarAnalysis* most_recent_induction, - LoadStoreAnalysis* most_recent_lsa) { - std::string opt_name = ConvertPassNameToOptimizationName(pass_name); - if (opt_name == BoundsCheckElimination::kBoundsCheckEliminationPassName) { - CHECK(most_recent_side_effects != nullptr && most_recent_induction != nullptr); - return new (allocator) BoundsCheckElimination(graph, - *most_recent_side_effects, - most_recent_induction); - } else if (opt_name == GVNOptimization::kGlobalValueNumberingPassName) { - CHECK(most_recent_side_effects != nullptr); - return new (allocator) GVNOptimization(graph, *most_recent_side_effects, pass_name.c_str()); - } else if (opt_name == HConstantFolding::kConstantFoldingPassName) { - return new (allocator) HConstantFolding(graph, pass_name.c_str()); - } else if (opt_name == HDeadCodeElimination::kDeadCodeEliminationPassName) { - return new (allocator) HDeadCodeElimination(graph, stats, pass_name.c_str()); - } else if (opt_name == HInliner::kInlinerPassName) { - size_t number_of_dex_registers = dex_compilation_unit.GetCodeItem()->registers_size_; - return new (allocator) HInliner(graph, // outer_graph - graph, // outermost_graph - codegen, - dex_compilation_unit, // outer_compilation_unit - dex_compilation_unit, // outermost_compilation_unit - driver, - handles, - stats, - number_of_dex_registers, - /* total_number_of_instructions */ 0, - /* parent */ nullptr); - } else if (opt_name == HSharpening::kSharpeningPassName) { - return new (allocator) HSharpening(graph, codegen, dex_compilation_unit, driver, handles); - } else if (opt_name == HSelectGenerator::kSelectGeneratorPassName) { - return new (allocator) HSelectGenerator(graph, handles, stats); - } else if (opt_name == HInductionVarAnalysis::kInductionPassName) { - return new (allocator) HInductionVarAnalysis(graph); - } else if (opt_name == InstructionSimplifier::kInstructionSimplifierPassName) { - return new (allocator) InstructionSimplifier(graph, codegen, driver, stats, pass_name.c_str()); - } else if (opt_name == IntrinsicsRecognizer::kIntrinsicsRecognizerPassName) { - return new (allocator) IntrinsicsRecognizer(graph, stats); - } else if (opt_name == LICM::kLoopInvariantCodeMotionPassName) { - CHECK(most_recent_side_effects != nullptr); - return new (allocator) LICM(graph, *most_recent_side_effects, stats); - } else if (opt_name == LoadStoreAnalysis::kLoadStoreAnalysisPassName) { - return new (allocator) LoadStoreAnalysis(graph); - } else if (opt_name == LoadStoreElimination::kLoadStoreEliminationPassName) { - CHECK(most_recent_side_effects != nullptr); - CHECK(most_recent_lsa != nullptr); - return new (allocator) LoadStoreElimination(graph, - *most_recent_side_effects, - *most_recent_lsa, stats); - } else if (opt_name == SideEffectsAnalysis::kSideEffectsAnalysisPassName) { - return new (allocator) SideEffectsAnalysis(graph); - } else if (opt_name == HLoopOptimization::kLoopOptimizationPassName) { - return new (allocator) HLoopOptimization(graph, driver, most_recent_induction, stats); - } else if (opt_name == CHAGuardOptimization::kCHAGuardOptimizationPassName) { - return new (allocator) CHAGuardOptimization(graph); - } else if (opt_name == CodeSinking::kCodeSinkingPassName) { - return new (allocator) CodeSinking(graph, stats); - } else if (opt_name == ConstructorFenceRedundancyElimination::kPassName) { - return new (allocator) ConstructorFenceRedundancyElimination(graph, stats); -#ifdef ART_ENABLE_CODEGEN_arm - } else if (opt_name == arm::InstructionSimplifierArm::kInstructionSimplifierArmPassName) { - return new (allocator) arm::InstructionSimplifierArm(graph, stats); -#endif -#ifdef ART_ENABLE_CODEGEN_arm64 - } else if (opt_name == arm64::InstructionSimplifierArm64::kInstructionSimplifierArm64PassName) { - return new (allocator) arm64::InstructionSimplifierArm64(graph, stats); -#endif -#ifdef ART_ENABLE_CODEGEN_mips - } else if (opt_name == mips::PcRelativeFixups::kPcRelativeFixupsMipsPassName) { - return new (allocator) mips::PcRelativeFixups(graph, codegen, stats); - } else if (opt_name == mips::InstructionSimplifierMips::kInstructionSimplifierMipsPassName) { - return new (allocator) mips::InstructionSimplifierMips(graph, codegen, stats); -#endif -#ifdef ART_ENABLE_CODEGEN_x86 - } else if (opt_name == x86::PcRelativeFixups::kPcRelativeFixupsX86PassName) { - return new (allocator) x86::PcRelativeFixups(graph, codegen, stats); - } else if (opt_name == x86::X86MemoryOperandGeneration::kX86MemoryOperandGenerationPassName) { - return new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats); -#endif - } - return nullptr; -} - -static ArenaVector<HOptimization*> BuildOptimizations( - const std::vector<std::string>& pass_names, - ArenaAllocator* allocator, - HGraph* graph, - OptimizingCompilerStats* stats, - CodeGenerator* codegen, - CompilerDriver* driver, - const DexCompilationUnit& dex_compilation_unit, - VariableSizedHandleScope* handles) { - // Few HOptimizations constructors require SideEffectsAnalysis or HInductionVarAnalysis - // instances. This method assumes that each of them expects the nearest instance preceeding it - // in the pass name list. - SideEffectsAnalysis* most_recent_side_effects = nullptr; - HInductionVarAnalysis* most_recent_induction = nullptr; - LoadStoreAnalysis* most_recent_lsa = nullptr; - ArenaVector<HOptimization*> ret(allocator->Adapter()); - for (const std::string& pass_name : pass_names) { - HOptimization* opt = BuildOptimization( - pass_name, - allocator, - graph, - stats, - codegen, - driver, - dex_compilation_unit, - handles, - most_recent_side_effects, - most_recent_induction, - most_recent_lsa); - CHECK(opt != nullptr) << "Couldn't build optimization: \"" << pass_name << "\""; - ret.push_back(opt); - - std::string opt_name = ConvertPassNameToOptimizationName(pass_name); - if (opt_name == SideEffectsAnalysis::kSideEffectsAnalysisPassName) { - most_recent_side_effects = down_cast<SideEffectsAnalysis*>(opt); - } else if (opt_name == HInductionVarAnalysis::kInductionPassName) { - most_recent_induction = down_cast<HInductionVarAnalysis*>(opt); - } else if (opt_name == LoadStoreAnalysis::kLoadStoreAnalysisPassName) { - most_recent_lsa = down_cast<LoadStoreAnalysis*>(opt); - } - } - return ret; -} - -void OptimizingCompiler::RunOptimizations(HOptimization* optimizations[], - size_t length, - PassObserver* pass_observer) const { - for (size_t i = 0; i < length; ++i) { - PassScope scope(optimizations[i]->GetPassName(), pass_observer); - optimizations[i]->Run(); - } -} - void OptimizingCompiler::MaybeRunInliner(HGraph* graph, CodeGenerator* codegen, - CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, VariableSizedHandleScope* handles) const { - OptimizingCompilerStats* stats = compilation_stats_.get(); - const CompilerOptions& compiler_options = driver->GetCompilerOptions(); + const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); bool should_inline = (compiler_options.GetInlineMaxCodeUnits() > 0); if (!should_inline) { return; } - size_t number_of_dex_registers = dex_compilation_unit.GetCodeItem()->registers_size_; - HInliner* inliner = new (graph->GetAllocator()) HInliner( - graph, // outer_graph - graph, // outermost_graph - codegen, - dex_compilation_unit, // outer_compilation_unit - dex_compilation_unit, // outermost_compilation_unit - driver, - handles, - stats, - number_of_dex_registers, - /* total_number_of_instructions */ 0, - /* parent */ nullptr); - HOptimization* optimizations[] = { inliner }; - - RunOptimizations(optimizations, arraysize(optimizations), pass_observer); + OptimizationDef optimizations[] = { + OptDef(OptimizationPass::kInliner) + }; + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + optimizations); } -void OptimizingCompiler::RunArchOptimizations(InstructionSet instruction_set, - HGraph* graph, +void OptimizingCompiler::RunArchOptimizations(HGraph* graph, CodeGenerator* codegen, - PassObserver* pass_observer) const { - UNUSED(codegen); // To avoid compilation error when compiling for svelte - OptimizingCompilerStats* stats = compilation_stats_.get(); - ArenaAllocator* allocator = graph->GetAllocator(); - switch (instruction_set) { + const DexCompilationUnit& dex_compilation_unit, + PassObserver* pass_observer, + VariableSizedHandleScope* handles) const { + switch (GetCompilerDriver()->GetInstructionSet()) { #if defined(ART_ENABLE_CODEGEN_arm) case InstructionSet::kThumb2: case InstructionSet::kArm: { - arm::InstructionSimplifierArm* simplifier = - new (allocator) arm::InstructionSimplifierArm(graph, stats); - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - HInstructionScheduling* scheduling = - new (allocator) HInstructionScheduling(graph, instruction_set, codegen); - HOptimization* arm_optimizations[] = { - simplifier, - side_effects, - gvn, - scheduling, + OptimizationDef arm_optimizations[] = { + OptDef(OptimizationPass::kInstructionSimplifierArm), + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), + OptDef(OptimizationPass::kScheduling) }; - RunOptimizations(arm_optimizations, arraysize(arm_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + arm_optimizations); break; } #endif #ifdef ART_ENABLE_CODEGEN_arm64 case InstructionSet::kArm64: { - arm64::InstructionSimplifierArm64* simplifier = - new (allocator) arm64::InstructionSimplifierArm64(graph, stats); - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - HInstructionScheduling* scheduling = - new (allocator) HInstructionScheduling(graph, instruction_set); - HOptimization* arm64_optimizations[] = { - simplifier, - side_effects, - gvn, - scheduling, + OptimizationDef arm64_optimizations[] = { + OptDef(OptimizationPass::kInstructionSimplifierArm64), + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), + OptDef(OptimizationPass::kScheduling) }; - RunOptimizations(arm64_optimizations, arraysize(arm64_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + arm64_optimizations); break; } #endif #ifdef ART_ENABLE_CODEGEN_mips case InstructionSet::kMips: { - mips::InstructionSimplifierMips* simplifier = - new (allocator) mips::InstructionSimplifierMips(graph, codegen, stats); - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - mips::PcRelativeFixups* pc_relative_fixups = - new (allocator) mips::PcRelativeFixups(graph, codegen, stats); - HOptimization* mips_optimizations[] = { - simplifier, - side_effects, - gvn, - pc_relative_fixups, + OptimizationDef mips_optimizations[] = { + OptDef(OptimizationPass::kInstructionSimplifierMips), + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), + OptDef(OptimizationPass::kPcRelativeFixupsMips) }; - RunOptimizations(mips_optimizations, arraysize(mips_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + mips_optimizations); break; } #endif #ifdef ART_ENABLE_CODEGEN_mips64 case InstructionSet::kMips64: { - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - HOptimization* mips64_optimizations[] = { - side_effects, - gvn, + OptimizationDef mips64_optimizations[] = { + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch") }; - RunOptimizations(mips64_optimizations, arraysize(mips64_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + mips64_optimizations); break; } #endif #ifdef ART_ENABLE_CODEGEN_x86 case InstructionSet::kX86: { - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - x86::PcRelativeFixups* pc_relative_fixups = - new (allocator) x86::PcRelativeFixups(graph, codegen, stats); - x86::X86MemoryOperandGeneration* memory_gen = - new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats); - HOptimization* x86_optimizations[] = { - side_effects, - gvn, - pc_relative_fixups, - memory_gen + OptimizationDef x86_optimizations[] = { + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), + OptDef(OptimizationPass::kPcRelativeFixupsX86), + OptDef(OptimizationPass::kX86MemoryOperandGeneration) }; - RunOptimizations(x86_optimizations, arraysize(x86_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + x86_optimizations); break; } #endif #ifdef ART_ENABLE_CODEGEN_x86_64 case InstructionSet::kX86_64: { - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - x86::X86MemoryOperandGeneration* memory_gen = - new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats); - HOptimization* x86_64_optimizations[] = { - side_effects, - gvn, - memory_gen + OptimizationDef x86_64_optimizations[] = { + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), + OptDef(OptimizationPass::kX86MemoryOperandGeneration) }; - RunOptimizations(x86_64_optimizations, arraysize(x86_64_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + x86_64_optimizations); break; } #endif @@ -772,110 +595,93 @@ static void AllocateRegisters(HGraph* graph, } } +// Strip pass name suffix to get optimization name. +static std::string ConvertPassNameToOptimizationName(const std::string& pass_name) { + size_t pos = pass_name.find(kPassNameSeparator); + return pos == std::string::npos ? pass_name : pass_name.substr(0, pos); +} + void OptimizingCompiler::RunOptimizations(HGraph* graph, CodeGenerator* codegen, - CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, VariableSizedHandleScope* handles) const { - OptimizingCompilerStats* stats = compilation_stats_.get(); - ArenaAllocator* allocator = graph->GetAllocator(); - if (driver->GetCompilerOptions().GetPassesToRun() != nullptr) { - ArenaVector<HOptimization*> optimizations = BuildOptimizations( - *driver->GetCompilerOptions().GetPassesToRun(), - allocator, - graph, - stats, - codegen, - driver, - dex_compilation_unit, - handles); - RunOptimizations(&optimizations[0], optimizations.size(), pass_observer); + const std::vector<std::string>* pass_names = + GetCompilerDriver()->GetCompilerOptions().GetPassesToRun(); + if (pass_names != nullptr) { + // If passes were defined on command-line, build the optimization + // passes and run these instead of the built-in optimizations. + const size_t length = pass_names->size(); + std::vector<OptimizationDef> optimizations; + for (const std::string& pass_name : *pass_names) { + std::string opt_name = ConvertPassNameToOptimizationName(pass_name); + optimizations.push_back(OptDef(OptimizationPassByName(opt_name.c_str()), pass_name.c_str())); + } + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + optimizations.data(), + length); return; } - HDeadCodeElimination* dce1 = new (allocator) HDeadCodeElimination( - graph, stats, "dead_code_elimination$initial"); - HDeadCodeElimination* dce2 = new (allocator) HDeadCodeElimination( - graph, stats, "dead_code_elimination$after_inlining"); - HDeadCodeElimination* dce3 = new (allocator) HDeadCodeElimination( - graph, stats, "dead_code_elimination$final"); - HConstantFolding* fold1 = new (allocator) HConstantFolding(graph, "constant_folding"); - InstructionSimplifier* simplify1 = new (allocator) InstructionSimplifier( - graph, codegen, driver, stats); - HSelectGenerator* select_generator = new (allocator) HSelectGenerator(graph, handles, stats); - HConstantFolding* fold2 = new (allocator) HConstantFolding( - graph, "constant_folding$after_inlining"); - HConstantFolding* fold3 = new (allocator) HConstantFolding(graph, "constant_folding$after_bce"); - SideEffectsAnalysis* side_effects1 = new (allocator) SideEffectsAnalysis( - graph, "side_effects$before_gvn"); - SideEffectsAnalysis* side_effects2 = new (allocator) SideEffectsAnalysis( - graph, "side_effects$before_lse"); - GVNOptimization* gvn = new (allocator) GVNOptimization(graph, *side_effects1); - LICM* licm = new (allocator) LICM(graph, *side_effects1, stats); - HInductionVarAnalysis* induction = new (allocator) HInductionVarAnalysis(graph); - BoundsCheckElimination* bce = - new (allocator) BoundsCheckElimination(graph, *side_effects1, induction); - HLoopOptimization* loop = new (allocator) HLoopOptimization(graph, driver, induction, stats); - LoadStoreAnalysis* lsa = new (allocator) LoadStoreAnalysis(graph); - LoadStoreElimination* lse = - new (allocator) LoadStoreElimination(graph, *side_effects2, *lsa, stats); - HSharpening* sharpening = new (allocator) HSharpening( - graph, codegen, dex_compilation_unit, driver, handles); - InstructionSimplifier* simplify2 = new (allocator) InstructionSimplifier( - graph, codegen, driver, stats, "instruction_simplifier$after_inlining"); - InstructionSimplifier* simplify3 = new (allocator) InstructionSimplifier( - graph, codegen, driver, stats, "instruction_simplifier$after_bce"); - InstructionSimplifier* simplify4 = new (allocator) InstructionSimplifier( - graph, codegen, driver, stats, "instruction_simplifier$before_codegen"); - IntrinsicsRecognizer* intrinsics = new (allocator) IntrinsicsRecognizer(graph, stats); - CHAGuardOptimization* cha_guard = new (allocator) CHAGuardOptimization(graph); - CodeSinking* code_sinking = new (allocator) CodeSinking(graph, stats); - ConstructorFenceRedundancyElimination* cfre = - new (allocator) ConstructorFenceRedundancyElimination(graph, stats); - - HOptimization* optimizations1[] = { - intrinsics, - sharpening, - fold1, - simplify1, - dce1, + OptimizationDef optimizations1[] = { + OptDef(OptimizationPass::kIntrinsicsRecognizer), + OptDef(OptimizationPass::kSharpening), + OptDef(OptimizationPass::kConstantFolding), + OptDef(OptimizationPass::kInstructionSimplifier), + OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$initial") }; - RunOptimizations(optimizations1, arraysize(optimizations1), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + optimizations1); - MaybeRunInliner(graph, codegen, driver, dex_compilation_unit, pass_observer, handles); + MaybeRunInliner(graph, codegen, dex_compilation_unit, pass_observer, handles); - HOptimization* optimizations2[] = { + OptimizationDef optimizations2[] = { // SelectGenerator depends on the InstructionSimplifier removing // redundant suspend checks to recognize empty blocks. - select_generator, - fold2, // TODO: if we don't inline we can also skip fold2. - simplify2, - dce2, - side_effects1, - gvn, - licm, - induction, - bce, - loop, - fold3, // evaluates code generated by dynamic bce - simplify3, - side_effects2, - lsa, - lse, - cha_guard, - dce3, - code_sinking, + OptDef(OptimizationPass::kSelectGenerator), + // TODO: if we don't inline we can also skip fold2. + OptDef(OptimizationPass::kConstantFolding, "constant_folding$after_inlining"), + OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$after_inlining"), + OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$after_inlining"), + OptDef(OptimizationPass::kSideEffectsAnalysis, "side_effects$before_gvn"), + OptDef(OptimizationPass::kGlobalValueNumbering), + OptDef(OptimizationPass::kInvariantCodeMotion), + OptDef(OptimizationPass::kInductionVarAnalysis), + OptDef(OptimizationPass::kBoundsCheckElimination), + OptDef(OptimizationPass::kLoopOptimization), + // Evaluates code generated by dynamic bce. + OptDef(OptimizationPass::kConstantFolding, "constant_folding$after_bce"), + OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$after_bce"), + OptDef(OptimizationPass::kSideEffectsAnalysis, "side_effects$before_lse"), + OptDef(OptimizationPass::kLoadStoreAnalysis), + OptDef(OptimizationPass::kLoadStoreElimination), + OptDef(OptimizationPass::kCHAGuardOptimization), + OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$final"), + OptDef(OptimizationPass::kCodeSinking), // The codegen has a few assumptions that only the instruction simplifier // can satisfy. For example, the code generator does not expect to see a // HTypeConversion from a type to the same type. - simplify4, - cfre, // Eliminate constructor fences after code sinking to avoid - // complicated sinking logic to split a fence with many inputs. + OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$before_codegen"), + // Eliminate constructor fences after code sinking to avoid + // complicated sinking logic to split a fence with many inputs. + OptDef(OptimizationPass::kConstructorFenceRedundancyElimination) }; - RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + optimizations2); - RunArchOptimizations(driver->GetInstructionSet(), graph, codegen, pass_observer); + RunArchOptimizations(graph, codegen, dex_compilation_unit, pass_observer, handles); } static ArenaVector<linker::LinkerPatch> EmitAndSortLinkerPatches(CodeGenerator* codegen) { @@ -894,8 +700,7 @@ static ArenaVector<linker::LinkerPatch> EmitAndSortLinkerPatches(CodeGenerator* CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, CodeVectorAllocator* code_allocator, CodeGenerator* codegen, - CompilerDriver* compiler_driver, - const DexFile::CodeItem* code_item) const { + const DexFile::CodeItem* code_item_for_osr_check) const { ArenaVector<linker::LinkerPatch> linker_patches = EmitAndSortLinkerPatches(codegen); ArenaVector<uint8_t> stack_map(allocator->Adapter(kArenaAllocStackMaps)); ArenaVector<uint8_t> method_info(allocator->Adapter(kArenaAllocStackMaps)); @@ -906,10 +711,10 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, method_info.resize(method_info_size); codegen->BuildStackMaps(MemoryRegion(stack_map.data(), stack_map.size()), MemoryRegion(method_info.data(), method_info.size()), - *code_item); + code_item_for_osr_check); CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod( - compiler_driver, + GetCompilerDriver(), codegen->GetInstructionSet(), ArrayRef<const uint8_t>(code_allocator->GetMemory()), // Follow Quick's behavior and set the frame size to zero if it is @@ -929,21 +734,16 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, ArenaStack* arena_stack, CodeVectorAllocator* code_allocator, - const DexFile::CodeItem* code_item, - uint32_t access_flags, - InvokeType invoke_type, - uint16_t class_def_idx, - uint32_t method_idx, - Handle<mirror::ClassLoader> class_loader, - const DexFile& dex_file, - Handle<mirror::DexCache> dex_cache, + const DexCompilationUnit& dex_compilation_unit, ArtMethod* method, bool osr, VariableSizedHandleScope* handles) const { - MaybeRecordStat(compilation_stats_.get(), - MethodCompilationStat::kAttemptCompilation); + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kAttemptCompilation); CompilerDriver* compiler_driver = GetCompilerDriver(); InstructionSet instruction_set = compiler_driver->GetInstructionSet(); + const DexFile& dex_file = *dex_compilation_unit.GetDexFile(); + uint32_t method_idx = dex_compilation_unit.GetDexMethodIndex(); + const DexFile::CodeItem* code_item = dex_compilation_unit.GetCodeItem(); // Always use the Thumb-2 assembler: some runtime functionality // (like implicit stack overflow checks) assume Thumb-2. @@ -973,18 +773,6 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, return nullptr; } - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - DexCompilationUnit dex_compilation_unit( - class_loader, - class_linker, - dex_file, - code_item, - class_def_idx, - method_idx, - access_flags, - /* verified_method */ nullptr, - dex_cache); - HGraph* graph = new (allocator) HGraph( allocator, arena_stack, @@ -996,11 +784,6 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, osr); const uint8_t* interpreter_metadata = nullptr; - if (method == nullptr) { - ScopedObjectAccess soa(Thread::Current()); - method = compiler_driver->ResolveMethod( - soa, dex_cache, class_loader, &dex_compilation_unit, method_idx, invoke_type); - } // For AOT compilation, we may not get a method, for example if its class is erroneous. // JIT should always have a method. DCHECK(Runtime::Current()->IsAotCompiler() || method != nullptr); @@ -1034,6 +817,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, VLOG(compiler) << "Building " << pass_observer.GetMethodName(); PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer); HGraphBuilder builder(graph, + code_item, &dex_compilation_unit, &dex_compilation_unit, compiler_driver, @@ -1074,7 +858,6 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, RunOptimizations(graph, codegen.get(), - compiler_driver, dex_compilation_unit, &pass_observer, handles); @@ -1093,6 +876,112 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, return codegen.release(); } +CodeGenerator* OptimizingCompiler::TryCompileIntrinsic( + ArenaAllocator* allocator, + ArenaStack* arena_stack, + CodeVectorAllocator* code_allocator, + const DexCompilationUnit& dex_compilation_unit, + ArtMethod* method, + VariableSizedHandleScope* handles) const { + CompilerDriver* compiler_driver = GetCompilerDriver(); + InstructionSet instruction_set = compiler_driver->GetInstructionSet(); + const DexFile& dex_file = *dex_compilation_unit.GetDexFile(); + uint32_t method_idx = dex_compilation_unit.GetDexMethodIndex(); + + // Always use the Thumb-2 assembler: some runtime functionality + // (like implicit stack overflow checks) assume Thumb-2. + DCHECK_NE(instruction_set, InstructionSet::kArm); + + // Do not attempt to compile on architectures we do not support. + if (!IsInstructionSetSupported(instruction_set)) { + MaybeRecordStat(compilation_stats_.get(), + MethodCompilationStat::kNotCompiledUnsupportedIsa); + return nullptr; + } + + HGraph* graph = new (allocator) HGraph( + allocator, + arena_stack, + dex_file, + method_idx, + compiler_driver->GetInstructionSet(), + kInvalidInvokeType, + compiler_driver->GetCompilerOptions().GetDebuggable(), + /* osr */ false); + + DCHECK(Runtime::Current()->IsAotCompiler()); + DCHECK(method != nullptr); + graph->SetArtMethod(method); + + std::unique_ptr<CodeGenerator> codegen( + CodeGenerator::Create(graph, + instruction_set, + *compiler_driver->GetInstructionSetFeatures(), + compiler_driver->GetCompilerOptions(), + compilation_stats_.get())); + if (codegen.get() == nullptr) { + MaybeRecordStat(compilation_stats_.get(), + MethodCompilationStat::kNotCompiledNoCodegen); + return nullptr; + } + codegen->GetAssembler()->cfi().SetEnabled( + compiler_driver->GetCompilerOptions().GenerateAnyDebugInfo()); + + PassObserver pass_observer(graph, + codegen.get(), + visualizer_output_.get(), + compiler_driver, + dump_mutex_); + + { + VLOG(compiler) << "Building intrinsic graph " << pass_observer.GetMethodName(); + PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer); + HGraphBuilder builder(graph, + /* code_item */ nullptr, + &dex_compilation_unit, + &dex_compilation_unit, + compiler_driver, + codegen.get(), + compilation_stats_.get(), + /* interpreter_metadata */ nullptr, + handles); + builder.BuildIntrinsicGraph(method); + } + + OptimizationDef optimizations[] = { + OptDef(OptimizationPass::kIntrinsicsRecognizer), + // Some intrinsics are converted to HIR by the simplifier and the codegen also + // has a few assumptions that only the instruction simplifier can satisfy. + OptDef(OptimizationPass::kInstructionSimplifier), + }; + RunOptimizations(graph, + codegen.get(), + dex_compilation_unit, + &pass_observer, + handles, + optimizations); + + RunArchOptimizations(graph, codegen.get(), dex_compilation_unit, &pass_observer, handles); + + AllocateRegisters(graph, + codegen.get(), + &pass_observer, + compiler_driver->GetCompilerOptions().GetRegisterAllocationStrategy(), + compilation_stats_.get()); + if (!codegen->IsLeafMethod()) { + VLOG(compiler) << "Intrinsic method is not leaf: " << method->GetIntrinsic() + << " " << graph->PrettyMethod(); + return nullptr; + } + + codegen->Compile(code_allocator); + pass_observer.DumpDisassembly(); + + VLOG(compiler) << "Compiled intrinsic: " << method->GetIntrinsic() + << " " << graph->PrettyMethod(); + return codegen.release(); +} + CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, @@ -1102,42 +991,70 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) const { CompilerDriver* compiler_driver = GetCompilerDriver(); - CompiledMethod* method = nullptr; - DCHECK(Runtime::Current()->IsAotCompiler()); + CompiledMethod* compiled_method = nullptr; + Runtime* runtime = Runtime::Current(); + DCHECK(runtime->IsAotCompiler()); const VerifiedMethod* verified_method = compiler_driver->GetVerifiedMethod(&dex_file, method_idx); DCHECK(!verified_method->HasRuntimeThrow()); if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file) || verifier::CanCompilerHandleVerificationFailure( verified_method->GetEncounteredVerificationFailures())) { - ArenaAllocator allocator(Runtime::Current()->GetArenaPool()); - ArenaStack arena_stack(Runtime::Current()->GetArenaPool()); + ArenaAllocator allocator(runtime->GetArenaPool()); + ArenaStack arena_stack(runtime->GetArenaPool()); CodeVectorAllocator code_allocator(&allocator); std::unique_ptr<CodeGenerator> codegen; + bool compiled_intrinsic = false; { + DexCompilationUnit dex_compilation_unit( + jclass_loader, + runtime->GetClassLinker(), + dex_file, + code_item, + class_def_idx, + method_idx, + access_flags, + /* verified_method */ nullptr, // Not needed by the Optimizing compiler. + dex_cache); ScopedObjectAccess soa(Thread::Current()); + ArtMethod* method = compiler_driver->ResolveMethod( + soa, dex_cache, jclass_loader, &dex_compilation_unit, method_idx, invoke_type); VariableSizedHandleScope handles(soa.Self()); // Go to native so that we don't block GC during compilation. ScopedThreadSuspension sts(soa.Self(), kNative); - codegen.reset( - TryCompile(&allocator, - &arena_stack, - &code_allocator, - code_item, - access_flags, - invoke_type, - class_def_idx, - method_idx, - jclass_loader, - dex_file, - dex_cache, - nullptr, - /* osr */ false, - &handles)); + if (method != nullptr && UNLIKELY(method->IsIntrinsic())) { + DCHECK(compiler_driver->GetCompilerOptions().IsBootImage()); + codegen.reset( + TryCompileIntrinsic(&allocator, + &arena_stack, + &code_allocator, + dex_compilation_unit, + method, + &handles)); + if (codegen != nullptr) { + compiled_intrinsic = true; + } + } + if (codegen == nullptr) { + codegen.reset( + TryCompile(&allocator, + &arena_stack, + &code_allocator, + dex_compilation_unit, + method, + /* osr */ false, + &handles)); + } } if (codegen.get() != nullptr) { MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiled); - method = Emit(&allocator, &code_allocator, codegen.get(), compiler_driver, code_item); + compiled_method = Emit(&allocator, + &code_allocator, + codegen.get(), + compiled_intrinsic ? nullptr : code_item); + if (compiled_intrinsic) { + compiled_method->MarkAsIntrinsic(); + } if (kArenaAllocatorCountAllocations) { codegen.reset(); // Release codegen's ScopedArenaAllocator for memory accounting. @@ -1171,10 +1088,61 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, // regressing. std::string method_name = dex_file.PrettyMethod(method_idx); bool shouldCompile = method_name.find("$opt$") != std::string::npos; - DCHECK((method != nullptr) || !shouldCompile) << "Didn't compile " << method_name; + DCHECK((compiled_method != nullptr) || !shouldCompile) << "Didn't compile " << method_name; + } + + return compiled_method; +} + +CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags, + uint32_t method_idx, + const DexFile& dex_file, + Handle<mirror::DexCache> dex_cache) const { + if (GetCompilerDriver()->GetCompilerOptions().IsBootImage()) { + ScopedObjectAccess soa(Thread::Current()); + Runtime* runtime = Runtime::Current(); + ArtMethod* method = runtime->GetClassLinker()->LookupResolvedMethod( + method_idx, dex_cache.Get(), /* class_loader */ nullptr); + if (method != nullptr && UNLIKELY(method->IsIntrinsic())) { + ScopedNullHandle<mirror::ClassLoader> class_loader; // null means boot class path loader. + DexCompilationUnit dex_compilation_unit( + class_loader, + runtime->GetClassLinker(), + dex_file, + /* code_item */ nullptr, + /* class_def_idx */ DexFile::kDexNoIndex16, + method_idx, + access_flags, + /* verified_method */ nullptr, + dex_cache); + ArenaAllocator allocator(runtime->GetArenaPool()); + ArenaStack arena_stack(runtime->GetArenaPool()); + CodeVectorAllocator code_allocator(&allocator); + VariableSizedHandleScope handles(soa.Self()); + // Go to native so that we don't block GC during compilation. + ScopedThreadSuspension sts(soa.Self(), kNative); + std::unique_ptr<CodeGenerator> codegen( + TryCompileIntrinsic(&allocator, + &arena_stack, + &code_allocator, + dex_compilation_unit, + method, + &handles)); + if (codegen != nullptr) { + CompiledMethod* compiled_method = Emit(&allocator, + &code_allocator, + codegen.get(), + /* code_item_for_osr_check */ nullptr); + compiled_method->MarkAsIntrinsic(); + return compiled_method; + } + } } - return method; + return ArtQuickJniCompileMethod(GetCompilerDriver(), + access_flags, + method_idx, + dex_file); } Compiler* CreateOptimizingCompiler(CompilerDriver* driver) { @@ -1221,29 +1189,33 @@ bool OptimizingCompiler::JitCompile(Thread* self, const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset()); const uint32_t method_idx = method->GetDexMethodIndex(); const uint32_t access_flags = method->GetAccessFlags(); - const InvokeType invoke_type = method->GetInvokeType(); - ArenaAllocator allocator(Runtime::Current()->GetJitArenaPool()); + Runtime* runtime = Runtime::Current(); + ArenaAllocator allocator(runtime->GetJitArenaPool()); ArenaStack arena_stack(Runtime::Current()->GetJitArenaPool()); CodeVectorAllocator code_allocator(&allocator); VariableSizedHandleScope handles(self); std::unique_ptr<CodeGenerator> codegen; { + DexCompilationUnit dex_compilation_unit( + class_loader, + runtime->GetClassLinker(), + *dex_file, + code_item, + class_def_idx, + method_idx, + access_flags, + /* verified_method */ nullptr, + dex_cache); + // Go to native so that we don't block GC during compilation. ScopedThreadSuspension sts(self, kNative); codegen.reset( TryCompile(&allocator, &arena_stack, &code_allocator, - code_item, - access_flags, - invoke_type, - class_def_idx, - method_idx, - class_loader, - *dex_file, - dex_cache, + dex_compilation_unit, method, osr, &handles)); @@ -1286,7 +1258,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiled); codegen->BuildStackMaps(MemoryRegion(stack_map_data, stack_map_size), MemoryRegion(method_info_data, method_info_size), - *code_item); + code_item); codegen->EmitJitRoots(code_allocator.GetData(), roots, roots_data); const void* code = code_cache->CommitCode( diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h index e90c30d5ca..158c252f45 100644 --- a/compiler/optimizing/optimizing_unit_test.h +++ b/compiler/optimizing/optimizing_unit_test.h @@ -22,6 +22,7 @@ #include "common_compiler_test.h" #include "dex_file.h" #include "dex_instruction.h" +#include "driver/dex_compilation_unit.h" #include "handle_scope-inl.h" #include "mirror/class_loader.h" #include "mirror/dex_cache.h" @@ -133,12 +134,11 @@ class OptimizingUnitTest : public CommonCompilerTest { if (handles_ == nullptr) { handles_.reset(new VariableSizedHandleScope(soa.Self())); } - const DexFile* dex_file = graph->GetAllocator()->Alloc<DexFile>(); const DexCompilationUnit* dex_compilation_unit = new (graph->GetAllocator()) DexCompilationUnit( handles_->NewHandle<mirror::ClassLoader>(nullptr), /* class_linker */ nullptr, - *dex_file, + graph->GetDexFile(), code_item, /* class_def_index */ DexFile::kDexNoIndex16, /* method_idx */ dex::kDexNoIndex, diff --git a/compiler/optimizing/pc_relative_fixups_mips.h b/compiler/optimizing/pc_relative_fixups_mips.h index 5a7397bf9d..ec2c711f8d 100644 --- a/compiler/optimizing/pc_relative_fixups_mips.h +++ b/compiler/optimizing/pc_relative_fixups_mips.h @@ -29,7 +29,7 @@ namespace mips { class PcRelativeFixups : public HOptimization { public: PcRelativeFixups(HGraph* graph, CodeGenerator* codegen, OptimizingCompilerStats* stats) - : HOptimization(graph, "pc_relative_fixups_mips", stats), + : HOptimization(graph, kPcRelativeFixupsMipsPassName, stats), codegen_(codegen) {} static constexpr const char* kPcRelativeFixupsMipsPassName = "pc_relative_fixups_mips"; diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index fe98aa9561..1ed190d328 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -53,16 +53,18 @@ void PrepareForRegisterAllocation::VisitDeoptimize(HDeoptimize* deoptimize) { void PrepareForRegisterAllocation::VisitBoundsCheck(HBoundsCheck* check) { check->ReplaceWith(check->InputAt(0)); if (check->IsStringCharAt()) { - // Add a fake environment for String.charAt() inline info as we want - // the exception to appear as being thrown from there. + // Add a fake environment for String.charAt() inline info as we want the exception + // to appear as being thrown from there. Skip if we're compiling String.charAt() itself. ArtMethod* char_at_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt); - ArenaAllocator* allocator = GetGraph()->GetAllocator(); - HEnvironment* environment = new (allocator) HEnvironment(allocator, - /* number_of_vregs */ 0u, - char_at_method, - /* dex_pc */ dex::kDexNoIndex, - check); - check->InsertRawEnvironment(environment); + if (GetGraph()->GetArtMethod() != char_at_method) { + ArenaAllocator* allocator = GetGraph()->GetAllocator(); + HEnvironment* environment = new (allocator) HEnvironment(allocator, + /* number_of_vregs */ 0u, + char_at_method, + /* dex_pc */ dex::kDexNoIndex, + check); + check->InsertRawEnvironment(environment); + } } } diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h index a6e160379b..bb7c353bc2 100644 --- a/compiler/optimizing/scheduler.h +++ b/compiler/optimizing/scheduler.h @@ -495,8 +495,11 @@ inline bool SchedulingGraph::IsSchedulingBarrier(const HInstruction* instruction class HInstructionScheduling : public HOptimization { public: - HInstructionScheduling(HGraph* graph, InstructionSet instruction_set, CodeGenerator* cg = nullptr) - : HOptimization(graph, kInstructionScheduling), + HInstructionScheduling(HGraph* graph, + InstructionSet instruction_set, + CodeGenerator* cg = nullptr, + const char* name = kInstructionSchedulingPassName) + : HOptimization(graph, name), codegen_(cg), instruction_set_(instruction_set) {} @@ -505,7 +508,7 @@ class HInstructionScheduling : public HOptimization { } void Run(bool only_optimize_loop_blocks, bool schedule_randomly); - static constexpr const char* kInstructionScheduling = "scheduler"; + static constexpr const char* kInstructionSchedulingPassName = "scheduler"; private: CodeGenerator* const codegen_; diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc index 77ec9a6285..66e51421ca 100644 --- a/compiler/optimizing/select_generator.cc +++ b/compiler/optimizing/select_generator.cc @@ -24,8 +24,9 @@ static constexpr size_t kMaxInstructionsInBranch = 1u; HSelectGenerator::HSelectGenerator(HGraph* graph, VariableSizedHandleScope* handles, - OptimizingCompilerStats* stats) - : HOptimization(graph, kSelectGeneratorPassName, stats), + OptimizingCompilerStats* stats, + const char* name) + : HOptimization(graph, name, stats), handle_scope_(handles) { } diff --git a/compiler/optimizing/select_generator.h b/compiler/optimizing/select_generator.h index f8cf00e35a..bda57fd5c8 100644 --- a/compiler/optimizing/select_generator.h +++ b/compiler/optimizing/select_generator.h @@ -65,7 +65,8 @@ class HSelectGenerator : public HOptimization { public: HSelectGenerator(HGraph* graph, VariableSizedHandleScope* handles, - OptimizingCompilerStats* stats); + OptimizingCompilerStats* stats, + const char* name = kSelectGeneratorPassName); void Run() OVERRIDE; diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index f74b0afdbf..bb1954eeeb 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -36,8 +36,9 @@ class HSharpening : public HOptimization { CodeGenerator* codegen, const DexCompilationUnit& compilation_unit, CompilerDriver* compiler_driver, - VariableSizedHandleScope* handles) - : HOptimization(graph, kSharpeningPassName), + VariableSizedHandleScope* handles, + const char* name = kSharpeningPassName) + : HOptimization(graph, name), codegen_(codegen), compilation_unit_(compilation_unit), compiler_driver_(compiler_driver), diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 9bc80457a3..4f43eb374c 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -32,7 +32,6 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, uint32_t num_dex_registers, uint8_t inlining_depth) { DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry"; - DCHECK_NE(dex_pc, static_cast<uint32_t>(-1)) << "invalid dex_pc"; current_entry_.dex_pc = dex_pc; current_entry_.native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_); current_entry_.register_mask = register_mask; @@ -56,7 +55,10 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, number_of_stack_maps_with_inline_info_++; } - dex_pc_max_ = std::max(dex_pc_max_, dex_pc); + // Note: dex_pc can be kNoDexPc for native method intrinsics. + if (dex_pc != dex::kDexNoIndex && (dex_pc_max_ == dex::kDexNoIndex || dex_pc_max_ < dex_pc)) { + dex_pc_max_ = dex_pc; + } register_mask_max_ = std::max(register_mask_max_, register_mask); current_dex_register_ = 0; } diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index e126609dba..579aabdb5f 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -73,7 +73,7 @@ class StackMapStream : public ValueObject { method_indices_(allocator->Adapter(kArenaAllocStackMapStream)), dex_register_entries_(allocator->Adapter(kArenaAllocStackMapStream)), stack_mask_max_(-1), - dex_pc_max_(0), + dex_pc_max_(kNoDexPc), register_mask_max_(0), number_of_stack_maps_with_inline_info_(0), dex_map_hash_to_stack_map_indices_(std::less<uint32_t>(), diff --git a/compiler/utils/intrusive_forward_list_test.cc b/compiler/utils/intrusive_forward_list_test.cc index 939676cdc8..e97c3044d0 100644 --- a/compiler/utils/intrusive_forward_list_test.cc +++ b/compiler/utils/intrusive_forward_list_test.cc @@ -574,11 +574,11 @@ void IntrusiveForwardListTest::Remove() { ref.remove(4); ifl.remove(4); ASSERT_LISTS_EQUAL(ref, ifl); - auto odd = [](ValueType value) { return (value.value & 1) != 0; }; // NOLINT(readability/braces) + auto odd = [](ValueType value) { return (value.value & 1) != 0; }; ref.remove_if(odd); ifl.remove_if(odd); ASSERT_LISTS_EQUAL(ref, ifl); - auto all = [](ValueType value ATTRIBUTE_UNUSED) { return true; }; // NOLINT(readability/braces) + auto all = [](ValueType value ATTRIBUTE_UNUSED) { return true; }; ref.remove_if(all); ifl.remove_if(all); ASSERT_LISTS_EQUAL(ref, ifl); @@ -721,7 +721,7 @@ void IntrusiveForwardListTest::ModifyValue() { ListType ifl(storage.begin(), storage.end()); ASSERT_LISTS_EQUAL(ref, ifl); - auto add1 = [](const ValueType& value) { return value.value + 1; }; // NOLINT [readability/braces] + auto add1 = [](const ValueType& value) { return value.value + 1; }; std::transform(ref.begin(), ref.end(), ref.begin(), add1); std::transform(ifl.begin(), ifl.end(), ifl.begin(), add1); ASSERT_LISTS_EQUAL(ref, ifl); diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h index 08e243b644..2280f8b993 100644 --- a/compiler/utils/swap_space.h +++ b/compiler/utils/swap_space.h @@ -124,7 +124,7 @@ class SwapAllocator<void> { explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {} template <typename U> - SwapAllocator(const SwapAllocator<U>& other) // NOLINT, implicit + SwapAllocator(const SwapAllocator<U>& other) : swap_space_(other.swap_space_) {} SwapAllocator(const SwapAllocator& other) = default; @@ -160,7 +160,7 @@ class SwapAllocator { explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {} template <typename U> - SwapAllocator(const SwapAllocator<U>& other) // NOLINT, implicit + SwapAllocator(const SwapAllocator<U>& other) : swap_space_(other.swap_space_) {} SwapAllocator(const SwapAllocator& other) = default; diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index e232addd26..36c5c3c0c4 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -90,7 +90,7 @@ class AssemblerX86Test : public AssemblerTest<x86::X86Assembler, } if (registers_.size() == 0) { registers_.insert(end(registers_), - { // NOLINT(whitespace/braces) + { new x86::Register(x86::EAX), new x86::Register(x86::EBX), new x86::Register(x86::ECX), @@ -104,7 +104,7 @@ class AssemblerX86Test : public AssemblerTest<x86::X86Assembler, if (fp_registers_.size() == 0) { fp_registers_.insert(end(fp_registers_), - { // NOLINT(whitespace/braces) + { new x86::XmmRegister(x86::XMM0), new x86::XmmRegister(x86::XMM1), new x86::XmmRegister(x86::XMM2), diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc index 708665534a..a02fbf862f 100644 --- a/dex2oat/dex2oat_image_test.cc +++ b/dex2oat/dex2oat_image_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <regex> // NOLINT [build/c++11] [5] +#include <regex> #include <sstream> #include <string> #include <vector> diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc index 2cf070155f..7f177b9ff6 100644 --- a/dex2oat/dex2oat_options.cc +++ b/dex2oat/dex2oat_options.cc @@ -43,7 +43,7 @@ struct CmdlineType<InstructionSet> : CmdlineTypeParser<InstructionSet> { // Specify storage for the Dex2oatOptions keys. #define DEX2OAT_OPTIONS_KEY(Type, Name, ...) \ - const Dex2oatArgumentMap::Key<Type> Dex2oatArgumentMap::Name {__VA_ARGS__}; // NOLINT [readability/braces] [4] + const Dex2oatArgumentMap::Key<Type> Dex2oatArgumentMap::Name {__VA_ARGS__}; #include "dex2oat_options.def" #pragma GCC diagnostic push diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index fdada8f926..bd8583bd41 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <regex> // NOLINT [build/c++11] [5] +#include <regex> #include <sstream> #include <string> #include <vector> diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index d3e920f9a0..663a8896ff 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -1143,7 +1143,6 @@ class OatWriter::LayoutReserveOffsetCodeMethodVisitor : public OrderedMethodVisi uint16_t method_offsets_index_ = method_data.method_offsets_index; size_t class_def_index = method_data.class_def_index; uint32_t access_flags = method_data.access_flags; - const DexFile::CodeItem* code_item = method_data.code_item; bool has_debug_info = method_data.HasDebugInfo(); size_t debug_info_idx = method_data.debug_info_idx; @@ -1244,7 +1243,8 @@ class OatWriter::LayoutReserveOffsetCodeMethodVisitor : public OrderedMethodVisi info.class_def_index = class_def_index; info.dex_method_index = method_ref.index; info.access_flags = access_flags; - info.code_item = code_item; + // For intrinsics emitted by codegen, the code has no relation to the original code item. + info.code_item = compiled_method->IsIntrinsic() ? nullptr : method_data.code_item; info.isa = compiled_method->GetInstructionSet(); info.deduped = deduped; info.is_native_debuggable = native_debuggable_; @@ -1303,6 +1303,9 @@ class OatWriter::LayoutReserveOffsetCodeMethodVisitor : public OrderedMethodVisi if (UNLIKELY(lhs->GetPatches().data() != rhs->GetPatches().data())) { return lhs->GetPatches().data() < rhs->GetPatches().data(); } + if (UNLIKELY(lhs->IsIntrinsic() != rhs->IsIntrinsic())) { + return rhs->IsIntrinsic(); + } return false; } }; diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 022aa1b58c..7509d91677 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -718,7 +718,7 @@ void OatTest::TestZipFileInput(bool verify) { key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); { // Test using the AddDexFileSource() interface with the zip file. - std::vector<const char*> input_filenames { zip_file.GetFilename().c_str() }; // NOLINT [readability/braces] [4] + std::vector<const char*> input_filenames = { zip_file.GetFilename().c_str() }; ScratchFile oat_file, vdex_file(oat_file, ".vdex"); success = WriteElf(vdex_file.GetFile(), oat_file.GetFile(), input_filenames, @@ -829,7 +829,7 @@ void OatTest::TestZipFileInputWithEmptyDex() { SafeMap<std::string, std::string> key_value_store; key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); - std::vector<const char*> input_filenames { zip_file.GetFilename().c_str() }; // NOLINT [readability/braces] [4] + std::vector<const char*> input_filenames = { zip_file.GetFilename().c_str() }; ScratchFile oat_file, vdex_file(oat_file, ".vdex"); std::unique_ptr<ProfileCompilationInfo> profile_compilation_info(new ProfileCompilationInfo()); success = WriteElf(vdex_file.GetFile(), oat_file.GetFile(), input_filenames, diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc index f0c9158748..05fce96780 100644 --- a/imgdiag/imgdiag.cc +++ b/imgdiag/imgdiag.cc @@ -1610,7 +1610,7 @@ class ImgDiagDumper { // BacktraceMap used for finding the memory mapping of the image file. std::unique_ptr<BacktraceMap> proc_maps_; // Boot image mapping. - backtrace_map_t boot_map_{}; // NOLINT + backtrace_map_t boot_map_{}; // The size of the boot image mapping. size_t boot_map_size_; // The contents of /proc/<image_diff_pid_>/maps. diff --git a/libart_fake/Android.mk b/libart_fake/Android.mk index ed868a5bd5..96e6a14903 100644 --- a/libart_fake/Android.mk +++ b/libart_fake/Android.mk @@ -22,6 +22,7 @@ LOCAL_INSTALLED_MODULE_STEM := libart.so LOCAL_SDK_VERSION := 9 LOCAL_CPP_EXTENSION := .cc LOCAL_SRC_FILES := fake.cc +LOCAL_CFLAGS := -Wall -Werror LOCAL_SHARED_LIBRARIES := liblog ifdef TARGET_2ND_ARCH diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp index aef4dfc201..9ba7068176 100644 --- a/openjdkjvmti/Android.bp +++ b/openjdkjvmti/Android.bp @@ -34,6 +34,7 @@ cc_defaults { "ti_class.cc", "ti_class_definition.cc", "ti_class_loader.cc", + "ti_ddms.cc", "ti_dump.cc", "ti_extension.cc", "ti_field.cc", diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc index 481492402b..62f723dec1 100644 --- a/openjdkjvmti/OpenjdkJvmTi.cc +++ b/openjdkjvmti/OpenjdkJvmTi.cc @@ -1014,14 +1014,21 @@ class JvmtiFunctions { return ERR(NONE); } - std::unique_ptr<jvmtiEventCallbacks> tmp(new jvmtiEventCallbacks()); - memset(tmp.get(), 0, sizeof(jvmtiEventCallbacks)); + // Lock the event_info_mutex_ while we replace the callbacks. + ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env); + art::WriterMutexLock lk(art::Thread::Current(), art_env->event_info_mutex_); + std::unique_ptr<ArtJvmtiEventCallbacks> tmp(new ArtJvmtiEventCallbacks()); + // Copy over the extension events. + tmp->CopyExtensionsFrom(art_env->event_callbacks.get()); + // Never overwrite the extension events. size_t copy_size = std::min(sizeof(jvmtiEventCallbacks), static_cast<size_t>(size_of_callbacks)); copy_size = art::RoundDown(copy_size, sizeof(void*)); + // Copy non-extension events. memcpy(tmp.get(), callbacks, copy_size); - ArtJvmTiEnv::AsArtJvmTiEnv(env)->event_callbacks = std::move(tmp); + // replace the event table. + art_env->event_callbacks = std::move(tmp); return ERR(NONE); } @@ -1077,8 +1084,10 @@ class JvmtiFunctions { jint extension_event_index, jvmtiExtensionEvent callback) { ENSURE_VALID_ENV(env); - // We do not have any extension events, so any call is illegal. - return ExtensionUtil::SetExtensionEventCallback(env, extension_event_index, callback); + return ExtensionUtil::SetExtensionEventCallback(env, + extension_event_index, + callback, + &gEventHandler); } static jvmtiError GetPotentialCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr) { diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h index 97801e09aa..682b82b5cd 100644 --- a/openjdkjvmti/art_jvmti.h +++ b/openjdkjvmti/art_jvmti.h @@ -68,7 +68,7 @@ struct ArtJvmTiEnv : public jvmtiEnv { jvmtiCapabilities capabilities; EventMasks event_masks; - std::unique_ptr<jvmtiEventCallbacks> event_callbacks; + std::unique_ptr<ArtJvmtiEventCallbacks> event_callbacks; // Tagging is specific to the jvmtiEnv. std::unique_ptr<ObjectTagTable> object_tag_table; diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h index 7f77f90862..5344e0fbde 100644 --- a/openjdkjvmti/events-inl.h +++ b/openjdkjvmti/events-inl.h @@ -80,16 +80,17 @@ namespace impl { fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \ fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \ fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \ - fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) + fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) \ + fn(DdmPublishChunk, ArtJvmtiEvent::kDdmPublishChunk) template <ArtJvmtiEvent kEvent> struct EventFnType { }; -#define EVENT_FN_TYPE(name, enum_name) \ -template <> \ -struct EventFnType<enum_name> { \ - using type = decltype(jvmtiEventCallbacks().name); \ +#define EVENT_FN_TYPE(name, enum_name) \ +template <> \ +struct EventFnType<enum_name> { \ + using type = decltype(ArtJvmtiEventCallbacks().name); \ }; FORALL_EVENT_TYPES(EVENT_FN_TYPE) diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index 6a64441a4a..d1d606de48 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -60,6 +60,45 @@ namespace openjdkjvmti { +void ArtJvmtiEventCallbacks::CopyExtensionsFrom(const ArtJvmtiEventCallbacks* cb) { + if (art::kIsDebugBuild) { + ArtJvmtiEventCallbacks clean; + DCHECK_EQ(memcmp(&clean, this, sizeof(clean)), 0) + << "CopyExtensionsFrom called with initialized eventsCallbacks!"; + } + if (cb != nullptr) { + memcpy(this, cb, sizeof(*this)); + } else { + memset(this, 0, sizeof(*this)); + } +} + +jvmtiError ArtJvmtiEventCallbacks::Set(jint index, jvmtiExtensionEvent cb) { + switch (index) { + case static_cast<jint>(ArtJvmtiEvent::kDdmPublishChunk): + DdmPublishChunk = reinterpret_cast<ArtJvmtiEventDdmPublishChunk>(cb); + return OK; + default: + return ERR(ILLEGAL_ARGUMENT); + } +} + + +bool IsExtensionEvent(jint e) { + return e >= static_cast<jint>(ArtJvmtiEvent::kMinEventTypeVal) && + e <= static_cast<jint>(ArtJvmtiEvent::kMaxEventTypeVal) && + IsExtensionEvent(static_cast<ArtJvmtiEvent>(e)); +} + +bool IsExtensionEvent(ArtJvmtiEvent e) { + switch (e) { + case ArtJvmtiEvent::kDdmPublishChunk: + return true; + default: + return false; + } +} + bool EventMasks::IsEnabledAnywhere(ArtJvmtiEvent event) { return global_event_mask.Test(event) || unioned_thread_event_mask.Test(event); } @@ -213,6 +252,38 @@ static void RunEventCallback(EventHandler* handler, args...); } +static void SetupDdmTracking(art::DdmCallback* listener, bool enable) { + art::ScopedObjectAccess soa(art::Thread::Current()); + if (enable) { + art::Runtime::Current()->GetRuntimeCallbacks()->AddDdmCallback(listener); + } else { + art::Runtime::Current()->GetRuntimeCallbacks()->RemoveDdmCallback(listener); + } +} + +class JvmtiDdmChunkListener : public art::DdmCallback { + public: + explicit JvmtiDdmChunkListener(EventHandler* handler) : handler_(handler) {} + + void DdmPublishChunk(uint32_t type, const art::ArrayRef<const uint8_t>& data) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kDdmPublishChunk)) { + art::Thread* self = art::Thread::Current(); + handler_->DispatchEvent<ArtJvmtiEvent::kDdmPublishChunk>( + self, + static_cast<JNIEnv*>(self->GetJniEnv()), + static_cast<jint>(type), + static_cast<jint>(data.size()), + reinterpret_cast<const jbyte*>(data.data())); + } + } + + private: + EventHandler* handler_; + + DISALLOW_COPY_AND_ASSIGN(JvmtiDdmChunkListener); +}; + class JvmtiAllocationListener : public art::gc::AllocationListener { public: explicit JvmtiAllocationListener(EventHandler* handler) : handler_(handler) {} @@ -924,6 +995,9 @@ bool EventHandler::OtherMonitorEventsEnabledAnywhere(ArtJvmtiEvent event) { // Handle special work for the given event type, if necessary. void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { switch (event) { + case ArtJvmtiEvent::kDdmPublishChunk: + SetupDdmTracking(ddm_listener_.get(), enable); + return; case ArtJvmtiEvent::kVmObjectAlloc: SetupObjectAllocationTracking(alloc_listener_.get(), enable); return; @@ -1104,6 +1178,7 @@ void EventHandler::Shutdown() { EventHandler::EventHandler() { alloc_listener_.reset(new JvmtiAllocationListener(this)); + ddm_listener_.reset(new JvmtiDdmChunkListener(this)); gc_pause_listener_.reset(new JvmtiGcPauseListener(this)); method_trace_listener_.reset(new JvmtiMethodTraceListener(this)); monitor_listener_.reset(new JvmtiMonitorListener(this)); diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h index aed24e59f3..a99ed7b212 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -28,13 +28,14 @@ namespace openjdkjvmti { struct ArtJvmTiEnv; class JvmtiAllocationListener; +class JvmtiDdmChunkListener; class JvmtiGcPauseListener; class JvmtiMethodTraceListener; class JvmtiMonitorListener; // an enum for ArtEvents. This differs from the JVMTI events only in that we distinguish between // retransformation capable and incapable loading -enum class ArtJvmtiEvent { +enum class ArtJvmtiEvent : jint { kMinEventTypeVal = JVMTI_MIN_EVENT_TYPE_VAL, kVmInit = JVMTI_EVENT_VM_INIT, kVmDeath = JVMTI_EVENT_VM_DEATH, @@ -68,9 +69,33 @@ enum class ArtJvmtiEvent { kObjectFree = JVMTI_EVENT_OBJECT_FREE, kVmObjectAlloc = JVMTI_EVENT_VM_OBJECT_ALLOC, kClassFileLoadHookRetransformable = JVMTI_MAX_EVENT_TYPE_VAL + 1, - kMaxEventTypeVal = kClassFileLoadHookRetransformable, + kDdmPublishChunk = JVMTI_MAX_EVENT_TYPE_VAL + 2, + kMaxEventTypeVal = kDdmPublishChunk, }; +using ArtJvmtiEventDdmPublishChunk = void (*)(jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jint data_type, + jint data_len, + const jbyte* data); + +struct ArtJvmtiEventCallbacks : jvmtiEventCallbacks { + ArtJvmtiEventCallbacks() : DdmPublishChunk(nullptr) { + memset(this, 0, sizeof(jvmtiEventCallbacks)); + } + + // Copies extension functions from other callback struct if it exists. There must not have been + // any modifications to this struct when it is called. + void CopyExtensionsFrom(const ArtJvmtiEventCallbacks* cb); + + jvmtiError Set(jint index, jvmtiExtensionEvent cb); + + ArtJvmtiEventDdmPublishChunk DdmPublishChunk; +}; + +bool IsExtensionEvent(jint e); +bool IsExtensionEvent(ArtJvmtiEvent e); + // Convert a jvmtiEvent into a ArtJvmtiEvent ALWAYS_INLINE static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e); @@ -245,6 +270,7 @@ class EventHandler { EventMask global_mask; std::unique_ptr<JvmtiAllocationListener> alloc_listener_; + std::unique_ptr<JvmtiDdmChunkListener> ddm_listener_; std::unique_ptr<JvmtiGcPauseListener> gc_pause_listener_; std::unique_ptr<JvmtiMethodTraceListener> method_trace_listener_; std::unique_ptr<JvmtiMonitorListener> monitor_listener_; diff --git a/openjdkjvmti/include/CPPLINT.cfg b/openjdkjvmti/include/CPPLINT.cfg new file mode 100644 index 0000000000..3e717a697f --- /dev/null +++ b/openjdkjvmti/include/CPPLINT.cfg @@ -0,0 +1,18 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# External headers, not subject to our lint rules. +exclude_files=^jvmti[.]h$ diff --git a/openjdkjvmti/jvmti_allocator.h b/openjdkjvmti/jvmti_allocator.h index e6cbc85170..11af7b67e7 100644 --- a/openjdkjvmti/jvmti_allocator.h +++ b/openjdkjvmti/jvmti_allocator.h @@ -58,7 +58,7 @@ class JvmtiAllocator<void> { JvmtiAllocator() : env_(nullptr) {} template <typename U> - JvmtiAllocator(const JvmtiAllocator<U>& other) // NOLINT, implicit + JvmtiAllocator(const JvmtiAllocator<U>& other) : env_(other.env_) {} JvmtiAllocator(const JvmtiAllocator& other) = default; @@ -95,7 +95,7 @@ class JvmtiAllocator { JvmtiAllocator() : env_(nullptr) {} template <typename U> - JvmtiAllocator(const JvmtiAllocator<U>& other) // NOLINT, implicit + JvmtiAllocator(const JvmtiAllocator<U>& other) : env_(other.env_) {} JvmtiAllocator(const JvmtiAllocator& other) = default; diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index 2c5e5f9ccf..e69c78bab1 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -33,7 +33,7 @@ #include "android-base/stringprintf.h" -#include <mutex> // NOLINT [build/c++11] [5] +#include <mutex> #include <unordered_set> #include "art_jvmti.h" diff --git a/openjdkjvmti/ti_ddms.cc b/openjdkjvmti/ti_ddms.cc new file mode 100644 index 0000000000..500a453f78 --- /dev/null +++ b/openjdkjvmti/ti_ddms.cc @@ -0,0 +1,85 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include <functional> +#include <vector> + +#include "ti_ddms.h" + +#include "art_jvmti.h" +#include "base/array_ref.h" +#include "debugger.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-inl.h" + +namespace openjdkjvmti { + +jvmtiError DDMSUtil::HandleChunk(jvmtiEnv* env, + jint type_in, + jint length_in, + const jbyte* data_in, + /*out*/jint* type_out, + /*out*/jint* data_length_out, + /*out*/jbyte** data_out) { + constexpr uint32_t kDdmHeaderSize = sizeof(uint32_t) * 2; + if (env == nullptr || data_in == nullptr || data_out == nullptr || data_length_out == nullptr) { + return ERR(NULL_POINTER); + } else if (length_in < static_cast<jint>(kDdmHeaderSize)) { + // need to get type and length at least. + return ERR(ILLEGAL_ARGUMENT); + } + + art::Thread* self = art::Thread::Current(); + art::ScopedThreadStateChange(self, art::ThreadState::kNative); + + art::ArrayRef<const jbyte> data_arr(data_in, length_in); + std::vector<uint8_t> out_data; + if (!art::Dbg::DdmHandleChunk(self->GetJniEnv(), + type_in, + data_arr, + /*out*/reinterpret_cast<uint32_t*>(type_out), + /*out*/&out_data)) { + LOG(WARNING) << "Something went wrong with handling the ddm chunk."; + return ERR(INTERNAL); + } else { + jvmtiError error = OK; + JvmtiUniquePtr<jbyte[]> ret = AllocJvmtiUniquePtr<jbyte[]>(env, out_data.size(), &error); + if (error != OK) { + return error; + } + memcpy(ret.get(), out_data.data(), out_data.size()); + *data_out = ret.release(); + *data_length_out = static_cast<jint>(out_data.size()); + return OK; + } +} + +} // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_ddms.h b/openjdkjvmti/ti_ddms.h new file mode 100644 index 0000000000..1ea7548607 --- /dev/null +++ b/openjdkjvmti/ti_ddms.h @@ -0,0 +1,53 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_OPENJDKJVMTI_TI_DDMS_H_ +#define ART_OPENJDKJVMTI_TI_DDMS_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +class DDMSUtil { + public: + static jvmtiError HandleChunk(jvmtiEnv* env, + jint type_in, + jint length_in, + const jbyte* data_in, + /*out*/ jint* type_out, + /*out*/ jint* data_length_out, + /*out*/ jbyte** data_out); +}; + +} // namespace openjdkjvmti + +#endif // ART_OPENJDKJVMTI_TI_DDMS_H_ diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc index fbed9640a0..afd0723d0f 100644 --- a/openjdkjvmti/ti_extension.cc +++ b/openjdkjvmti/ti_extension.cc @@ -34,8 +34,11 @@ #include "ti_extension.h" #include "art_jvmti.h" +#include "events.h" #include "ti_allocator.h" +#include "ti_ddms.h" #include "ti_heap.h" +#include "thread-inl.h" namespace openjdkjvmti { @@ -51,7 +54,7 @@ struct CParamInfo { JvmtiUniquePtr<char[]> param_name = CopyString(env, name, err); char* name_ptr = param_name.get(); char_buffers->push_back(std::move(param_name)); - return jvmtiParamInfo { name_ptr, kind, base_type, null_ok }; // NOLINT [whitespace/braces] [4] + return jvmtiParamInfo{ name_ptr, kind, base_type, null_ok }; } }; @@ -143,7 +146,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, "com.android.art.heap.get_object_heap_id", "Retrieve the heap id of the the object tagged with the given argument. An " "arbitrary object is chosen if multiple objects exist with the same tag.", - { // NOLINT [whitespace/braces] [4] + { { "tag", JVMTI_KIND_IN, JVMTI_TYPE_JLONG, false}, { "heap_id", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false} }, @@ -156,7 +159,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::GetHeapName), "com.android.art.heap.get_heap_name", "Retrieve the name of the heap with the given id.", - { // NOLINT [whitespace/braces] [4] + { { "heap_id", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false}, { "heap_name", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_CCHAR, false} }, @@ -172,13 +175,13 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, " except for additionally passing the heap id of the current object. The jvmtiHeapCallbacks" " structure is reused, with the callbacks field overloaded to a signature of " "jint (*)(jlong, jlong, jlong*, jint length, void*, jint).", - { // NOLINT [whitespace/braces] [4] + { { "heap_filter", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false}, { "klass", JVMTI_KIND_IN, JVMTI_TYPE_JCLASS, true}, { "callbacks", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, false}, { "user_data", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, true} }, - { // NOLINT [whitespace/braces] [4] + { ERR(MUST_POSSESS_CAPABILITY), ERR(INVALID_CLASS), ERR(NULL_POINTER), @@ -194,7 +197,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, " 'Allocate' jvmti function. This does not include any memory that has been deallocated" " through the 'Deallocate' function. This number is approximate and might not correspond" " exactly to the sum of the sizes of all not freed allocations.", - { // NOLINT [whitespace/braces] [4] + { { "currently_allocated", JVMTI_KIND_OUT, JVMTI_TYPE_JLONG, false}, }, { ERR(NULL_POINTER) }); @@ -202,6 +205,27 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, return error; } + // DDMS extension + error = add_extension( + reinterpret_cast<jvmtiExtensionFunction>(DDMSUtil::HandleChunk), + "com.android.art.internal.ddm.process_chunk", + "Handles a single ddms chunk request and returns a response. The reply data is in the ddms" + " chunk format. It returns the processed chunk. This is provided for backwards compatibility" + " reasons only. Agents should avoid making use of this extension when possible and instead" + " use the other JVMTI entrypoints explicitly.", + { + { "type_in", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "length_in", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "data_in", JVMTI_KIND_IN_BUF, JVMTI_TYPE_JBYTE, false }, + { "type_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false }, + { "data_len_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false }, + { "data_out", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_JBYTE, false } + }, + { ERR(NULL_POINTER), ERR(ILLEGAL_ARGUMENT), ERR(OUT_OF_MEMORY) }); + if (error != ERR(NONE)) { + return error; + } + // Copy into output buffer. *extension_count_ptr = ext_vector.size(); @@ -230,20 +254,133 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, } -jvmtiError ExtensionUtil::GetExtensionEvents(jvmtiEnv* env ATTRIBUTE_UNUSED, +jvmtiError ExtensionUtil::GetExtensionEvents(jvmtiEnv* env, jint* extension_count_ptr, jvmtiExtensionEventInfo** extensions) { - // We don't have any extension events at the moment. - *extension_count_ptr = 0; - *extensions = nullptr; + std::vector<jvmtiExtensionEventInfo> ext_vector; + + // Holders for allocated values. + std::vector<JvmtiUniquePtr<char[]>> char_buffers; + std::vector<JvmtiUniquePtr<jvmtiParamInfo[]>> param_buffers; + + auto add_extension = [&](ArtJvmtiEvent extension_event_index, + const char* id, + const char* short_description, + const std::vector<CParamInfo>& params) { + DCHECK(IsExtensionEvent(extension_event_index)); + jvmtiExtensionEventInfo event_info; + jvmtiError error; + + event_info.extension_event_index = static_cast<jint>(extension_event_index); + + JvmtiUniquePtr<char[]> id_ptr = CopyString(env, id, &error); + if (id_ptr == nullptr) { + return error; + } + event_info.id = id_ptr.get(); + char_buffers.push_back(std::move(id_ptr)); + + JvmtiUniquePtr<char[]> descr = CopyString(env, short_description, &error); + if (descr == nullptr) { + return error; + } + event_info.short_description = descr.get(); + char_buffers.push_back(std::move(descr)); + + event_info.param_count = params.size(); + if (!params.empty()) { + JvmtiUniquePtr<jvmtiParamInfo[]> params_ptr = + AllocJvmtiUniquePtr<jvmtiParamInfo[]>(env, params.size(), &error); + if (params_ptr == nullptr) { + return error; + } + event_info.params = params_ptr.get(); + param_buffers.push_back(std::move(params_ptr)); + + for (jint i = 0; i != event_info.param_count; ++i) { + event_info.params[i] = params[i].ToParamInfo(env, &char_buffers, &error); + if (error != OK) { + return error; + } + } + } else { + event_info.params = nullptr; + } + + ext_vector.push_back(event_info); + + return ERR(NONE); + }; + + jvmtiError error; + error = add_extension( + ArtJvmtiEvent::kDdmPublishChunk, + "com.android.art.internal.ddm.publish_chunk", + "Called when there is new ddms information that the agent or other clients can use. The" + " agent is given the 'type' of the ddms chunk and a 'data_size' byte-buffer in 'data'." + " The 'data' pointer is only valid for the duration of the publish_chunk event. The agent" + " is responsible for interpreting the information present in the 'data' buffer. This is" + " provided for backwards-compatibility support only. Agents should prefer to use relevant" + " JVMTI events and functions above listening for this event.", + { + { "jni_env", JVMTI_KIND_IN_PTR, JVMTI_TYPE_JNIENV, false }, + { "type", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "data_size", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "data", JVMTI_KIND_IN_BUF, JVMTI_TYPE_JBYTE, false }, + }); + if (error != OK) { + return error; + } + + // Copy into output buffer. + + *extension_count_ptr = ext_vector.size(); + JvmtiUniquePtr<jvmtiExtensionEventInfo[]> out_data = + AllocJvmtiUniquePtr<jvmtiExtensionEventInfo[]>(env, ext_vector.size(), &error); + if (out_data == nullptr) { + return error; + } + memcpy(out_data.get(), + ext_vector.data(), + ext_vector.size() * sizeof(jvmtiExtensionEventInfo)); + *extensions = out_data.release(); + + // Release all the buffer holders, we're OK now. + for (auto& holder : char_buffers) { + holder.release(); + } + for (auto& holder : param_buffers) { + holder.release(); + } + return OK; } -jvmtiError ExtensionUtil::SetExtensionEventCallback(jvmtiEnv* env ATTRIBUTE_UNUSED, - jint extension_event_index ATTRIBUTE_UNUSED, - jvmtiExtensionEvent callback ATTRIBUTE_UNUSED) { - // We do not have any extension events, so any call is illegal. - return ERR(ILLEGAL_ARGUMENT); +jvmtiError ExtensionUtil::SetExtensionEventCallback(jvmtiEnv* env, + jint extension_event_index, + jvmtiExtensionEvent callback, + EventHandler* event_handler) { + if (!IsExtensionEvent(extension_event_index)) { + return ERR(ILLEGAL_ARGUMENT); + } + ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env); + jvmtiEventMode mode = callback == nullptr ? JVMTI_DISABLE : JVMTI_ENABLE; + // Lock the event_info_mutex_ while we set the event to make sure it isn't lost by a concurrent + // change to the normal callbacks. + { + art::WriterMutexLock lk(art::Thread::Current(), art_env->event_info_mutex_); + if (art_env->event_callbacks.get() == nullptr) { + art_env->event_callbacks.reset(new ArtJvmtiEventCallbacks()); + } + jvmtiError err = art_env->event_callbacks->Set(extension_event_index, callback); + if (err != OK) { + return err; + } + } + return event_handler->SetEvent(art_env, + /*event_thread*/nullptr, + static_cast<ArtJvmtiEvent>(extension_event_index), + mode); } } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_extension.h b/openjdkjvmti/ti_extension.h index d705ba7f59..18133e98e2 100644 --- a/openjdkjvmti/ti_extension.h +++ b/openjdkjvmti/ti_extension.h @@ -37,6 +37,8 @@ namespace openjdkjvmti { +class EventHandler; + class ExtensionUtil { public: static jvmtiError GetExtensionFunctions(jvmtiEnv* env, @@ -49,7 +51,8 @@ class ExtensionUtil { static jvmtiError SetExtensionEventCallback(jvmtiEnv* env, jint extension_event_index, - jvmtiExtensionEvent callback); + jvmtiExtensionEvent callback, + EventHandler* event_handler); }; } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_monitor.cc b/openjdkjvmti/ti_monitor.cc index b31e9f239d..7db0566a2e 100644 --- a/openjdkjvmti/ti_monitor.cc +++ b/openjdkjvmti/ti_monitor.cc @@ -32,9 +32,9 @@ #include "ti_monitor.h" #include <atomic> -#include <chrono> // NOLINT [build/c++11] [5] -#include <condition_variable> // NOLINT [build/c++11] [5] -#include <mutex> // NOLINT [build/c++11] [5] +#include <chrono> +#include <condition_variable> +#include <mutex> #include "art_jvmti.h" #include "monitor.h" diff --git a/runtime/CPPLINT.cfg b/runtime/CPPLINT.cfg new file mode 100644 index 0000000000..6f274994a9 --- /dev/null +++ b/runtime/CPPLINT.cfg @@ -0,0 +1,18 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# External headers, not subject to our lint rules. +exclude_files=^elf[.]h$ diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h index fba9308e8e..99cdb49984 100644 --- a/runtime/base/allocator.h +++ b/runtime/base/allocator.h @@ -111,7 +111,7 @@ class TrackingAllocatorImpl : public std::allocator<T> { // Used internally by STL data structures. template <class U> - TrackingAllocatorImpl( // NOLINT, implicit + TrackingAllocatorImpl( const TrackingAllocatorImpl<U, kTag>& alloc ATTRIBUTE_UNUSED) noexcept {} // Used internally by STL data structures. diff --git a/runtime/base/arena_containers.h b/runtime/base/arena_containers.h index 2e71156ee8..dcdb92b9d8 100644 --- a/runtime/base/arena_containers.h +++ b/runtime/base/arena_containers.h @@ -143,7 +143,7 @@ class ArenaAllocatorAdapter<void> : private ArenaAllocatorAdapterKind { allocator_(allocator) { } template <typename U> - ArenaAllocatorAdapter(const ArenaAllocatorAdapter<U>& other) // NOLINT, implicit + ArenaAllocatorAdapter(const ArenaAllocatorAdapter<U>& other) : ArenaAllocatorAdapterKind(other), allocator_(other.allocator_) { } @@ -179,7 +179,7 @@ class ArenaAllocatorAdapter : private ArenaAllocatorAdapterKind { allocator_(allocator) { } template <typename U> - ArenaAllocatorAdapter(const ArenaAllocatorAdapter<U>& other) // NOLINT, implicit + ArenaAllocatorAdapter(const ArenaAllocatorAdapter<U>& other) : ArenaAllocatorAdapterKind(other), allocator_(other.allocator_) { } diff --git a/runtime/base/bit_string.h b/runtime/base/bit_string.h index 1cda021017..a2164f335d 100644 --- a/runtime/base/bit_string.h +++ b/runtime/base/bit_string.h @@ -248,7 +248,7 @@ struct BitString { // Does this bitstring contain exactly 0 characters? bool IsEmpty() const { - return (*this) == BitString{}; // NOLINT + return (*this) == BitString{}; } // Remove all BitStringChars starting at end. diff --git a/runtime/base/bit_string_test.cc b/runtime/base/bit_string_test.cc index d5610e7a73..96aa154ef3 100644 --- a/runtime/base/bit_string_test.cc +++ b/runtime/base/bit_string_test.cc @@ -47,7 +47,7 @@ BitStringChar MakeBitStringChar(size_t val) { BitString MakeBitString(std::initializer_list<size_t> values = {}) { CHECK_GE(BitString::kCapacity, values.size()); - BitString bs{}; // NOLINT + BitString bs{}; size_t i = 0; for (size_t val : values) { @@ -68,7 +68,7 @@ size_t AsUint(const T& value) { // Make max bitstring, e.g. BitString[4095,7,255] for {12,3,8} template <size_t kCount = BitString::kCapacity> BitString MakeBitStringMax() { - BitString bs{}; // NOLINT + BitString bs{}; for (size_t i = 0; i < kCount; ++i) { bs.SetAt(i, diff --git a/runtime/base/bit_struct.h b/runtime/base/bit_struct.h index 16b555e1c6..b207459419 100644 --- a/runtime/base/bit_struct.h +++ b/runtime/base/bit_struct.h @@ -288,7 +288,7 @@ using BitStructUint = // // See top of file for usage example. #define BITSTRUCT_DEFINE_END(name) \ - }; /* NOLINT [readability/braces] [4] */ \ + }; \ static_assert(art::detail::ValidateBitStructSize<name>(), \ #name "bitsize incorrect: " \ "did you insert extra fields that weren't BitStructX, " \ diff --git a/runtime/base/bit_struct_detail.h b/runtime/base/bit_struct_detail.h index 49d432e69c..912f51c7b0 100644 --- a/runtime/base/bit_struct_detail.h +++ b/runtime/base/bit_struct_detail.h @@ -79,7 +79,7 @@ struct HasUnderscoreField { using FalseT = std::integral_constant<bool, false>::type; template <typename C> - static constexpr auto Test(void*) -> decltype(std::declval<C>()._, TrueT{}); // NOLINT + static constexpr auto Test(void*) -> decltype(std::declval<C>()._, TrueT{}); template <typename> static constexpr FalseT Test(...); diff --git a/runtime/base/bit_struct_test.cc b/runtime/base/bit_struct_test.cc index a80d39eb91..577682ccce 100644 --- a/runtime/base/bit_struct_test.cc +++ b/runtime/base/bit_struct_test.cc @@ -73,7 +73,7 @@ struct CustomBitStruct { TEST(BitStructs, Custom) { CustomBitStruct expected(0b1111); - BitStructField<CustomBitStruct, /*lsb*/4, /*width*/4> f{}; // NOLINT + BitStructField<CustomBitStruct, /*lsb*/4, /*width*/4> f{}; EXPECT_EQ(1u, sizeof(f)); @@ -95,7 +95,7 @@ TEST(BitStructs, TwoCustom) { VALIDATE_BITSTRUCT_SIZE(TestTwoCustom); - TestTwoCustom cst{}; // NOLINT + TestTwoCustom cst{}; // Test the write to most-significant field doesn't clobber least-significant. cst.f4_a = CustomBitStruct(0b0110); @@ -122,7 +122,7 @@ TEST(BitStructs, TwoCustom) { } TEST(BitStructs, Number) { - BitStructNumber<uint16_t, /*lsb*/4, /*width*/4> bsn{}; // NOLINT + BitStructNumber<uint16_t, /*lsb*/4, /*width*/4> bsn{}; EXPECT_EQ(2u, sizeof(bsn)); bsn = 0b1111; @@ -154,7 +154,7 @@ TEST(BitStructs, Test1) { EXPECT_EQ(1u, sizeof(u4)); EXPECT_EQ(1u, sizeof(alias_all)); } - TestBitStruct tst{}; // NOLINT + TestBitStruct tst{}; // Check minimal size selection is correct. EXPECT_EQ(1u, sizeof(TestBitStruct)); @@ -229,7 +229,7 @@ BITSTRUCT_DEFINE_END(MixedSizeBitStruct); TEST(BitStructs, Mixed) { EXPECT_EQ(4u, sizeof(MixedSizeBitStruct)); - MixedSizeBitStruct tst{}; // NOLINT + MixedSizeBitStruct tst{}; // Check operator assignment. tst.u3 = 0b111u; @@ -263,11 +263,11 @@ BITSTRUCT_DEFINE_START(TestBitStruct_u8, /* size */ 8) BITSTRUCT_DEFINE_END(TestBitStruct_u8); TEST(BitStructs, FieldAssignment) { - TestBitStruct_u8 all_1s{}; // NOLINT + TestBitStruct_u8 all_1s{}; all_1s.alias_all = 0xffu; { - TestBitStruct_u8 tst{}; // NOLINT + TestBitStruct_u8 tst{}; tst.i3 = all_1s.i3; // Copying a single bitfield does not copy all bitfields. @@ -275,7 +275,7 @@ TEST(BitStructs, FieldAssignment) { } { - TestBitStruct_u8 tst{}; // NOLINT + TestBitStruct_u8 tst{}; tst.u4 = all_1s.u4; // Copying a single bitfield does not copy all bitfields. @@ -291,13 +291,13 @@ BITSTRUCT_DEFINE_START(NestedStruct, /* size */ 64) BITSTRUCT_DEFINE_END(NestedStruct); TEST(BitStructs, NestedFieldAssignment) { - MixedSizeBitStruct mixed_all_1s{}; // NOLINT + MixedSizeBitStruct mixed_all_1s{}; mixed_all_1s.alias_all = 0xFFFFFFFFu; { - NestedStruct xyz{}; // NOLINT + NestedStruct xyz{}; - NestedStruct other{}; // NOLINT + NestedStruct other{}; other.mixed_upper = mixed_all_1s; other.mixed_lower = mixed_all_1s; @@ -307,9 +307,9 @@ TEST(BitStructs, NestedFieldAssignment) { } { - NestedStruct xyz{}; // NOLINT + NestedStruct xyz{}; - NestedStruct other{}; // NOLINT + NestedStruct other{}; other.mixed_upper = mixed_all_1s; other.mixed_lower = mixed_all_1s; diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 877f052006..3adebe7e00 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -584,7 +584,7 @@ ReaderWriterMutex::ReaderWriterMutex(const char* name, LockLevel level) #if ART_USE_FUTEXES , state_(0), num_pending_readers_(0), num_pending_writers_(0) #endif -{ // NOLINT(whitespace/braces) +{ #if !ART_USE_FUTEXES CHECK_MUTEX_CALL(pthread_rwlock_init, (&rwlock_, nullptr)); #endif diff --git a/runtime/base/scoped_arena_containers.h b/runtime/base/scoped_arena_containers.h index fccaaeaa42..756089f438 100644 --- a/runtime/base/scoped_arena_containers.h +++ b/runtime/base/scoped_arena_containers.h @@ -110,7 +110,7 @@ class ScopedArenaAllocatorAdapter<void> arena_stack_(allocator->arena_stack_) { } template <typename U> - ScopedArenaAllocatorAdapter(const ScopedArenaAllocatorAdapter<U>& other) // NOLINT, implicit + ScopedArenaAllocatorAdapter(const ScopedArenaAllocatorAdapter<U>& other) : DebugStackReference(other), DebugStackIndirectTopRef(other), ArenaAllocatorAdapterKind(other), @@ -153,7 +153,7 @@ class ScopedArenaAllocatorAdapter arena_stack_(allocator->arena_stack_) { } template <typename U> - ScopedArenaAllocatorAdapter(const ScopedArenaAllocatorAdapter<U>& other) // NOLINT, implicit + ScopedArenaAllocatorAdapter(const ScopedArenaAllocatorAdapter<U>& other) : DebugStackReference(other), DebugStackIndirectTopRef(other), ArenaAllocatorAdapterKind(other), diff --git a/runtime/base/transform_array_ref.h b/runtime/base/transform_array_ref.h index b432f86d77..a4e0bc27ed 100644 --- a/runtime/base/transform_array_ref.h +++ b/runtime/base/transform_array_ref.h @@ -72,7 +72,7 @@ class TransformArrayRef { template <typename OtherBT, typename = typename std::enable_if<std::is_same<BaseType, const OtherBT>::value>::type> - TransformArrayRef(const TransformArrayRef<OtherBT, Function>& other) // NOLINT, implicit + TransformArrayRef(const TransformArrayRef<OtherBT, Function>& other) : TransformArrayRef(other.base(), other.GetFunction()) { } // Assignment operators. diff --git a/runtime/base/transform_array_ref_test.cc b/runtime/base/transform_array_ref_test.cc index 494dbb29aa..da0340d36b 100644 --- a/runtime/base/transform_array_ref_test.cc +++ b/runtime/base/transform_array_ref_test.cc @@ -38,7 +38,7 @@ ATTRIBUTE_UNUSED bool operator==(const ValueHolder& lhs, const ValueHolder& rhs) } // anonymous namespace TEST(TransformArrayRef, ConstRefAdd1) { - auto add1 = [](const ValueHolder& h) { return h.value + 1; }; // NOLINT [readability/braces] + auto add1 = [](const ValueHolder& h) { return h.value + 1; }; std::vector<ValueHolder> input({ 7, 6, 4, 0 }); std::vector<int> output; @@ -79,7 +79,7 @@ TEST(TransformArrayRef, ConstRefAdd1) { } TEST(TransformArrayRef, NonConstRefSub1) { - auto sub1 = [](ValueHolder& h) { return h.value - 1; }; // NOLINT [readability/braces] + auto sub1 = [](ValueHolder& h) { return h.value - 1; }; std::vector<ValueHolder> input({ 4, 4, 5, 7, 10 }); std::vector<int> output; diff --git a/runtime/base/transform_iterator.h b/runtime/base/transform_iterator.h index f1a8a52ceb..9c8f822b71 100644 --- a/runtime/base/transform_iterator.h +++ b/runtime/base/transform_iterator.h @@ -62,7 +62,7 @@ class TransformIterator { : data_(base, fn) { } template <typename OtherBI> - TransformIterator(const TransformIterator<OtherBI, Function>& other) // NOLINT, implicit + TransformIterator(const TransformIterator<OtherBI, Function>& other) : data_(other.base(), other.GetFunction()) { } diff --git a/runtime/base/transform_iterator_test.cc b/runtime/base/transform_iterator_test.cc index a85dda8958..63b6e4f531 100644 --- a/runtime/base/transform_iterator_test.cc +++ b/runtime/base/transform_iterator_test.cc @@ -41,7 +41,7 @@ bool operator==(const ValueHolder& lhs, const ValueHolder& rhs) { } // anonymous namespace TEST(TransformIterator, VectorAdd1) { - auto add1 = [](const ValueHolder& h) { return h.value + 1; }; // NOLINT [readability/braces] + auto add1 = [](const ValueHolder& h) { return h.value + 1; }; std::vector<ValueHolder> input({ 1, 7, 3, 8 }); std::vector<int> output; @@ -144,7 +144,7 @@ TEST(TransformIterator, VectorAdd1) { } TEST(TransformIterator, ListSub1) { - auto sub1 = [](const ValueHolder& h) { return h.value - 1; }; // NOLINT [readability/braces] + auto sub1 = [](const ValueHolder& h) { return h.value - 1; }; std::list<ValueHolder> input({ 2, 3, 5, 7, 11 }); std::vector<int> output; @@ -208,7 +208,7 @@ TEST(TransformIterator, ListSub1) { } TEST(TransformIterator, ForwardListSub1) { - auto mul3 = [](const ValueHolder& h) { return h.value * 3; }; // NOLINT [readability/braces] + auto mul3 = [](const ValueHolder& h) { return h.value * 3; }; std::forward_list<ValueHolder> input({ 1, 1, 2, 3, 5, 8 }); std::vector<int> output; @@ -246,7 +246,7 @@ TEST(TransformIterator, ForwardListSub1) { } TEST(TransformIterator, VectorConstReference) { - auto ref = [](const ValueHolder& h) -> const int& { return h.value; }; // NOLINT [readability/braces] + auto ref = [](const ValueHolder& h) -> const int& { return h.value; }; std::vector<ValueHolder> input({ 7, 3, 1, 2, 4, 8 }); std::vector<int> output; @@ -339,7 +339,7 @@ TEST(TransformIterator, VectorConstReference) { } TEST(TransformIterator, VectorNonConstReference) { - auto ref = [](ValueHolder& h) -> int& { return h.value; }; // NOLINT [readability/braces] + auto ref = [](ValueHolder& h) -> int& { return h.value; }; std::vector<ValueHolder> input({ 7, 3, 1, 2, 4, 8 }); std::vector<int> output; @@ -519,7 +519,7 @@ TEST(TransformIterator, VectorConstAndNonConstReference) { } TEST(TransformIterator, TransformRange) { - auto ref = [](ValueHolder& h) -> int& { return h.value; }; // NOLINT [readability/braces] + auto ref = [](ValueHolder& h) -> int& { return h.value; }; std::vector<ValueHolder> data({ 1, 0, 1, 3, 1, 0 }); for (int& v : MakeTransformRange(data, ref)) { diff --git a/runtime/base/variant_map.h b/runtime/base/variant_map.h index fe332d1e3b..c480b5162d 100644 --- a/runtime/base/variant_map.h +++ b/runtime/base/variant_map.h @@ -139,7 +139,7 @@ struct VariantMapKey : detail::VariantMapKeyRaw { // then that is used. Otherwise, the default value for the type TValue{} is returned. TValue CreateDefaultValue() const { if (default_value_ == nullptr) { - return TValue{}; // NOLINT [readability/braces] [4] + return TValue{}; } else { return TValue(*default_value_); } diff --git a/runtime/base/variant_map_test.cc b/runtime/base/variant_map_test.cc index 9dd29baf48..4677b6d3b3 100644 --- a/runtime/base/variant_map_test.cc +++ b/runtime/base/variant_map_test.cc @@ -107,8 +107,8 @@ TEST(VariantMaps, RuleOfFive) { fmFilled.Set(FruitMap::Orange, 555.0); EXPECT_EQ(size_t(2), fmFilled.Size()); - // Test copy constructor (NOLINT as a reference is suggested, instead) - FruitMap fmEmptyCopy(fmEmpty); // NOLINT + // Test copy constructor + FruitMap fmEmptyCopy(fmEmpty); EXPECT_EQ(size_t(0), fmEmptyCopy.Size()); // Test copy constructor diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 493468bb3d..ef1647caf3 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -78,11 +78,11 @@ static const uint8_t kBase64Map[256] = { 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, // NOLINT - 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, // NOLINT + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, // NOLINT - 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, // NOLINT + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, diff --git a/runtime/debugger.cc b/runtime/debugger.cc index c7f245309f..613e4fe7c7 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -18,6 +18,7 @@ #include <sys/uio.h> +#include <functional> #include <memory> #include <set> #include <vector> @@ -325,6 +326,7 @@ bool Dbg::gDebuggerActive = false; bool Dbg::gDisposed = false; ObjectRegistry* Dbg::gRegistry = nullptr; DebuggerActiveMethodInspectionCallback Dbg::gDebugActiveCallback; +DebuggerDdmCallback Dbg::gDebugDdmCallback; // Deoptimization support. std::vector<DeoptimizationRequest> Dbg::deoptimization_requests_; @@ -342,6 +344,10 @@ uint32_t Dbg::instrumentation_events_ = 0; Dbg::DbgThreadLifecycleCallback Dbg::thread_lifecycle_callback_; Dbg::DbgClassLoadCallback Dbg::class_load_callback_; +void DebuggerDdmCallback::DdmPublishChunk(uint32_t type, const ArrayRef<const uint8_t>& data) { + Dbg::DdmSendChunk(type, data); +} + bool DebuggerActiveMethodInspectionCallback::IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED) { return Dbg::IsDebuggerActive(); } @@ -531,6 +537,12 @@ void Dbg::StartJdwp() { CHECK(gRegistry == nullptr); gRegistry = new ObjectRegistry; + { + // Setup the Ddm listener + ScopedObjectAccess soa(Thread::Current()); + Runtime::Current()->GetRuntimeCallbacks()->AddDdmCallback(&gDebugDdmCallback); + } + // Init JDWP if the debugger is enabled. This may connect out to a // debugger, passively listen for a debugger, or block waiting for a // debugger. @@ -4285,47 +4297,28 @@ void Dbg::FinishInvokeMethod(DebugInvokeReq* pReq) { } } -/* - * "request" contains a full JDWP packet, possibly with multiple chunks. We - * need to process each, accumulate the replies, and ship the whole thing - * back. - * - * Returns "true" if we have a reply. The reply buffer is newly allocated, - * and includes the chunk type/length, followed by the data. - * - * OLD-TODO: we currently assume that the request and reply include a single - * chunk. If this becomes inconvenient we will need to adapt. - */ -bool Dbg::DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pReplyLen) { - Thread* self = Thread::Current(); - JNIEnv* env = self->GetJniEnv(); - - uint32_t type = request->ReadUnsigned32("type"); - uint32_t length = request->ReadUnsigned32("length"); - - // Create a byte[] corresponding to 'request'. - size_t request_length = request->size(); - ScopedLocalRef<jbyteArray> dataArray(env, env->NewByteArray(request_length)); +bool Dbg::DdmHandleChunk(JNIEnv* env, + uint32_t type, + const ArrayRef<const jbyte>& data, + /*out*/uint32_t* out_type, + /*out*/std::vector<uint8_t>* out_data) { + ScopedLocalRef<jbyteArray> dataArray(env, env->NewByteArray(data.size())); if (dataArray.get() == nullptr) { - LOG(WARNING) << "byte[] allocation failed: " << request_length; + LOG(WARNING) << "byte[] allocation failed: " << data.size(); env->ExceptionClear(); return false; } - env->SetByteArrayRegion(dataArray.get(), 0, request_length, - reinterpret_cast<const jbyte*>(request->data())); - request->Skip(request_length); - - // Run through and find all chunks. [Currently just find the first.] - ScopedByteArrayRO contents(env, dataArray.get()); - if (length != request_length) { - LOG(WARNING) << StringPrintf("bad chunk found (len=%u pktLen=%zd)", length, request_length); - return false; - } - + env->SetByteArrayRegion(dataArray.get(), + 0, + data.size(), + reinterpret_cast<const jbyte*>(data.data())); // Call "private static Chunk dispatch(int type, byte[] data, int offset, int length)". - ScopedLocalRef<jobject> chunk(env, env->CallStaticObjectMethod(WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer, - WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch, - type, dataArray.get(), 0, length)); + ScopedLocalRef<jobject> chunk( + env, + env->CallStaticObjectMethod( + WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer, + WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch, + type, dataArray.get(), 0, data.size())); if (env->ExceptionCheck()) { LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type); env->ExceptionDescribe(); @@ -4349,30 +4342,78 @@ bool Dbg::DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pRep * * So we're pretty much stuck with copying data around multiple times. */ - ScopedLocalRef<jbyteArray> replyData(env, reinterpret_cast<jbyteArray>(env->GetObjectField(chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data))); - jint offset = env->GetIntField(chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset); - length = env->GetIntField(chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length); - type = env->GetIntField(chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type); - - VLOG(jdwp) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d", type, replyData.get(), offset, length); + ScopedLocalRef<jbyteArray> replyData( + env, + reinterpret_cast<jbyteArray>( + env->GetObjectField( + chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data))); + jint offset = env->GetIntField(chunk.get(), + WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset); + jint length = env->GetIntField(chunk.get(), + WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length); + *out_type = env->GetIntField(chunk.get(), + WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type); + + VLOG(jdwp) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d", + type, + replyData.get(), + offset, + length); if (length == 0 || replyData.get() == nullptr) { return false; } - const int kChunkHdrLen = 8; - uint8_t* reply = new uint8_t[length + kChunkHdrLen]; - if (reply == nullptr) { - LOG(WARNING) << "malloc failed: " << (length + kChunkHdrLen); + out_data->resize(length); + env->GetByteArrayRegion(replyData.get(), + offset, + length, + reinterpret_cast<jbyte*>(out_data->data())); + return true; +} + +/* + * "request" contains a full JDWP packet, possibly with multiple chunks. We + * need to process each, accumulate the replies, and ship the whole thing + * back. + * + * Returns "true" if we have a reply. The reply buffer is newly allocated, + * and includes the chunk type/length, followed by the data. + * + * OLD-TODO: we currently assume that the request and reply include a single + * chunk. If this becomes inconvenient we will need to adapt. + */ +bool Dbg::DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pReplyLen) { + Thread* self = Thread::Current(); + JNIEnv* env = self->GetJniEnv(); + + uint32_t type = request->ReadUnsigned32("type"); + uint32_t length = request->ReadUnsigned32("length"); + + // Create a byte[] corresponding to 'request'. + size_t request_length = request->size(); + // Run through and find all chunks. [Currently just find the first.] + if (length != request_length) { + LOG(WARNING) << StringPrintf("bad chunk found (len=%u pktLen=%zd)", length, request_length); return false; } - JDWP::Set4BE(reply + 0, type); - JDWP::Set4BE(reply + 4, length); - env->GetByteArrayRegion(replyData.get(), offset, length, reinterpret_cast<jbyte*>(reply + kChunkHdrLen)); - *pReplyBuf = reply; - *pReplyLen = length + kChunkHdrLen; - - VLOG(jdwp) << StringPrintf("dvmHandleDdm returning type=%.4s %p len=%d", reinterpret_cast<char*>(reply), reply, length); + ArrayRef<const jbyte> data(reinterpret_cast<const jbyte*>(request->data()), request_length); + std::vector<uint8_t> out_data; + uint32_t out_type = 0; + request->Skip(request_length); + if (!DdmHandleChunk(env, type, data, &out_type, &out_data)) { + return false; + } + const uint32_t kDdmHeaderSize = 8; + *pReplyLen = out_data.size() + kDdmHeaderSize; + *pReplyBuf = new uint8_t[out_data.size() + kDdmHeaderSize]; + memcpy((*pReplyBuf) + kDdmHeaderSize, out_data.data(), out_data.size()); + JDWP::Set4BE(*pReplyBuf, out_type); + JDWP::Set4BE((*pReplyBuf) + 4, static_cast<uint32_t>(out_data.size())); + VLOG(jdwp) + << StringPrintf("dvmHandleDdm returning type=%.4s", reinterpret_cast<char*>(*pReplyBuf)) + << "0x" << std::hex << reinterpret_cast<uintptr_t>(*pReplyBuf) << std::dec + << " len= " << out_data.size(); return true; } @@ -4482,6 +4523,10 @@ void Dbg::PostThreadDeath(Thread* t) { Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THDE")); } +void Dbg::DdmSendChunk(uint32_t type, const ArrayRef<const uint8_t>& data) { + DdmSendChunk(type, data.size(), data.data()); +} + void Dbg::DdmSendChunk(uint32_t type, size_t byte_count, const uint8_t* buf) { CHECK(buf != nullptr); iovec vec[1]; diff --git a/runtime/debugger.h b/runtime/debugger.h index ec37833f6d..c3184e8374 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -27,6 +27,7 @@ #include <string> #include <vector> +#include "base/array_ref.h" #include "class_linker.h" #include "gc_root.h" #include "handle.h" @@ -52,6 +53,11 @@ class ScopedObjectAccessUnchecked; class StackVisitor; class Thread; +struct DebuggerDdmCallback : public DdmCallback { + void DdmPublishChunk(uint32_t type, const ArrayRef<const uint8_t>& data) + OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); +}; + struct DebuggerActiveMethodInspectionCallback : public MethodInspectionCallback { bool IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); @@ -647,9 +653,17 @@ class Dbg { REQUIRES_SHARED(Locks::mutator_lock_); static void DdmSetThreadNotification(bool enable) REQUIRES(!Locks::thread_list_lock_); + static bool DdmHandleChunk( + JNIEnv* env, + uint32_t type, + const ArrayRef<const jbyte>& data, + /*out*/uint32_t* out_type, + /*out*/std::vector<uint8_t>* out_data); static bool DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pReplyLen); static void DdmConnected() REQUIRES_SHARED(Locks::mutator_lock_); static void DdmDisconnected() REQUIRES_SHARED(Locks::mutator_lock_); + static void DdmSendChunk(uint32_t type, const ArrayRef<const uint8_t>& bytes) + REQUIRES_SHARED(Locks::mutator_lock_); static void DdmSendChunk(uint32_t type, const std::vector<uint8_t>& bytes) REQUIRES_SHARED(Locks::mutator_lock_); static void DdmSendChunk(uint32_t type, size_t len, const uint8_t* buf) @@ -782,6 +796,7 @@ class Dbg { static bool gDebuggerActive; static DebuggerActiveMethodInspectionCallback gDebugActiveCallback; + static DebuggerDdmCallback gDebugDdmCallback; // Indicates whether we should drop the JDWP connection because the runtime stops or the // debugger called VirtualMachine.Dispose. diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index 90bc4b8f94..c963f6e111 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -487,52 +487,52 @@ TEST_F(DexFileTest, GetMethodSignature) { "(IDJLjava/lang/Object;)Ljava/lang/Float;", "java.lang.Float GetMethodSignature.m1(int, double, long, java.lang.Object)" }, - { // NOLINT [whitespace/braces] [4] + { "m2", "(ZSC)LGetMethodSignature;", "GetMethodSignature GetMethodSignature.m2(boolean, short, char)" }, - { // NOLINT [whitespace/braces] [4] + { "m3", "()V", "void GetMethodSignature.m3()" }, - { // NOLINT [whitespace/braces] [4] + { "m4", "(I)V", "void GetMethodSignature.m4(int)" }, - { // NOLINT [whitespace/braces] [4] + { "m5", "(II)V", "void GetMethodSignature.m5(int, int)" }, - { // NOLINT [whitespace/braces] [4] + { "m6", "(II[[I)V", "void GetMethodSignature.m6(int, int, int[][])" }, - { // NOLINT [whitespace/braces] [4] + { "m7", "(II[[ILjava/lang/Object;)V", "void GetMethodSignature.m7(int, int, int[][], java.lang.Object)" }, - { // NOLINT [whitespace/braces] [4] + { "m8", "(II[[ILjava/lang/Object;[[Ljava/lang/Object;)V", "void GetMethodSignature.m8(int, int, int[][], java.lang.Object, java.lang.Object[][])" }, - { // NOLINT [whitespace/braces] [4] + { "m9", "()I", "int GetMethodSignature.m9()" }, - { // NOLINT [whitespace/braces] [4] + { "mA", "()[[I", "int[][] GetMethodSignature.mA()" }, - { // NOLINT [whitespace/braces] [4] + { "mB", "()[[Ljava/lang/Object;", "java.lang.Object[][] GetMethodSignature.mB()" diff --git a/runtime/experimental_flags.h b/runtime/experimental_flags.h index b7f40372db..5f1443814a 100644 --- a/runtime/experimental_flags.h +++ b/runtime/experimental_flags.h @@ -30,10 +30,10 @@ struct ExperimentalFlags { }; constexpr ExperimentalFlags() : value_(0x0000) {} - constexpr ExperimentalFlags(decltype(kNone) t) // NOLINT, implicit + constexpr ExperimentalFlags(decltype(kNone) t) // NOLINT [runtime/explicit] : value_(static_cast<uint32_t>(t)) {} - constexpr operator decltype(kNone)() const { // NOLINT, implicit + constexpr operator decltype(kNone)() const { return static_cast<decltype(kNone)>(value_); } diff --git a/runtime/indenter.h b/runtime/indenter.h index cc6d4c4e96..69b973252d 100644 --- a/runtime/indenter.h +++ b/runtime/indenter.h @@ -32,7 +32,7 @@ class Indenter : public std::streambuf { public: Indenter(std::streambuf* out, char text, size_t count) : indent_next_(true), out_sbuf_(out), - text_{text, text, text, text, text, text, text, text}, // NOLINT(whitespace/braces) + text_{text, text, text, text, text, text, text, text}, count_(count) {} private: diff --git a/runtime/mem_map.h b/runtime/mem_map.h index 36a24169d5..5603963eac 100644 --- a/runtime/mem_map.h +++ b/runtime/mem_map.h @@ -21,7 +21,7 @@ #include <sys/types.h> #include <map> -#include <mutex> // NOLINT [build/c++11] [5] +#include <mutex> #include <string> #include "android-base/thread_annotations.h" diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc index f8f4b1f0ad..c79f51b51e 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc @@ -16,6 +16,7 @@ #include "org_apache_harmony_dalvik_ddmc_DdmServer.h" +#include "base/array_ref.h" #include "base/logging.h" #include "debugger.h" #include "jni_internal.h" @@ -31,7 +32,9 @@ static void DdmServer_nativeSendChunk(JNIEnv* env, jclass, jint type, ScopedFastNativeObjectAccess soa(env); ScopedByteArrayRO data(env, javaData); DCHECK_LE(offset + length, static_cast<int32_t>(data.size())); - Dbg::DdmSendChunk(type, length, reinterpret_cast<const uint8_t*>(&data[offset])); + ArrayRef<const uint8_t> chunk(reinterpret_cast<const uint8_t*>(&data[offset]), + static_cast<size_t>(length)); + Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(static_cast<uint32_t>(type), chunk); } static JNINativeMethod gMethods[] = { diff --git a/runtime/obj_ptr.h b/runtime/obj_ptr.h index 9a66983de7..70e767acf6 100644 --- a/runtime/obj_ptr.h +++ b/runtime/obj_ptr.h @@ -49,23 +49,22 @@ class ObjPtr { // Note: The following constructors allow implicit conversion. This simplifies code that uses // them, e.g., for parameter passing. However, in general, implicit-conversion constructors - // are discouraged and detected by cpplint and clang-tidy. So mark these constructors - // as NOLINT (without category, as the categories are different). + // are discouraged and detected by clang-tidy. - ALWAYS_INLINE ObjPtr(std::nullptr_t) // NOLINT + ALWAYS_INLINE ObjPtr(std::nullptr_t) REQUIRES_SHARED(Locks::mutator_lock_) : reference_(0u) {} template <typename Type, typename = typename std::enable_if<std::is_base_of<MirrorType, Type>::value>::type> - ALWAYS_INLINE ObjPtr(Type* ptr) // NOLINT + ALWAYS_INLINE ObjPtr(Type* ptr) REQUIRES_SHARED(Locks::mutator_lock_) : reference_(Encode(static_cast<MirrorType*>(ptr))) { } template <typename Type, typename = typename std::enable_if<std::is_base_of<MirrorType, Type>::value>::type> - ALWAYS_INLINE ObjPtr(const ObjPtr<Type>& other) // NOLINT + ALWAYS_INLINE ObjPtr(const ObjPtr<Type>& other) REQUIRES_SHARED(Locks::mutator_lock_) : reference_(kObjPtrPoisoningValidateOnCopy ? Encode(static_cast<MirrorType*>(other.Ptr())) diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index cc09a776b8..85af560ce3 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -548,7 +548,7 @@ bool ParsedOptions::DoParse(const RuntimeOptions& options, // If not low memory mode, semispace otherwise. gc::CollectorType background_collector_type_; - gc::CollectorType collector_type_ = (XGcOption{}).collector_type_; // NOLINT [whitespace/braces] [5] + gc::CollectorType collector_type_ = (XGcOption{}).collector_type_; bool low_memory_mode_ = args.Exists(M::LowMemoryMode); background_collector_type_ = args.GetOrDefault(M::BackgroundGc); diff --git a/runtime/prebuilt_tools_test.cc b/runtime/prebuilt_tools_test.cc index 6fa9b3424d..158d9d6e69 100644 --- a/runtime/prebuilt_tools_test.cc +++ b/runtime/prebuilt_tools_test.cc @@ -29,7 +29,7 @@ class PrebuiltToolsTest : public CommonRuntimeTest { }; static void CheckToolsExist(const std::string& tools_dir) { - const char* tools[] { "as", "objcopy", "objdump" }; // NOLINT + const char* tools[] = { "as", "objcopy", "objdump" }; for (const char* tool : tools) { struct stat exec_st; std::string exec_path = tools_dir + tool; @@ -50,7 +50,7 @@ TEST_F(PrebuiltToolsTest, CheckHostTools) { TEST_F(PrebuiltToolsTest, CheckTargetTools) { // Other prebuilts are missing from the build server's repo manifest. - InstructionSet isas[] = { InstructionSet::kThumb2 }; // NOLINT + InstructionSet isas[] = { InstructionSet::kThumb2 }; for (InstructionSet isa : isas) { std::string tools_dir = GetAndroidTargetToolsDir(isa); if (tools_dir.empty()) { diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc index 720a9d6219..1e7fc3ee92 100644 --- a/runtime/reference_table_test.cc +++ b/runtime/reference_table_test.cc @@ -16,7 +16,7 @@ #include "reference_table.h" -#include <regex> // NOLINT [build/c++11] [5] +#include <regex> #include "android-base/stringprintf.h" diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc index 339fe822fd..40d7889565 100644 --- a/runtime/runtime_callbacks.cc +++ b/runtime/runtime_callbacks.cc @@ -35,6 +35,20 @@ static inline void Remove(T* cb, std::vector<T*>* data) { } } +void RuntimeCallbacks::AddDdmCallback(DdmCallback* cb) { + ddm_callbacks_.push_back(cb); +} + +void RuntimeCallbacks::RemoveDdmCallback(DdmCallback* cb) { + Remove(cb, &ddm_callbacks_); +} + +void RuntimeCallbacks::DdmPublishChunk(uint32_t type, const ArrayRef<const uint8_t>& data) { + for (DdmCallback* cb : ddm_callbacks_) { + cb->DdmPublishChunk(type, data); + } +} + void RuntimeCallbacks::AddMethodInspectionCallback(MethodInspectionCallback* cb) { method_inspection_callbacks_.push_back(cb); } diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h index c1ba9643a7..baf941a8e1 100644 --- a/runtime/runtime_callbacks.h +++ b/runtime/runtime_callbacks.h @@ -19,6 +19,7 @@ #include <vector> +#include "base/array_ref.h" #include "base/macros.h" #include "base/mutex.h" #include "dex_file.h" @@ -54,6 +55,13 @@ class ThreadLifecycleCallback; // any state checking (is the listener enabled) in the listener itself. For an example, see // Dbg. +class DdmCallback { + public: + virtual ~DdmCallback() {} + virtual void DdmPublishChunk(uint32_t type, const ArrayRef<const uint8_t>& data) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; +}; + class RuntimeSigQuitCallback { public: virtual ~RuntimeSigQuitCallback() {} @@ -182,6 +190,13 @@ class RuntimeCallbacks { void RemoveMethodInspectionCallback(MethodInspectionCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_); + // DDMS callbacks + void DdmPublishChunk(uint32_t type, const ArrayRef<const uint8_t>& data) + REQUIRES_SHARED(Locks::mutator_lock_); + + void AddDdmCallback(DdmCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_); + void RemoveDdmCallback(DdmCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_); + private: std::vector<ThreadLifecycleCallback*> thread_callbacks_ GUARDED_BY(Locks::mutator_lock_); @@ -197,6 +212,8 @@ class RuntimeCallbacks { GUARDED_BY(Locks::mutator_lock_); std::vector<MethodInspectionCallback*> method_inspection_callbacks_ GUARDED_BY(Locks::mutator_lock_); + std::vector<DdmCallback*> ddm_callbacks_ + GUARDED_BY(Locks::mutator_lock_); }; } // namespace art diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc index d283c79960..0b69851a55 100644 --- a/runtime/runtime_callbacks_test.cc +++ b/runtime/runtime_callbacks_test.cc @@ -22,7 +22,7 @@ #include <initializer_list> #include <memory> -#include <mutex> // NOLINT [build/c++11] [5] +#include <mutex> #include <string> #include "jni.h" diff --git a/runtime/runtime_options.cc b/runtime/runtime_options.cc index b072bb0c37..bce0d81cfb 100644 --- a/runtime/runtime_options.cc +++ b/runtime/runtime_options.cc @@ -30,7 +30,7 @@ namespace art { // Specify storage for the RuntimeOptions keys. -#define RUNTIME_OPTIONS_KEY(Type, Name, ...) const RuntimeArgumentMap::Key<Type> RuntimeArgumentMap::Name {__VA_ARGS__}; // NOLINT [readability/braces] [4] +#define RUNTIME_OPTIONS_KEY(Type, Name, ...) const RuntimeArgumentMap::Key<Type> RuntimeArgumentMap::Name {__VA_ARGS__}; #include "runtime_options.def" } // namespace art diff --git a/runtime/stack_map.h b/runtime/stack_map.h index db776eaa1a..85c734ee4c 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -711,7 +711,11 @@ class StackMapEncoding { total_bit_size_ += MinimumBitsToStore(native_pc_max); dex_pc_bit_offset_ = total_bit_size_; - total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max); + // Note: We're not encoding the dex pc if there is none. That's the case + // for an intrinsified native method, such as String.charAt(). + if (dex_pc_max != dex::kDexNoIndex) { + total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max); + } // We also need +1 for kNoDexRegisterMap, but since the size is strictly // greater than any offset we might try to encode, we already implicitly have it. diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h index 24f07632ce..1556abe67f 100644 --- a/runtime/subtype_check.h +++ b/runtime/subtype_check.h @@ -274,7 +274,7 @@ struct SubtypeCheck { // more complicated (e.g. ObjPtr or Depth call) will fail dchecks. // OK. zero-initializing subtype_check_info_ puts us into the kUninitialized state. - SubtypeCheckBits scb_uninitialized = SubtypeCheckBits{}; // NOLINT + SubtypeCheckBits scb_uninitialized = SubtypeCheckBits{}; WriteSubtypeCheckBits(klass, scb_uninitialized); // Do not use "SubtypeCheckInfo" API here since that requires Depth() diff --git a/runtime/subtype_check_info.h b/runtime/subtype_check_info.h index d10d4728ad..cd579c3a5c 100644 --- a/runtime/subtype_check_info.h +++ b/runtime/subtype_check_info.h @@ -188,7 +188,7 @@ struct SubtypeCheckInfo { // Returns a new root SubtypeCheckInfo with a blank PathToRoot. // Post-condition: The return valued has an Assigned state. static SubtypeCheckInfo CreateRoot() { - SubtypeCheckInfo io{}; // NOLINT + SubtypeCheckInfo io{}; io.depth_ = 0u; io.SetNext(io.GetNext() + 1u); @@ -220,7 +220,7 @@ struct SubtypeCheckInfo { child.MaybeInitNext(); // Always clear the inherited Parent's next Value on the child. - OverwriteNextValueFromParent(/*inout*/&child, BitStringChar{}); // NOLINT + OverwriteNextValueFromParent(/*inout*/&child, BitStringChar{}); // The state is now Initialized | Overflowed. DCHECK_NE(kAssigned, child.GetState()) << child.GetBitString(); @@ -392,7 +392,7 @@ struct SubtypeCheckInfo { // Clearing out the "Next" value like this // is often an intermediate operation which temporarily // violates the invariants. Do not do the extra dchecks. - SetNextUnchecked(BitStringChar{}); // NOLINT + SetNextUnchecked(BitStringChar{}); SetNextUnchecked(GetNext()+1u); } } diff --git a/runtime/subtype_check_info_test.cc b/runtime/subtype_check_info_test.cc index bc2e84e37d..338d75a285 100644 --- a/runtime/subtype_check_info_test.cc +++ b/runtime/subtype_check_info_test.cc @@ -47,7 +47,7 @@ BitStringChar MakeBitStringChar(size_t val) { BitString MakeBitString(std::initializer_list<size_t> values = {}) { CHECK_GE(BitString::kCapacity, values.size()); - BitString bs{}; // NOLINT + BitString bs{}; size_t i = 0; for (size_t val : values) { @@ -68,7 +68,7 @@ size_t AsUint(const T& value) { // Make max bistring, e.g. BitString[4095,7,255] for {12,3,8} template <size_t kCount = BitString::kCapacity> BitString MakeBitStringMax() { - BitString bs{}; // NOLINT + BitString bs{}; for (size_t i = 0; i < kCount; ++i) { bs.SetAt(i, @@ -132,7 +132,7 @@ struct SubtypeCheckInfoTest : public ::testing::Test { // Create an SubtypeCheckInfo with the same depth, but with everything else reset. // Returns: SubtypeCheckInfo in the Uninitialized state. static SubtypeCheckInfo CopyCleared(SubtypeCheckInfo sc) { - SubtypeCheckInfo cleared_copy{}; // NOLINT + SubtypeCheckInfo cleared_copy{}; cleared_copy.depth_ = sc.depth_; DCHECK_EQ(SubtypeCheckInfo::kUninitialized, cleared_copy.GetState()); return cleared_copy; @@ -260,7 +260,7 @@ TEST_F(SubtypeCheckInfoTest, EncodedPathToRoot) { SubtypeCheckInfo io = MakeSubtypeCheckInfo(/*path_to_root*/MakeBitStringMax(), - /*next*/BitStringChar{}, // NOLINT + /*next*/BitStringChar{}, /*overflow*/false, /*depth*/BitString::kCapacity); // 0b11111...000 where MSB == 1, and leading 1s = the maximum bitstring representation. @@ -329,7 +329,7 @@ TEST_F(SubtypeCheckInfoTest, CopyCleared) { // CopyCleared is just a thin wrapper around value-init and providing the depth. SubtypeCheckInfo cleared_copy_value = - SubtypeCheckInfo::Create(SubtypeCheckBits{}, /*depth*/1u); // NOLINT + SubtypeCheckInfo::Create(SubtypeCheckBits{}, /*depth*/1u); EXPECT_EQ(SubtypeCheckInfo::kUninitialized, cleared_copy_value.GetState()); EXPECT_EQ(MakeBitString({}), GetPathToRoot(cleared_copy_value)); } diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc index 6800d027f8..b8ab51b629 100644 --- a/sigchainlib/sigchain.cc +++ b/sigchainlib/sigchain.cc @@ -30,7 +30,7 @@ #include <string.h> #include <initializer_list> -#include <mutex> // NOLINT [build/c++11] [5] +#include <mutex> #include <utility> #include "sigchain.h" diff --git a/test/1919-vminit-thread-start-timing/vminit.cc b/test/1919-vminit-thread-start-timing/vminit.cc index c492e8bb62..109c61f05c 100644 --- a/test/1919-vminit-thread-start-timing/vminit.cc +++ b/test/1919-vminit-thread-start-timing/vminit.cc @@ -16,8 +16,8 @@ #include "1919-vminit-thread-start-timing/vminit.h" -#include <mutex> // NOLINT [build/c++11] [5] -#include <thread> // NOLINT [build/c++11] [5] +#include <mutex> +#include <thread> #include <vector> #include <jni.h> diff --git a/test/1940-ddms-ext/ddm_ext.cc b/test/1940-ddms-ext/ddm_ext.cc new file mode 100644 index 0000000000..cc29df9a49 --- /dev/null +++ b/test/1940-ddms-ext/ddm_ext.cc @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jvmti.h" + +// Test infrastructure +#include "jvmti_helper.h" +#include "nativehelper/scoped_local_ref.h" +#include "nativehelper/scoped_primitive_array.h" +#include "test_env.h" + +namespace art { +namespace Test1940DdmExt { + +typedef jvmtiError (*DdmHandleChunk)(jvmtiEnv* env, + jint type_in, + jint len_in, + const jbyte* data_in, + jint* type_out, + jint* len_data_out, + jbyte** data_out); + +struct DdmsTrackingData { + DdmHandleChunk send_ddm_chunk; + jclass test_klass; + jmethodID publish_method; +}; + +template <typename T> +static void Dealloc(T* t) { + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(t)); +} + +template <typename T, typename ...Rest> +static void Dealloc(T* t, Rest... rs) { + Dealloc(t); + Dealloc(rs...); +} + +extern "C" JNIEXPORT jobject JNICALL Java_art_Test1940_processChunk(JNIEnv* env, + jclass, + jobject chunk) { + DdmsTrackingData* data = nullptr; + if (JvmtiErrorToException( + env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { + return nullptr; + } + CHECK(chunk != nullptr); + CHECK(data != nullptr); + CHECK(data->send_ddm_chunk != nullptr); + ScopedLocalRef<jclass> chunk_class(env, env->FindClass("org/apache/harmony/dalvik/ddmc/Chunk")); + if (env->ExceptionCheck()) { + return nullptr; + } + jfieldID type_field_id = env->GetFieldID(chunk_class.get(), "type", "I"); + jfieldID offset_field_id = env->GetFieldID(chunk_class.get(), "offset", "I"); + jfieldID length_field_id = env->GetFieldID(chunk_class.get(), "length", "I"); + jfieldID data_field_id = env->GetFieldID(chunk_class.get(), "data", "[B"); + jint type = env->GetIntField(chunk, type_field_id); + jint off = env->GetIntField(chunk, offset_field_id); + jint len = env->GetIntField(chunk, length_field_id); + ScopedLocalRef<jbyteArray> chunk_buf( + env, reinterpret_cast<jbyteArray>(env->GetObjectField(chunk, data_field_id))); + if (env->ExceptionCheck()) { + return nullptr; + } + ScopedByteArrayRO byte_data(env, chunk_buf.get()); + jint out_type; + jint out_size; + jbyte* out_data; + if (JvmtiErrorToException(env, jvmti_env, data->send_ddm_chunk(jvmti_env, + type, + len, + &byte_data[off], + /*out*/&out_type, + /*out*/&out_size, + /*out*/&out_data))) { + return nullptr; + } else { + ScopedLocalRef<jbyteArray> chunk_data(env, env->NewByteArray(out_size)); + env->SetByteArrayRegion(chunk_data.get(), 0, out_size, out_data); + Dealloc(out_data); + ScopedLocalRef<jobject> res(env, env->NewObject(chunk_class.get(), + env->GetMethodID(chunk_class.get(), + "<init>", + "(I[BII)V"), + out_type, + chunk_data.get(), + 0, + out_size)); + return res.release(); + } +} + +static void DeallocParams(jvmtiParamInfo* params, jint n_params) { + for (jint i = 0; i < n_params; i++) { + Dealloc(params[i].name); + } +} + +static void JNICALL PublishCB(jvmtiEnv* jvmti, JNIEnv* jnienv, jint type, jint size, jbyte* bytes) { + DdmsTrackingData* data = nullptr; + if (JvmtiErrorToException(jnienv, jvmti, + jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { + return; + } + ScopedLocalRef<jbyteArray> res(jnienv, jnienv->NewByteArray(size)); + jnienv->SetByteArrayRegion(res.get(), 0, size, bytes); + jnienv->CallStaticVoidMethod(data->test_klass, data->publish_method, type, res.get()); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1940_initializeTest(JNIEnv* env, + jclass, + jclass method_klass, + jobject publish_method) { + void* old_data = nullptr; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) { + return; + } else if (old_data != nullptr) { + ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Environment already has local storage set!"); + return; + } + DdmsTrackingData* data = nullptr; + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->Allocate(sizeof(DdmsTrackingData), + reinterpret_cast<unsigned char**>(&data)))) { + return; + } + memset(data, 0, sizeof(DdmsTrackingData)); + data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(method_klass)); + data->publish_method = env->FromReflectedMethod(publish_method); + if (env->ExceptionCheck()) { + return; + } + // Get the extensions. + jint n_ext = 0; + jvmtiExtensionFunctionInfo* infos = nullptr; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionFunctions(&n_ext, &infos))) { + return; + } + for (jint i = 0; i < n_ext; i++) { + jvmtiExtensionFunctionInfo* cur_info = &infos[i]; + if (strcmp("com.android.art.internal.ddm.process_chunk", cur_info->id) == 0) { + data->send_ddm_chunk = reinterpret_cast<DdmHandleChunk>(cur_info->func); + } + // Cleanup the cur_info + DeallocParams(cur_info->params, cur_info->param_count); + Dealloc(cur_info->id, cur_info->short_description, cur_info->params, cur_info->errors); + } + // Cleanup the array. + Dealloc(infos); + if (data->send_ddm_chunk == nullptr) { + ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Unable to find memory tracking extensions."); + return; + } + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) { + return; + } + + jint event_index = -1; + bool found_event = false; + jvmtiExtensionEventInfo* events = nullptr; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionEvents(&n_ext, &events))) { + return; + } + for (jint i = 0; i < n_ext; i++) { + jvmtiExtensionEventInfo* cur_info = &events[i]; + if (strcmp("com.android.art.internal.ddm.publish_chunk", cur_info->id) == 0) { + found_event = true; + event_index = cur_info->extension_event_index; + } + // Cleanup the cur_info + DeallocParams(cur_info->params, cur_info->param_count); + Dealloc(cur_info->id, cur_info->short_description, cur_info->params); + } + // Cleanup the array. + Dealloc(events); + if (!found_event) { + ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Unable to find ddms extension event."); + return; + } + JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetExtensionEventCallback( + event_index, reinterpret_cast<jvmtiExtensionEvent>(PublishCB))); + return; +} + +} // namespace Test1940DdmExt +} // namespace art diff --git a/test/1940-ddms-ext/expected.txt b/test/1940-ddms-ext/expected.txt new file mode 100644 index 0000000000..cf4ad50e90 --- /dev/null +++ b/test/1940-ddms-ext/expected.txt @@ -0,0 +1,7 @@ +Sending data [1, 2, 3, 4, 5, 6, 7, 8] +MyDdmHandler: Chunk received: Chunk(Type: 0xDEADBEEF, Len: 8, data: [1, 2, 3, 4, 5, 6, 7, 8]) +MyDdmHandler: Putting value 0x800025 +MyDdmHandler: Chunk returned: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, -128, 0, 37]) +JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, -128, 0, 37]) +Sending chunk: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16]) +Chunk published: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16]) diff --git a/test/1940-ddms-ext/info.txt b/test/1940-ddms-ext/info.txt new file mode 100644 index 0000000000..e1d35ae026 --- /dev/null +++ b/test/1940-ddms-ext/info.txt @@ -0,0 +1 @@ +Tests the jvmti-extension to get allocated memory snapshot. diff --git a/test/1940-ddms-ext/run b/test/1940-ddms-ext/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/1940-ddms-ext/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +./default-run "$@" --jvmti diff --git a/test/1940-ddms-ext/src-art/art/Test1940.java b/test/1940-ddms-ext/src-art/art/Test1940.java new file mode 100644 index 0000000000..f0ee7102a9 --- /dev/null +++ b/test/1940-ddms-ext/src-art/art/Test1940.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +import org.apache.harmony.dalvik.ddmc.*; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.zip.Adler32; +import java.nio.*; + +public class Test1940 { + public static final int DDMS_TYPE_INDEX = 0; + public static final int DDMS_LEN_INDEX = 4; + public static final int DDMS_HEADER_LENGTH = 8; + public static final int MY_DDMS_TYPE = 0xDEADBEEF; + public static final int MY_DDMS_RESPONSE_TYPE = 0xFADE7357; + + public static final class TestError extends Error { + public TestError(String s) { super(s); } + } + + private static void checkEq(Object a, Object b) { + if (!a.equals(b)) { + throw new TestError("Failure: " + a + " != " + b); + } + } + + private static String printChunk(Chunk k) { + byte[] out = new byte[k.length]; + System.arraycopy(k.data, k.offset, out, 0, k.length); + return String.format("Chunk(Type: 0x%X, Len: %d, data: %s)", + k.type, k.length, Arrays.toString(out)); + } + + private static final class MyDdmHandler extends ChunkHandler { + public void connected() {} + public void disconnected() {} + public Chunk handleChunk(Chunk req) { + // For this test we will simply calculate the checksum + checkEq(req.type, MY_DDMS_TYPE); + System.out.println("MyDdmHandler: Chunk received: " + printChunk(req)); + ByteBuffer b = ByteBuffer.wrap(new byte[8]); + Adler32 a = new Adler32(); + a.update(req.data, req.offset, req.length); + b.order(ByteOrder.BIG_ENDIAN); + long val = a.getValue(); + b.putLong(val); + System.out.printf("MyDdmHandler: Putting value 0x%X\n", val); + Chunk ret = new Chunk(MY_DDMS_RESPONSE_TYPE, b.array(), 0, 8); + System.out.println("MyDdmHandler: Chunk returned: " + printChunk(ret)); + return ret; + } + } + + public static final ChunkHandler SINGLE_HANDLER = new MyDdmHandler(); + + public static void HandlePublish(int type, byte[] data) { + System.out.println("Chunk published: " + printChunk(new Chunk(type, data, 0, data.length))); + } + + public static void run() throws Exception { + initializeTest( + Test1940.class, + Test1940.class.getDeclaredMethod("HandlePublish", Integer.TYPE, new byte[0].getClass())); + // Test sending chunk directly. + DdmServer.registerHandler(MY_DDMS_TYPE, SINGLE_HANDLER); + DdmServer.registrationComplete(); + byte[] data = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; + System.out.println("Sending data " + Arrays.toString(data)); + Chunk res = processChunk(data); + System.out.println("JVMTI returned chunk: " + printChunk(res)); + + // Test sending chunk through DdmServer#sendChunk + Chunk c = new Chunk( + MY_DDMS_TYPE, new byte[] { 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }, 0, 8); + System.out.println("Sending chunk: " + printChunk(c)); + DdmServer.sendChunk(c); + } + + private static Chunk processChunk(byte[] val) { + return processChunk(new Chunk(MY_DDMS_TYPE, val, 0, val.length)); + } + + private static native void initializeTest(Class<?> k, Method m); + private static native Chunk processChunk(Chunk val); +} diff --git a/test/1940-ddms-ext/src/Main.java b/test/1940-ddms-ext/src/Main.java new file mode 100644 index 0000000000..1fd4cd3e41 --- /dev/null +++ b/test/1940-ddms-ext/src/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) throws Exception { + art.Test1940.run(); + } +} diff --git a/test/1940-ddms-ext/src/art/Test1940.java b/test/1940-ddms-ext/src/art/Test1940.java new file mode 100644 index 0000000000..c8dc19ca7f --- /dev/null +++ b/test/1940-ddms-ext/src/art/Test1940.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +public class Test1940 { + public static void run() throws Exception { + throw new RuntimeException("Should not be called. Should use src-art/art/Test1940.java"); + } +} diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java index ca8108f058..c4cc3b0121 100644 --- a/test/530-checker-lse/src/Main.java +++ b/test/530-checker-lse/src/Main.java @@ -949,6 +949,56 @@ public class Main { return array[1] + array[i]; } + /// CHECK-START: int Main.testExitMerge(boolean) load_store_elimination (before) + /// CHECK: NewInstance + /// CHECK: InstanceFieldSet + /// CHECK: InstanceFieldGet + /// CHECK: Return + /// CHECK: InstanceFieldSet + /// CHECK: Throw + + /// CHECK-START: int Main.testExitMerge(boolean) load_store_elimination (after) + /// CHECK-NOT: NewInstance + /// CHECK-NOT: InstanceFieldSet + /// CHECK-NOT: InstanceFieldGet + /// CHECK: Return + /// CHECK-NOT: InstanceFieldSet + /// CHECK: Throw + private static int testExitMerge(boolean cond) { + TestClass obj = new TestClass(); + if (cond) { + obj.i = 1; + return obj.i + 1; + } else { + obj.i = 2; + throw new Error(); + } + } + + /// CHECK-START: int Main.testExitMerge2(boolean) load_store_elimination (before) + /// CHECK: NewInstance + /// CHECK: InstanceFieldSet + /// CHECK: InstanceFieldGet + /// CHECK: InstanceFieldSet + /// CHECK: InstanceFieldGet + + /// CHECK-START: int Main.testExitMerge2(boolean) load_store_elimination (after) + /// CHECK-NOT: NewInstance + /// CHECK-NOT: InstanceFieldSet + /// CHECK-NOT: InstanceFieldGet + private static int testExitMerge2(boolean cond) { + TestClass obj = new TestClass(); + int res; + if (cond) { + obj.i = 1; + res = obj.i + 1; + } else { + obj.i = 2; + res = obj.j + 2; + } + return res; + } + static void assertIntEquals(int result, int expected) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); @@ -1039,6 +1089,10 @@ public class Main { assertIntEquals(testStoreStore().j, 43); assertIntEquals(testStoreStoreWithDeoptimize(new int[4]), 4); + assertIntEquals(testExitMerge(true), 2); + assertIntEquals(testExitMerge2(true), 2); + assertIntEquals(testExitMerge2(false), 2); + int ret = testNoSideEffects(iarray); assertIntEquals(iarray[0], 101); assertIntEquals(iarray[1], 102); diff --git a/test/626-checker-arm64-scratch-register/smali/Smali.smali b/test/626-checker-arm64-scratch-register/smali/Smali.smali deleted file mode 100644 index e6943cf717..0000000000 --- a/test/626-checker-arm64-scratch-register/smali/Smali.smali +++ /dev/null @@ -1,2119 +0,0 @@ -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -.class public LSmali; -.super Ljava/lang/Object; -.field b00:Z -.field b01:Z -.field b02:Z -.field b03:Z -.field b04:Z -.field b05:Z -.field b06:Z -.field b07:Z -.field b08:Z -.field b09:Z -.field b10:Z -.field b11:Z -.field b12:Z -.field b13:Z -.field b14:Z -.field b15:Z -.field b16:Z -.field b17:Z -.field b18:Z -.field b19:Z -.field b20:Z -.field b21:Z -.field b22:Z -.field b23:Z -.field b24:Z -.field b25:Z -.field b26:Z -.field b27:Z -.field b28:Z -.field b29:Z -.field b30:Z -.field b31:Z -.field b32:Z -.field b33:Z -.field b34:Z -.field b35:Z -.field b36:Z - -.field conditionA:Z -.field conditionB:Z -.field conditionC:Z - -.method public constructor <init>()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;-><init>()V - return-void -.end method - -## CHECK-START-ARM64: void Smali.test() register (after) -## CHECK: begin_block -## CHECK: name "B0" -## CHECK: <<This:l\d+>> ParameterValue -## CHECK: end_block -## CHECK: begin_block -## CHECK: successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>" -## CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Smali.conditionB -## CHECK: If [<<CondB>>] -## CHECK: end_block -## CHECK: begin_block -## CHECK: name "<<ElseBlock>>" -## CHECK: ParallelMove moves:[40(sp)->d0,24(sp)->32(sp),28(sp)->36(sp),d0->d3,d3->d4,d2->d5,d4->d6,d5->d7,d6->d18,d7->d19,d18->d20,d19->d21,d20->d22,d21->d23,d22->d10,d23->d11,16(sp)->24(sp),20(sp)->28(sp),d10->d14,d11->d12,d12->d13,d13->d1,d14->d2,32(sp)->16(sp),36(sp)->20(sp)] -## CHECK: end_block - -## CHECK-START-ARM64: void Smali.test() disassembly (after) -## CHECK: begin_block -## CHECK: name "B0" -## CHECK: <<This:l\d+>> ParameterValue -## CHECK: end_block -## CHECK: begin_block -## CHECK: successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>" -## CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Smali.conditionB -## CHECK: If [<<CondB>>] -## CHECK: end_block -## CHECK: begin_block -## CHECK: name "<<ElseBlock>>" -## CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid] -## CHECK: fmov d31, d2 -## CHECK: ldr s2, [sp, #36] -## CHECK: ldr w16, [sp, #16] -## CHECK: str w16, [sp, #36] -## CHECK: str s14, [sp, #16] -## CHECK: ldr s14, [sp, #28] -## CHECK: str s1, [sp, #28] -## CHECK: ldr s1, [sp, #32] -## CHECK: str s31, [sp, #32] -## CHECK: ldr s31, [sp, #20] -## CHECK: str s31, [sp, #40] -## CHECK: str s12, [sp, #20] -## CHECK: fmov d12, d11 -## CHECK: fmov d11, d10 -## CHECK: fmov d10, d23 -## CHECK: fmov d23, d22 -## CHECK: fmov d22, d21 -## CHECK: fmov d21, d20 -## CHECK: fmov d20, d19 -## CHECK: fmov d19, d18 -## CHECK: fmov d18, d7 -## CHECK: fmov d7, d6 -## CHECK: fmov d6, d5 -## CHECK: fmov d5, d4 -## CHECK: fmov d4, d3 -## CHECK: fmov d3, d13 -## CHECK: ldr s13, [sp, #24] -## CHECK: str s3, [sp, #24] -## CHECK: ldr s3, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100) -## CHECK: end_block -.method public test()V - .registers 45 - - const-string v39, "" - - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b17:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_367 - - const/16 v19, 0x0 - - :goto_c - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b16:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_36b - - const/16 v18, 0x0 - - :goto_16 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b18:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_36f - - const/16 v20, 0x0 - - :goto_20 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b19:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_373 - - const/16 v21, 0x0 - - :goto_2a - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b20:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_377 - - const/16 v22, 0x0 - - :goto_34 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b21:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_37b - - const/16 v23, 0x0 - - :goto_3e - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b15:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_37f - - const/16 v17, 0x0 - - :goto_48 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b00:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_383 - - const/4 v2, 0x0 - - :goto_51 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b22:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_387 - - const/16 v24, 0x0 - - :goto_5b - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b23:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_38b - - const/16 v25, 0x0 - - :goto_65 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b24:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_38f - - const/16 v26, 0x0 - - :goto_6f - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b25:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_393 - - const/16 v27, 0x0 - - :goto_79 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b26:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_397 - - const/16 v28, 0x0 - - :goto_83 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b27:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_39b - - const/16 v29, 0x0 - - :goto_8d - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b29:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_39f - - const/16 v31, 0x0 - - :goto_97 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b28:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3a3 - - const/16 v30, 0x0 - - :goto_a1 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b01:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3a7 - - const/4 v3, 0x0 - - :goto_aa - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b02:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3ab - - const/4 v4, 0x0 - - :goto_b3 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b03:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3af - - const/4 v5, 0x0 - - :goto_bc - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b04:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3b3 - - const/4 v6, 0x0 - - :goto_c5 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b05:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3b7 - - const/4 v7, 0x0 - - :goto_ce - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b07:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3bb - - const/4 v9, 0x0 - - :goto_d7 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b06:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3bf - - const/4 v8, 0x0 - - :goto_e0 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b30:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3c3 - - const/16 v32, 0x0 - - :goto_ea - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b31:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3c7 - - const/16 v33, 0x0 - - :goto_f4 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b32:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3cb - - const/16 v34, 0x0 - - :goto_fe - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b33:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3cf - - const/16 v35, 0x0 - - :goto_108 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b34:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3d3 - - const/16 v36, 0x0 - - :goto_112 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b36:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3d7 - - const/16 v38, 0x0 - - :goto_11c - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b35:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3db - - const/16 v37, 0x0 - - :goto_126 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b08:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3df - - const/4 v10, 0x0 - - :goto_12f - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b09:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3e3 - - const/4 v11, 0x0 - - :goto_138 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b10:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3e7 - - const/4 v12, 0x0 - - :goto_141 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b11:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3eb - - const/4 v13, 0x0 - - :goto_14a - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b12:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3ef - - const/4 v14, 0x0 - - :goto_153 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b14:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3f3 - - const/16 v16, 0x0 - - :goto_15d - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b13:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3f7 - - const/4 v15, 0x0 - - :goto_166 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->conditionA:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_202 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v18, v18, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v19, v19, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v20, v20, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v21, v21, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v22, v22, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v23, v23, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v17, v17, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v10, v10, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v11, v11, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v12, v12, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v13, v13, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v14, v14, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v32, v32, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v33, v33, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v34, v34, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v35, v35, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v36, v36, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v3, v3, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v4, v4, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v5, v5, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v6, v6, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v7, v7, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v25, v25, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v26, v26, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v27, v27, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v28, v28, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v29, v29, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v24, v24, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v2, v2, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v16, v16, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v15, v15, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v38, v38, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v37, v37, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v9, v9, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v8, v8, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v31, v31, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v30, v30, v42 - - :cond_202 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->conditionB:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_29e - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v18, v18, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v19, v19, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v20, v20, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v21, v21, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v22, v22, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v23, v23, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v17, v17, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v10, v10, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v11, v11, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v12, v12, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v13, v13, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v14, v14, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v32, v32, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v33, v33, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v34, v34, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v35, v35, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v36, v36, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v3, v3, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v4, v4, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v5, v5, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v6, v6, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v7, v7, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v25, v25, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v26, v26, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v27, v27, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v28, v28, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v29, v29, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v24, v24, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v2, v2, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v16, v16, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v15, v15, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v38, v38, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v37, v37, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v9, v9, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v8, v8, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v31, v31, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v30, v30, v42 - - :cond_29e - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->conditionC:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_33a - - const/high16 v42, 0x41400000 # 12.0f - - div-float v18, v18, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v19, v19, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v20, v20, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v21, v21, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v22, v22, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v23, v23, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v17, v17, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v10, v10, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v11, v11, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v12, v12, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v13, v13, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v14, v14, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v32, v32, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v33, v33, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v34, v34, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v35, v35, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v36, v36, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v3, v3, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v4, v4, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v5, v5, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v6, v6, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v7, v7, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v25, v25, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v26, v26, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v27, v27, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v28, v28, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v29, v29, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v24, v24, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v2, v2, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v16, v16, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v15, v15, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v38, v38, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v37, v37, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v9, v9, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v8, v8, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v31, v31, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v30, v30, v42 - - :cond_33a - const/16 v41, 0x0 - - const/high16 v42, 0x42c80000 # 100.0f - - mul-float v42, v42, v41 - - invoke-static/range {v42 .. v42}, Ljava/lang/Math;->round(F)I - - move-result v42 - - move/from16 v0, v42 - - int-to-float v0, v0 - - move/from16 v42, v0 - - const/high16 v43, 0x42c80000 # 100.0f - - div-float v41, v42, v43 - - new-instance v42, Ljava/lang/StringBuilder; - - invoke-direct/range {v42 .. v42}, Ljava/lang/StringBuilder;-><init>()V - - move-object/from16 v0, v42 - - move/from16 v1, v41 - - invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(F)Ljava/lang/StringBuilder; - - move-result-object v42 - - move-object/from16 v0, v42 - - move-object/from16 v1, v39 - - invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - - move-result-object v42 - - invoke-virtual/range {v42 .. v42}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; - - move-result-object v40 - - return-void - - :cond_367 - const/high16 v19, 0x3f800000 # 1.0f - - goto/16 :goto_c - - :cond_36b - const/high16 v18, 0x3f800000 # 1.0f - - goto/16 :goto_16 - - :cond_36f - const/high16 v20, 0x3f800000 # 1.0f - - goto/16 :goto_20 - - :cond_373 - const/high16 v21, 0x3f800000 # 1.0f - - goto/16 :goto_2a - - :cond_377 - const/high16 v22, 0x3f800000 # 1.0f - - goto/16 :goto_34 - - :cond_37b - const/high16 v23, 0x3f800000 # 1.0f - - goto/16 :goto_3e - - :cond_37f - const/high16 v17, 0x3f800000 # 1.0f - - goto/16 :goto_48 - - :cond_383 - const/high16 v2, 0x3f800000 # 1.0f - - goto/16 :goto_51 - - :cond_387 - const/high16 v24, 0x3f800000 # 1.0f - - goto/16 :goto_5b - - :cond_38b - const/high16 v25, 0x3f800000 # 1.0f - - goto/16 :goto_65 - - :cond_38f - const/high16 v26, 0x3f800000 # 1.0f - - goto/16 :goto_6f - - :cond_393 - const/high16 v27, 0x3f800000 # 1.0f - - goto/16 :goto_79 - - :cond_397 - const/high16 v28, 0x3f800000 # 1.0f - - goto/16 :goto_83 - - :cond_39b - const/high16 v29, 0x3f800000 # 1.0f - - goto/16 :goto_8d - - :cond_39f - const/high16 v31, 0x3f800000 # 1.0f - - goto/16 :goto_97 - - :cond_3a3 - const/high16 v30, 0x3f800000 # 1.0f - - goto/16 :goto_a1 - - :cond_3a7 - const/high16 v3, 0x3f800000 # 1.0f - - goto/16 :goto_aa - - :cond_3ab - const/high16 v4, 0x3f800000 # 1.0f - - goto/16 :goto_b3 - - :cond_3af - const/high16 v5, 0x3f800000 # 1.0f - - goto/16 :goto_bc - - :cond_3b3 - const/high16 v6, 0x3f800000 # 1.0f - - goto/16 :goto_c5 - - :cond_3b7 - const/high16 v7, 0x3f800000 # 1.0f - - goto/16 :goto_ce - - :cond_3bb - const/high16 v9, 0x3f800000 # 1.0f - - goto/16 :goto_d7 - - :cond_3bf - const/high16 v8, 0x3f800000 # 1.0f - - goto/16 :goto_e0 - - :cond_3c3 - const/high16 v32, 0x3f800000 # 1.0f - - goto/16 :goto_ea - - :cond_3c7 - const/high16 v33, 0x3f800000 # 1.0f - - goto/16 :goto_f4 - - :cond_3cb - const/high16 v34, 0x3f800000 # 1.0f - - goto/16 :goto_fe - - :cond_3cf - const/high16 v35, 0x3f800000 # 1.0f - - goto/16 :goto_108 - - :cond_3d3 - const/high16 v36, 0x3f800000 # 1.0f - - goto/16 :goto_112 - - :cond_3d7 - const/high16 v38, 0x3f800000 # 1.0f - - goto/16 :goto_11c - - :cond_3db - const/high16 v37, 0x3f800000 # 1.0f - - goto/16 :goto_126 - - :cond_3df - const/high16 v10, 0x3f800000 # 1.0f - - goto/16 :goto_12f - - :cond_3e3 - const/high16 v11, 0x3f800000 # 1.0f - - goto/16 :goto_138 - - :cond_3e7 - const/high16 v12, 0x3f800000 # 1.0f - - goto/16 :goto_141 - - :cond_3eb - const/high16 v13, 0x3f800000 # 1.0f - - goto/16 :goto_14a - - :cond_3ef - const/high16 v14, 0x3f800000 # 1.0f - - goto/16 :goto_153 - - :cond_3f3 - const/high16 v16, 0x3f800000 # 1.0f - - goto/16 :goto_15d - - :cond_3f7 - const/high16 v15, 0x3f800000 # 1.0f - - goto/16 :goto_166 -.end method - -## CHECK-START-ARM64: void Smali.testD8() register (after) -## CHECK: begin_block -## CHECK: name "B0" -## CHECK: <<This:l\d+>> ParameterValue -## CHECK: end_block - -## CHECK: begin_block -## CHECK: successors "<<ThenBlockA:B\d+>>" "<<ElseBlockA:B\d+>>" -## CHECK: <<CondA:z\d+>> InstanceFieldGet [<<This>>] field_name:Smali.conditionA -## CHECK: If [<<CondA>>] -## CHECK: end_block - -## CHECK: begin_block -## CHECK: name "<<ThenBlockA>>" -## CHECK: end_block -## CHECK: begin_block -## CHECK: name "<<ElseBlockA>>" -## CHECK: ParallelMove moves:[d2->d0,40(sp)->d17,d20->d26,d19->d27,d27->d1,d26->d2,d14->d20,d13->d19,d17->d14,d0->d13] -## CHECK: end_block - -## CHECK: begin_block -## CHECK: successors "<<ThenBlockB:B\d+>>" "<<ElseBlockB:B\d+>>" -## CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Smali.conditionB -## CHECK: If [<<CondB>>] -## CHECK: end_block - -## CHECK: begin_block -## CHECK: name "<<ThenBlockB>>" -## CHECK: end_block -## CHECK: begin_block -## CHECK: name "<<ElseBlockB>>" -## CHECK: ParallelMove moves:[#100->d13,16(sp)->d1,20(sp)->d2,28(sp)->d19,24(sp)->d20,36(sp)->d14,32(sp)->16(sp),d1->20(sp),d2->24(sp),d20->28(sp),d19->32(sp),d14->36(sp),d13->40(sp)] -## CHECK: end_block - -## CHECK-START-ARM64: void Smali.testD8() disassembly (after) -## CHECK: begin_block -## CHECK: name "B0" -## CHECK: <<This:l\d+>> ParameterValue -## CHECK: end_block - -## CHECK: begin_block -## CHECK: successors "<<ThenBlockA:B\d+>>" "<<ElseBlockA:B\d+>>" -## CHECK: <<CondA:z\d+>> InstanceFieldGet [<<This>>] field_name:Smali.conditionA -## CHECK: If [<<CondA>>] -## CHECK: end_block - -## CHECK: begin_block -## CHECK: name "<<ThenBlockA>>" -## CHECK: end_block -## CHECK: begin_block -## CHECK: name "<<ElseBlockA>>" -## CHECK: end_block - -## CHECK: begin_block -## CHECK: successors "<<ThenBlockB:B\d+>>" "<<ElseBlockB:B\d+>>" -## CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Smali.conditionB -## CHECK: If [<<CondB>>] -## CHECK: end_block - -## CHECK: begin_block -## CHECK: name "<<ThenBlockB>>" -## CHECK: end_block -## CHECK: begin_block -## CHECK: name "<<ElseBlockB>>" -## CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid] -## CHECK: ldr w16, [sp, #32] -## CHECK: str s19, [sp, #32] -## CHECK: ldr s19, [sp, #28] -## CHECK: str s20, [sp, #28] -## CHECK: ldr s20, [sp, #24] -## CHECK: str s2, [sp, #24] -## CHECK: ldr s2, [sp, #20] -## CHECK: str s1, [sp, #20] -## CHECK: ldr s1, [sp, #16] -## CHECK: str w16, [sp, #16] -## CHECK: fmov d31, d14 -## CHECK: ldr s14, [sp, #36] -## CHECK: str s31, [sp, #36] -## CHECK: str s13, [sp, #40] -## CHECK: ldr s13, pc+580 (addr 0x61c) (100) -## CHECK: end_block -.method public testD8()V - .registers 47 - - move-object/from16 v0, p0 - - const-string v1, "" - - iget-boolean v2, v0, LSmali;->b17:Z - - if-eqz v2, :cond_a - - const/4 v2, 0x0 - - goto :goto_c - - :cond_a - const/high16 v2, 0x3f800000 # 1.0f - - :goto_c - iget-boolean v5, v0, LSmali;->b16:Z - - if-eqz v5, :cond_12 - - const/4 v5, 0x0 - - goto :goto_14 - - :cond_12 - const/high16 v5, 0x3f800000 # 1.0f - - :goto_14 - iget-boolean v6, v0, LSmali;->b18:Z - - if-eqz v6, :cond_1a - - const/4 v6, 0x0 - - goto :goto_1c - - :cond_1a - const/high16 v6, 0x3f800000 # 1.0f - - :goto_1c - iget-boolean v7, v0, LSmali;->b19:Z - - if-eqz v7, :cond_22 - - const/4 v7, 0x0 - - goto :goto_24 - - :cond_22 - const/high16 v7, 0x3f800000 # 1.0f - - :goto_24 - iget-boolean v8, v0, LSmali;->b20:Z - - if-eqz v8, :cond_2a - - const/4 v8, 0x0 - - goto :goto_2c - - :cond_2a - const/high16 v8, 0x3f800000 # 1.0f - - :goto_2c - iget-boolean v9, v0, LSmali;->b21:Z - - if-eqz v9, :cond_32 - - const/4 v9, 0x0 - - goto :goto_34 - - :cond_32 - const/high16 v9, 0x3f800000 # 1.0f - - :goto_34 - iget-boolean v10, v0, LSmali;->b15:Z - - if-eqz v10, :cond_3a - - const/4 v10, 0x0 - - goto :goto_3c - - :cond_3a - const/high16 v10, 0x3f800000 # 1.0f - - :goto_3c - iget-boolean v11, v0, LSmali;->b00:Z - - if-eqz v11, :cond_42 - - const/4 v11, 0x0 - - goto :goto_44 - - :cond_42 - const/high16 v11, 0x3f800000 # 1.0f - - :goto_44 - iget-boolean v12, v0, LSmali;->b22:Z - - if-eqz v12, :cond_4a - - const/4 v12, 0x0 - - goto :goto_4c - - :cond_4a - const/high16 v12, 0x3f800000 # 1.0f - - :goto_4c - iget-boolean v13, v0, LSmali;->b23:Z - - if-eqz v13, :cond_52 - - const/4 v13, 0x0 - - goto :goto_54 - - :cond_52 - const/high16 v13, 0x3f800000 # 1.0f - - :goto_54 - iget-boolean v14, v0, LSmali;->b24:Z - - if-eqz v14, :cond_5a - - const/4 v14, 0x0 - - goto :goto_5c - - :cond_5a - const/high16 v14, 0x3f800000 # 1.0f - - :goto_5c - iget-boolean v15, v0, LSmali;->b25:Z - - if-eqz v15, :cond_62 - - const/4 v15, 0x0 - - goto :goto_64 - - :cond_62 - const/high16 v15, 0x3f800000 # 1.0f - - :goto_64 - iget-boolean v3, v0, LSmali;->b26:Z - - if-eqz v3, :cond_6a - - const/4 v3, 0x0 - - goto :goto_6c - - :cond_6a - const/high16 v3, 0x3f800000 # 1.0f - - :goto_6c - iget-boolean v4, v0, LSmali;->b27:Z - - if-eqz v4, :cond_72 - - const/4 v4, 0x0 - - goto :goto_74 - - :cond_72 - const/high16 v4, 0x3f800000 # 1.0f - - :goto_74 - move-object/from16 v18, v1 - - iget-boolean v1, v0, LSmali;->b29:Z - - if-eqz v1, :cond_7c - - const/4 v1, 0x0 - - goto :goto_7e - - :cond_7c - const/high16 v1, 0x3f800000 # 1.0f - - :goto_7e - move/from16 v19, v1 - - iget-boolean v1, v0, LSmali;->b28:Z - - if-eqz v1, :cond_86 - - const/4 v1, 0x0 - - goto :goto_88 - - :cond_86 - const/high16 v1, 0x3f800000 # 1.0f - - :goto_88 - move/from16 v20, v1 - - iget-boolean v1, v0, LSmali;->b01:Z - - if-eqz v1, :cond_90 - - const/4 v1, 0x0 - - goto :goto_92 - - :cond_90 - const/high16 v1, 0x3f800000 # 1.0f - - :goto_92 - move/from16 v21, v11 - - iget-boolean v11, v0, LSmali;->b02:Z - - if-eqz v11, :cond_9a - - const/4 v11, 0x0 - - goto :goto_9c - - :cond_9a - const/high16 v11, 0x3f800000 # 1.0f - - :goto_9c - move/from16 v22, v12 - - iget-boolean v12, v0, LSmali;->b03:Z - - if-eqz v12, :cond_a4 - - const/4 v12, 0x0 - - goto :goto_a6 - - :cond_a4 - const/high16 v12, 0x3f800000 # 1.0f - - :goto_a6 - move/from16 v23, v4 - - iget-boolean v4, v0, LSmali;->b04:Z - - if-eqz v4, :cond_ae - - const/4 v4, 0x0 - - goto :goto_b0 - - :cond_ae - const/high16 v4, 0x3f800000 # 1.0f - - :goto_b0 - move/from16 v24, v3 - - iget-boolean v3, v0, LSmali;->b05:Z - - if-eqz v3, :cond_b8 - - const/4 v3, 0x0 - - goto :goto_ba - - :cond_b8 - const/high16 v3, 0x3f800000 # 1.0f - - :goto_ba - move/from16 v25, v15 - - iget-boolean v15, v0, LSmali;->b07:Z - - if-eqz v15, :cond_c2 - - const/4 v15, 0x0 - - goto :goto_c4 - - :cond_c2 - const/high16 v15, 0x3f800000 # 1.0f - - :goto_c4 - move/from16 v26, v15 - - iget-boolean v15, v0, LSmali;->b06:Z - - if-eqz v15, :cond_cc - - const/4 v15, 0x0 - - goto :goto_ce - - :cond_cc - const/high16 v15, 0x3f800000 # 1.0f - - :goto_ce - move/from16 v27, v15 - - iget-boolean v15, v0, LSmali;->b30:Z - - if-eqz v15, :cond_d6 - - const/4 v15, 0x0 - - goto :goto_d8 - - :cond_d6 - const/high16 v15, 0x3f800000 # 1.0f - - :goto_d8 - move/from16 v28, v14 - - iget-boolean v14, v0, LSmali;->b31:Z - - if-eqz v14, :cond_e0 - - const/4 v14, 0x0 - - goto :goto_e2 - - :cond_e0 - const/high16 v14, 0x3f800000 # 1.0f - - :goto_e2 - move/from16 v29, v13 - - iget-boolean v13, v0, LSmali;->b32:Z - - if-eqz v13, :cond_ea - - const/4 v13, 0x0 - - goto :goto_ec - - :cond_ea - const/high16 v13, 0x3f800000 # 1.0f - - :goto_ec - move/from16 v30, v3 - - iget-boolean v3, v0, LSmali;->b33:Z - - if-eqz v3, :cond_f4 - - const/4 v3, 0x0 - - goto :goto_f6 - - :cond_f4 - const/high16 v3, 0x3f800000 # 1.0f - - :goto_f6 - move/from16 v31, v4 - - iget-boolean v4, v0, LSmali;->b34:Z - - if-eqz v4, :cond_fe - - const/4 v4, 0x0 - - goto :goto_100 - - :cond_fe - const/high16 v4, 0x3f800000 # 1.0f - - :goto_100 - move/from16 v32, v12 - - iget-boolean v12, v0, LSmali;->b36:Z - - if-eqz v12, :cond_108 - - const/4 v12, 0x0 - - goto :goto_10a - - :cond_108 - const/high16 v12, 0x3f800000 # 1.0f - - :goto_10a - move/from16 v33, v12 - - iget-boolean v12, v0, LSmali;->b35:Z - - if-eqz v12, :cond_112 - - const/4 v12, 0x0 - - goto :goto_114 - - :cond_112 - const/high16 v12, 0x3f800000 # 1.0f - - :goto_114 - move/from16 v34, v12 - - iget-boolean v12, v0, LSmali;->b08:Z - - if-eqz v12, :cond_11c - - const/4 v12, 0x0 - - goto :goto_11e - - :cond_11c - const/high16 v12, 0x3f800000 # 1.0f - - :goto_11e - move/from16 v35, v11 - - iget-boolean v11, v0, LSmali;->b09:Z - - if-eqz v11, :cond_126 - - const/4 v11, 0x0 - - goto :goto_128 - - :cond_126 - const/high16 v11, 0x3f800000 # 1.0f - - :goto_128 - move/from16 v36, v1 - - iget-boolean v1, v0, LSmali;->b10:Z - - if-eqz v1, :cond_130 - - const/4 v1, 0x0 - - goto :goto_132 - - :cond_130 - const/high16 v1, 0x3f800000 # 1.0f - - :goto_132 - move/from16 v37, v4 - - iget-boolean v4, v0, LSmali;->b11:Z - - if-eqz v4, :cond_13a - - const/4 v4, 0x0 - - goto :goto_13c - - :cond_13a - const/high16 v4, 0x3f800000 # 1.0f - - :goto_13c - move/from16 v38, v3 - - iget-boolean v3, v0, LSmali;->b12:Z - - if-eqz v3, :cond_144 - - const/4 v3, 0x0 - - goto :goto_146 - - :cond_144 - const/high16 v3, 0x3f800000 # 1.0f - - :goto_146 - move/from16 v39, v13 - - iget-boolean v13, v0, LSmali;->b14:Z - - if-eqz v13, :cond_14e - - const/4 v13, 0x0 - - goto :goto_150 - - :cond_14e - const/high16 v13, 0x3f800000 # 1.0f - - :goto_150 - move/from16 v40, v13 - - iget-boolean v13, v0, LSmali;->b13:Z - - if-eqz v13, :cond_159 - - const/16 v16, 0x0 - - goto :goto_15b - - :cond_159 - const/high16 v16, 0x3f800000 # 1.0f - - :goto_15b - move/from16 v13, v16 - - move/from16 v41, v13 - - iget-boolean v13, v0, LSmali;->conditionA:Z - - if-eqz v13, :cond_1a2 - - const/high16 v13, 0x447a0000 # 1000.0f - - div-float/2addr v5, v13 - - div-float/2addr v2, v13 - - div-float/2addr v6, v13 - - div-float/2addr v7, v13 - - div-float/2addr v8, v13 - - div-float/2addr v9, v13 - - div-float/2addr v10, v13 - - div-float/2addr v12, v13 - - div-float/2addr v11, v13 - - div-float/2addr v1, v13 - - div-float/2addr v4, v13 - - div-float/2addr v3, v13 - - div-float/2addr v15, v13 - - div-float/2addr v14, v13 - - div-float v16, v39, v13 - - div-float v38, v38, v13 - - div-float v37, v37, v13 - - div-float v36, v36, v13 - - div-float v35, v35, v13 - - div-float v32, v32, v13 - - div-float v31, v31, v13 - - div-float v30, v30, v13 - - div-float v29, v29, v13 - - div-float v28, v28, v13 - - div-float v25, v25, v13 - - div-float v24, v24, v13 - - div-float v23, v23, v13 - - div-float v22, v22, v13 - - div-float v21, v21, v13 - - div-float v39, v40, v13 - - div-float v40, v41, v13 - - div-float v33, v33, v13 - - div-float v34, v34, v13 - - div-float v26, v26, v13 - - div-float v27, v27, v13 - - div-float v19, v19, v13 - - div-float v13, v20, v13 - - goto :goto_1aa - - :cond_1a2 - move/from16 v13, v20 - - move/from16 v16, v39 - - move/from16 v39, v40 - - move/from16 v40, v41 - - :goto_1aa - move/from16 v42, v13 - - iget-boolean v13, v0, LSmali;->conditionB:Z - - const/high16 v20, 0x42c80000 # 100.0f - - if-eqz v13, :cond_1fd - - div-float v5, v5, v20 - - div-float v2, v2, v20 - - div-float v6, v6, v20 - - div-float v7, v7, v20 - - div-float v8, v8, v20 - - div-float v9, v9, v20 - - div-float v10, v10, v20 - - div-float v12, v12, v20 - - div-float v11, v11, v20 - - div-float v1, v1, v20 - - div-float v4, v4, v20 - - div-float v3, v3, v20 - - div-float v15, v15, v20 - - div-float v14, v14, v20 - - div-float v16, v16, v20 - - div-float v38, v38, v20 - - div-float v37, v37, v20 - - div-float v36, v36, v20 - - div-float v35, v35, v20 - - div-float v32, v32, v20 - - div-float v31, v31, v20 - - div-float v30, v30, v20 - - div-float v29, v29, v20 - - div-float v28, v28, v20 - - div-float v25, v25, v20 - - div-float v24, v24, v20 - - div-float v23, v23, v20 - - div-float v22, v22, v20 - - div-float v21, v21, v20 - - div-float v39, v39, v20 - - div-float v40, v40, v20 - - div-float v33, v33, v20 - - div-float v34, v34, v20 - - div-float v26, v26, v20 - - div-float v27, v27, v20 - - div-float v19, v19, v20 - - div-float v13, v42, v20 - - goto :goto_1ff - - :cond_1fd - move/from16 v13, v42 - - :goto_1ff - move/from16 v43, v13 - - iget-boolean v13, v0, LSmali;->conditionC:Z - - if-eqz v13, :cond_244 - - const/high16 v13, 0x41400000 # 12.0f - - div-float/2addr v5, v13 - - div-float/2addr v2, v13 - - div-float/2addr v6, v13 - - div-float/2addr v7, v13 - - div-float/2addr v8, v13 - - div-float/2addr v9, v13 - - div-float/2addr v10, v13 - - div-float/2addr v12, v13 - - div-float/2addr v11, v13 - - div-float/2addr v1, v13 - - div-float/2addr v4, v13 - - div-float/2addr v3, v13 - - div-float/2addr v15, v13 - - div-float/2addr v14, v13 - - div-float v16, v16, v13 - - div-float v38, v38, v13 - - div-float v37, v37, v13 - - div-float v36, v36, v13 - - div-float v35, v35, v13 - - div-float v32, v32, v13 - - div-float v31, v31, v13 - - div-float v30, v30, v13 - - div-float v29, v29, v13 - - div-float v28, v28, v13 - - div-float v25, v25, v13 - - div-float v24, v24, v13 - - div-float v23, v23, v13 - - div-float v22, v22, v13 - - div-float v21, v21, v13 - - div-float v39, v39, v13 - - div-float v40, v40, v13 - - div-float v33, v33, v13 - - div-float v34, v34, v13 - - div-float v26, v26, v13 - - div-float v27, v27, v13 - - div-float v19, v19, v13 - - div-float v13, v43, v13 - - goto :goto_246 - - :cond_244 - move/from16 v13, v43 - - :goto_246 - const/16 v17, 0x0 - - mul-float v0, v20, v17 - - invoke-static {v0}, Ljava/lang/Math;->round(F)I - - move-result v0 - - int-to-float v0, v0 - - div-float v0, v0, v20 - - move/from16 v44, v1 - - new-instance v1, Ljava/lang/StringBuilder; - - invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V - - invoke-virtual {v1, v0}, Ljava/lang/StringBuilder;->append(F)Ljava/lang/StringBuilder; - - move/from16 v45, v0 - - move-object/from16 v0, v18 - - invoke-virtual {v1, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - - invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; - - move-result-object v1 - - return-void -.end method diff --git a/test/626-checker-arm64-scratch-register/src/Main.java b/test/626-checker-arm64-scratch-register/src/Main.java index cf94b87666..139491769e 100644 --- a/test/626-checker-arm64-scratch-register/src/Main.java +++ b/test/626-checker-arm64-scratch-register/src/Main.java @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; public class Main { @@ -65,17 +63,14 @@ public class Main { /// CHECK: name "B0" /// CHECK: <<This:l\d+>> ParameterValue /// CHECK: end_block - - /// CHECK: begin_block - /// CHECK: successors "<<ThenBlockA:B\d+>>" "<<ElseBlockA:B\d+>>" - /// CHECK: <<CondA:z\d+>> InstanceFieldGet [<<This>>] field_name:Main.conditionA - /// CHECK: If [<<CondA>>] - /// CHECK: end_block - /// CHECK: begin_block - /// CHECK: successors "<<ThenBlockB:B\d+>>" "<<ElseBlockB:B\d+>>" + /// CHECK: successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>" /// CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Main.conditionB /// CHECK: If [<<CondB>>] + /// CHECK: end_block + /// CHECK: begin_block + /// CHECK: name "<<ElseBlock>>" + /// CHECK: ParallelMove moves:[40(sp)->d0,24(sp)->32(sp),28(sp)->36(sp),d0->d3,d3->d4,d2->d5,d4->d6,d5->d7,d6->d18,d7->d19,d18->d20,d19->d21,d20->d22,d21->d23,d22->d10,d23->d11,16(sp)->24(sp),20(sp)->28(sp),d10->d14,d11->d12,d12->d13,d13->d1,d14->d2,32(sp)->16(sp),36(sp)->20(sp)] /// CHECK: end_block /// CHECK-START-ARM64: void Main.test() disassembly (after) @@ -87,6 +82,39 @@ public class Main { /// CHECK: successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>" /// CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Main.conditionB /// CHECK: If [<<CondB>>] + /// CHECK: end_block + /// CHECK: begin_block + /// CHECK: name "<<ElseBlock>>" + /// CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid] + /// CHECK: fmov d31, d2 + /// CHECK: ldr s2, [sp, #36] + /// CHECK: ldr w16, [sp, #16] + /// CHECK: str w16, [sp, #36] + /// CHECK: str s14, [sp, #16] + /// CHECK: ldr s14, [sp, #28] + /// CHECK: str s1, [sp, #28] + /// CHECK: ldr s1, [sp, #32] + /// CHECK: str s31, [sp, #32] + /// CHECK: ldr s31, [sp, #20] + /// CHECK: str s31, [sp, #40] + /// CHECK: str s12, [sp, #20] + /// CHECK: fmov d12, d11 + /// CHECK: fmov d11, d10 + /// CHECK: fmov d10, d23 + /// CHECK: fmov d23, d22 + /// CHECK: fmov d22, d21 + /// CHECK: fmov d21, d20 + /// CHECK: fmov d20, d19 + /// CHECK: fmov d19, d18 + /// CHECK: fmov d18, d7 + /// CHECK: fmov d7, d6 + /// CHECK: fmov d6, d5 + /// CHECK: fmov d5, d4 + /// CHECK: fmov d4, d3 + /// CHECK: fmov d3, d13 + /// CHECK: ldr s13, [sp, #24] + /// CHECK: str s3, [sp, #24] + /// CHECK: ldr s3, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100) /// CHECK: end_block public void test() { @@ -261,20 +289,9 @@ public class Main { String res = s + r; } - public static void main(String[] args) throws Exception { + public static void main(String[] args) { Main main = new Main(); main.test(); - - Class<?> cl = Class.forName("Smali"); - Constructor<?> cons = cl.getConstructor(); - Object o = cons.newInstance(); - - Method test = cl.getMethod("test"); - test.invoke(o); - - Method testD8 = cl.getMethod("testD8"); - testD8.invoke(o); - System.out.println("passed"); } } diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc index 11b32e46c3..472f2b768e 100644 --- a/test/901-hello-ti-agent/basics.cc +++ b/test/901-hello-ti-agent/basics.cc @@ -16,7 +16,7 @@ #include "901-hello-ti-agent/basics.h" -#include <thread> // NOLINT [build/c++11] [5] +#include <thread> #include <jni.h> #include <stdio.h> diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc index 81d1b2cda9..9d2592a675 100644 --- a/test/904-object-allocation/tracking.cc +++ b/test/904-object-allocation/tracking.cc @@ -18,7 +18,7 @@ #include <cstdio> #include <iostream> -#include <mutex> // NOLINT [build/c++11] [5] +#include <mutex> #include <vector> #include "android-base/logging.h" diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc index a84747289f..1f6954ef99 100644 --- a/test/912-classes/classes.cc +++ b/test/912-classes/classes.cc @@ -16,8 +16,8 @@ #include <stdio.h> -#include <condition_variable> // NOLINT [build/c++11] [5] -#include <mutex> // NOLINT [build/c++11] [5] +#include <condition_variable> +#include <mutex> #include <vector> #include "android-base/macros.h" diff --git a/test/912-classes/classes_art.cc b/test/912-classes/classes_art.cc index 92a3ba0c97..de2e456a53 100644 --- a/test/912-classes/classes_art.cc +++ b/test/912-classes/classes_art.cc @@ -16,7 +16,7 @@ #include <stdio.h> -#include <mutex> // NOLINT [build/c++11] [5] +#include <mutex> #include <vector> #include "android-base/macros.h" diff --git a/test/924-threads/threads.cc b/test/924-threads/threads.cc index 8330179fbf..8caff768c1 100644 --- a/test/924-threads/threads.cc +++ b/test/924-threads/threads.cc @@ -16,7 +16,7 @@ #include <stdio.h> -#include <mutex> // NOLINT [build/c++11] [5] +#include <mutex> #include <string> #include <vector> diff --git a/test/Android.bp b/test/Android.bp index 17ef1141df..ba24119e9c 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -263,7 +263,10 @@ art_cc_defaults { shared_libs: [ "libbase", ], - header_libs: ["libopenjdkjvmti_headers"], + header_libs: [ + "libnativehelper_header_only", + "libopenjdkjvmti_headers", + ], include_dirs: ["art/test/ti-agent"], } @@ -282,6 +285,7 @@ art_cc_defaults { "912-classes/classes_art.cc", "936-search-onload/search_onload.cc", "983-source-transform-verify/source_transform.cc", + "1940-ddms-ext/ddm_ext.cc", ], } diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 8755f04320..fe4a327a1f 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -27,7 +27,7 @@ TEST_ART_RUN_TEST_DEPENDENCIES := \ # Add d8 dependency, if enabled. ifeq ($(USE_D8),true) TEST_ART_RUN_TEST_DEPENDENCIES += \ - $(HOST_OUT_EXECUTABLES)/d8 + $(HOST_OUT_EXECUTABLES)/d8-compat-dx endif # Convert's a rule name to the form used in variables, e.g. no-relocate to NO_RELOCATE diff --git a/test/etc/default-build b/test/etc/default-build index f14424ecf9..5c8257f210 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -294,7 +294,7 @@ function make_dex() { local dexer="${DX}" if [ ${USE_D8} = "true" ]; then - dexer="${ANDROID_HOST_OUT}/bin/d8" + dexer="${ANDROID_HOST_OUT}/bin/d8-compat-dx" fi # Make dex file from desugared JAR. diff --git a/tools/breakpoint-logger/breakpoint_logger.cc b/tools/breakpoint-logger/breakpoint_logger.cc index b48a1788e3..2f8b68239b 100644 --- a/tools/breakpoint-logger/breakpoint_logger.cc +++ b/tools/breakpoint-logger/breakpoint_logger.cc @@ -383,7 +383,7 @@ static jint AgentStart(StartType start, return JNI_ERR; } - jvmtiCapabilities caps {}; // NOLINT [readability/braces] + jvmtiCapabilities caps{}; caps.can_generate_breakpoint_events = JNI_TRUE; caps.can_get_line_numbers = JNI_TRUE; caps.can_get_source_file_name = JNI_TRUE; @@ -394,7 +394,7 @@ static jint AgentStart(StartType start, return JNI_ERR; } - jvmtiEventCallbacks callbacks {}; // NOLINT [readability/braces] + jvmtiEventCallbacks callbacks{}; callbacks.Breakpoint = &BreakpointCB; callbacks.VMInit = &VMInitCB; diff --git a/tools/cpplint.py b/tools/cpplint.py deleted file mode 100755 index 308dd8c479..0000000000 --- a/tools/cpplint.py +++ /dev/null @@ -1,4095 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# Here are some issues that I've had people identify in my code during reviews, -# that I think are possible to flag automatically in a lint tool. If these were -# caught by lint, it would save time both for myself and that of my reviewers. -# Most likely, some of these are beyond the scope of the current lint framework, -# but I think it is valuable to retain these wish-list items even if they cannot -# be immediately implemented. -# -# Suggestions -# ----------- -# - Check for no 'explicit' for multi-arg ctor -# - Check for boolean assign RHS in parens -# - Check for ctor initializer-list colon position and spacing -# - Check that if there's a ctor, there should be a dtor -# - Check accessors that return non-pointer member variables are -# declared const -# - Check accessors that return non-const pointer member vars are -# *not* declared const -# - Check for using public includes for testing -# - Check for spaces between brackets in one-line inline method -# - Check for no assert() -# - Check for spaces surrounding operators -# - Check for 0 in pointer context (should be NULL) -# - Check for 0 in char context (should be '\0') -# - Check for camel-case method name conventions for methods -# that are not simple inline getters and setters -# - Do not indent namespace contents -# - Avoid inlining non-trivial constructors in header files -# - Check for old-school (void) cast for call-sites of functions -# ignored return value -# - Check gUnit usage of anonymous namespace -# - Check for class declaration order (typedefs, consts, enums, -# ctor(s?), dtor, friend declarations, methods, member vars) -# - -"""Does google-lint on c++ files. - -The goal of this script is to identify places in the code that *may* -be in non-compliance with google style. It does not attempt to fix -up these problems -- the point is to educate. It does also not -attempt to find all problems, or to ensure that everything it does -find is legitimately a problem. - -In particular, we can get very confused by /* and // inside strings! -We do a small hack, which is to ignore //'s with "'s after them on the -same line, but it is far from perfect (in either direction). -""" - -import codecs -import copy -import getopt -import math # for log -import os -import re -import sre_compile -import string -import sys -import unicodedata - - -_USAGE = """ -Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] - [--counting=total|toplevel|detailed] - [--quiet] - <file> [file] ... - - The style guidelines this tries to follow are those in - http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml - - Every problem is given a confidence score from 1-5, with 5 meaning we are - certain of the problem, and 1 meaning it could be a legitimate construct. - This will miss some errors, and is not a substitute for a code review. - - To suppress false-positive errors of a certain category, add a - 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) - suppresses errors of all categories on that line. - - The files passed in will be linted; at least one file must be provided. - Linted extensions are .cc, .cpp, and .h. Other file types will be ignored. - - Flags: - - output=vs7 - By default, the output is formatted to ease emacs parsing. Visual Studio - compatible output (vs7) may also be used. Other formats are unsupported. - - verbose=# - Specify a number 0-5 to restrict errors to certain verbosity levels. - - quiet - Don't print anything if no errors are found. - - filter=-x,+y,... - Specify a comma-separated list of category-filters to apply: only - error messages whose category names pass the filters will be printed. - (Category names are printed with the message and look like - "[whitespace/indent]".) Filters are evaluated left to right. - "-FOO" and "FOO" means "do not print categories that start with FOO". - "+FOO" means "do print categories that start with FOO". - - Examples: --filter=-whitespace,+whitespace/braces - --filter=whitespace,runtime/printf,+runtime/printf_format - --filter=-,+build/include_what_you_use - - To see a list of all the categories used in cpplint, pass no arg: - --filter= - - counting=total|toplevel|detailed - The total number of errors found is always printed. If - 'toplevel' is provided, then the count of errors in each of - the top-level categories like 'build' and 'whitespace' will - also be printed. If 'detailed' is provided, then a count - is provided for each category like 'build/class'. - - root=subdir - The root directory used for deriving header guard CPP variable. - By default, the header guard CPP variable is calculated as the relative - path to the directory that contains .git, .hg, or .svn. When this flag - is specified, the relative path is calculated from the specified - directory. If the specified directory does not exist, this flag is - ignored. - - Examples: - Assuing that src/.git exists, the header guard CPP variables for - src/chrome/browser/ui/browser.h are: - - No flag => CHROME_BROWSER_UI_BROWSER_H_ - --root=chrome => BROWSER_UI_BROWSER_H_ - --root=chrome/browser => UI_BROWSER_H_ -""" - -# We categorize each error message we print. Here are the categories. -# We want an explicit list so we can list them all in cpplint --filter=. -# If you add a new error message with a new category, add it to the list -# here! cpplint_unittest.py should tell you if you forget to do this. -# \ used for clearer layout -- pylint: disable-msg=C6013 -_ERROR_CATEGORIES = [ - 'build/class', - 'build/deprecated', - 'build/endif_comment', - 'build/explicit_make_pair', - 'build/forward_decl', - 'build/header_guard', - 'build/include', - 'build/include_alpha', - 'build/include_order', - 'build/include_what_you_use', - 'build/namespaces', - 'build/printf_format', - 'build/storage_class', - 'legal/copyright', - 'readability/alt_tokens', - 'readability/braces', - 'readability/casting', - 'readability/check', - 'readability/constructors', - 'readability/fn_size', - 'readability/function', - 'readability/multiline_comment', - 'readability/multiline_string', - 'readability/namespace', - 'readability/nolint', - 'readability/streams', - 'readability/todo', - 'readability/utf8', - 'runtime/arrays', - 'runtime/casting', - 'runtime/explicit', - 'runtime/int', - 'runtime/init', - 'runtime/invalid_increment', - 'runtime/member_string_references', - 'runtime/memset', - 'runtime/operator', - 'runtime/printf', - 'runtime/printf_format', - 'runtime/references', - 'runtime/rtti', - 'runtime/sizeof', - 'runtime/string', - 'runtime/threadsafe_fn', - 'whitespace/blank_line', - 'whitespace/braces', - 'whitespace/comma', - 'whitespace/comments', - 'whitespace/empty_loop_body', - 'whitespace/end_of_line', - 'whitespace/ending_newline', - 'whitespace/forcolon', - 'whitespace/indent', - 'whitespace/labels', - 'whitespace/line_length', - 'whitespace/newline', - 'whitespace/operators', - 'whitespace/parens', - 'whitespace/semicolon', - 'whitespace/tab', - 'whitespace/todo' - ] - -# The default state of the category filter. This is overrided by the --filter= -# flag. By default all errors are on, so only add here categories that should be -# off by default (i.e., categories that must be enabled by the --filter= flags). -# All entries here should start with a '-' or '+', as in the --filter= flag. -_DEFAULT_FILTERS = ['-build/include_alpha'] - -# We used to check for high-bit characters, but after much discussion we -# decided those were OK, as long as they were in UTF-8 and didn't represent -# hard-coded international strings, which belong in a separate i18n file. - -# Headers that we consider STL headers. -_STL_HEADERS = frozenset([ - 'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception', - 'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set', - 'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'new', - 'pair.h', 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack', - 'stl_alloc.h', 'stl_relops.h', 'type_traits.h', - 'utility', 'vector', 'vector.h', - ]) - - -# Non-STL C++ system headers. -_CPP_HEADERS = frozenset([ - 'algo.h', 'builtinbuf.h', 'bvector.h', 'cassert', 'cctype', - 'cerrno', 'cfloat', 'ciso646', 'climits', 'clocale', 'cmath', - 'complex', 'complex.h', 'csetjmp', 'csignal', 'cstdarg', 'cstddef', - 'cstdio', 'cstdlib', 'cstring', 'ctime', 'cwchar', 'cwctype', - 'defalloc.h', 'deque.h', 'editbuf.h', 'exception', 'fstream', - 'fstream.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip', - 'iomanip.h', 'ios', 'iosfwd', 'iostream', 'iostream.h', 'istream', - 'istream.h', 'iterator.h', 'limits', 'map.h', 'multimap.h', 'multiset.h', - 'numeric', 'ostream', 'ostream.h', 'parsestream.h', 'pfstream.h', - 'PlotFile.h', 'procbuf.h', 'pthread_alloc.h', 'rope', 'rope.h', - 'ropeimpl.h', 'SFile.h', 'slist', 'slist.h', 'stack.h', 'stdexcept', - 'stdiostream.h', 'streambuf', 'streambuf.h', 'stream.h', 'strfile.h', - 'string', 'strstream', 'strstream.h', 'tempbuf.h', 'tree.h', 'typeinfo', - 'valarray', - ]) - - -# Assertion macros. These are defined in base/logging.h and -# testing/base/gunit.h. Note that the _M versions need to come first -# for substring matching to work. -_CHECK_MACROS = [ - 'DCHECK', 'CHECK', - 'EXPECT_TRUE_M', 'EXPECT_TRUE', - 'ASSERT_TRUE_M', 'ASSERT_TRUE', - 'EXPECT_FALSE_M', 'EXPECT_FALSE', - 'ASSERT_FALSE_M', 'ASSERT_FALSE', - ] - -# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE -_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) - -for op, replacement in [('==', 'EQ'), ('!=', 'NE'), - ('>=', 'GE'), ('>', 'GT'), - ('<=', 'LE'), ('<', 'LT')]: - _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement - _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement - _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement - _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement - _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement - _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement - -for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), - ('>=', 'LT'), ('>', 'LE'), - ('<=', 'GT'), ('<', 'GE')]: - _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement - _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement - _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement - _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement - -# Alternative tokens and their replacements. For full list, see section 2.5 -# Alternative tokens [lex.digraph] in the C++ standard. -# -# Digraphs (such as '%:') are not included here since it's a mess to -# match those on a word boundary. -_ALT_TOKEN_REPLACEMENT = { - 'and': '&&', - 'bitor': '|', - 'or': '||', - 'xor': '^', - 'compl': '~', - 'bitand': '&', - 'and_eq': '&=', - 'or_eq': '|=', - 'xor_eq': '^=', - 'not': '!', - 'not_eq': '!=' - } - -# Compile regular expression that matches all the above keywords. The "[ =()]" -# bit is meant to avoid matching these keywords outside of boolean expressions. -# -# False positives include C-style multi-line comments (http://go/nsiut ) -# and multi-line strings (http://go/beujw ), but those have always been -# troublesome for cpplint. -_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( - r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') - - -# These constants define types of headers for use with -# _IncludeState.CheckNextIncludeOrder(). -_C_SYS_HEADER = 1 -_CPP_SYS_HEADER = 2 -_LIKELY_MY_HEADER = 3 -_POSSIBLE_MY_HEADER = 4 -_OTHER_HEADER = 5 - -# These constants define the current inline assembly state -_NO_ASM = 0 # Outside of inline assembly block -_INSIDE_ASM = 1 # Inside inline assembly block -_END_ASM = 2 # Last line of inline assembly block -_BLOCK_ASM = 3 # The whole block is an inline assembly block - -# Match start of assembly blocks -_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' - r'(?:\s+(volatile|__volatile__))?' - r'\s*[{(]') - - -_regexp_compile_cache = {} - -# Finds occurrences of NOLINT or NOLINT(...). -_RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?') - -# {str, set(int)}: a map from error categories to sets of linenumbers -# on which those errors are expected and should be suppressed. -_error_suppressions = {} - -# The root directory used for deriving header guard CPP variable. -# This is set by --root flag. -_root = None - -def ParseNolintSuppressions(filename, raw_line, linenum, error): - """Updates the global list of error-suppressions. - - Parses any NOLINT comments on the current line, updating the global - error_suppressions store. Reports an error if the NOLINT comment - was malformed. - - Args: - filename: str, the name of the input file. - raw_line: str, the line of input text, with comments. - linenum: int, the number of the current line. - error: function, an error handler. - """ - # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*). - matched = _RE_SUPPRESSION.search(raw_line) - if matched: - category = matched.group(1) - if category in (None, '(*)'): # => "suppress all" - _error_suppressions.setdefault(None, set()).add(linenum) - else: - if category.startswith('(') and category.endswith(')'): - category = category[1:-1] - if category in _ERROR_CATEGORIES: - _error_suppressions.setdefault(category, set()).add(linenum) - else: - error(filename, linenum, 'readability/nolint', 5, - 'Unknown NOLINT error category: %s' % category) - - -def ResetNolintSuppressions(): - "Resets the set of NOLINT suppressions to empty." - _error_suppressions.clear() - - -def IsErrorSuppressedByNolint(category, linenum): - """Returns true if the specified error category is suppressed on this line. - - Consults the global error_suppressions map populated by - ParseNolintSuppressions/ResetNolintSuppressions. - - Args: - category: str, the category of the error. - linenum: int, the current line number. - Returns: - bool, True iff the error should be suppressed due to a NOLINT comment. - """ - return (linenum in _error_suppressions.get(category, set()) or - linenum in _error_suppressions.get(None, set())) - -def Match(pattern, s): - """Matches the string with the pattern, caching the compiled regexp.""" - # The regexp compilation caching is inlined in both Match and Search for - # performance reasons; factoring it out into a separate function turns out - # to be noticeably expensive. - if not pattern in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].match(s) - - -def Search(pattern, s): - """Searches the string for the pattern, caching the compiled regexp.""" - if not pattern in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].search(s) - - -class _IncludeState(dict): - """Tracks line numbers for includes, and the order in which includes appear. - - As a dict, an _IncludeState object serves as a mapping between include - filename and line number on which that file was included. - - Call CheckNextIncludeOrder() once for each header in the file, passing - in the type constants defined above. Calls in an illegal order will - raise an _IncludeError with an appropriate error message. - - """ - # self._section will move monotonically through this set. If it ever - # needs to move backwards, CheckNextIncludeOrder will raise an error. - _INITIAL_SECTION = 0 - _MY_H_SECTION = 1 - _C_SECTION = 2 - _CPP_SECTION = 3 - _OTHER_H_SECTION = 4 - - _TYPE_NAMES = { - _C_SYS_HEADER: 'C system header', - _CPP_SYS_HEADER: 'C++ system header', - _LIKELY_MY_HEADER: 'header this file implements', - _POSSIBLE_MY_HEADER: 'header this file may implement', - _OTHER_HEADER: 'other header', - } - _SECTION_NAMES = { - _INITIAL_SECTION: "... nothing. (This can't be an error.)", - _MY_H_SECTION: 'a header this file implements', - _C_SECTION: 'C system header', - _CPP_SECTION: 'C++ system header', - _OTHER_H_SECTION: 'other header', - } - - def __init__(self): - dict.__init__(self) - # The name of the current section. - self._section = self._INITIAL_SECTION - # The path of last found header. - self._last_header = '' - - def CanonicalizeAlphabeticalOrder(self, header_path): - """Returns a path canonicalized for alphabetical comparison. - - - replaces "-" with "_" so they both cmp the same. - - removes '-inl' since we don't require them to be after the main header. - - lowercase everything, just in case. - - Args: - header_path: Path to be canonicalized. - - Returns: - Canonicalized path. - """ - return header_path.replace('-inl.h', '.h').replace('-', '_').lower() - - def IsInAlphabeticalOrder(self, header_path): - """Check if a header is in alphabetical order with the previous header. - - Args: - header_path: Header to be checked. - - Returns: - Returns true if the header is in alphabetical order. - """ - canonical_header = self.CanonicalizeAlphabeticalOrder(header_path) - if self._last_header > canonical_header: - return False - self._last_header = canonical_header - return True - - def CheckNextIncludeOrder(self, header_type): - """Returns a non-empty error message if the next header is out of order. - - This function also updates the internal state to be ready to check - the next include. - - Args: - header_type: One of the _XXX_HEADER constants defined above. - - Returns: - The empty string if the header is in the right order, or an - error message describing what's wrong. - - """ - error_message = ('Found %s after %s' % - (self._TYPE_NAMES[header_type], - self._SECTION_NAMES[self._section])) - - last_section = self._section - - if header_type == _C_SYS_HEADER: - if self._section <= self._C_SECTION: - self._section = self._C_SECTION - else: - self._last_header = '' - return error_message - elif header_type == _CPP_SYS_HEADER: - if self._section <= self._CPP_SECTION: - self._section = self._CPP_SECTION - else: - self._last_header = '' - return error_message - elif header_type == _LIKELY_MY_HEADER: - if self._section <= self._MY_H_SECTION: - self._section = self._MY_H_SECTION - else: - self._section = self._OTHER_H_SECTION - elif header_type == _POSSIBLE_MY_HEADER: - if self._section <= self._MY_H_SECTION: - self._section = self._MY_H_SECTION - else: - # This will always be the fallback because we're not sure - # enough that the header is associated with this file. - self._section = self._OTHER_H_SECTION - else: - assert header_type == _OTHER_HEADER - self._section = self._OTHER_H_SECTION - - if last_section != self._section: - self._last_header = '' - - return '' - - -class _CppLintState(object): - """Maintains module-wide state..""" - - def __init__(self): - self.verbose_level = 1 # global setting. - self.error_count = 0 # global count of reported errors - # filters to apply when emitting error messages - self.filters = _DEFAULT_FILTERS[:] - self.counting = 'total' # In what way are we counting errors? - self.errors_by_category = {} # string to int dict storing error counts - # BEGIN android-added - self.quiet = False # global setting. - # END android-added - - # output format: - # "emacs" - format that emacs can parse (default) - # "vs7" - format that Microsoft Visual Studio 7 can parse - self.output_format = 'emacs' - - def SetOutputFormat(self, output_format): - """Sets the output format for errors.""" - self.output_format = output_format - - # BEGIN android-added - def SetQuiet(self, level): - """Sets the module's quiet setting, and returns the previous setting.""" - last_quiet = self.quiet - self.quiet = level - return last_quiet - # END android-added - - def SetVerboseLevel(self, level): - """Sets the module's verbosity, and returns the previous setting.""" - last_verbose_level = self.verbose_level - self.verbose_level = level - return last_verbose_level - - def SetCountingStyle(self, counting_style): - """Sets the module's counting options.""" - self.counting = counting_style - - def SetFilters(self, filters): - """Sets the error-message filters. - - These filters are applied when deciding whether to emit a given - error message. - - Args: - filters: A string of comma-separated filters (eg "+whitespace/indent"). - Each filter should start with + or -; else we die. - - Raises: - ValueError: The comma-separated filters did not all start with '+' or '-'. - E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" - """ - # Default filters always have less priority than the flag ones. - self.filters = _DEFAULT_FILTERS[:] - for filt in filters.split(','): - clean_filt = filt.strip() - if clean_filt: - self.filters.append(clean_filt) - for filt in self.filters: - if not (filt.startswith('+') or filt.startswith('-')): - raise ValueError('Every filter in --filters must start with + or -' - ' (%s does not)' % filt) - - def ResetErrorCounts(self): - """Sets the module's error statistic back to zero.""" - self.error_count = 0 - self.errors_by_category = {} - - def IncrementErrorCount(self, category): - """Bumps the module's error statistic.""" - self.error_count += 1 - if self.counting in ('toplevel', 'detailed'): - if self.counting != 'detailed': - category = category.split('/')[0] - if category not in self.errors_by_category: - self.errors_by_category[category] = 0 - self.errors_by_category[category] += 1 - - def PrintErrorCounts(self): - """Print a summary of errors by category, and the total.""" - for category, count in self.errors_by_category.iteritems(): - sys.stderr.write('Category \'%s\' errors found: %d\n' % - (category, count)) - sys.stderr.write('Total errors found: %d\n' % self.error_count) - -_cpplint_state = _CppLintState() - - -def _OutputFormat(): - """Gets the module's output format.""" - return _cpplint_state.output_format - - -def _SetOutputFormat(output_format): - """Sets the module's output format.""" - _cpplint_state.SetOutputFormat(output_format) - - -# BEGIN android-added -def _Quiet(): - """Returns the module's quiet setting.""" - return _cpplint_state.quiet - - -def _SetQuiet(level): - """Sets the module's quiet status, and returns the previous setting.""" - return _cpplint_state.SetQuiet(level) -# END android-added - -def _VerboseLevel(): - """Returns the module's verbosity setting.""" - return _cpplint_state.verbose_level - - -def _SetVerboseLevel(level): - """Sets the module's verbosity, and returns the previous setting.""" - return _cpplint_state.SetVerboseLevel(level) - - -def _SetCountingStyle(level): - """Sets the module's counting options.""" - _cpplint_state.SetCountingStyle(level) - - -def _Filters(): - """Returns the module's list of output filters, as a list.""" - return _cpplint_state.filters - - -def _SetFilters(filters): - """Sets the module's error-message filters. - - These filters are applied when deciding whether to emit a given - error message. - - Args: - filters: A string of comma-separated filters (eg "whitespace/indent"). - Each filter should start with + or -; else we die. - """ - _cpplint_state.SetFilters(filters) - - -class _FunctionState(object): - """Tracks current function name and the number of lines in its body.""" - - _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. - _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. - - def __init__(self): - self.in_a_function = False - self.lines_in_function = 0 - self.current_function = '' - - def Begin(self, function_name): - """Start analyzing function body. - - Args: - function_name: The name of the function being tracked. - """ - self.in_a_function = True - self.lines_in_function = 0 - self.current_function = function_name - - def Count(self): - """Count line in current function body.""" - if self.in_a_function: - self.lines_in_function += 1 - - def Check(self, error, filename, linenum): - """Report if too many lines in function body. - - Args: - error: The function to call with any errors found. - filename: The name of the current file. - linenum: The number of the line to check. - """ - # BEGIN android-added - if not self.in_a_function: - return - # END android-added - if Match(r'T(EST|est)', self.current_function): - base_trigger = self._TEST_TRIGGER - else: - base_trigger = self._NORMAL_TRIGGER - trigger = base_trigger * 2**_VerboseLevel() - - if self.lines_in_function > trigger: - error_level = int(math.log(self.lines_in_function / base_trigger, 2)) - # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... - if error_level > 5: - error_level = 5 - error(filename, linenum, 'readability/fn_size', error_level, - 'Small and focused functions are preferred:' - ' %s has %d non-comment lines' - ' (error triggered by exceeding %d lines).' % ( - self.current_function, self.lines_in_function, trigger)) - - def End(self): - """Stop analyzing function body.""" - self.in_a_function = False - - -class _IncludeError(Exception): - """Indicates a problem with the include order in a file.""" - pass - - -class FileInfo: - """Provides utility functions for filenames. - - FileInfo provides easy access to the components of a file's path - relative to the project root. - """ - - def __init__(self, filename): - self._filename = filename - - def FullName(self): - """Make Windows paths like Unix.""" - return os.path.abspath(self._filename).replace('\\', '/') - - def RepositoryName(self): - """FullName after removing the local path to the repository. - - If we have a real absolute path name here we can try to do something smart: - detecting the root of the checkout and truncating /path/to/checkout from - the name so that we get header guards that don't include things like - "C:\Documents and Settings\..." or "/home/username/..." in them and thus - people on different computers who have checked the source out to different - locations won't see bogus errors. - """ - fullname = self.FullName() - - if os.path.exists(fullname): - project_dir = os.path.dirname(fullname) - - if os.path.exists(os.path.join(project_dir, ".svn")): - # If there's a .svn file in the current directory, we recursively look - # up the directory tree for the top of the SVN checkout - root_dir = project_dir - one_up_dir = os.path.dirname(root_dir) - while os.path.exists(os.path.join(one_up_dir, ".svn")): - root_dir = os.path.dirname(root_dir) - one_up_dir = os.path.dirname(one_up_dir) - - prefix = os.path.commonprefix([root_dir, project_dir]) - return fullname[len(prefix) + 1:] - - # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by - # searching up from the current path. - root_dir = os.path.dirname(fullname) - while (root_dir != os.path.dirname(root_dir) and - not os.path.exists(os.path.join(root_dir, ".git")) and - not os.path.exists(os.path.join(root_dir, ".hg")) and - not os.path.exists(os.path.join(root_dir, ".svn"))): - root_dir = os.path.dirname(root_dir) - - if (os.path.exists(os.path.join(root_dir, ".git")) or - os.path.exists(os.path.join(root_dir, ".hg")) or - os.path.exists(os.path.join(root_dir, ".svn"))): - prefix = os.path.commonprefix([root_dir, project_dir]) - # BEGIN android-changed - # return fullname[len(prefix) + 1:] - return "art/" + fullname[len(prefix) + 1:] - # END android-changed - - # Don't know what to do; header guard warnings may be wrong... - return fullname - - def Split(self): - """Splits the file into the directory, basename, and extension. - - For 'chrome/browser/browser.cc', Split() would - return ('chrome/browser', 'browser', '.cc') - - Returns: - A tuple of (directory, basename, extension). - """ - - googlename = self.RepositoryName() - project, rest = os.path.split(googlename) - return (project,) + os.path.splitext(rest) - - def BaseName(self): - """File base name - text after the final slash, before the final period.""" - return self.Split()[1] - - def Extension(self): - """File extension - text following the final period.""" - return self.Split()[2] - - def NoExtension(self): - """File has no source file extension.""" - return '/'.join(self.Split()[0:2]) - - def IsSource(self): - """File has a source file extension.""" - return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') - - -def _ShouldPrintError(category, confidence, linenum): - """If confidence >= verbose, category passes filter and is not suppressed.""" - - # There are three ways we might decide not to print an error message: - # a "NOLINT(category)" comment appears in the source, - # the verbosity level isn't high enough, or the filters filter it out. - if IsErrorSuppressedByNolint(category, linenum): - return False - if confidence < _cpplint_state.verbose_level: - return False - - is_filtered = False - for one_filter in _Filters(): - if one_filter.startswith('-'): - if category.startswith(one_filter[1:]): - is_filtered = True - elif one_filter.startswith('+'): - if category.startswith(one_filter[1:]): - is_filtered = False - else: - assert False # should have been checked for in SetFilter. - if is_filtered: - return False - - return True - - -def Error(filename, linenum, category, confidence, message): - """Logs the fact we've found a lint error. - - We log where the error was found, and also our confidence in the error, - that is, how certain we are this is a legitimate style regression, and - not a misidentification or a use that's sometimes justified. - - False positives can be suppressed by the use of - "cpplint(category)" comments on the offending line. These are - parsed into _error_suppressions. - - Args: - filename: The name of the file containing the error. - linenum: The number of the line containing the error. - category: A string used to describe the "category" this bug - falls under: "whitespace", say, or "runtime". Categories - may have a hierarchy separated by slashes: "whitespace/indent". - confidence: A number from 1-5 representing a confidence score for - the error, with 5 meaning that we are certain of the problem, - and 1 meaning that it could be a legitimate construct. - message: The error message. - """ - if _ShouldPrintError(category, confidence, linenum): - _cpplint_state.IncrementErrorCount(category) - if _cpplint_state.output_format == 'vs7': - sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - elif _cpplint_state.output_format == 'eclipse': - sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - else: - sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - - -# Matches standard C++ escape esequences per 2.13.2.3 of the C++ standard. -_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( - r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') -# Matches strings. Escape codes should already be removed by ESCAPES. -_RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"') -# Matches characters. Escape codes should already be removed by ESCAPES. -_RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'") -# Matches multi-line C++ comments. -# This RE is a little bit more complicated than one might expect, because we -# have to take care of space removals tools so we can handle comments inside -# statements better. -# The current rule is: We only clear spaces from both sides when we're at the -# end of the line. Otherwise, we try to remove spaces from the right side, -# if this doesn't work we try on left side but only if there's a non-character -# on the right. -_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( - r"""(\s*/\*.*\*/\s*$| - /\*.*\*/\s+| - \s+/\*.*\*/(?=\W)| - /\*.*\*/)""", re.VERBOSE) - - -def IsCppString(line): - """Does line terminate so, that the next symbol is in string constant. - - This function does not consider single-line nor multi-line comments. - - Args: - line: is a partial line of code starting from the 0..n. - - Returns: - True, if next character appended to 'line' is inside a - string constant. - """ - - line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" - return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 - - -def FindNextMultiLineCommentStart(lines, lineix): - """Find the beginning marker for a multiline comment.""" - while lineix < len(lines): - if lines[lineix].strip().startswith('/*'): - # Only return this marker if the comment goes beyond this line - if lines[lineix].strip().find('*/', 2) < 0: - return lineix - lineix += 1 - return len(lines) - - -def FindNextMultiLineCommentEnd(lines, lineix): - """We are inside a comment, find the end marker.""" - while lineix < len(lines): - if lines[lineix].strip().endswith('*/'): - return lineix - lineix += 1 - return len(lines) - - -def RemoveMultiLineCommentsFromRange(lines, begin, end): - """Clears a range of lines for multi-line comments.""" - # Having // dummy comments makes the lines non-empty, so we will not get - # unnecessary blank line warnings later in the code. - for i in range(begin, end): - lines[i] = '// dummy' - - -def RemoveMultiLineComments(filename, lines, error): - """Removes multiline (c-style) comments from lines.""" - lineix = 0 - while lineix < len(lines): - lineix_begin = FindNextMultiLineCommentStart(lines, lineix) - if lineix_begin >= len(lines): - return - lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) - if lineix_end >= len(lines): - error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, - 'Could not find end of multi-line comment') - return - RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) - lineix = lineix_end + 1 - - -def CleanseComments(line): - """Removes //-comments and single-line C-style /* */ comments. - - Args: - line: A line of C++ source. - - Returns: - The line with single-line comments removed. - """ - commentpos = line.find('//') - if commentpos != -1 and not IsCppString(line[:commentpos]): - line = line[:commentpos].rstrip() - # get rid of /* ... */ - return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) - - -class CleansedLines(object): - """Holds 3 copies of all lines with different preprocessing applied to them. - - 1) elided member contains lines without strings and comments, - 2) lines member contains lines without comments, and - 3) raw_lines member contains all the lines without processing. - All these three members are of <type 'list'>, and of the same length. - """ - - def __init__(self, lines): - self.elided = [] - self.lines = [] - self.raw_lines = lines - self.num_lines = len(lines) - for linenum in range(len(lines)): - self.lines.append(CleanseComments(lines[linenum])) - elided = self._CollapseStrings(lines[linenum]) - self.elided.append(CleanseComments(elided)) - - def NumLines(self): - """Returns the number of lines represented.""" - return self.num_lines - - @staticmethod - def _CollapseStrings(elided): - """Collapses strings and chars on a line to simple "" or '' blocks. - - We nix strings first so we're not fooled by text like '"http://"' - - Args: - elided: The line being processed. - - Returns: - The line with collapsed strings. - """ - if not _RE_PATTERN_INCLUDE.match(elided): - # Remove escaped characters first to make quote/single quote collapsing - # basic. Things that look like escaped characters shouldn't occur - # outside of strings and chars. - elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) - elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided) - elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided) - return elided - - -def FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar): - """Find the position just after the matching endchar. - - Args: - line: a CleansedLines line. - startpos: start searching at this position. - depth: nesting level at startpos. - startchar: expression opening character. - endchar: expression closing character. - - Returns: - Index just after endchar. - """ - for i in xrange(startpos, len(line)): - if line[i] == startchar: - depth += 1 - elif line[i] == endchar: - depth -= 1 - if depth == 0: - return i + 1 - return -1 - - -def CloseExpression(clean_lines, linenum, pos): - """If input points to ( or { or [, finds the position that closes it. - - If lines[linenum][pos] points to a '(' or '{' or '[', finds the - linenum/pos that correspond to the closing of the expression. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - pos: A position on the line. - - Returns: - A tuple (line, linenum, pos) pointer *past* the closing brace, or - (line, len(lines), -1) if we never find a close. Note we ignore - strings and comments when matching; and the line we return is the - 'cleansed' line at linenum. - """ - - line = clean_lines.elided[linenum] - startchar = line[pos] - if startchar not in '({[': - return (line, clean_lines.NumLines(), -1) - if startchar == '(': endchar = ')' - if startchar == '[': endchar = ']' - if startchar == '{': endchar = '}' - - # Check first line - end_pos = FindEndOfExpressionInLine(line, pos, 0, startchar, endchar) - if end_pos > -1: - return (line, linenum, end_pos) - tail = line[pos:] - num_open = tail.count(startchar) - tail.count(endchar) - while linenum < clean_lines.NumLines() - 1: - linenum += 1 - line = clean_lines.elided[linenum] - delta = line.count(startchar) - line.count(endchar) - if num_open + delta <= 0: - return (line, linenum, - FindEndOfExpressionInLine(line, 0, num_open, startchar, endchar)) - num_open += delta - - # Did not find endchar before end of file, give up - return (line, clean_lines.NumLines(), -1) - -def CheckForCopyright(filename, lines, error): - """Logs an error if no Copyright message appears at the top of the file.""" - - # We'll say it should occur by line 10. Don't forget there's a - # dummy line at the front. - for line in xrange(1, min(len(lines), 11)): - if re.search(r'Copyright', lines[line], re.I): break - else: # means no copyright line was found - error(filename, 0, 'legal/copyright', 5, - 'No copyright message found. ' - 'You should have a line: "Copyright [year] <Copyright Owner>"') - - -def GetHeaderGuardCPPVariable(filename): - """Returns the CPP variable that should be used as a header guard. - - Args: - filename: The name of a C++ header file. - - Returns: - The CPP variable that should be used as a header guard in the - named file. - - """ - - # Restores original filename in case that cpplint is invoked from Emacs's - # flymake. - filename = re.sub(r'_flymake\.h$', '.h', filename) - filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) - - fileinfo = FileInfo(filename) - file_path_from_root = fileinfo.RepositoryName() - if _root: - file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root) - return re.sub(r'[-./\s]', '_', file_path_from_root).upper() + '_' - - -def CheckForHeaderGuard(filename, lines, error): - """Checks that the file contains a header guard. - - Logs an error if no #ifndef header guard is present. For other - headers, checks that the full pathname is used. - - Args: - filename: The name of the C++ header file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - - cppvar = GetHeaderGuardCPPVariable(filename) - - ifndef = None - ifndef_linenum = 0 - define = None - endif = None - endif_linenum = 0 - for linenum, line in enumerate(lines): - linesplit = line.split() - if len(linesplit) >= 2: - # find the first occurrence of #ifndef and #define, save arg - if not ifndef and linesplit[0] == '#ifndef': - # set ifndef to the header guard presented on the #ifndef line. - ifndef = linesplit[1] - ifndef_linenum = linenum - if not define and linesplit[0] == '#define': - define = linesplit[1] - # find the last occurrence of #endif, save entire line - if line.startswith('#endif'): - endif = line - endif_linenum = linenum - - if not ifndef: - error(filename, 0, 'build/header_guard', 5, - 'No #ifndef header guard found, suggested CPP variable is: %s' % - cppvar) - return - - if not define: - error(filename, 0, 'build/header_guard', 5, - 'No #define header guard found, suggested CPP variable is: %s' % - cppvar) - return - - # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ - # for backward compatibility. - if ifndef != cppvar: - error_level = 0 - if ifndef != cppvar + '_': - error_level = 5 - - ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum, - error) - error(filename, ifndef_linenum, 'build/header_guard', error_level, - '#ifndef header guard has wrong style, please use: %s' % cppvar) - - if define != ifndef: - error(filename, 0, 'build/header_guard', 5, - '#ifndef and #define don\'t match, suggested CPP variable is: %s' % - cppvar) - return - - if endif != ('#endif // %s' % cppvar): - error_level = 0 - if endif != ('#endif // %s' % (cppvar + '_')): - error_level = 5 - - ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum, - error) - error(filename, endif_linenum, 'build/header_guard', error_level, - '#endif line should be "#endif // %s"' % cppvar) - - -def CheckForUnicodeReplacementCharacters(filename, lines, error): - """Logs an error for each line containing Unicode replacement characters. - - These indicate that either the file contained invalid UTF-8 (likely) - or Unicode replacement characters (which it shouldn't). Note that - it's possible for this to throw off line numbering if the invalid - UTF-8 occurred adjacent to a newline. - - Args: - filename: The name of the current file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - for linenum, line in enumerate(lines): - if u'\ufffd' in line: - error(filename, linenum, 'readability/utf8', 5, - 'Line contains invalid UTF-8 (or Unicode replacement character).') - - -def CheckForNewlineAtEOF(filename, lines, error): - """Logs an error if there is no newline char at the end of the file. - - Args: - filename: The name of the current file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - - # The array lines() was created by adding two newlines to the - # original file (go figure), then splitting on \n. - # To verify that the file ends in \n, we just have to make sure the - # last-but-two element of lines() exists and is empty. - if len(lines) < 3 or lines[-2]: - error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, - 'Could not find a newline character at the end of the file.') - - -def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): - """Logs an error if we see /* ... */ or "..." that extend past one line. - - /* ... */ comments are legit inside macros, for one line. - Otherwise, we prefer // comments, so it's ok to warn about the - other. Likewise, it's ok for strings to extend across multiple - lines, as long as a line continuation character (backslash) - terminates each line. Although not currently prohibited by the C++ - style guide, it's ugly and unnecessary. We don't do well with either - in this lint program, so we warn about both. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Remove all \\ (escaped backslashes) from the line. They are OK, and the - # second (escaped) slash may trigger later \" detection erroneously. - line = line.replace('\\\\', '') - - if line.count('/*') > line.count('*/'): - error(filename, linenum, 'readability/multiline_comment', 5, - 'Complex multi-line /*...*/-style comment found. ' - 'Lint may give bogus warnings. ' - 'Consider replacing these with //-style comments, ' - 'with #if 0...#endif, ' - 'or with more clearly structured multi-line comments.') - - if (line.count('"') - line.count('\\"')) % 2: - error(filename, linenum, 'readability/multiline_string', 5, - 'Multi-line string ("...") found. This lint script doesn\'t ' - 'do well with such strings, and may give bogus warnings. They\'re ' - 'ugly and unnecessary, and you should use concatenation instead".') - - -threading_list = ( - ('asctime(', 'asctime_r('), - ('ctime(', 'ctime_r('), - ('getgrgid(', 'getgrgid_r('), - ('getgrnam(', 'getgrnam_r('), - ('getlogin(', 'getlogin_r('), - ('getpwnam(', 'getpwnam_r('), - ('getpwuid(', 'getpwuid_r('), - ('gmtime(', 'gmtime_r('), - ('localtime(', 'localtime_r('), - ('rand(', 'rand_r('), - ('readdir(', 'readdir_r('), - ('strtok(', 'strtok_r('), - ('ttyname(', 'ttyname_r('), - ) - - -def CheckPosixThreading(filename, clean_lines, linenum, error): - """Checks for calls to thread-unsafe functions. - - Much code has been originally written without consideration of - multi-threading. Also, engineers are relying on their old experience; - they have learned posix before threading extensions were added. These - tests guide the engineers to use thread-safe functions (when using - posix directly). - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - for single_thread_function, multithread_safe_function in threading_list: - ix = line.find(single_thread_function) - # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 - if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and - line[ix - 1] not in ('_', '.', '>'))): - error(filename, linenum, 'runtime/threadsafe_fn', 2, - 'Consider using ' + multithread_safe_function + - '...) instead of ' + single_thread_function + - '...) for improved thread safety.') - - -# Matches invalid increment: *count++, which moves pointer instead of -# incrementing a value. -_RE_PATTERN_INVALID_INCREMENT = re.compile( - r'^\s*\*\w+(\+\+|--);') - - -def CheckInvalidIncrement(filename, clean_lines, linenum, error): - """Checks for invalid increment *count++. - - For example following function: - void increment_counter(int* count) { - *count++; - } - is invalid, because it effectively does count++, moving pointer, and should - be replaced with ++*count, (*count)++ or *count += 1. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - if _RE_PATTERN_INVALID_INCREMENT.match(line): - error(filename, linenum, 'runtime/invalid_increment', 5, - 'Changing pointer instead of value (or unused value of operator*).') - - -class _BlockInfo(object): - """Stores information about a generic block of code.""" - - def __init__(self, seen_open_brace): - self.seen_open_brace = seen_open_brace - self.open_parentheses = 0 - self.inline_asm = _NO_ASM - - def CheckBegin(self, filename, clean_lines, linenum, error): - """Run checks that applies to text up to the opening brace. - - This is mostly for checking the text after the class identifier - and the "{", usually where the base class is specified. For other - blocks, there isn't much to check, so we always pass. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - pass - - def CheckEnd(self, filename, clean_lines, linenum, error): - """Run checks that applies to text after the closing brace. - - This is mostly used for checking end of namespace comments. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - pass - - -class _ClassInfo(_BlockInfo): - """Stores information about a class.""" - - def __init__(self, name, class_or_struct, clean_lines, linenum): - _BlockInfo.__init__(self, False) - self.name = name - self.starting_linenum = linenum - self.is_derived = False - if class_or_struct == 'struct': - self.access = 'public' - else: - self.access = 'private' - - # Try to find the end of the class. This will be confused by things like: - # class A { - # } *x = { ... - # - # But it's still good enough for CheckSectionSpacing. - self.last_line = 0 - depth = 0 - for i in range(linenum, clean_lines.NumLines()): - line = clean_lines.elided[i] - depth += line.count('{') - line.count('}') - if not depth: - self.last_line = i - break - - def CheckBegin(self, filename, clean_lines, linenum, error): - # Look for a bare ':' - if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): - self.is_derived = True - - -class _NamespaceInfo(_BlockInfo): - """Stores information about a namespace.""" - - def __init__(self, name, linenum): - _BlockInfo.__init__(self, False) - self.name = name or '' - self.starting_linenum = linenum - - def CheckEnd(self, filename, clean_lines, linenum, error): - """Check end of namespace comments.""" - line = clean_lines.raw_lines[linenum] - - # Check how many lines is enclosed in this namespace. Don't issue - # warning for missing namespace comments if there aren't enough - # lines. However, do apply checks if there is already an end of - # namespace comment and it's incorrect. - # - # TODO(unknown): We always want to check end of namespace comments - # if a namespace is large, but sometimes we also want to apply the - # check if a short namespace contained nontrivial things (something - # other than forward declarations). There is currently no logic on - # deciding what these nontrivial things are, so this check is - # triggered by namespace size only, which works most of the time. - if (linenum - self.starting_linenum < 10 - and not Match(r'};*\s*(//|/\*).*\bnamespace\b', line)): - return - - # Look for matching comment at end of namespace. - # - # Note that we accept C style "/* */" comments for terminating - # namespaces, so that code that terminate namespaces inside - # preprocessor macros can be cpplint clean. Example: http://go/nxpiz - # - # We also accept stuff like "// end of namespace <name>." with the - # period at the end. - # - # Besides these, we don't accept anything else, otherwise we might - # get false negatives when existing comment is a substring of the - # expected namespace. Example: http://go/ldkdc, http://cl/23548205 - if self.name: - # Named namespace - if not Match((r'};*\s*(//|/\*).*\bnamespace\s+' + re.escape(self.name) + - r'[\*/\.\\\s]*$'), - line): - error(filename, linenum, 'readability/namespace', 5, - 'Namespace should be terminated with "// namespace %s"' % - self.name) - else: - # Anonymous namespace - if not Match(r'};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): - error(filename, linenum, 'readability/namespace', 5, - 'Namespace should be terminated with "// namespace"') - - -class _PreprocessorInfo(object): - """Stores checkpoints of nesting stacks when #if/#else is seen.""" - - def __init__(self, stack_before_if): - # The entire nesting stack before #if - self.stack_before_if = stack_before_if - - # The entire nesting stack up to #else - self.stack_before_else = [] - - # Whether we have already seen #else or #elif - self.seen_else = False - - -class _NestingState(object): - """Holds states related to parsing braces.""" - - def __init__(self): - # Stack for tracking all braces. An object is pushed whenever we - # see a "{", and popped when we see a "}". Only 3 types of - # objects are possible: - # - _ClassInfo: a class or struct. - # - _NamespaceInfo: a namespace. - # - _BlockInfo: some other type of block. - self.stack = [] - - # Stack of _PreprocessorInfo objects. - self.pp_stack = [] - - def SeenOpenBrace(self): - """Check if we have seen the opening brace for the innermost block. - - Returns: - True if we have seen the opening brace, False if the innermost - block is still expecting an opening brace. - """ - return (not self.stack) or self.stack[-1].seen_open_brace - - def InNamespaceBody(self): - """Check if we are currently one level inside a namespace body. - - Returns: - True if top of the stack is a namespace block, False otherwise. - """ - return self.stack and isinstance(self.stack[-1], _NamespaceInfo) - - def UpdatePreprocessor(self, line): - """Update preprocessor stack. - - We need to handle preprocessors due to classes like this: - #ifdef SWIG - struct ResultDetailsPageElementExtensionPoint { - #else - struct ResultDetailsPageElementExtensionPoint : public Extension { - #endif - (see http://go/qwddn for original example) - - We make the following assumptions (good enough for most files): - - Preprocessor condition evaluates to true from #if up to first - #else/#elif/#endif. - - - Preprocessor condition evaluates to false from #else/#elif up - to #endif. We still perform lint checks on these lines, but - these do not affect nesting stack. - - Args: - line: current line to check. - """ - if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): - # Beginning of #if block, save the nesting stack here. The saved - # stack will allow us to restore the parsing state in the #else case. - self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) - elif Match(r'^\s*#\s*(else|elif)\b', line): - # Beginning of #else block - if self.pp_stack: - if not self.pp_stack[-1].seen_else: - # This is the first #else or #elif block. Remember the - # whole nesting stack up to this point. This is what we - # keep after the #endif. - self.pp_stack[-1].seen_else = True - self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) - - # Restore the stack to how it was before the #if - self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) - else: - # TODO(unknown): unexpected #else, issue warning? - pass - elif Match(r'^\s*#\s*endif\b', line): - # End of #if or #else blocks. - if self.pp_stack: - # If we saw an #else, we will need to restore the nesting - # stack to its former state before the #else, otherwise we - # will just continue from where we left off. - if self.pp_stack[-1].seen_else: - # Here we can just use a shallow copy since we are the last - # reference to it. - self.stack = self.pp_stack[-1].stack_before_else - # Drop the corresponding #if - self.pp_stack.pop() - else: - # TODO(unknown): unexpected #endif, issue warning? - pass - - def Update(self, filename, clean_lines, linenum, error): - """Update nesting state with current line. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Update pp_stack first - self.UpdatePreprocessor(line) - - # Count parentheses. This is to avoid adding struct arguments to - # the nesting stack. - if self.stack: - inner_block = self.stack[-1] - depth_change = line.count('(') - line.count(')') - inner_block.open_parentheses += depth_change - - # Also check if we are starting or ending an inline assembly block. - if inner_block.inline_asm in (_NO_ASM, _END_ASM): - if (depth_change != 0 and - inner_block.open_parentheses == 1 and - _MATCH_ASM.match(line)): - # Enter assembly block - inner_block.inline_asm = _INSIDE_ASM - else: - # Not entering assembly block. If previous line was _END_ASM, - # we will now shift to _NO_ASM state. - inner_block.inline_asm = _NO_ASM - elif (inner_block.inline_asm == _INSIDE_ASM and - inner_block.open_parentheses == 0): - # Exit assembly block - inner_block.inline_asm = _END_ASM - - # Consume namespace declaration at the beginning of the line. Do - # this in a loop so that we catch same line declarations like this: - # namespace proto2 { namespace bridge { class MessageSet; } } - while True: - # Match start of namespace. The "\b\s*" below catches namespace - # declarations even if it weren't followed by a whitespace, this - # is so that we don't confuse our namespace checker. The - # missing spaces will be flagged by CheckSpacing. - namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) - if not namespace_decl_match: - break - - new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) - self.stack.append(new_namespace) - - line = namespace_decl_match.group(2) - if line.find('{') != -1: - new_namespace.seen_open_brace = True - line = line[line.find('{') + 1:] - - # Look for a class declaration in whatever is left of the line - # after parsing namespaces. The regexp accounts for decorated classes - # such as in: - # class LOCKABLE API Object { - # }; - # - # Templates with class arguments may confuse the parser, for example: - # template <class T - # class Comparator = less<T>, - # class Vector = vector<T> > - # class HeapQueue { - # - # Because this parser has no nesting state about templates, by the - # time it saw "class Comparator", it may think that it's a new class. - # Nested templates have a similar problem: - # template < - # typename ExportedType, - # typename TupleType, - # template <typename, typename> class ImplTemplate> - # - # To avoid these cases, we ignore classes that are followed by '=' or '>' - class_decl_match = Match( - r'\s*(template\s*<[\w\s<>,:]*>\s*)?' - '(class|struct)\s+([A-Z_]+\s+)*(\w+(?:::\w+)*)' - '(([^=>]|<[^<>]*>)*)$', line) - if (class_decl_match and - (not self.stack or self.stack[-1].open_parentheses == 0)): - self.stack.append(_ClassInfo( - class_decl_match.group(4), class_decl_match.group(2), - clean_lines, linenum)) - line = class_decl_match.group(5) - - # If we have not yet seen the opening brace for the innermost block, - # run checks here. - if not self.SeenOpenBrace(): - self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) - - # Update access control if we are inside a class/struct - if self.stack and isinstance(self.stack[-1], _ClassInfo): - access_match = Match(r'\s*(public|private|protected)\s*:', line) - if access_match: - self.stack[-1].access = access_match.group(1) - - # Consume braces or semicolons from what's left of the line - while True: - # Match first brace, semicolon, or closed parenthesis. - matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) - if not matched: - break - - token = matched.group(1) - if token == '{': - # If namespace or class hasn't seen a opening brace yet, mark - # namespace/class head as complete. Push a new block onto the - # stack otherwise. - if not self.SeenOpenBrace(): - self.stack[-1].seen_open_brace = True - else: - self.stack.append(_BlockInfo(True)) - if _MATCH_ASM.match(line): - self.stack[-1].inline_asm = _BLOCK_ASM - elif token == ';' or token == ')': - # If we haven't seen an opening brace yet, but we already saw - # a semicolon, this is probably a forward declaration. Pop - # the stack for these. - # - # Similarly, if we haven't seen an opening brace yet, but we - # already saw a closing parenthesis, then these are probably - # function arguments with extra "class" or "struct" keywords. - # Also pop these stack for these. - if not self.SeenOpenBrace(): - self.stack.pop() - else: # token == '}' - # Perform end of block checks and pop the stack. - if self.stack: - self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) - self.stack.pop() - line = matched.group(2) - - def InnermostClass(self): - """Get class info on the top of the stack. - - Returns: - A _ClassInfo object if we are inside a class, or None otherwise. - """ - for i in range(len(self.stack), 0, -1): - classinfo = self.stack[i - 1] - if isinstance(classinfo, _ClassInfo): - return classinfo - return None - - def CheckClassFinished(self, filename, error): - """Checks that all classes have been completely parsed. - - Call this when all lines in a file have been processed. - Args: - filename: The name of the current file. - error: The function to call with any errors found. - """ - # Note: This test can result in false positives if #ifdef constructs - # get in the way of brace matching. See the testBuildClass test in - # cpplint_unittest.py for an example of this. - for obj in self.stack: - if isinstance(obj, _ClassInfo): - error(filename, obj.starting_linenum, 'build/class', 5, - 'Failed to find complete declaration of class %s' % - obj.name) - - -def CheckForNonStandardConstructs(filename, clean_lines, linenum, - nesting_state, error): - """Logs an error if we see certain non-ANSI constructs ignored by gcc-2. - - Complain about several constructs which gcc-2 accepts, but which are - not standard C++. Warning about these in lint is one way to ease the - transition to new compilers. - - put storage class first (e.g. "static const" instead of "const static"). - - "%lld" instead of %qd" in printf-type functions. - - "%1$d" is non-standard in printf-type functions. - - "\%" is an undefined character escape sequence. - - text after #endif is not allowed. - - invalid inner-style forward declaration. - - >? and <? operators, and their >?= and <?= cousins. - - Additionally, check for constructor/destructor style violations and reference - members, as it is very convenient to do so while checking for - gcc-2 compliance. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - """ - - # Remove comments from the line, but leave in strings for now. - line = clean_lines.lines[linenum] - - if Search(r'printf\s*\(.*".*%[-+ ]?\d*q', line): - error(filename, linenum, 'runtime/printf_format', 3, - '%q in format strings is deprecated. Use %ll instead.') - - if Search(r'printf\s*\(.*".*%\d+\$', line): - error(filename, linenum, 'runtime/printf_format', 2, - '%N$ formats are unconventional. Try rewriting to avoid them.') - - # Remove escaped backslashes before looking for undefined escapes. - line = line.replace('\\\\', '') - - if Search(r'("|\').*\\(%|\[|\(|{)', line): - error(filename, linenum, 'build/printf_format', 3, - '%, [, (, and { are undefined character escapes. Unescape them.') - - # For the rest, work with both comments and strings removed. - line = clean_lines.elided[linenum] - - if Search(r'\b(const|volatile|void|char|short|int|long' - r'|float|double|signed|unsigned' - r'|schar|u?int8|u?int16|u?int32|u?int64)' - r'\s+(register|static|extern|typedef)\b', - line): - error(filename, linenum, 'build/storage_class', 5, - 'Storage class (static, extern, typedef, etc) should be first.') - - if Match(r'\s*#\s*endif\s*[^/\s]+', line): - error(filename, linenum, 'build/endif_comment', 5, - 'Uncommented text after #endif is non-standard. Use a comment.') - - if Match(r'\s*class\s+(\w+\s*::\s*)+\w+\s*;', line): - error(filename, linenum, 'build/forward_decl', 5, - 'Inner-style forward declarations are invalid. Remove this line.') - - if Search(r'(\w+|[+-]?\d+(\.\d*)?)\s*(<|>)\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', - line): - error(filename, linenum, 'build/deprecated', 3, - '>? and <? (max and min) operators are non-standard and deprecated.') - - if Search(r'^\s*const\s*string\s*&\s*\w+\s*;', line): - # TODO(unknown): Could it be expanded safely to arbitrary references, - # without triggering too many false positives? The first - # attempt triggered 5 warnings for mostly benign code in the regtest, hence - # the restriction. - # Here's the original regexp, for the reference: - # type_name = r'\w+((\s*::\s*\w+)|(\s*<\s*\w+?\s*>))?' - # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' - error(filename, linenum, 'runtime/member_string_references', 2, - 'const string& members are dangerous. It is much better to use ' - 'alternatives, such as pointers or simple constants.') - - # Everything else in this function operates on class declarations. - # Return early if the top of the nesting stack is not a class, or if - # the class head is not completed yet. - classinfo = nesting_state.InnermostClass() - if not classinfo or not classinfo.seen_open_brace: - return - - # The class may have been declared with namespace or classname qualifiers. - # The constructor and destructor will not have those qualifiers. - base_classname = classinfo.name.split('::')[-1] - - # Look for single-argument constructors that aren't marked explicit. - # Technically a valid construct, but against style. - args = Match(r'\s+(?:inline\s+)?%s\s*\(([^,()]+)\)' - % re.escape(base_classname), - line) - if (args and - args.group(1) != 'void' and - not Match(r'(const\s+)?%s\s*(?:<\w+>\s*)?&' % re.escape(base_classname), - args.group(1).strip())): - error(filename, linenum, 'runtime/explicit', 5, - 'Single-argument constructors should be marked explicit.') - - -def CheckSpacingForFunctionCall(filename, line, linenum, error): - """Checks for the correctness of various spacing around function calls. - - Args: - filename: The name of the current file. - line: The text of the line to check. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Since function calls often occur inside if/for/while/switch - # expressions - which have their own, more liberal conventions - we - # first see if we should be looking inside such an expression for a - # function call, to which we can apply more strict standards. - fncall = line # if there's no control flow construct, look at whole line - for pattern in (r'\bif\s*\((.*)\)\s*{', - r'\bfor\s*\((.*)\)\s*{', - r'\bwhile\s*\((.*)\)\s*[{;]', - r'\bswitch\s*\((.*)\)\s*{'): - match = Search(pattern, line) - if match: - fncall = match.group(1) # look inside the parens for function calls - break - - # Except in if/for/while/switch, there should never be space - # immediately inside parens (eg "f( 3, 4 )"). We make an exception - # for nested parens ( (a+b) + c ). Likewise, there should never be - # a space before a ( when it's a function argument. I assume it's a - # function argument when the char before the whitespace is legal in - # a function name (alnum + _) and we're not starting a macro. Also ignore - # pointers and references to arrays and functions coz they're too tricky: - # we use a very simple way to recognize these: - # " (something)(maybe-something)" or - # " (something)(maybe-something," or - # " (something)[something]" - # Note that we assume the contents of [] to be short enough that - # they'll never need to wrap. - if ( # Ignore control structures. - # BEGIN android-changed - # not Search(r'\b(if|for|while|switch|return|delete)\b', fncall) and - not Search(r'\b(if|for|while|switch|return|delete|new)\b', fncall) and - # END android-changed - # Ignore pointers/references to functions. - not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and - # Ignore pointers/references to arrays. - not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): - if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space after ( in function call') - elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space after (') - if (Search(r'\w\s+\(', fncall) and - not Search(r'#\s*define|typedef', fncall) and - not Search(r'\w\s+\((\w+::)?\*\w+\)\(', fncall)): - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space before ( in function call') - # If the ) is followed only by a newline or a { + newline, assume it's - # part of a control statement (if/while/etc), and don't complain - if Search(r'[^)]\s+\)\s*[^{\s]', fncall): - # If the closing parenthesis is preceded by only whitespaces, - # try to give a more descriptive error message. - if Search(r'^\s+\)', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Closing ) should be moved to the previous line') - else: - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space before )') - - -def IsBlankLine(line): - """Returns true if the given line is blank. - - We consider a line to be blank if the line is empty or consists of - only white spaces. - - Args: - line: A line of a string. - - Returns: - True, if the given line is blank. - """ - return not line or line.isspace() - - -def CheckForFunctionLengths(filename, clean_lines, linenum, - function_state, error): - """Reports for long function bodies. - - For an overview why this is done, see: - http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions - - Uses a simplistic algorithm assuming other style guidelines - (especially spacing) are followed. - Only checks unindented functions, so class members are unchecked. - Trivial bodies are unchecked, so constructors with huge initializer lists - may be missed. - Blank/comment lines are not counted so as to avoid encouraging the removal - of vertical space and comments just to get through a lint check. - NOLINT *on the last line of a function* disables this check. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - function_state: Current function name and lines in body so far. - error: The function to call with any errors found. - """ - lines = clean_lines.lines - line = lines[linenum] - raw = clean_lines.raw_lines - raw_line = raw[linenum] - joined_line = '' - - starting_func = False - regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... - match_result = Match(regexp, line) - if match_result: - # If the name is all caps and underscores, figure it's a macro and - # ignore it, unless it's TEST or TEST_F. - function_name = match_result.group(1).split()[-1] - if function_name == 'TEST' or function_name == 'TEST_F' or ( - not Match(r'[A-Z_]+$', function_name)): - starting_func = True - - if starting_func: - body_found = False - for start_linenum in xrange(linenum, clean_lines.NumLines()): - start_line = lines[start_linenum] - joined_line += ' ' + start_line.lstrip() - if Search(r'(;|})', start_line): # Declarations and trivial functions - body_found = True - break # ... ignore - elif Search(r'{', start_line): - body_found = True - function = Search(r'((\w|:)*)\(', line).group(1) - if Match(r'TEST', function): # Handle TEST... macros - parameter_regexp = Search(r'(\(.*\))', joined_line) - if parameter_regexp: # Ignore bad syntax - function += parameter_regexp.group(1) - else: - function += '()' - function_state.Begin(function) - break - if not body_found: - # No body for the function (or evidence of a non-function) was found. - error(filename, linenum, 'readability/fn_size', 5, - 'Lint failed to find start of function body.') - elif Match(r'^\}\s*$', line): # function end - function_state.Check(error, filename, linenum) - function_state.End() - elif not Match(r'^\s*$', line): - function_state.Count() # Count non-blank/non-comment lines. - - -_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') - - -def CheckComment(comment, filename, linenum, error): - """Checks for common mistakes in TODO comments. - - Args: - comment: The text of the comment from the line in question. - filename: The name of the current file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - match = _RE_PATTERN_TODO.match(comment) - if match: - # One whitespace is correct; zero whitespace is handled elsewhere. - leading_whitespace = match.group(1) - if len(leading_whitespace) > 1: - error(filename, linenum, 'whitespace/todo', 2, - 'Too many spaces before TODO') - - username = match.group(2) - if not username: - error(filename, linenum, 'readability/todo', 2, - 'Missing username in TODO; it should look like ' - '"// TODO(my_username): Stuff."') - - middle_whitespace = match.group(3) - # Comparisons made explicit for correctness -- pylint: disable-msg=C6403 - if middle_whitespace != ' ' and middle_whitespace != '': - error(filename, linenum, 'whitespace/todo', 2, - 'TODO(my_username) should be followed by a space') - -def CheckAccess(filename, clean_lines, linenum, nesting_state, error): - """Checks for improper use of DISALLOW* macros. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] # get rid of comments and strings - - matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|' - r'DISALLOW_EVIL_CONSTRUCTORS|' - r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line) - if not matched: - return - if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo): - if nesting_state.stack[-1].access != 'private': - error(filename, linenum, 'readability/constructors', 3, - '%s must be in the private: section' % matched.group(1)) - - else: - # Found DISALLOW* macro outside a class declaration, or perhaps it - # was used inside a function when it should have been part of the - # class declaration. We could issue a warning here, but it - # probably resulted in a compiler error already. - pass - - -def FindNextMatchingAngleBracket(clean_lines, linenum, init_suffix): - """Find the corresponding > to close a template. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: Current line number. - init_suffix: Remainder of the current line after the initial <. - - Returns: - True if a matching bracket exists. - """ - line = init_suffix - nesting_stack = ['<'] - while True: - # Find the next operator that can tell us whether < is used as an - # opening bracket or as a less-than operator. We only want to - # warn on the latter case. - # - # We could also check all other operators and terminate the search - # early, e.g. if we got something like this "a<b+c", the "<" is - # most likely a less-than operator, but then we will get false - # positives for default arguments (e.g. http://go/prccd) and - # other template expressions (e.g. http://go/oxcjq). - match = Search(r'^[^<>(),;\[\]]*([<>(),;\[\]])(.*)$', line) - if match: - # Found an operator, update nesting stack - operator = match.group(1) - line = match.group(2) - - if nesting_stack[-1] == '<': - # Expecting closing angle bracket - if operator in ('<', '(', '['): - nesting_stack.append(operator) - elif operator == '>': - nesting_stack.pop() - if not nesting_stack: - # Found matching angle bracket - return True - elif operator == ',': - # Got a comma after a bracket, this is most likely a template - # argument. We have not seen a closing angle bracket yet, but - # it's probably a few lines later if we look for it, so just - # return early here. - return True - else: - # Got some other operator. - return False - - else: - # Expecting closing parenthesis or closing bracket - if operator in ('<', '(', '['): - nesting_stack.append(operator) - elif operator in (')', ']'): - # We don't bother checking for matching () or []. If we got - # something like (] or [), it would have been a syntax error. - nesting_stack.pop() - - else: - # Scan the next line - linenum += 1 - if linenum >= len(clean_lines.elided): - break - line = clean_lines.elided[linenum] - - # Exhausted all remaining lines and still no matching angle bracket. - # Most likely the input was incomplete, otherwise we should have - # seen a semicolon and returned early. - return True - - -def FindPreviousMatchingAngleBracket(clean_lines, linenum, init_prefix): - """Find the corresponding < that started a template. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: Current line number. - init_prefix: Part of the current line before the initial >. - - Returns: - True if a matching bracket exists. - """ - line = init_prefix - nesting_stack = ['>'] - while True: - # Find the previous operator - match = Search(r'^(.*)([<>(),;\[\]])[^<>(),;\[\]]*$', line) - if match: - # Found an operator, update nesting stack - operator = match.group(2) - line = match.group(1) - - if nesting_stack[-1] == '>': - # Expecting opening angle bracket - if operator in ('>', ')', ']'): - nesting_stack.append(operator) - elif operator == '<': - nesting_stack.pop() - if not nesting_stack: - # Found matching angle bracket - return True - elif operator == ',': - # Got a comma before a bracket, this is most likely a - # template argument. The opening angle bracket is probably - # there if we look for it, so just return early here. - return True - else: - # Got some other operator. - return False - - else: - # Expecting opening parenthesis or opening bracket - if operator in ('>', ')', ']'): - nesting_stack.append(operator) - elif operator in ('(', '['): - nesting_stack.pop() - - else: - # Scan the previous line - linenum -= 1 - if linenum < 0: - break - line = clean_lines.elided[linenum] - - # Exhausted all earlier lines and still no matching angle bracket. - return False - - -def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): - """Checks for the correctness of various spacing issues in the code. - - Things we check for: spaces around operators, spaces after - if/for/while/switch, no spaces around parens in function calls, two - spaces between code and comment, don't start a block with a blank - line, don't end a function with a blank line, don't add a blank line - after public/protected/private, don't have too many blank lines in a row. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - - raw = clean_lines.raw_lines - line = raw[linenum] - - # Before nixing comments, check if the line is blank for no good - # reason. This includes the first line after a block is opened, and - # blank lines at the end of a function (ie, right before a line like '}' - # - # Skip all the blank line checks if we are immediately inside a - # namespace body. In other words, don't issue blank line warnings - # for this block: - # namespace { - # - # } - # - # A warning about missing end of namespace comments will be issued instead. - if IsBlankLine(line) and not nesting_state.InNamespaceBody(): - elided = clean_lines.elided - prev_line = elided[linenum - 1] - prevbrace = prev_line.rfind('{') - # TODO(unknown): Don't complain if line before blank line, and line after, - # both start with alnums and are indented the same amount. - # This ignores whitespace at the start of a namespace block - # because those are not usually indented. - if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: - # OK, we have a blank line at the start of a code block. Before we - # complain, we check if it is an exception to the rule: The previous - # non-empty line has the parameters of a function header that are indented - # 4 spaces (because they did not fit in a 80 column line when placed on - # the same line as the function name). We also check for the case where - # the previous line is indented 6 spaces, which may happen when the - # initializers of a constructor do not fit into a 80 column line. - exception = False - if Match(r' {6}\w', prev_line): # Initializer list? - # We are looking for the opening column of initializer list, which - # should be indented 4 spaces to cause 6 space indentation afterwards. - search_position = linenum-2 - while (search_position >= 0 - and Match(r' {6}\w', elided[search_position])): - search_position -= 1 - exception = (search_position >= 0 - and elided[search_position][:5] == ' :') - else: - # Search for the function arguments or an initializer list. We use a - # simple heuristic here: If the line is indented 4 spaces; and we have a - # closing paren, without the opening paren, followed by an opening brace - # or colon (for initializer lists) we assume that it is the last line of - # a function header. If we have a colon indented 4 spaces, it is an - # initializer list. - exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', - prev_line) - or Match(r' {4}:', prev_line)) - - if not exception: - error(filename, linenum, 'whitespace/blank_line', 2, - 'Blank line at the start of a code block. Is this needed?') - # Ignore blank lines at the end of a block in a long if-else - # chain, like this: - # if (condition1) { - # // Something followed by a blank line - # - # } else if (condition2) { - # // Something else - # } - if linenum + 1 < clean_lines.NumLines(): - next_line = raw[linenum + 1] - if (next_line - and Match(r'\s*}', next_line) - and next_line.find('} else ') == -1): - error(filename, linenum, 'whitespace/blank_line', 3, - 'Blank line at the end of a code block. Is this needed?') - - matched = Match(r'\s*(public|protected|private):', prev_line) - if matched: - error(filename, linenum, 'whitespace/blank_line', 3, - 'Do not leave a blank line after "%s:"' % matched.group(1)) - - # Next, we complain if there's a comment too near the text - commentpos = line.find('//') - if commentpos != -1: - # Check if the // may be in quotes. If so, ignore it - # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 - if (line.count('"', 0, commentpos) - - line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes - # Allow one space for new scopes, two spaces otherwise: - if (not Match(r'^\s*{ //', line) and - ((commentpos >= 1 and - line[commentpos-1] not in string.whitespace) or - (commentpos >= 2 and - line[commentpos-2] not in string.whitespace))): - error(filename, linenum, 'whitespace/comments', 2, - 'At least two spaces is best between code and comments') - # There should always be a space between the // and the comment - commentend = commentpos + 2 - if commentend < len(line) and not line[commentend] == ' ': - # but some lines are exceptions -- e.g. if they're big - # comment delimiters like: - # //---------------------------------------------------------- - # or are an empty C++ style Doxygen comment, like: - # /// - # or they begin with multiple slashes followed by a space: - # //////// Header comment - match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or - Search(r'^/$', line[commentend:]) or - Search(r'^/+ ', line[commentend:])) - if not match: - error(filename, linenum, 'whitespace/comments', 4, - 'Should have a space between // and comment') - CheckComment(line[commentpos:], filename, linenum, error) - - line = clean_lines.elided[linenum] # get rid of comments and strings - - # Don't try to do spacing checks for operator methods - line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) - - # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". - # Otherwise not. Note we only check for non-spaces on *both* sides; - # sometimes people put non-spaces on one side when aligning ='s among - # many lines (not that this is behavior that I approve of...) - if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line): - error(filename, linenum, 'whitespace/operators', 4, - 'Missing spaces around =') - - # It's ok not to have spaces around binary operators like + - * /, but if - # there's too little whitespace, we get concerned. It's hard to tell, - # though, so we punt on this one for now. TODO. - - # You should always have whitespace around binary operators. - # - # Check <= and >= first to avoid false positives with < and >, then - # check non-include lines for spacing around < and >. - match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line) - if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around %s' % match.group(1)) - # We allow no-spaces around << when used like this: 10<<20, but - # not otherwise (particularly, not when used as streams) - match = Search(r'(\S)(?:L|UL|ULL|l|ul|ull)?<<(\S)', line) - if match and not (match.group(1).isdigit() and match.group(2).isdigit()): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <<') - elif not Match(r'#.*include', line): - # Avoid false positives on -> - reduced_line = line.replace('->', '') - - # Look for < that is not surrounded by spaces. This is only - # triggered if both sides are missing spaces, even though - # technically should should flag if at least one side is missing a - # space. This is done to avoid some false positives with shifts. - match = Search(r'[^\s<]<([^\s=<].*)', reduced_line) - if (match and - not FindNextMatchingAngleBracket(clean_lines, linenum, match.group(1))): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <') - - # Look for > that is not surrounded by spaces. Similar to the - # above, we only trigger if both sides are missing spaces to avoid - # false positives with shifts. - match = Search(r'^(.*[^\s>])>[^\s=>]', reduced_line) - if (match and - not FindPreviousMatchingAngleBracket(clean_lines, linenum, - match.group(1))): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >') - - # We allow no-spaces around >> for almost anything. This is because - # C++11 allows ">>" to close nested templates, which accounts for - # most cases when ">>" is not followed by a space. - # - # We still warn on ">>" followed by alpha character, because that is - # likely due to ">>" being used for right shifts, e.g.: - # value >> alpha - # - # When ">>" is used to close templates, the alphanumeric letter that - # follows would be part of an identifier, and there should still be - # a space separating the template type and the identifier. - # type<type<type>> alpha - match = Search(r'>>[a-zA-Z_]', line) - if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >>') - - # There shouldn't be space around unary operators - match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) - if match: - error(filename, linenum, 'whitespace/operators', 4, - 'Extra space for operator %s' % match.group(1)) - - # A pet peeve of mine: no spaces after an if, while, switch, or for - match = Search(r' (if\(|for\(|while\(|switch\()', line) - if match: - error(filename, linenum, 'whitespace/parens', 5, - 'Missing space before ( in %s' % match.group(1)) - - # For if/for/while/switch, the left and right parens should be - # consistent about how many spaces are inside the parens, and - # there should either be zero or one spaces inside the parens. - # We don't want: "if ( foo)" or "if ( foo )". - # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. - match = Search(r'\b(if|for|while|switch)\s*' - r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', - line) - if match: - if len(match.group(2)) != len(match.group(4)): - if not (match.group(3) == ';' and - len(match.group(2)) == 1 + len(match.group(4)) or - not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): - error(filename, linenum, 'whitespace/parens', 5, - 'Mismatching spaces inside () in %s' % match.group(1)) - if not len(match.group(2)) in [0, 1]: - error(filename, linenum, 'whitespace/parens', 5, - 'Should have zero or one spaces inside ( and ) in %s' % - match.group(1)) - - # You should always have a space after a comma (either as fn arg or operator) - if Search(r',[^\s]', line): - error(filename, linenum, 'whitespace/comma', 3, - 'Missing space after ,') - - # You should always have a space after a semicolon - # except for few corner cases - # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more - # space after ; - if Search(r';[^\s};\\)/]', line): - error(filename, linenum, 'whitespace/semicolon', 3, - 'Missing space after ;') - - # Next we will look for issues with function calls. - CheckSpacingForFunctionCall(filename, line, linenum, error) - - # Except after an opening paren, or after another opening brace (in case of - # an initializer list, for instance), you should have spaces before your - # braces. And since you should never have braces at the beginning of a line, - # this is an easy test. - if Search(r'[^ ({]{', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before {') - - # Make sure '} else {' has spaces. - if Search(r'}else', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before else') - - # You shouldn't have spaces before your brackets, except maybe after - # 'delete []' or 'new char * []'. - if Search(r'\w\s+\[', line) and not Search(r'delete\s+\[', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Extra space before [') - - # You shouldn't have a space before a semicolon at the end of the line. - # There's a special case for "for" since the style guide allows space before - # the semicolon there. - if Search(r':\s*;\s*$', line): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Semicolon defining empty statement. Use {} instead.') - elif Search(r'^\s*;\s*$', line): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Line contains only semicolon. If this should be an empty statement, ' - 'use {} instead.') - elif (Search(r'\s+;\s*$', line) and - not Search(r'\bfor\b', line)): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Extra space before last semicolon. If this should be an empty ' - 'statement, use {} instead.') - - # In range-based for, we wanted spaces before and after the colon, but - # not around "::" tokens that might appear. - if (Search('for *\(.*[^:]:[^: ]', line) or - Search('for *\(.*[^: ]:[^:]', line)): - error(filename, linenum, 'whitespace/forcolon', 2, - 'Missing space around colon in range-based for loop') - - -def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): - """Checks for additional blank line issues related to sections. - - Currently the only thing checked here is blank line before protected/private. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - class_info: A _ClassInfo objects. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - # Skip checks if the class is small, where small means 25 lines or less. - # 25 lines seems like a good cutoff since that's the usual height of - # terminals, and any class that can't fit in one screen can't really - # be considered "small". - # - # Also skip checks if we are on the first line. This accounts for - # classes that look like - # class Foo { public: ... }; - # - # If we didn't find the end of the class, last_line would be zero, - # and the check will be skipped by the first condition. - if (class_info.last_line - class_info.starting_linenum <= 24 or - linenum <= class_info.starting_linenum): - return - - matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) - if matched: - # Issue warning if the line before public/protected/private was - # not a blank line, but don't do this if the previous line contains - # "class" or "struct". This can happen two ways: - # - We are at the beginning of the class. - # - We are forward-declaring an inner class that is semantically - # private, but needed to be public for implementation reasons. - # Also ignores cases where the previous line ends with a backslash as can be - # common when defining classes in C macros. - prev_line = clean_lines.lines[linenum - 1] - if (not IsBlankLine(prev_line) and - not Search(r'\b(class|struct)\b', prev_line) and - not Search(r'\\$', prev_line)): - # Try a bit harder to find the beginning of the class. This is to - # account for multi-line base-specifier lists, e.g.: - # class Derived - # : public Base { - end_class_head = class_info.starting_linenum - for i in range(class_info.starting_linenum, linenum): - if Search(r'\{\s*$', clean_lines.lines[i]): - end_class_head = i - break - if end_class_head < linenum - 1: - error(filename, linenum, 'whitespace/blank_line', 3, - '"%s:" should be preceded by a blank line' % matched.group(1)) - - -def GetPreviousNonBlankLine(clean_lines, linenum): - """Return the most recent non-blank line and its line number. - - Args: - clean_lines: A CleansedLines instance containing the file contents. - linenum: The number of the line to check. - - Returns: - A tuple with two elements. The first element is the contents of the last - non-blank line before the current line, or the empty string if this is the - first non-blank line. The second is the line number of that line, or -1 - if this is the first non-blank line. - """ - - prevlinenum = linenum - 1 - while prevlinenum >= 0: - prevline = clean_lines.elided[prevlinenum] - if not IsBlankLine(prevline): # if not a blank line... - return (prevline, prevlinenum) - prevlinenum -= 1 - return ('', -1) - - -def CheckBraces(filename, clean_lines, linenum, error): - """Looks for misplaced braces (e.g. at the end of line). - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - line = clean_lines.elided[linenum] # get rid of comments and strings - - if Match(r'\s*{\s*$', line): - # We allow an open brace to start a line in the case where someone - # is using braces in a block to explicitly create a new scope, - # which is commonly used to control the lifetime of - # stack-allocated variables. We don't detect this perfectly: we - # just don't complain if the last non-whitespace character on the - # previous non-blank line is ';', ':', '{', or '}', or if the previous - # line starts a preprocessor block. - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if (not Search(r'[;:}{]\s*$', prevline) and - not Match(r'\s*#', prevline)): - error(filename, linenum, 'whitespace/braces', 4, - '{ should almost always be at the end of the previous line') - - # An else clause should be on the same line as the preceding closing brace. - if Match(r'\s*else\s*', line): - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if Match(r'\s*}\s*$', prevline): - error(filename, linenum, 'whitespace/newline', 4, - 'An else should appear on the same line as the preceding }') - - # If braces come on one side of an else, they should be on both. - # However, we have to worry about "else if" that spans multiple lines! - if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): - if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if - # find the ( after the if - pos = line.find('else if') - pos = line.find('(', pos) - if pos > 0: - (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) - if endline[endpos:].find('{') == -1: # must be brace after if - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side, it should have it on both') - else: # common case: else not followed by a multi-line if - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side, it should have it on both') - - # Likewise, an else should never have the else clause on the same line - if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): - error(filename, linenum, 'whitespace/newline', 4, - 'Else clause should never be on same line as else (use 2 lines)') - - # In the same way, a do/while should never be on one line - if Match(r'\s*do [^\s{]', line): - error(filename, linenum, 'whitespace/newline', 4, - 'do/while clauses should not be on a single line') - - # Braces shouldn't be followed by a ; unless they're defining a struct - # or initializing an array. - # We can't tell in general, but we can for some common cases. - prevlinenum = linenum - while True: - (prevline, prevlinenum) = GetPreviousNonBlankLine(clean_lines, prevlinenum) - if Match(r'\s+{.*}\s*;', line) and not prevline.count(';'): - line = prevline + line - else: - break - if (Search(r'{.*}\s*;', line) and - line.count('{') == line.count('}') and - not Search(r'struct|class|enum|\s*=\s*{', line)): - error(filename, linenum, 'readability/braces', 4, - "You don't need a ; after a }") - - -def CheckEmptyLoopBody(filename, clean_lines, linenum, error): - """Loop for empty loop body with only a single semicolon. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Search for loop keywords at the beginning of the line. Because only - # whitespaces are allowed before the keywords, this will also ignore most - # do-while-loops, since those lines should start with closing brace. - line = clean_lines.elided[linenum] - if Match(r'\s*(for|while)\s*\(', line): - # Find the end of the conditional expression - (end_line, end_linenum, end_pos) = CloseExpression( - clean_lines, linenum, line.find('(')) - - # Output warning if what follows the condition expression is a semicolon. - # No warning for all other cases, including whitespace or newline, since we - # have a separate check for semicolons preceded by whitespace. - if end_pos >= 0 and Match(r';', end_line[end_pos:]): - error(filename, end_linenum, 'whitespace/empty_loop_body', 5, - 'Empty loop bodies should use {} or continue') - - -def ReplaceableCheck(operator, macro, line): - """Determine whether a basic CHECK can be replaced with a more specific one. - - For example suggest using CHECK_EQ instead of CHECK(a == b) and - similarly for CHECK_GE, CHECK_GT, CHECK_LE, CHECK_LT, CHECK_NE. - - Args: - operator: The C++ operator used in the CHECK. - macro: The CHECK or EXPECT macro being called. - line: The current source line. - - Returns: - True if the CHECK can be replaced with a more specific one. - """ - - # This matches decimal and hex integers, strings, and chars (in that order). - match_constant = r'([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')' - - # Expression to match two sides of the operator with something that - # looks like a literal, since CHECK(x == iterator) won't compile. - # This means we can't catch all the cases where a more specific - # CHECK is possible, but it's less annoying than dealing with - # extraneous warnings. - match_this = (r'\s*' + macro + r'\((\s*' + - match_constant + r'\s*' + operator + r'[^<>].*|' - r'.*[^<>]' + operator + r'\s*' + match_constant + - r'\s*\))') - - # Don't complain about CHECK(x == NULL) or similar because - # CHECK_EQ(x, NULL) won't compile (requires a cast). - # Also, don't complain about more complex boolean expressions - # involving && or || such as CHECK(a == b || c == d). - return Match(match_this, line) and not Search(r'NULL|&&|\|\|', line) - - -def CheckCheck(filename, clean_lines, linenum, error): - """Checks the use of CHECK and EXPECT macros. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Decide the set of replacement macros that should be suggested - raw_lines = clean_lines.raw_lines - current_macro = '' - for macro in _CHECK_MACROS: - if raw_lines[linenum].find(macro) >= 0: - current_macro = macro - break - if not current_macro: - # Don't waste time here if line doesn't contain 'CHECK' or 'EXPECT' - return - - line = clean_lines.elided[linenum] # get rid of comments and strings - - # Encourage replacing plain CHECKs with CHECK_EQ/CHECK_NE/etc. - for operator in ['==', '!=', '>=', '>', '<=', '<']: - if ReplaceableCheck(operator, current_macro, line): - error(filename, linenum, 'readability/check', 2, - 'Consider using %s instead of %s(a %s b)' % ( - _CHECK_REPLACEMENT[current_macro][operator], - current_macro, operator)) - break - - -def CheckAltTokens(filename, clean_lines, linenum, error): - """Check alternative keywords being used in boolean expressions. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Avoid preprocessor lines - if Match(r'^\s*#', line): - return - - # Last ditch effort to avoid multi-line comments. This will not help - # if the comment started before the current line or ended after the - # current line, but it catches most of the false positives. At least, - # it provides a way to workaround this warning for people who use - # multi-line comments in preprocessor macros. - # - # TODO(unknown): remove this once cpplint has better support for - # multi-line comments. - if line.find('/*') >= 0 or line.find('*/') >= 0: - return - - for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): - error(filename, linenum, 'readability/alt_tokens', 2, - 'Use operator %s instead of %s' % ( - _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) - - -def GetLineWidth(line): - """Determines the width of the line in column positions. - - Args: - line: A string, which may be a Unicode string. - - Returns: - The width of the line in column positions, accounting for Unicode - combining characters and wide characters. - """ - if isinstance(line, unicode): - width = 0 - for uc in unicodedata.normalize('NFC', line): - if unicodedata.east_asian_width(uc) in ('W', 'F'): - width += 2 - elif not unicodedata.combining(uc): - width += 1 - return width - else: - return len(line) - - -def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, - error): - """Checks rules from the 'C++ style rules' section of cppguide.html. - - Most of these rules are hard to test (naming, comment style), but we - do what we can. In particular we check for 2-space indents, line lengths, - tab usage, spaces inside code, etc. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - file_extension: The extension (without the dot) of the filename. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - - raw_lines = clean_lines.raw_lines - line = raw_lines[linenum] - - if line.find('\t') != -1: - error(filename, linenum, 'whitespace/tab', 1, - 'Tab found; better to use spaces') - - # One or three blank spaces at the beginning of the line is weird; it's - # hard to reconcile that with 2-space indents. - # NOTE: here are the conditions rob pike used for his tests. Mine aren't - # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces - # if(RLENGTH > 20) complain = 0; - # if(match($0, " +(error|private|public|protected):")) complain = 0; - # if(match(prev, "&& *$")) complain = 0; - # if(match(prev, "\\|\\| *$")) complain = 0; - # if(match(prev, "[\",=><] *$")) complain = 0; - # if(match($0, " <<")) complain = 0; - # if(match(prev, " +for \\(")) complain = 0; - # if(prevodd && match(prevprev, " +for \\(")) complain = 0; - initial_spaces = 0 - cleansed_line = clean_lines.elided[linenum] - while initial_spaces < len(line) and line[initial_spaces] == ' ': - initial_spaces += 1 - if line and line[-1].isspace(): - error(filename, linenum, 'whitespace/end_of_line', 4, - 'Line ends in whitespace. Consider deleting these extra spaces.') - # There are certain situations we allow one space, notably for labels - elif ((initial_spaces == 1 or initial_spaces == 3) and - not Match(r'\s*\w+\s*:\s*$', cleansed_line)): - error(filename, linenum, 'whitespace/indent', 3, - 'Weird number of spaces at line-start. ' - 'Are you using a 2-space indent?') - # Labels should always be indented at least one space. - elif not initial_spaces and line[:2] != '//' and Search(r'[^:]:\s*$', - line): - error(filename, linenum, 'whitespace/labels', 4, - 'Labels should always be indented at least one space. ' - 'If this is a member-initializer list in a constructor or ' - 'the base class list in a class definition, the colon should ' - 'be on the following line.') - - - # Check if the line is a header guard. - is_header_guard = False - if file_extension == 'h': - cppvar = GetHeaderGuardCPPVariable(filename) - if (line.startswith('#ifndef %s' % cppvar) or - line.startswith('#define %s' % cppvar) or - line.startswith('#endif // %s' % cppvar)): - is_header_guard = True - # #include lines and header guards can be long, since there's no clean way to - # split them. - # - # URLs can be long too. It's possible to split these, but it makes them - # harder to cut&paste. - # - # The "$Id:...$" comment may also get very long without it being the - # developers fault. - if (not line.startswith('#include') and not is_header_guard and - not Match(r'^\s*//.*http(s?)://\S*$', line) and - not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): - line_width = GetLineWidth(line) - if line_width > 100: - error(filename, linenum, 'whitespace/line_length', 4, - 'Lines should very rarely be longer than 100 characters') - elif line_width > 80: - error(filename, linenum, 'whitespace/line_length', 2, - 'Lines should be <= 80 characters long') - - if (cleansed_line.count(';') > 1 and - # for loops are allowed two ;'s (and may run over two lines). - cleansed_line.find('for') == -1 and - (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or - GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and - # It's ok to have many commands in a switch case that fits in 1 line - not ((cleansed_line.find('case ') != -1 or - cleansed_line.find('default:') != -1) and - cleansed_line.find('break;') != -1)): - error(filename, linenum, 'whitespace/newline', 0, - 'More than one command on the same line') - - # Some more style checks - CheckBraces(filename, clean_lines, linenum, error) - CheckEmptyLoopBody(filename, clean_lines, linenum, error) - CheckAccess(filename, clean_lines, linenum, nesting_state, error) - CheckSpacing(filename, clean_lines, linenum, nesting_state, error) - CheckCheck(filename, clean_lines, linenum, error) - CheckAltTokens(filename, clean_lines, linenum, error) - classinfo = nesting_state.InnermostClass() - if classinfo: - CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) - - -_RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"') -_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') -# Matches the first component of a filename delimited by -s and _s. That is: -# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' -_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') - - -def _DropCommonSuffixes(filename): - """Drops common suffixes like _test.cc or -inl.h from filename. - - For example: - >>> _DropCommonSuffixes('foo/foo-inl.h') - 'foo/foo' - >>> _DropCommonSuffixes('foo/bar/foo.cc') - 'foo/bar/foo' - >>> _DropCommonSuffixes('foo/foo_internal.h') - 'foo/foo' - >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') - 'foo/foo_unusualinternal' - - Args: - filename: The input filename. - - Returns: - The filename with the common suffix removed. - """ - for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', - 'inl.h', 'impl.h', 'internal.h'): - if (filename.endswith(suffix) and len(filename) > len(suffix) and - filename[-len(suffix) - 1] in ('-', '_')): - return filename[:-len(suffix) - 1] - return os.path.splitext(filename)[0] - - -def _IsTestFilename(filename): - """Determines if the given filename has a suffix that identifies it as a test. - - Args: - filename: The input filename. - - Returns: - True if 'filename' looks like a test, False otherwise. - """ - if (filename.endswith('_test.cc') or - filename.endswith('_unittest.cc') or - filename.endswith('_regtest.cc')): - return True - else: - return False - - -def _ClassifyInclude(fileinfo, include, is_system): - """Figures out what kind of header 'include' is. - - Args: - fileinfo: The current file cpplint is running over. A FileInfo instance. - include: The path to a #included file. - is_system: True if the #include used <> rather than "". - - Returns: - One of the _XXX_HEADER constants. - - For example: - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) - _C_SYS_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) - _CPP_SYS_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) - _LIKELY_MY_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), - ... 'bar/foo_other_ext.h', False) - _POSSIBLE_MY_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) - _OTHER_HEADER - """ - # This is a list of all standard c++ header files, except - # those already checked for above. - is_stl_h = include in _STL_HEADERS - is_cpp_h = is_stl_h or include in _CPP_HEADERS - - if is_system: - if is_cpp_h: - return _CPP_SYS_HEADER - else: - return _C_SYS_HEADER - - # If the target file and the include we're checking share a - # basename when we drop common extensions, and the include - # lives in . , then it's likely to be owned by the target file. - target_dir, target_base = ( - os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) - include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) - if target_base == include_base and ( - include_dir == target_dir or - include_dir == os.path.normpath(target_dir + '/../public')): - return _LIKELY_MY_HEADER - - # If the target and include share some initial basename - # component, it's possible the target is implementing the - # include, so it's allowed to be first, but we'll never - # complain if it's not there. - target_first_component = _RE_FIRST_COMPONENT.match(target_base) - include_first_component = _RE_FIRST_COMPONENT.match(include_base) - if (target_first_component and include_first_component and - target_first_component.group(0) == - include_first_component.group(0)): - return _POSSIBLE_MY_HEADER - - return _OTHER_HEADER - - - -def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): - """Check rules that are applicable to #include lines. - - Strings on #include lines are NOT removed from elided line, to make - certain tasks easier. However, to prevent false positives, checks - applicable to #include lines in CheckLanguage must be put here. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - include_state: An _IncludeState instance in which the headers are inserted. - error: The function to call with any errors found. - """ - fileinfo = FileInfo(filename) - - line = clean_lines.lines[linenum] - - # "include" should use the new style "foo/bar.h" instead of just "bar.h" - if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line): - error(filename, linenum, 'build/include', 4, - 'Include the directory when naming .h files') - - # we shouldn't include a file more than once. actually, there are a - # handful of instances where doing so is okay, but in general it's - # not. - match = _RE_PATTERN_INCLUDE.search(line) - if match: - include = match.group(2) - is_system = (match.group(1) == '<') - if include in include_state: - error(filename, linenum, 'build/include', 4, - '"%s" already included at %s:%s' % - (include, filename, include_state[include])) - else: - include_state[include] = linenum - - # We want to ensure that headers appear in the right order: - # 1) for foo.cc, foo.h (preferred location) - # 2) c system files - # 3) cpp system files - # 4) for foo.cc, foo.h (deprecated location) - # 5) other google headers - # - # We classify each include statement as one of those 5 types - # using a number of techniques. The include_state object keeps - # track of the highest type seen, and complains if we see a - # lower type after that. - error_message = include_state.CheckNextIncludeOrder( - _ClassifyInclude(fileinfo, include, is_system)) - if error_message: - error(filename, linenum, 'build/include_order', 4, - '%s. Should be: %s.h, c system, c++ system, other.' % - (error_message, fileinfo.BaseName())) - if not include_state.IsInAlphabeticalOrder(include): - error(filename, linenum, 'build/include_alpha', 4, - 'Include "%s" not in alphabetical order' % include) - - # Look for any of the stream classes that are part of standard C++. - match = _RE_PATTERN_INCLUDE.match(line) - if match: - include = match.group(2) - if Match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include): - # Many unit tests use cout, so we exempt them. - if not _IsTestFilename(filename): - error(filename, linenum, 'readability/streams', 3, - 'Streams are highly discouraged.') - - -def _GetTextInside(text, start_pattern): - """Retrieves all the text between matching open and close parentheses. - - Given a string of lines and a regular expression string, retrieve all the text - following the expression and between opening punctuation symbols like - (, [, or {, and the matching close-punctuation symbol. This properly nested - occurrences of the punctuations, so for the text like - printf(a(), b(c())); - a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. - start_pattern must match string having an open punctuation symbol at the end. - - Args: - text: The lines to extract text. Its comments and strings must be elided. - It can be single line and can span multiple lines. - start_pattern: The regexp string indicating where to start extracting - the text. - Returns: - The extracted text. - None if either the opening string or ending punctuation could not be found. - """ - # TODO(sugawarayu): Audit cpplint.py to see what places could be profitably - # rewritten to use _GetTextInside (and use inferior regexp matching today). - - # Give opening punctuations to get the matching close-punctuations. - matching_punctuation = {'(': ')', '{': '}', '[': ']'} - closing_punctuation = set(matching_punctuation.itervalues()) - - # Find the position to start extracting text. - match = re.search(start_pattern, text, re.M) - if not match: # start_pattern not found in text. - return None - start_position = match.end(0) - - assert start_position > 0, ( - 'start_pattern must ends with an opening punctuation.') - assert text[start_position - 1] in matching_punctuation, ( - 'start_pattern must ends with an opening punctuation.') - # Stack of closing punctuations we expect to have in text after position. - punctuation_stack = [matching_punctuation[text[start_position - 1]]] - position = start_position - while punctuation_stack and position < len(text): - if text[position] == punctuation_stack[-1]: - punctuation_stack.pop() - elif text[position] in closing_punctuation: - # A closing punctuation without matching opening punctuations. - return None - elif text[position] in matching_punctuation: - punctuation_stack.append(matching_punctuation[text[position]]) - position += 1 - if punctuation_stack: - # Opening punctuations left without matching close-punctuations. - return None - # punctuations match. - return text[start_position:position - 1] - - -def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, - error): - """Checks rules from the 'C++ language rules' section of cppguide.html. - - Some of these rules are hard to test (function overloading, using - uint32 inappropriately), but we do the best we can. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - file_extension: The extension (without the dot) of the filename. - include_state: An _IncludeState instance in which the headers are inserted. - error: The function to call with any errors found. - """ - # If the line is empty or consists of entirely a comment, no need to - # check it. - line = clean_lines.elided[linenum] - if not line: - return - - match = _RE_PATTERN_INCLUDE.search(line) - if match: - CheckIncludeLine(filename, clean_lines, linenum, include_state, error) - return - - # Create an extended_line, which is the concatenation of the current and - # next lines, for more effective checking of code that may span more than one - # line. - if linenum + 1 < clean_lines.NumLines(): - extended_line = line + clean_lines.elided[linenum + 1] - else: - extended_line = line - - # Make Windows paths like Unix. - fullname = os.path.abspath(filename).replace('\\', '/') - - # TODO(unknown): figure out if they're using default arguments in fn proto. - - # Check for non-const references in functions. This is tricky because & - # is also used to take the address of something. We allow <> for templates, - # (ignoring whatever is between the braces) and : for classes. - # These are complicated re's. They try to capture the following: - # paren (for fn-prototype start), typename, &, varname. For the const - # version, we're willing for const to be before typename or after - # Don't check the implementation on same line. - fnline = line.split('{', 1)[0] - if (len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) > - len(re.findall(r'\([^()]*\bconst\s+(?:typename\s+)?(?:struct\s+)?' - r'(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) + - len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+\s+const(\s?&|&\s?)[\w]+', - fnline))): - - # We allow non-const references in a few standard places, like functions - # called "swap()" or iostream operators like "<<" or ">>". We also filter - # out for loops, which lint otherwise mistakenly thinks are functions. - if not Search( - r'(for|swap|Swap|operator[<>][<>])\s*\(\s*' - r'(?:(?:typename\s*)?[\w:]|<.*>)+\s*&', - fnline): - error(filename, linenum, 'runtime/references', 2, - 'Is this a non-const reference? ' - 'If so, make const or use a pointer.') - - # Check to see if they're using an conversion function cast. - # I just try to capture the most common basic types, though there are more. - # Parameterless conversion functions, such as bool(), are allowed as they are - # probably a member operator declaration or default constructor. - match = Search( - r'(\bnew\s+)?\b' # Grab 'new' operator, if it's there - r'(int|float|double|bool|char|int32|uint32|int64|uint64)\([^)]', line) - if match: - # gMock methods are defined using some variant of MOCK_METHODx(name, type) - # where type may be float(), int(string), etc. Without context they are - # virtually indistinguishable from int(x) casts. Likewise, gMock's - # MockCallback takes a template parameter of the form return_type(arg_type), - # which looks much like the cast we're trying to detect. - # BEGIN android-added - # The C++ 2011 std::function class template exhibits a similar issue. - # END android-added - if (match.group(1) is None and # If new operator, then this isn't a cast - not (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or - # BEGIN android-changed - # Match(r'^\s*MockCallback<.*>', line))): - Match(r'^\s*MockCallback<.*>', line) or - Match(r'^\s*std::function<.*>', line))): - # END android-changed - # Try a bit harder to catch gmock lines: the only place where - # something looks like an old-style cast is where we declare the - # return type of the mocked method, and the only time when we - # are missing context is if MOCK_METHOD was split across - # multiple lines (for example http://go/hrfhr ), so we only need - # to check the previous line for MOCK_METHOD. - if (linenum == 0 or - not Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(\S+,\s*$', - clean_lines.elided[linenum - 1])): - error(filename, linenum, 'readability/casting', 4, - 'Using deprecated casting style. ' - 'Use static_cast<%s>(...) instead' % - match.group(2)) - - CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], - 'static_cast', - r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) - - # This doesn't catch all cases. Consider (const char * const)"hello". - # - # (char *) "foo" should always be a const_cast (reinterpret_cast won't - # compile). - if CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], - 'const_cast', r'\((char\s?\*+\s?)\)\s*"', error): - pass - else: - # Check pointer casts for other than string constants - CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], - 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error) - - # In addition, we look for people taking the address of a cast. This - # is dangerous -- casts can assign to temporaries, so the pointer doesn't - # point where you think. - if Search( - r'(&\([^)]+\)[\w(])|(&(static|dynamic|reinterpret)_cast\b)', line): - error(filename, linenum, 'runtime/casting', 4, - ('Are you taking an address of a cast? ' - 'This is dangerous: could be a temp var. ' - 'Take the address before doing the cast, rather than after')) - - # Check for people declaring static/global STL strings at the top level. - # This is dangerous because the C++ language does not guarantee that - # globals with constructors are initialized before the first access. - match = Match( - r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)', - line) - # Make sure it's not a function. - # Function template specialization looks like: "string foo<Type>(...". - # Class template definitions look like: "string Foo<Type>::Method(...". - if match and not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', - match.group(3)): - error(filename, linenum, 'runtime/string', 4, - 'For a static/global string constant, use a C style string instead: ' - '"%schar %s[]".' % - (match.group(1), match.group(2))) - - # Check that we're not using RTTI outside of testing code. - if Search(r'\bdynamic_cast<', line) and not _IsTestFilename(filename): - error(filename, linenum, 'runtime/rtti', 5, - 'Do not use dynamic_cast<>. If you need to cast within a class ' - "hierarchy, use static_cast<> to upcast. Google doesn't support " - 'RTTI.') - - if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): - error(filename, linenum, 'runtime/init', 4, - 'You seem to be initializing a member variable with itself.') - - if file_extension == 'h': - # TODO(unknown): check that 1-arg constructors are explicit. - # How to tell it's a constructor? - # (handled in CheckForNonStandardConstructs for now) - # TODO(unknown): check that classes have DISALLOW_EVIL_CONSTRUCTORS - # (level 1 error) - pass - - # Check if people are using the verboten C basic types. The only exception - # we regularly allow is "unsigned short port" for port. - if Search(r'\bshort port\b', line): - if not Search(r'\bunsigned short port\b', line): - error(filename, linenum, 'runtime/int', 4, - 'Use "unsigned short" for ports, not "short"') - else: - match = Search(r'\b(short|long(?! +double)|long long)\b', line) - if match: - error(filename, linenum, 'runtime/int', 4, - 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) - - # When snprintf is used, the second argument shouldn't be a literal. - match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) - if match and match.group(2) != '0': - # If 2nd arg is zero, snprintf is used to calculate size. - error(filename, linenum, 'runtime/printf', 3, - 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' - 'to snprintf.' % (match.group(1), match.group(2))) - - # Check if some verboten C functions are being used. - if Search(r'\bsprintf\b', line): - error(filename, linenum, 'runtime/printf', 5, - 'Never use sprintf. Use snprintf instead.') - match = Search(r'\b(strcpy|strcat)\b', line) - if match: - error(filename, linenum, 'runtime/printf', 4, - 'Almost always, snprintf is better than %s' % match.group(1)) - - if Search(r'\bsscanf\b', line): - error(filename, linenum, 'runtime/printf', 1, - 'sscanf can be ok, but is slow and can overflow buffers.') - - # Check if some verboten operator overloading is going on - # TODO(unknown): catch out-of-line unary operator&: - # class X {}; - # int operator&(const X& x) { return 42; } // unary operator& - # The trick is it's hard to tell apart from binary operator&: - # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& - if Search(r'\boperator\s*&\s*\(\s*\)', line): - error(filename, linenum, 'runtime/operator', 4, - 'Unary operator& is dangerous. Do not use it.') - - # Check for suspicious usage of "if" like - # } if (a == b) { - if Search(r'\}\s*if\s*\(', line): - error(filename, linenum, 'readability/braces', 4, - 'Did you mean "else if"? If not, start a new line for "if".') - - # Check for potential format string bugs like printf(foo). - # We constrain the pattern not to pick things like DocidForPrintf(foo). - # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) - # TODO(sugawarayu): Catch the following case. Need to change the calling - # convention of the whole function to process multiple line to handle it. - # printf( - # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); - printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') - if printf_args: - match = Match(r'([\w.\->()]+)$', printf_args) - if match and match.group(1) != '__VA_ARGS__': - function_name = re.search(r'\b((?:string)?printf)\s*\(', - line, re.I).group(1) - error(filename, linenum, 'runtime/printf', 4, - 'Potential format string bug. Do %s("%%s", %s) instead.' - % (function_name, match.group(1))) - - # Check for potential memset bugs like memset(buf, sizeof(buf), 0). - match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) - if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): - error(filename, linenum, 'runtime/memset', 4, - 'Did you mean "memset(%s, 0, %s)"?' - % (match.group(1), match.group(2))) - - if Search(r'\busing namespace\b', line): - error(filename, linenum, 'build/namespaces', 5, - 'Do not use namespace using-directives. ' - 'Use using-declarations instead.') - - # Detect variable-length arrays. - match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) - if (match and match.group(2) != 'return' and match.group(2) != 'delete' and - match.group(3).find(']') == -1): - # Split the size using space and arithmetic operators as delimiters. - # If any of the resulting tokens are not compile time constants then - # report the error. - tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) - is_const = True - skip_next = False - for tok in tokens: - if skip_next: - skip_next = False - continue - - if Search(r'sizeof\(.+\)', tok): continue - if Search(r'arraysize\(\w+\)', tok): continue - - tok = tok.lstrip('(') - tok = tok.rstrip(')') - if not tok: continue - if Match(r'\d+', tok): continue - if Match(r'0[xX][0-9a-fA-F]+', tok): continue - if Match(r'k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue - # A catch all for tricky sizeof cases, including 'sizeof expression', - # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' - # requires skipping the next token because we split on ' ' and '*'. - if tok.startswith('sizeof'): - skip_next = True - continue - is_const = False - break - if not is_const: - error(filename, linenum, 'runtime/arrays', 1, - 'Do not use variable-length arrays. Use an appropriately named ' - "('k' followed by CamelCase) compile-time constant for the size.") - - # If DISALLOW_EVIL_CONSTRUCTORS, DISALLOW_COPY_AND_ASSIGN, or - # DISALLOW_IMPLICIT_CONSTRUCTORS is present, then it should be the last thing - # in the class declaration. - match = Match( - (r'\s*' - r'(DISALLOW_(EVIL_CONSTRUCTORS|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS))' - r'\(.*\);$'), - line) - if match and linenum + 1 < clean_lines.NumLines(): - next_line = clean_lines.elided[linenum + 1] - # We allow some, but not all, declarations of variables to be present - # in the statement that defines the class. The [\w\*,\s]* fragment of - # the regular expression below allows users to declare instances of - # the class or pointers to instances, but not less common types such - # as function pointers or arrays. It's a tradeoff between allowing - # reasonable code and avoiding trying to parse more C++ using regexps. - if not Search(r'^\s*}[\w\*,\s]*;', next_line): - error(filename, linenum, 'readability/constructors', 3, - match.group(1) + ' should be the last thing in the class') - - # Check for use of unnamed namespaces in header files. Registration - # macros are typically OK, so we allow use of "namespace {" on lines - # that end with backslashes. - if (file_extension == 'h' - and Search(r'\bnamespace\s*{', line) - and line[-1] != '\\'): - error(filename, linenum, 'build/namespaces', 4, - 'Do not use unnamed namespaces in header files. See ' - 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' - ' for more information.') - - -def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern, - error): - """Checks for a C-style cast by looking for the pattern. - - This also handles sizeof(type) warnings, due to similarity of content. - - Args: - filename: The name of the current file. - linenum: The number of the line to check. - line: The line of code to check. - raw_line: The raw line of code to check, with comments. - cast_type: The string for the C++ cast to recommend. This is either - reinterpret_cast, static_cast, or const_cast, depending. - pattern: The regular expression used to find C-style casts. - error: The function to call with any errors found. - - Returns: - True if an error was emitted. - False otherwise. - """ - match = Search(pattern, line) - if not match: - return False - - # e.g., sizeof(int) - sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1]) - if sizeof_match: - error(filename, linenum, 'runtime/sizeof', 1, - 'Using sizeof(type). Use sizeof(varname) instead if possible') - return True - - # operator++(int) and operator--(int) - if (line[0:match.start(1) - 1].endswith(' operator++') or - line[0:match.start(1) - 1].endswith(' operator--')): - return False - - remainder = line[match.end(0):] - - # The close paren is for function pointers as arguments to a function. - # eg, void foo(void (*bar)(int)); - # The semicolon check is a more basic function check; also possibly a - # function pointer typedef. - # eg, void foo(int); or void foo(int) const; - # The equals check is for function pointer assignment. - # eg, void *(*foo)(int) = ... - # The > is for MockCallback<...> ... - # - # Right now, this will only catch cases where there's a single argument, and - # it's unnamed. It should probably be expanded to check for multiple - # arguments with some unnamed. - function_match = Match(r'\s*(\)|=|(const)?\s*(;|\{|throw\(\)|>))', remainder) - if function_match: - if (not function_match.group(3) or - function_match.group(3) == ';' or - ('MockCallback<' not in raw_line and - '/*' not in raw_line)): - error(filename, linenum, 'readability/function', 3, - 'All parameters should be named in a function') - return True - - # At this point, all that should be left is actual casts. - error(filename, linenum, 'readability/casting', 4, - 'Using C-style cast. Use %s<%s>(...) instead' % - (cast_type, match.group(1))) - - return True - - -_HEADERS_CONTAINING_TEMPLATES = ( - ('<deque>', ('deque',)), - ('<functional>', ('unary_function', 'binary_function', - 'plus', 'minus', 'multiplies', 'divides', 'modulus', - 'negate', - 'equal_to', 'not_equal_to', 'greater', 'less', - 'greater_equal', 'less_equal', - 'logical_and', 'logical_or', 'logical_not', - 'unary_negate', 'not1', 'binary_negate', 'not2', - 'bind1st', 'bind2nd', - 'pointer_to_unary_function', - 'pointer_to_binary_function', - 'ptr_fun', - 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', - 'mem_fun_ref_t', - 'const_mem_fun_t', 'const_mem_fun1_t', - 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', - 'mem_fun_ref', - )), - ('<limits>', ('numeric_limits',)), - ('<list>', ('list',)), - ('<map>', ('map', 'multimap',)), - ('<memory>', ('allocator',)), - ('<queue>', ('queue', 'priority_queue',)), - ('<set>', ('set', 'multiset',)), - ('<stack>', ('stack',)), - ('<string>', ('char_traits', 'basic_string',)), - ('<utility>', ('pair',)), - ('<vector>', ('vector',)), - - # gcc extensions. - # Note: std::hash is their hash, ::hash is our hash - ('<hash_map>', ('hash_map', 'hash_multimap',)), - ('<hash_set>', ('hash_set', 'hash_multiset',)), - ('<slist>', ('slist',)), - ) - -_RE_PATTERN_STRING = re.compile(r'\bstring\b') - -_re_pattern_algorithm_header = [] -for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap', - 'transform'): - # Match max<type>(..., ...), max(..., ...), but not foo->max, foo.max or - # type::max(). - _re_pattern_algorithm_header.append( - (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), - _template, - '<algorithm>')) - -_re_pattern_templates = [] -for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: - for _template in _templates: - _re_pattern_templates.append( - (re.compile(r'(\<|\b)' + _template + r'\s*\<'), - _template + '<>', - _header)) - - -def FilesBelongToSameModule(filename_cc, filename_h): - """Check if these two filenames belong to the same module. - - The concept of a 'module' here is a as follows: - foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the - same 'module' if they are in the same directory. - some/path/public/xyzzy and some/path/internal/xyzzy are also considered - to belong to the same module here. - - If the filename_cc contains a longer path than the filename_h, for example, - '/absolute/path/to/base/sysinfo.cc', and this file would include - 'base/sysinfo.h', this function also produces the prefix needed to open the - header. This is used by the caller of this function to more robustly open the - header file. We don't have access to the real include paths in this context, - so we need this guesswork here. - - Known bugs: tools/base/bar.cc and base/bar.h belong to the same module - according to this implementation. Because of this, this function gives - some false positives. This should be sufficiently rare in practice. - - Args: - filename_cc: is the path for the .cc file - filename_h: is the path for the header path - - Returns: - Tuple with a bool and a string: - bool: True if filename_cc and filename_h belong to the same module. - string: the additional prefix needed to open the header file. - """ - - if not filename_cc.endswith('.cc'): - return (False, '') - filename_cc = filename_cc[:-len('.cc')] - if filename_cc.endswith('_unittest'): - filename_cc = filename_cc[:-len('_unittest')] - elif filename_cc.endswith('_test'): - filename_cc = filename_cc[:-len('_test')] - filename_cc = filename_cc.replace('/public/', '/') - filename_cc = filename_cc.replace('/internal/', '/') - - if not filename_h.endswith('.h'): - return (False, '') - filename_h = filename_h[:-len('.h')] - if filename_h.endswith('-inl'): - filename_h = filename_h[:-len('-inl')] - filename_h = filename_h.replace('/public/', '/') - filename_h = filename_h.replace('/internal/', '/') - - files_belong_to_same_module = filename_cc.endswith(filename_h) - common_path = '' - if files_belong_to_same_module: - common_path = filename_cc[:-len(filename_h)] - return files_belong_to_same_module, common_path - - -def UpdateIncludeState(filename, include_state, io=codecs): - """Fill up the include_state with new includes found from the file. - - Args: - filename: the name of the header to read. - include_state: an _IncludeState instance in which the headers are inserted. - io: The io factory to use to read the file. Provided for testability. - - Returns: - True if a header was succesfully added. False otherwise. - """ - headerfile = None - try: - headerfile = io.open(filename, 'r', 'utf8', 'replace') - except IOError: - return False - linenum = 0 - for line in headerfile: - linenum += 1 - clean_line = CleanseComments(line) - match = _RE_PATTERN_INCLUDE.search(clean_line) - if match: - include = match.group(2) - # The value formatting is cute, but not really used right now. - # What matters here is that the key is in include_state. - include_state.setdefault(include, '%s:%d' % (filename, linenum)) - return True - - -def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, - io=codecs): - """Reports for missing stl includes. - - This function will output warnings to make sure you are including the headers - necessary for the stl containers and functions that you use. We only give one - reason to include a header. For example, if you use both equal_to<> and - less<> in a .h file, only one (the latter in the file) of these will be - reported as a reason to include the <functional>. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - include_state: An _IncludeState instance. - error: The function to call with any errors found. - io: The IO factory to use to read the header file. Provided for unittest - injection. - """ - required = {} # A map of header name to linenumber and the template entity. - # Example of required: { '<functional>': (1219, 'less<>') } - - for linenum in xrange(clean_lines.NumLines()): - line = clean_lines.elided[linenum] - if not line or line[0] == '#': - continue - - # String is special -- it is a non-templatized type in STL. - matched = _RE_PATTERN_STRING.search(line) - if matched: - # Don't warn about strings in non-STL namespaces: - # (We check only the first match per line; good enough.) - prefix = line[:matched.start()] - if prefix.endswith('std::') or not prefix.endswith('::'): - required['<string>'] = (linenum, 'string') - - for pattern, template, header in _re_pattern_algorithm_header: - if pattern.search(line): - required[header] = (linenum, template) - - # The following function is just a speed up, no semantics are changed. - if not '<' in line: # Reduces the cpu time usage by skipping lines. - continue - - for pattern, template, header in _re_pattern_templates: - if pattern.search(line): - required[header] = (linenum, template) - - # The policy is that if you #include something in foo.h you don't need to - # include it again in foo.cc. Here, we will look at possible includes. - # Let's copy the include_state so it is only messed up within this function. - include_state = include_state.copy() - - # Did we find the header for this file (if any) and succesfully load it? - header_found = False - - # Use the absolute path so that matching works properly. - abs_filename = FileInfo(filename).FullName() - - # For Emacs's flymake. - # If cpplint is invoked from Emacs's flymake, a temporary file is generated - # by flymake and that file name might end with '_flymake.cc'. In that case, - # restore original file name here so that the corresponding header file can be - # found. - # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' - # instead of 'foo_flymake.h' - abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) - - # include_state is modified during iteration, so we iterate over a copy of - # the keys. - header_keys = include_state.keys() - for header in header_keys: - (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) - fullpath = common_path + header - if same_module and UpdateIncludeState(fullpath, include_state, io): - header_found = True - - # If we can't find the header file for a .cc, assume it's because we don't - # know where to look. In that case we'll give up as we're not sure they - # didn't include it in the .h file. - # TODO(unknown): Do a better job of finding .h files so we are confident that - # not having the .h file means there isn't one. - if filename.endswith('.cc') and not header_found: - return - - # All the lines have been processed, report the errors found. - for required_header_unstripped in required: - template = required[required_header_unstripped][1] - if required_header_unstripped.strip('<>"') not in include_state: - error(filename, required[required_header_unstripped][0], - 'build/include_what_you_use', 4, - 'Add #include ' + required_header_unstripped + ' for ' + template) - - -_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') - - -def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): - """Check that make_pair's template arguments are deduced. - - G++ 4.6 in C++0x mode fails badly if make_pair's template arguments are - specified explicitly, and such use isn't intended in any case. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - raw = clean_lines.raw_lines - line = raw[linenum] - match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) - if match: - error(filename, linenum, 'build/explicit_make_pair', - 4, # 4 = high confidence - 'For C++11-compatibility, omit template arguments from make_pair' - ' OR use pair directly OR if appropriate, construct a pair directly') - - -def ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, nesting_state, error, - extra_check_functions=[]): - """Processes a single line in the file. - - Args: - filename: Filename of the file that is being processed. - file_extension: The extension (dot not included) of the file. - clean_lines: An array of strings, each representing a line of the file, - with comments stripped. - line: Number of line being processed. - include_state: An _IncludeState instance in which the headers are inserted. - function_state: A _FunctionState instance which counts function lines, etc. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - raw_lines = clean_lines.raw_lines - ParseNolintSuppressions(filename, raw_lines[line], line, error) - nesting_state.Update(filename, clean_lines, line, error) - if nesting_state.stack and nesting_state.stack[-1].inline_asm != _NO_ASM: - return - CheckForFunctionLengths(filename, clean_lines, line, function_state, error) - CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) - CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) - CheckLanguage(filename, clean_lines, line, file_extension, include_state, - error) - CheckForNonStandardConstructs(filename, clean_lines, line, - nesting_state, error) - CheckPosixThreading(filename, clean_lines, line, error) - CheckInvalidIncrement(filename, clean_lines, line, error) - CheckMakePairUsesDeduction(filename, clean_lines, line, error) - for check_fn in extra_check_functions: - check_fn(filename, clean_lines, line, error) - -def ProcessFileData(filename, file_extension, lines, error, - extra_check_functions=[]): - """Performs lint checks and reports any errors to the given error function. - - Args: - filename: Filename of the file that is being processed. - file_extension: The extension (dot not included) of the file. - lines: An array of strings, each representing a line of the file, with the - last element being empty if the file is terminated with a newline. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - lines = (['// marker so line numbers and indices both start at 1'] + lines + - ['// marker so line numbers end in a known way']) - - include_state = _IncludeState() - function_state = _FunctionState() - nesting_state = _NestingState() - - ResetNolintSuppressions() - - CheckForCopyright(filename, lines, error) - - if file_extension == 'h': - CheckForHeaderGuard(filename, lines, error) - - RemoveMultiLineComments(filename, lines, error) - clean_lines = CleansedLines(lines) - for line in xrange(clean_lines.NumLines()): - ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, nesting_state, error, - extra_check_functions) - nesting_state.CheckClassFinished(filename, error) - - CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) - - # We check here rather than inside ProcessLine so that we see raw - # lines rather than "cleaned" lines. - CheckForUnicodeReplacementCharacters(filename, lines, error) - - CheckForNewlineAtEOF(filename, lines, error) - -def ProcessFile(filename, vlevel, extra_check_functions=[]): - """Does google-lint on a single file. - - Args: - filename: The name of the file to parse. - - vlevel: The level of errors to report. Every error of confidence - >= verbose_level will be reported. 0 is a good default. - - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - - _SetVerboseLevel(vlevel) -# BEGIN android-added - old_errors = _cpplint_state.error_count -# END android-added - - try: - # Support the UNIX convention of using "-" for stdin. Note that - # we are not opening the file with universal newline support - # (which codecs doesn't support anyway), so the resulting lines do - # contain trailing '\r' characters if we are reading a file that - # has CRLF endings. - # If after the split a trailing '\r' is present, it is removed - # below. If it is not expected to be present (i.e. os.linesep != - # '\r\n' as in Windows), a warning is issued below if this file - # is processed. - - if filename == '-': - lines = codecs.StreamReaderWriter(sys.stdin, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace').read().split('\n') - else: - lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') - - carriage_return_found = False - # Remove trailing '\r'. - for linenum in range(len(lines)): - if lines[linenum].endswith('\r'): - lines[linenum] = lines[linenum].rstrip('\r') - carriage_return_found = True - - except IOError: - sys.stderr.write( - "Skipping input '%s': Can't open for reading\n" % filename) - return - - # Note, if no dot is found, this will give the entire filename as the ext. - file_extension = filename[filename.rfind('.') + 1:] - - # When reading from stdin, the extension is unknown, so no cpplint tests - # should rely on the extension. - if (filename != '-' and file_extension != 'cc' and file_extension != 'h' - and file_extension != 'cpp'): - sys.stderr.write('Ignoring %s; not a .cc or .h file\n' % filename) - else: - ProcessFileData(filename, file_extension, lines, Error, - extra_check_functions) - if carriage_return_found and os.linesep != '\r\n': - # Use 0 for linenum since outputting only one error for potentially - # several lines. - Error(filename, 0, 'whitespace/newline', 1, - 'One or more unexpected \\r (^M) found;' - 'better to use only a \\n') - -# BEGIN android-changed - # sys.stderr.write('Done processing %s\n' % filename) - if not _cpplint_state.quiet or old_errors != _cpplint_state.error_count: - sys.stderr.write('Done processing %s\n' % filename) -# END android-changed - -def PrintUsage(message): - """Prints a brief usage string and exits, optionally with an error message. - - Args: - message: The optional error message. - """ - sys.stderr.write(_USAGE) - if message: - sys.exit('\nFATAL ERROR: ' + message) - else: - sys.exit(1) - - -def PrintCategories(): - """Prints a list of all the error-categories used by error messages. - - These are the categories used to filter messages via --filter. - """ - sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) - sys.exit(0) - - -def ParseArguments(args): - """Parses the command line arguments. - - This may set the output format and verbosity level as side-effects. - - Args: - args: The command line arguments: - - Returns: - The list of filenames to lint. - """ - try: - (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', - 'stdout', # TODO(enh): added --stdout - # BEGIN android-added - 'quiet', - # END android-added - 'counting=', - 'filter=', - 'root=']) - except getopt.GetoptError: - PrintUsage('Invalid arguments.') - - verbosity = _VerboseLevel() - output_format = _OutputFormat() - output_stream = sys.stderr # TODO(enh): added --stdout - filters = '' - # BEGIN android-added - quiet = _Quiet() - # END android-added - counting_style = '' - - for (opt, val) in opts: - if opt == '--help': - PrintUsage(None) - elif opt == '--stdout': # TODO(enh): added --stdout - output_stream = sys.stdout # TODO(enh): added --stdout - # BEGIN android-added - elif opt == '--quiet': - quiet = True - # END android-added - elif opt == '--output': - if not val in ('emacs', 'vs7', 'eclipse'): - PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') - output_format = val - elif opt == '--verbose': - verbosity = int(val) - elif opt == '--filter': - filters = val - if not filters: - PrintCategories() - elif opt == '--counting': - if val not in ('total', 'toplevel', 'detailed'): - PrintUsage('Valid counting options are total, toplevel, and detailed') - counting_style = val - elif opt == '--root': - global _root - _root = val - - if not filenames: - PrintUsage('No files were specified.') - - _SetOutputFormat(output_format) - _SetVerboseLevel(verbosity) - _SetFilters(filters) - _SetCountingStyle(counting_style) - # BEGIN android-added - _SetQuiet(quiet) - # END android-added - sys.stderr = output_stream # TODO(enh): added --stdout - - return filenames - - -def main(): - filenames = ParseArguments(sys.argv[1:]) - - # Change stderr to write with replacement characters so we don't die - # if we try to print something containing non-ASCII characters. - sys.stderr = codecs.StreamReaderWriter(sys.stderr, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace') - - _cpplint_state.ResetErrorCounts() - for filename in filenames: - ProcessFile(filename, _cpplint_state.verbose_level) - # BEGIN android-changed - # _cpplint_state.PrintErrorCounts() - if not _cpplint_state.quiet or _cpplint_state.error_count > 0: - _cpplint_state.PrintErrorCounts() - # END android-changed - - sys.exit(_cpplint_state.error_count > 0) - - -if __name__ == '__main__': - main() diff --git a/tools/cpplint_presubmit.py b/tools/cpplint_presubmit.py deleted file mode 100755 index b42a6913dc..0000000000 --- a/tools/cpplint_presubmit.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/python3 -# -# Copyright 2017, The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# TODO We should unify this with build/Android.cpplint.mk. - -import os -import pathlib -import subprocess -import sys - -IGNORED_FILES = {"runtime/elf.h", "openjdkjvmti/include/jvmti.h"} - -INTERESTING_SUFFIXES = {".h", ".cc"} - -CPPLINT_FLAGS = [ - '--filter=-whitespace/line_length,-build/include,-readability/function,-readability/streams,-readability/todo,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn,-runtime/printf', - '--quiet', -] - -def is_interesting(f): - """ - Returns true if this is a file we want to run through cpplint before uploading. False otherwise. - """ - path = pathlib.Path(f) - return f not in IGNORED_FILES and path.suffix in INTERESTING_SUFFIXES and path.exists() - -def get_changed_files(commit): - """ - Gets the files changed in the given commit. - """ - return subprocess.check_output( - ["git", 'diff-tree', '--no-commit-id', '--name-only', '-r', commit], - stderr=subprocess.STDOUT, - universal_newlines=True).split() - -def run_cpplint(files): - """ - Runs cpplint on the given files. - """ - if len(files) == 0: - return - sys.exit(subprocess.call(['tools/cpplint.py'] + CPPLINT_FLAGS + files)) - -def main(): - if 'PREUPLOAD_COMMIT' in os.environ: - commit = os.environ['PREUPLOAD_COMMIT'] - else: - print("WARNING: Not running as a pre-upload hook. Assuming commit to check = 'HEAD'") - commit = "HEAD" - files_to_check = [f for f in get_changed_files(commit) if is_interesting(f)] - run_cpplint(files_to_check) - -if __name__ == '__main__': - main() diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt index d27b8fc06c..0029b0a01a 100644 --- a/tools/libcore_gcstress_debug_failures.txt +++ b/tools/libcore_gcstress_debug_failures.txt @@ -9,9 +9,9 @@ result: EXEC_FAILED, modes: [device], names: ["jsr166.CompletableFutureTest#testCompleteOnTimeout_completed", - "libcore.icu.TransliteratorTest#testAll", - "libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045", - "libcore.icu.RelativeDateTimeFormatterTest#test_bug25883157", + "libcore.libcore.icu.TransliteratorTest#testAll", + "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045", + "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_bug25883157", "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndTimeout", "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndNoTimeout", "libcore.java.util.TimeZoneTest#testSetDefaultDeadlock", diff --git a/tools/libjdwp_art_failures.txt b/tools/libjdwp_art_failures.txt index 354bee81af..fd711bbd8b 100644 --- a/tools/libjdwp_art_failures.txt +++ b/tools/libjdwp_art_failures.txt @@ -97,5 +97,11 @@ result: EXEC_FAILED, bug: 69121056, name: "org.apache.harmony.jpda.tests.jdwp.ObjectReference.IsCollectedTest#testIsCollected001" +}, +{ + description: "Test for ddms extensions that are not implemented for prebuilt-libjdwp", + result: EXEC_FAILED, + bug: 69169846, + name: "org.apache.harmony.jpda.tests.jdwp.DDM.DDMTest#testChunk001" } ] diff --git a/tools/libjdwp_oj_art_failures.txt b/tools/libjdwp_oj_art_failures.txt index 787c4d28fb..3d06bcf100 100644 --- a/tools/libjdwp_oj_art_failures.txt +++ b/tools/libjdwp_oj_art_failures.txt @@ -67,5 +67,11 @@ result: EXEC_FAILED, bug: 69121056, name: "org.apache.harmony.jpda.tests.jdwp.ObjectReference.IsCollectedTest#testIsCollected001" +}, +{ + description: "Test for ddms extensions that are not yet implemented", + result: EXEC_FAILED, + bug: 69169846, + name: "org.apache.harmony.jpda.tests.jdwp.DDM.DDMTest#testChunk001" } ] diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index f5fbcd8def..6a846aee17 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -96,6 +96,14 @@ while true; do # We don't care about jit with the RI use_jit=false shift + elif [[ $1 == --test-timeout-ms ]]; then + # Remove the --test-timeout-ms from the arguments. + args=${args/$1} + shift + jdwp_test_timeout=$1 + # Remove the argument + args=${args/$1} + shift elif [[ $1 == --agent-wrapper ]]; then # Remove the --agent-wrapper from the arguments. args=${args/$1} diff --git a/tools/run-libjdwp-tests.sh b/tools/run-libjdwp-tests.sh index 964bb386ef..47e7c4595d 100755 --- a/tools/run-libjdwp-tests.sh +++ b/tools/run-libjdwp-tests.sh @@ -29,11 +29,16 @@ debug="no" has_variant="no" has_mode="no" mode="target" +has_timeout="no" while true; do if [[ $1 == "--debug" ]]; then debug="yes" shift + elif [[ $1 == --test-timeout-ms ]]; then + has_timeout="yes" + shift + shift elif [[ "$1" == "--mode=jvm" ]]; then has_mode="yes" mode="ri" @@ -60,6 +65,12 @@ if [[ "$has_variant" = "no" ]]; then args+=(--variant=X32) fi +if [[ "$has_timeout" = "no" ]]; then + # Double the timeout to 20 seconds + args+=(--test-timeout-ms) + args+=(20000) +fi + # We don't use full paths since it is difficult to determine them for device # tests and not needed due to resolution rules of dlopen. if [[ "$debug" = "yes" ]]; then diff --git a/tools/titrace/titrace.cc b/tools/titrace/titrace.cc index e8122807c1..981ad56e86 100644 --- a/tools/titrace/titrace.cc +++ b/tools/titrace/titrace.cc @@ -21,7 +21,7 @@ #include <jvmti.h> #include <map> #include <memory> -#include <mutex> // NOLINT [build/c++11] [5] +#include <mutex> // We could probably return a JNI_ERR here but lets crash instead if something fails. #define CHECK_JVMTI_ERROR(jvmti, errnum) \ @@ -195,8 +195,8 @@ struct TraceStatistics { std::unique_ptr<InstructionDecoder> instruction_decoder_; - std::atomic<size_t> single_step_counter_{0u}; // NOLINT [readability/braces] [4] [whitespace/braces] [5] - std::atomic<size_t> instruction_counter_[256]{}; // NOLINT [whitespace/braces] [5] + std::atomic<size_t> single_step_counter_{0u}; + std::atomic<size_t> instruction_counter_[256]{}; // Cache the bytecode to avoid calling into JVMTI repeatedly. // TODO: invalidate if the bytecode was updated? @@ -256,7 +256,7 @@ JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, TraceStatistics::Initialize(jvmti); } - jvmtiError error{}; // NOLINT [readability/braces] [4] [whitespace/braces] [5] + jvmtiError error{}; // Set capabilities. { diff --git a/tools/wrapagentproperties/wrapagentproperties.cc b/tools/wrapagentproperties/wrapagentproperties.cc index 9eaffbb240..8b4b062cf5 100644 --- a/tools/wrapagentproperties/wrapagentproperties.cc +++ b/tools/wrapagentproperties/wrapagentproperties.cc @@ -24,7 +24,7 @@ #include <unordered_map> #include <unordered_set> #include <memory> -#include <mutex> // NOLINT [build/c++11] [5] +#include <mutex> #include <sstream> #include <string> #include <vector> |