diff options
54 files changed, 2687 insertions, 523 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index a8f6a24908..7b42db8a7f 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -759,33 +759,214 @@ void HGraphBuilder::BuildReturn(const Instruction& instruction, Primitive::Type current_block_ = nullptr; } -void HGraphBuilder::PotentiallySimplifyFakeString(uint16_t original_dex_register, - uint32_t dex_pc, - HInvoke* actual_string) { - if (!graph_->IsDebuggable()) { - // Notify that we cannot compile with baseline. The dex registers aliasing - // with `original_dex_register` will be handled when we optimize - // (see HInstructionSimplifer::VisitFakeString). - can_use_baseline_for_string_init_ = false; - return; +static InvokeType GetInvokeTypeFromOpCode(Instruction::Code opcode) { + switch (opcode) { + case Instruction::INVOKE_STATIC: + case Instruction::INVOKE_STATIC_RANGE: + return kStatic; + case Instruction::INVOKE_DIRECT: + case Instruction::INVOKE_DIRECT_RANGE: + return kDirect; + case Instruction::INVOKE_VIRTUAL: + case Instruction::INVOKE_VIRTUAL_QUICK: + case Instruction::INVOKE_VIRTUAL_RANGE: + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: + return kVirtual; + case Instruction::INVOKE_INTERFACE: + case Instruction::INVOKE_INTERFACE_RANGE: + return kInterface; + case Instruction::INVOKE_SUPER_RANGE: + case Instruction::INVOKE_SUPER: + return kSuper; + default: + LOG(FATAL) << "Unexpected invoke opcode: " << opcode; + UNREACHABLE(); } - const VerifiedMethod* verified_method = - compiler_driver_->GetVerifiedMethod(dex_file_, dex_compilation_unit_->GetDexMethodIndex()); - if (verified_method != nullptr) { - UpdateLocal(original_dex_register, actual_string); - const SafeMap<uint32_t, std::set<uint32_t>>& string_init_map = - verified_method->GetStringInitPcRegMap(); - auto map_it = string_init_map.find(dex_pc); - if (map_it != string_init_map.end()) { - std::set<uint32_t> reg_set = map_it->second; - for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) { - HInstruction* load_local = LoadLocal(original_dex_register, Primitive::kPrimNot); - UpdateLocal(*set_it, load_local); - } +} + +bool HGraphBuilder::BuildInvoke(const Instruction& instruction, + uint32_t dex_pc, + uint32_t method_idx, + uint32_t number_of_vreg_arguments, + bool is_range, + uint32_t* args, + uint32_t register_index) { + InvokeType original_invoke_type = GetInvokeTypeFromOpCode(instruction.Opcode()); + InvokeType optimized_invoke_type = original_invoke_type; + const char* descriptor = dex_file_->GetMethodShorty(method_idx); + Primitive::Type return_type = Primitive::GetType(descriptor[0]); + + // Remove the return type from the 'proto'. + size_t number_of_arguments = strlen(descriptor) - 1; + if (original_invoke_type != kStatic) { // instance call + // One extra argument for 'this'. + number_of_arguments++; + } + + MethodReference target_method(dex_file_, method_idx); + int32_t table_index; + uintptr_t direct_code; + uintptr_t direct_method; + + if (!compiler_driver_->ComputeInvokeInfo(dex_compilation_unit_, + dex_pc, + true /* update_stats */, + true /* enable_devirtualization */, + &optimized_invoke_type, + &target_method, + &table_index, + &direct_code, + &direct_method)) { + VLOG(compiler) << "Did not compile " + << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_) + << " because a method call could not be resolved"; + MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedMethod); + return false; + } + + DCHECK(optimized_invoke_type != kSuper); + + // Special handling for string init. + int32_t string_init_offset = 0; + bool is_string_init = compiler_driver_->IsStringInit(method_idx, dex_file_, + &string_init_offset); + + // Potential class initialization check, in the case of a static method call. + HClinitCheck* clinit_check = nullptr; + HInvoke* invoke = nullptr; + + if (is_string_init + || optimized_invoke_type == kDirect + || optimized_invoke_type == kStatic) { + // By default, consider that the called method implicitly requires + // an initialization check of its declaring method. + HInvokeStaticOrDirect::ClinitCheckRequirement clinit_check_requirement + = HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit; + if (optimized_invoke_type == kStatic && !is_string_init) { + clinit_check = ProcessClinitCheckForInvoke(dex_pc, method_idx, &clinit_check_requirement); + } + + // Replace calls to String.<init> with StringFactory. + if (is_string_init) { + return_type = Primitive::kPrimNot; + number_of_arguments--; + optimized_invoke_type = kStatic; } + + HInvokeStaticOrDirect::DispatchInfo dispatch_info = ComputeDispatchInfo(is_string_init, + string_init_offset, + target_method, + direct_method, + direct_code); + invoke = new (arena_) HInvokeStaticOrDirect(arena_, + number_of_arguments, + return_type, + dex_pc, + method_idx, + target_method, + dispatch_info, + original_invoke_type, + optimized_invoke_type, + clinit_check_requirement); + } else if (optimized_invoke_type == kVirtual) { + invoke = new (arena_) HInvokeVirtual(arena_, + number_of_arguments, + return_type, + dex_pc, + method_idx, + table_index); } else { - can_use_baseline_for_string_init_ = false; + DCHECK_EQ(optimized_invoke_type, kInterface); + invoke = new (arena_) HInvokeInterface(arena_, + number_of_arguments, + return_type, + dex_pc, + method_idx, + table_index); + } + + return SetupArgumentsAndAddInvoke(invoke, + number_of_vreg_arguments, + args, + register_index, + is_range, + descriptor, + clinit_check); +} + +HClinitCheck* HGraphBuilder::ProcessClinitCheckForInvoke( + uint32_t dex_pc, + uint32_t method_idx, + HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<4> hs(soa.Self()); + Handle<mirror::DexCache> dex_cache(hs.NewHandle( + dex_compilation_unit_->GetClassLinker()->FindDexCache( + *dex_compilation_unit_->GetDexFile()))); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader()))); + ArtMethod* resolved_method = compiler_driver_->ResolveMethod( + soa, dex_cache, class_loader, dex_compilation_unit_, method_idx, InvokeType::kStatic); + + DCHECK(resolved_method != nullptr); + + const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); + Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle( + outer_compilation_unit_->GetClassLinker()->FindDexCache(outer_dex_file))); + Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); + + // The index at which the method's class is stored in the DexCache's type array. + uint32_t storage_index = DexFile::kDexNoIndex; + bool is_outer_class = (resolved_method->GetDeclaringClass() == outer_class.Get()); + if (is_outer_class) { + storage_index = outer_class->GetDexTypeIndex(); + } else if (outer_dex_cache.Get() == dex_cache.Get()) { + // Get `storage_index` from IsClassOfStaticMethodAvailableToReferrer. + compiler_driver_->IsClassOfStaticMethodAvailableToReferrer(outer_dex_cache.Get(), + GetCompilingClass(), + resolved_method, + method_idx, + &storage_index); + } + + HClinitCheck* clinit_check = nullptr; + + if (!outer_class->IsInterface() + && outer_class->IsSubClass(resolved_method->GetDeclaringClass())) { + // If the outer class is the declaring class or a subclass + // of the declaring class, no class initialization is needed + // before the static method call. + // Note that in case of inlining, we do not need to add clinit checks + // to calls that satisfy this subclass check with any inlined methods. This + // will be detected by the optimization passes. + *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone; + } else if (storage_index != DexFile::kDexNoIndex) { + // If the method's class type index is available, check + // whether we should add an explicit class initialization + // check for its declaring class before the static method call. + + // TODO: find out why this check is needed. + bool is_in_dex_cache = compiler_driver_->CanAssumeTypeIsPresentInDexCache( + *outer_compilation_unit_->GetDexFile(), storage_index); + bool is_initialized = + resolved_method->GetDeclaringClass()->IsInitialized() && is_in_dex_cache; + + if (is_initialized) { + *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone; + } else { + *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit; + HLoadClass* load_class = new (arena_) HLoadClass( + graph_->GetCurrentMethod(), + storage_index, + *dex_compilation_unit_->GetDexFile(), + is_outer_class, + dex_pc); + current_block_->AddInstruction(load_class); + clinit_check = new (arena_) HClinitCheck(load_class, dex_pc); + current_block_->AddInstruction(clinit_check); + } } + return clinit_check; } HInvokeStaticOrDirect::DispatchInfo HGraphBuilder::ComputeDispatchInfo( @@ -859,210 +1040,40 @@ HInvokeStaticOrDirect::DispatchInfo HGraphBuilder::ComputeDispatchInfo( method_load_kind, code_ptr_location, method_load_data, direct_code_ptr }; } -bool HGraphBuilder::BuildInvoke(const Instruction& instruction, - uint32_t dex_pc, - uint32_t method_idx, - uint32_t number_of_vreg_arguments, - bool is_range, - uint32_t* args, - uint32_t register_index) { - Instruction::Code opcode = instruction.Opcode(); - InvokeType invoke_type; - switch (opcode) { - case Instruction::INVOKE_STATIC: - case Instruction::INVOKE_STATIC_RANGE: - invoke_type = kStatic; - break; - case Instruction::INVOKE_DIRECT: - case Instruction::INVOKE_DIRECT_RANGE: - invoke_type = kDirect; - break; - case Instruction::INVOKE_VIRTUAL: - case Instruction::INVOKE_VIRTUAL_QUICK: - case Instruction::INVOKE_VIRTUAL_RANGE: - case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: - invoke_type = kVirtual; - break; - case Instruction::INVOKE_INTERFACE: - case Instruction::INVOKE_INTERFACE_RANGE: - invoke_type = kInterface; - break; - case Instruction::INVOKE_SUPER_RANGE: - case Instruction::INVOKE_SUPER: - invoke_type = kSuper; - break; - default: - LOG(FATAL) << "Unexpected invoke op: " << opcode; - return false; - } - - const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); - const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(method_id.proto_idx_); - const char* descriptor = dex_file_->StringDataByIdx(proto_id.shorty_idx_); - Primitive::Type return_type = Primitive::GetType(descriptor[0]); - bool is_instance_call = invoke_type != kStatic; - // Remove the return type from the 'proto'. - size_t number_of_arguments = strlen(descriptor) - 1; - if (is_instance_call) { - // One extra argument for 'this'. - ++number_of_arguments; - } +bool HGraphBuilder::SetupArgumentsAndAddInvoke(HInvoke* invoke, + uint32_t number_of_vreg_arguments, + uint32_t* args, + uint32_t register_index, + bool is_range, + const char* descriptor, + HClinitCheck* clinit_check) { + size_t start_index = 0; + size_t argument_index = 0; + uint32_t descriptor_index = 1; // Skip the return type. - MethodReference target_method(dex_file_, method_idx); - uintptr_t direct_code; - uintptr_t direct_method; - int table_index; - InvokeType optimized_invoke_type = invoke_type; + bool is_instance_call = invoke->GetOriginalInvokeType() != InvokeType::kStatic; + bool is_string_init = invoke->IsInvokeStaticOrDirect() + && invoke->AsInvokeStaticOrDirect()->IsStringInit(); - if (!compiler_driver_->ComputeInvokeInfo(dex_compilation_unit_, dex_pc, true, true, - &optimized_invoke_type, &target_method, &table_index, - &direct_code, &direct_method)) { - VLOG(compiler) << "Did not compile " - << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_) - << " because a method call could not be resolved"; - MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedMethod); - return false; - } - DCHECK(optimized_invoke_type != kSuper); - - // By default, consider that the called method implicitly requires - // an initialization check of its declaring method. - HInvokeStaticOrDirect::ClinitCheckRequirement clinit_check_requirement = - HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit; - // Potential class initialization check, in the case of a static method call. - HClinitCheck* clinit_check = nullptr; - // Replace calls to String.<init> with StringFactory. - int32_t string_init_offset = 0; - bool is_string_init = compiler_driver_->IsStringInit(method_idx, dex_file_, &string_init_offset); if (is_string_init) { - return_type = Primitive::kPrimNot; - is_instance_call = false; - number_of_arguments--; - invoke_type = kStatic; - optimized_invoke_type = kStatic; - } - - HInvoke* invoke = nullptr; - - if (optimized_invoke_type == kVirtual) { - invoke = new (arena_) HInvokeVirtual( - arena_, number_of_arguments, return_type, dex_pc, method_idx, table_index); - } else if (optimized_invoke_type == kInterface) { - invoke = new (arena_) HInvokeInterface( - arena_, number_of_arguments, return_type, dex_pc, method_idx, table_index); - } else { - DCHECK(optimized_invoke_type == kDirect || optimized_invoke_type == kStatic); - - if (optimized_invoke_type == kStatic && !is_string_init) { - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<4> hs(soa.Self()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle( - dex_compilation_unit_->GetClassLinker()->FindDexCache( - *dex_compilation_unit_->GetDexFile()))); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle( - soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader()))); - ArtMethod* resolved_method = compiler_driver_->ResolveMethod( - soa, dex_cache, class_loader, dex_compilation_unit_, method_idx, optimized_invoke_type); - - if (resolved_method == nullptr) { - MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedMethod); - return false; - } - - const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); - Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle( - outer_compilation_unit_->GetClassLinker()->FindDexCache(outer_dex_file))); - Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); - - // The index at which the method's class is stored in the DexCache's type array. - uint32_t storage_index = DexFile::kDexNoIndex; - bool is_outer_class = (resolved_method->GetDeclaringClass() == outer_class.Get()); - if (is_outer_class) { - storage_index = outer_class->GetDexTypeIndex(); - } else if (outer_dex_cache.Get() == dex_cache.Get()) { - // Get `storage_index` from IsClassOfStaticMethodAvailableToReferrer. - compiler_driver_->IsClassOfStaticMethodAvailableToReferrer(outer_dex_cache.Get(), - GetCompilingClass(), - resolved_method, - method_idx, - &storage_index); - } - - if (!outer_class->IsInterface() - && outer_class->IsSubClass(resolved_method->GetDeclaringClass())) { - // If the outer class is the declaring class or a subclass - // of the declaring class, no class initialization is needed - // before the static method call. - // Note that in case of inlining, we do not need to add clinit checks - // to calls that satisfy this subclass check with any inlined methods. This - // will be detected by the optimization passes. - clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone; - } else if (storage_index != DexFile::kDexNoIndex) { - // If the method's class type index is available, check - // whether we should add an explicit class initialization - // check for its declaring class before the static method call. - - // TODO: find out why this check is needed. - bool is_in_dex_cache = compiler_driver_->CanAssumeTypeIsPresentInDexCache( - *outer_compilation_unit_->GetDexFile(), storage_index); - bool is_initialized = - resolved_method->GetDeclaringClass()->IsInitialized() && is_in_dex_cache; - - if (is_initialized) { - clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone; - } else { - clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit; - HLoadClass* load_class = new (arena_) HLoadClass( - graph_->GetCurrentMethod(), - storage_index, - *dex_compilation_unit_->GetDexFile(), - is_outer_class, - dex_pc); - current_block_->AddInstruction(load_class); - clinit_check = new (arena_) HClinitCheck(load_class, dex_pc); - current_block_->AddInstruction(clinit_check); - } - } - } - - HInvokeStaticOrDirect::DispatchInfo dispatch_info = ComputeDispatchInfo(is_string_init, - string_init_offset, - target_method, - direct_method, - direct_code); - invoke = new (arena_) HInvokeStaticOrDirect(arena_, - number_of_arguments, - return_type, - dex_pc, - method_idx, - target_method, - dispatch_info, - invoke_type, - optimized_invoke_type, - clinit_check_requirement); - } - - size_t start_index = 0; - Temporaries temps(graph_); - if (is_instance_call) { + start_index = 1; + argument_index = 0; + } else if (is_instance_call) { + Temporaries temps(graph_); HInstruction* arg = LoadLocal(is_range ? register_index : args[0], Primitive::kPrimNot); - HNullCheck* null_check = new (arena_) HNullCheck(arg, dex_pc); + HNullCheck* null_check = new (arena_) HNullCheck(arg, invoke->GetDexPc()); current_block_->AddInstruction(null_check); temps.Add(null_check); invoke->SetArgumentAt(0, null_check); start_index = 1; + argument_index = 1; } - uint32_t descriptor_index = 1; // Skip the return type. - uint32_t argument_index = start_index; - if (is_string_init) { - start_index = 1; - } for (size_t i = start_index; // Make sure we don't go over the expected arguments or over the number of // dex registers given. If the instruction was seen as dead by the verifier, // it hasn't been properly checked. - (i < number_of_vreg_arguments) && (argument_index < number_of_arguments); + (i < number_of_vreg_arguments) && (argument_index < invoke->GetNumberOfArguments()); i++, argument_index++) { Primitive::Type type = Primitive::GetType(descriptor[descriptor_index++]); bool is_wide = (type == Primitive::kPrimLong) || (type == Primitive::kPrimDouble); @@ -1085,7 +1096,7 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, } } - if (argument_index != number_of_arguments) { + if (argument_index != invoke->GetNumberOfArguments()) { VLOG(compiler) << "Did not compile " << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_) << " because of wrong number of arguments in invoke instruction"; @@ -1098,10 +1109,12 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, argument_index++; } - if (clinit_check_requirement == HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit) { + if (clinit_check != nullptr) { // Add the class initialization check as last input of `invoke`. - DCHECK(clinit_check != nullptr); DCHECK(!is_string_init); + DCHECK(invoke->IsInvokeStaticOrDirect()); + DCHECK(invoke->AsInvokeStaticOrDirect()->GetClinitCheckRequirement() + == HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit); invoke->SetArgumentAt(argument_index, clinit_check); argument_index++; } @@ -1112,15 +1125,45 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, HInstruction* fake_string = LoadLocal(orig_this_reg, Primitive::kPrimNot); invoke->SetArgumentAt(argument_index, fake_string); current_block_->AddInstruction(invoke); - PotentiallySimplifyFakeString(orig_this_reg, dex_pc, invoke); + PotentiallySimplifyFakeString(orig_this_reg, invoke->GetDexPc(), invoke); } else { current_block_->AddInstruction(invoke); } + latest_result_ = invoke; return true; } +void HGraphBuilder::PotentiallySimplifyFakeString(uint16_t original_dex_register, + uint32_t dex_pc, + HInvoke* actual_string) { + if (!graph_->IsDebuggable()) { + // Notify that we cannot compile with baseline. The dex registers aliasing + // with `original_dex_register` will be handled when we optimize + // (see HInstructionSimplifer::VisitFakeString). + can_use_baseline_for_string_init_ = false; + return; + } + const VerifiedMethod* verified_method = + compiler_driver_->GetVerifiedMethod(dex_file_, dex_compilation_unit_->GetDexMethodIndex()); + if (verified_method != nullptr) { + UpdateLocal(original_dex_register, actual_string); + const SafeMap<uint32_t, std::set<uint32_t>>& string_init_map = + verified_method->GetStringInitPcRegMap(); + auto map_it = string_init_map.find(dex_pc); + if (map_it != string_init_map.end()) { + std::set<uint32_t> reg_set = map_it->second; + for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) { + HInstruction* load_local = LoadLocal(original_dex_register, Primitive::kPrimNot); + UpdateLocal(*set_it, load_local); + } + } + } else { + can_use_baseline_for_string_init_ = false; + } +} + bool HGraphBuilder::BuildInstanceFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put) { diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 08600c756d..19dd94475a 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -272,6 +272,19 @@ class HGraphBuilder : public ValueObject { uintptr_t direct_method, uintptr_t direct_code); + bool SetupArgumentsAndAddInvoke(HInvoke* invoke, + uint32_t number_of_vreg_arguments, + uint32_t* args, + uint32_t register_index, + bool is_range, + const char* descriptor, + HClinitCheck* clinit_check); + + HClinitCheck* ProcessClinitCheckForInvoke( + uint32_t dex_pc, + uint32_t method_idx, + HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement); + ArenaAllocator* const arena_; // A list of the size of the dex code holding block information for diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 41523557c3..25b3ea2f5f 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -3202,6 +3202,15 @@ void InstructionCodeGeneratorARM64::VisitTypeConversion(HTypeConversion* convers Register source = InputRegisterAt(conversion, 0); if ((result_type == Primitive::kPrimChar) && (input_size < result_size)) { __ Ubfx(output, source, 0, result_size * kBitsPerByte); + } else if (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimLong) { + // 'int' values are used directly as W registers, discarding the top + // bits, so we don't need to sign-extend and can just perform a move. + // We do not pass the `kDiscardForSameWReg` argument to force clearing the + // top 32 bits of the target register. We theoretically could leave those + // bits unchanged, but we would have to make sure that no code uses a + // 32bit input value as a 64bit value assuming that the top 32 bits are + // zero. + __ Mov(output.W(), source.W()); } else if ((result_type == Primitive::kPrimChar) || ((input_type == Primitive::kPrimChar) && (result_size > input_size))) { __ Ubfx(output, output.IsX() ? source.X() : source.W(), 0, min_size * kBitsPerByte); diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 1527a6aa23..806fd7a8f4 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -919,6 +919,104 @@ void IntrinsicCodeGeneratorARM::VisitStringCompareTo(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +void IntrinsicLocationsBuilderARM::VisitStringEquals(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + // Temporary registers to store lengths of strings and for calculations. + // Using instruction cbz requires a low register, so explicitly set a temp to be R0. + locations->AddTemp(Location::RegisterLocation(R0)); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + + locations->SetOut(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorARM::VisitStringEquals(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register str = locations->InAt(0).AsRegister<Register>(); + Register arg = locations->InAt(1).AsRegister<Register>(); + Register out = locations->Out().AsRegister<Register>(); + + Register temp = locations->GetTemp(0).AsRegister<Register>(); + Register temp1 = locations->GetTemp(1).AsRegister<Register>(); + Register temp2 = locations->GetTemp(2).AsRegister<Register>(); + + Label loop; + Label end; + Label return_true; + Label return_false; + + // Get offsets of count, value, and class fields within a string object. + const uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); + const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value(); + const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value(); + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); + + // Check if input is null, return false if it is. + __ CompareAndBranchIfZero(arg, &return_false); + + // Instanceof check for the argument by comparing class fields. + // All string objects must have the same type since String cannot be subclassed. + // Receiver must be a string object, so its class field is equal to all strings' class fields. + // If the argument is a string object, its class field must be equal to receiver's class field. + __ ldr(temp, Address(str, class_offset)); + __ ldr(temp1, Address(arg, class_offset)); + __ cmp(temp, ShifterOperand(temp1)); + __ b(&return_false, NE); + + // Load lengths of this and argument strings. + __ ldr(temp, Address(str, count_offset)); + __ ldr(temp1, Address(arg, count_offset)); + // Check if lengths are equal, return false if they're not. + __ cmp(temp, ShifterOperand(temp1)); + __ b(&return_false, NE); + // Return true if both strings are empty. + __ cbz(temp, &return_true); + + // Reference equality check, return true if same reference. + __ cmp(str, ShifterOperand(arg)); + __ b(&return_true, EQ); + + // Assertions that must hold in order to compare strings 2 characters at a time. + DCHECK_ALIGNED(value_offset, 4); + static_assert(IsAligned<4>(kObjectAlignment), "String of odd length is not zero padded"); + + // temp cannot overflow because we cannot allocate a String object with size 4GiB or greater. + __ add(temp, temp, ShifterOperand(temp)); + __ LoadImmediate(temp1, value_offset); + __ add(temp, temp, ShifterOperand(value_offset)); + + // Loop to compare strings 2 characters at a time starting at the front of the string. + // Ok to do this because strings with an odd length are zero-padded. + __ Bind(&loop); + __ ldr(out, Address(str, temp1)); + __ ldr(temp2, Address(arg, temp1)); + __ cmp(out, ShifterOperand(temp2)); + __ b(&return_false, NE); + __ add(temp1, temp1, ShifterOperand(sizeof(uint32_t))); + __ cmp(temp1, ShifterOperand(temp)); + __ b(&loop, LO); + + // Return true and exit the function. + // If loop does not result in returning false, we return true. + __ Bind(&return_true); + __ LoadImmediate(out, 1); + __ b(&end); + + // Return false and exit the function. + __ Bind(&return_false); + __ LoadImmediate(out, 0); + __ Bind(&end); +} + static void GenerateVisitStringIndexOf(HInvoke* invoke, ArmAssembler* assembler, CodeGeneratorARM* codegen, @@ -1110,7 +1208,6 @@ UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure. UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) -UNIMPLEMENTED_INTRINSIC(StringEquals) #undef UNIMPLEMENTED_INTRINSIC diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index f2db33086c..2ed2d9ab20 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -3181,6 +3181,8 @@ class HInvokeStaticOrDirect : public HInvoke { return dispatch_info_.direct_code_ptr; } + ClinitCheckRequirement GetClinitCheckRequirement() const { return clinit_check_requirement_; } + // Is this instruction a call to a static method? bool IsStatic() const { return GetInvokeType() == kStatic; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 45b3df008b..97b9972001 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -475,7 +475,7 @@ void RTPVisitor::VisitLoadException(HLoadException* instr) { if (catch_info->IsCatchAllTypeIndex()) { instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(throwable_class_handle_, - /* is_exact */ false)); + /* is_exact */ false)); } else { UpdateReferenceTypeInfo(instr, catch_info->GetCatchTypeIndex(), diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index 9f32a9eaf8..37c8bc5f51 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -587,9 +587,17 @@ void RegisterAllocator::LinearScan() { while (!unhandled_->IsEmpty()) { // (1) Remove interval with the lowest start position from unhandled. LiveInterval* current = unhandled_->Pop(); + + // Make sure the interval is an expected state. DCHECK(!current->IsFixed() && !current->HasSpillSlot()); + // Make sure we are going in the right order. DCHECK(unhandled_->IsEmpty() || unhandled_->Peek()->GetStart() >= current->GetStart()); + // Make sure a low interval is always with a high. DCHECK(!current->IsLowInterval() || unhandled_->Peek()->IsHighInterval()); + // Make sure a high interval is always with a low. + DCHECK(current->IsLowInterval() || + unhandled_->IsEmpty() || + !unhandled_->Peek()->IsHighInterval()); size_t position = current->GetStart(); @@ -916,13 +924,19 @@ bool RegisterAllocator::TrySplitNonPairOrUnalignedPairIntervalAt(size_t position if (active->IsHighInterval()) continue; if (first_register_use > next_use[active->GetRegister()]) continue; - // Split the first interval found. - if (!active->IsLowInterval() || IsLowOfUnalignedPairInterval(active)) { + // Split the first interval found that is either: + // 1) A non-pair interval. + // 2) A pair interval whose high is not low + 1. + // 3) A pair interval whose low is not even. + if (!active->IsLowInterval() || + IsLowOfUnalignedPairInterval(active) || + !IsLowRegister(active->GetRegister())) { LiveInterval* split = Split(active, position); active_.DeleteAt(i); if (split != active) { handled_.Add(active); } + PotentiallyRemoveOtherHalf(active, &active_, i); AddSorted(unhandled_, split); return true; } diff --git a/runtime/asm_support.h b/runtime/asm_support.h index 35acd424ba..084c88e239 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -141,10 +141,10 @@ ADD_TEST_EQ(MIRROR_CLASS_COMPONENT_TYPE_OFFSET, #define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (36 + MIRROR_OBJECT_HEADER_SIZE) ADD_TEST_EQ(MIRROR_CLASS_ACCESS_FLAGS_OFFSET, art::mirror::Class::AccessFlagsOffset().Int32Value()) -#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (96 + MIRROR_OBJECT_HEADER_SIZE) +#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (100 + MIRROR_OBJECT_HEADER_SIZE) ADD_TEST_EQ(MIRROR_CLASS_OBJECT_SIZE_OFFSET, art::mirror::Class::ObjectSizeOffset().Int32Value()) -#define MIRROR_CLASS_STATUS_OFFSET (108 + MIRROR_OBJECT_HEADER_SIZE) +#define MIRROR_CLASS_STATUS_OFFSET (112 + MIRROR_OBJECT_HEADER_SIZE) ADD_TEST_EQ(MIRROR_CLASS_STATUS_OFFSET, art::mirror::Class::StatusOffset().Int32Value()) @@ -153,7 +153,7 @@ ADD_TEST_EQ(static_cast<uint32_t>(MIRROR_CLASS_STATUS_INITIALIZED), static_cast<uint32_t>(art::mirror::Class::kStatusInitialized)) #define ACCESS_FLAGS_CLASS_IS_FINALIZABLE 0x80000000 ADD_TEST_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_FINALIZABLE), - static_cast<uint32_t>(kAccClassIsFinalizable)) + static_cast<uint32_t>(art::kAccClassIsFinalizable)) // Array offsets. #define MIRROR_ARRAY_LENGTH_OFFSET MIRROR_OBJECT_HEADER_SIZE diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 4172b89244..b6ad5473ff 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -66,6 +66,8 @@ namespace art { #define kFlag_Invocation 0x8000 // Part of the invocation interface (JavaVM*). #define kFlag_ForceTrace 0x80000000 // Add this to a JNI function's flags if you want to trace every call. + +class VarArgs; /* * Java primitive types: * B - jbyte @@ -126,6 +128,116 @@ union JniValueType { jshort S; const void* V; // void jboolean Z; + const VarArgs* va; +}; + +/* + * A structure containing all the information needed to validate varargs arguments. + * + * Note that actually getting the arguments from this structure mutates it so should only be done on + * owned copies. + */ +class VarArgs { + public: + VarArgs(jmethodID m, va_list var) : m_(m), type_(kTypeVaList), cnt_(0) { + va_copy(vargs_, var); + } + + VarArgs(jmethodID m, const jvalue* vals) : m_(m), type_(kTypePtr), cnt_(0), ptr_(vals) {} + + ~VarArgs() { + if (type_ == kTypeVaList) { + va_end(vargs_); + } + } + + VarArgs(VarArgs&& other) { + m_ = other.m_; + cnt_ = other.cnt_; + type_ = other.type_; + if (other.type_ == kTypeVaList) { + va_copy(vargs_, other.vargs_); + } else { + ptr_ = other.ptr_; + } + } + + // This method is const because we need to ensure that one only uses the GetValue method on an + // owned copy of the VarArgs. This is because getting the next argument from a va_list is a + // mutating operation. Therefore we pass around these VarArgs with the 'const' qualifier and when + // we want to use one we need to Clone() it. + VarArgs Clone() const { + if (type_ == kTypeVaList) { + // const_cast needed to make sure the compiler is okay with va_copy, which (being a macro) is + // messed up if the source argument is not the exact type 'va_list'. + return VarArgs(m_, cnt_, const_cast<VarArgs*>(this)->vargs_); + } else { + return VarArgs(m_, cnt_, ptr_); + } + } + + jmethodID GetMethodID() const { + return m_; + } + + JniValueType GetValue(char fmt) { + JniValueType o; + if (type_ == kTypeVaList) { + switch (fmt) { + case 'Z': o.Z = static_cast<jboolean>(va_arg(vargs_, jint)); break; + case 'B': o.B = static_cast<jbyte>(va_arg(vargs_, jint)); break; + case 'C': o.C = static_cast<jchar>(va_arg(vargs_, jint)); break; + case 'S': o.S = static_cast<jshort>(va_arg(vargs_, jint)); break; + case 'I': o.I = va_arg(vargs_, jint); break; + case 'J': o.J = va_arg(vargs_, jlong); break; + case 'F': o.F = static_cast<jfloat>(va_arg(vargs_, jdouble)); break; + case 'D': o.D = va_arg(vargs_, jdouble); break; + case 'L': o.L = va_arg(vargs_, jobject); break; + default: + LOG(FATAL) << "Illegal type format char " << fmt; + UNREACHABLE(); + } + } else { + CHECK(type_ == kTypePtr); + jvalue v = ptr_[cnt_]; + cnt_++; + switch (fmt) { + case 'Z': o.Z = v.z; break; + case 'B': o.B = v.b; break; + case 'C': o.C = v.c; break; + case 'S': o.S = v.s; break; + case 'I': o.I = v.i; break; + case 'J': o.J = v.j; break; + case 'F': o.F = v.f; break; + case 'D': o.D = v.d; break; + case 'L': o.L = v.l; break; + default: + LOG(FATAL) << "Illegal type format char " << fmt; + UNREACHABLE(); + } + } + return o; + } + + private: + VarArgs(jmethodID m, uint32_t cnt, va_list var) : m_(m), type_(kTypeVaList), cnt_(cnt) { + va_copy(vargs_, var); + } + + VarArgs(jmethodID m, uint32_t cnt, const jvalue* vals) : m_(m), type_(kTypePtr), cnt_(cnt), ptr_(vals) {} + + enum VarArgsType { + kTypeVaList, + kTypePtr, + }; + + jmethodID m_; + VarArgsType type_; + uint32_t cnt_; + union { + va_list vargs_; + const jvalue* ptr_; + }; }; class ScopedCheck { @@ -339,7 +451,7 @@ class ScopedCheck { * z - jsize (for lengths; use i if negative values are okay) * v - JavaVM* * E - JNIEnv* - * . - no argument; just print "..." (used for varargs JNI calls) + * . - VarArgs* for Jni calls with variable length arguments * * Use the kFlag_NullableUtf flag where 'u' field(s) are nullable. */ @@ -736,11 +848,35 @@ class ScopedCheck { return CheckThread(arg.E); case 'L': // jobject return CheckInstance(soa, kObject, arg.L, true); + case '.': // A VarArgs list + return CheckVarArgs(soa, arg.va); default: return CheckNonHeapValue(fmt, arg); } } + bool CheckVarArgs(ScopedObjectAccess& soa, const VarArgs* args_p) + SHARED_REQUIRES(Locks::mutator_lock_) { + CHECK(args_p != nullptr); + VarArgs args(args_p->Clone()); + ArtMethod* m = CheckMethodID(soa, args.GetMethodID()); + if (m == nullptr) { + return false; + } + uint32_t len = 0; + const char* shorty = m->GetShorty(&len); + // Skip the return type + CHECK_GE(len, 1u); + len--; + shorty++; + for (uint32_t i = 0; i < len; i++) { + if (!CheckPossibleHeapValue(soa, shorty[i], args.GetValue(shorty[i]))) { + return false; + } + } + return true; + } + bool CheckNonHeapValue(char fmt, JniValueType arg) { switch (fmt) { case 'p': // TODO: pointer - null or readable? @@ -833,6 +969,24 @@ class ScopedCheck { } break; } + case '.': { + const VarArgs* va = arg.va; + VarArgs args(va->Clone()); + ArtMethod* m = soa.DecodeMethod(args.GetMethodID()); + uint32_t len; + const char* shorty = m->GetShorty(&len); + CHECK_GE(len, 1u); + // Skip past return value. + len--; + shorty++; + // Remove the previous ', ' from the message. + msg->erase(msg->length() - 2); + for (uint32_t i = 0; i < len; i++) { + *msg += ", "; + TracePossibleHeapValue(soa, entry, shorty[i], args.GetValue(shorty[i]), msg); + } + break; + } default: TraceNonHeapValue(fmt, arg, msg); break; @@ -1836,8 +1990,9 @@ class CheckJNI { static jobject NewObjectV(JNIEnv* env, jclass c, jmethodID mid, va_list vargs) { ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); - JniValueType args[3] = {{.E = env}, {.c = c}, {.m = mid}}; - if (sc.Check(soa, true, "Ecm", args) && sc.CheckInstantiableNonArray(soa, c) && + VarArgs rest(mid, vargs); + JniValueType args[4] = {{.E = env}, {.c = c}, {.m = mid}, {.va = &rest}}; + if (sc.Check(soa, true, "Ecm.", args) && sc.CheckInstantiableNonArray(soa, c) && sc.CheckConstructor(soa, mid)) { JniValueType result; result.L = baseEnv(env)->NewObjectV(env, c, mid, vargs); @@ -1859,8 +2014,9 @@ class CheckJNI { static jobject NewObjectA(JNIEnv* env, jclass c, jmethodID mid, jvalue* vargs) { ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, __FUNCTION__); - JniValueType args[3] = {{.E = env}, {.c = c}, {.m = mid}}; - if (sc.Check(soa, true, "Ecm", args) && sc.CheckInstantiableNonArray(soa, c) && + VarArgs rest(mid, vargs); + JniValueType args[4] = {{.E = env}, {.c = c}, {.m = mid}, {.va = &rest}}; + if (sc.Check(soa, true, "Ecm.", args) && sc.CheckInstantiableNonArray(soa, c) && sc.CheckConstructor(soa, mid)) { JniValueType result; result.L = baseEnv(env)->NewObjectA(env, c, mid, vargs); @@ -2689,25 +2845,25 @@ class CheckJNI { } static bool CheckCallArgs(ScopedObjectAccess& soa, ScopedCheck& sc, JNIEnv* env, jobject obj, - jclass c, jmethodID mid, InvokeType invoke) + jclass c, jmethodID mid, InvokeType invoke, const VarArgs* vargs) SHARED_REQUIRES(Locks::mutator_lock_) { bool checked; switch (invoke) { case kVirtual: { DCHECK(c == nullptr); - JniValueType args[3] = {{.E = env}, {.L = obj}, {.m = mid}}; - checked = sc.Check(soa, true, "ELm", args); + JniValueType args[4] = {{.E = env}, {.L = obj}, {.m = mid}, {.va = vargs}}; + checked = sc.Check(soa, true, "ELm.", args); break; } case kDirect: { - JniValueType args[4] = {{.E = env}, {.L = obj}, {.c = c}, {.m = mid}}; - checked = sc.Check(soa, true, "ELcm", args); + JniValueType args[5] = {{.E = env}, {.L = obj}, {.c = c}, {.m = mid}, {.va = vargs}}; + checked = sc.Check(soa, true, "ELcm.", args); break; } case kStatic: { DCHECK(obj == nullptr); - JniValueType args[3] = {{.E = env}, {.c = c}, {.m = mid}}; - checked = sc.Check(soa, true, "Ecm", args); + JniValueType args[4] = {{.E = env}, {.c = c}, {.m = mid}, {.va = vargs}}; + checked = sc.Check(soa, true, "Ecm.", args); break; } default: @@ -2724,7 +2880,8 @@ class CheckJNI { ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, function_name); JniValueType result; - if (CheckCallArgs(soa, sc, env, obj, c, mid, invoke) && + VarArgs rest(mid, vargs); + if (CheckCallArgs(soa, sc, env, obj, c, mid, invoke, &rest) && sc.CheckMethodAndSig(soa, obj, c, mid, type, invoke)) { const char* result_check; switch (type) { @@ -2907,7 +3064,8 @@ class CheckJNI { ScopedObjectAccess soa(env); ScopedCheck sc(kFlag_Default, function_name); JniValueType result; - if (CheckCallArgs(soa, sc, env, obj, c, mid, invoke) && + VarArgs rest(mid, vargs); + if (CheckCallArgs(soa, sc, env, obj, c, mid, invoke, &rest) && sc.CheckMethodAndSig(soa, obj, c, mid, type, invoke)) { const char* result_check; switch (type) { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index c179c64491..dc273d8fe0 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -358,9 +358,9 @@ void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b // Setup String. Handle<mirror::Class> java_lang_String(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::String::ClassSize(image_pointer_size_)))); + java_lang_String->SetStringClass(); mirror::String::SetClass(java_lang_String.Get()); mirror::Class::SetStatus(java_lang_String, mirror::Class::kStatusResolved, self); - java_lang_String->SetStringClass(); // Setup java.lang.ref.Reference. Handle<mirror::Class> java_lang_ref_Reference(hs.NewHandle( @@ -570,16 +570,13 @@ void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b CHECK_EQ(java_lang_ref_Reference->GetClassSize(), mirror::Reference::ClassSize(image_pointer_size_)); class_root = FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;"); - class_root->SetAccessFlags(class_root->GetAccessFlags() | - kAccClassIsReference | kAccClassIsFinalizerReference); + class_root->SetClassFlags(class_root->GetClassFlags() | mirror::kClassFlagFinalizerReference); class_root = FindSystemClass(self, "Ljava/lang/ref/PhantomReference;"); - class_root->SetAccessFlags(class_root->GetAccessFlags() | kAccClassIsReference | - kAccClassIsPhantomReference); + class_root->SetClassFlags(class_root->GetClassFlags() | mirror::kClassFlagPhantomReference); class_root = FindSystemClass(self, "Ljava/lang/ref/SoftReference;"); - class_root->SetAccessFlags(class_root->GetAccessFlags() | kAccClassIsReference); + class_root->SetClassFlags(class_root->GetClassFlags() | mirror::kClassFlagSoftReference); class_root = FindSystemClass(self, "Ljava/lang/ref/WeakReference;"); - class_root->SetAccessFlags(class_root->GetAccessFlags() | kAccClassIsReference | - kAccClassIsWeakReference); + class_root->SetClassFlags(class_root->GetClassFlags() | mirror::kClassFlagWeakReference); // Setup the ClassLoader, verifying the object_size_. class_root = FindSystemClass(self, "Ljava/lang/ClassLoader;"); @@ -2701,6 +2698,11 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto new_class->SetVTable(java_lang_Object->GetVTable()); new_class->SetPrimitiveType(Primitive::kPrimNot); new_class->SetClassLoader(component_type->GetClassLoader()); + if (component_type->IsPrimitive()) { + new_class->SetClassFlags(mirror::kClassFlagNoReferenceFields); + } else { + new_class->SetClassFlags(mirror::kClassFlagObjectArray); + } mirror::Class::SetStatus(new_class, mirror::Class::kStatusLoaded, self); { ArtMethod* imt[mirror::Class::kImtSize]; @@ -4385,9 +4387,9 @@ bool ClassLinker::LinkSuperClass(Handle<mirror::Class> klass) { } // Inherit reference flags (if any) from the superclass. - int reference_flags = (super->GetAccessFlags() & kAccReferenceFlagsMask); + int reference_flags = (super->GetClassFlags() & mirror::kClassFlagReference); if (reference_flags != 0) { - klass->SetAccessFlags(klass->GetAccessFlags() | reference_flags); + klass->SetClassFlags(klass->GetClassFlags() | reference_flags); } // Disallow custom direct subclasses of java.lang.ref.Reference. if (init_done_ && super == GetClassRoot(kJavaLangRefReference)) { @@ -5227,6 +5229,22 @@ bool ClassLinker::LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_ *class_size = size; } else { klass->SetNumReferenceInstanceFields(num_reference_fields); + mirror::Class* super_class = klass->GetSuperClass(); + if (num_reference_fields == 0 || super_class == nullptr) { + // object has one reference field, klass, but we ignore it since we always visit the class. + // If the super_class is null then we are java.lang.Object. + if (super_class == nullptr || + (super_class->GetClassFlags() & mirror::kClassFlagNoReferenceFields) != 0) { + klass->SetClassFlags(klass->GetClassFlags() | mirror::kClassFlagNoReferenceFields); + } else if (kIsDebugBuild) { + size_t total_reference_instance_fields = 0; + while (super_class != nullptr) { + total_reference_instance_fields += super_class->NumReferenceInstanceFields(); + super_class = super_class->GetSuperClass(); + } + CHECK_GT(total_reference_instance_fields, 1u); + } + } if (!klass->IsVariableSize()) { std::string temp; DCHECK_GE(size, sizeof(mirror::Object)) << klass->GetDescriptor(&temp); @@ -5528,6 +5546,36 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_i } } +ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(const DexFile& dex_file, + uint32_t method_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader) { + ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_); + if (resolved != nullptr && !resolved->IsRuntimeMethod()) { + DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); + return resolved; + } + // Fail, get the declaring class. + const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); + mirror::Class* klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); + if (klass == nullptr) { + Thread::Current()->AssertPendingException(); + return nullptr; + } + if (klass->IsInterface()) { + LOG(FATAL) << "ResolveAmbiguousMethod: unexpected method in interface: " << PrettyClass(klass); + return nullptr; + } + + // Search both direct and virtual methods + resolved = klass->FindDirectMethod(dex_cache.Get(), method_idx, image_pointer_size_); + if (resolved == nullptr) { + resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_); + } + + return resolved; +} + ArtField* ClassLinker::ResolveField(const DexFile& dex_file, uint32_t field_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, bool is_static) { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 7243a25a48..fbf4035842 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -229,6 +229,12 @@ class ClassLinker { SHARED_REQUIRES(Locks::mutator_lock_); ArtMethod* ResolveMethod(Thread* self, uint32_t method_idx, ArtMethod* referrer, InvokeType type) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); + ArtMethod* ResolveMethodWithoutInvokeType(const DexFile& dex_file, + uint32_t method_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader) + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!dex_lock_, !Roles::uninterruptible_); ArtField* GetResolvedField(uint32_t field_idx, mirror::Class* field_declaring_class) SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 3c84d8fc0a..0d1c875fdf 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -488,6 +488,7 @@ struct ObjectOffsets : public CheckOffsets<mirror::Object> { struct ClassOffsets : public CheckOffsets<mirror::Class> { ClassOffsets() : CheckOffsets<mirror::Class>(false, "Ljava/lang/Class;") { addOffset(OFFSETOF_MEMBER(mirror::Class, access_flags_), "accessFlags"); + addOffset(OFFSETOF_MEMBER(mirror::Class, class_flags_), "classFlags"); addOffset(OFFSETOF_MEMBER(mirror::Class, class_loader_), "classLoader"); addOffset(OFFSETOF_MEMBER(mirror::Class, class_size_), "classSize"); addOffset(OFFSETOF_MEMBER(mirror::Class, clinit_thread_id_), "clinitThreadId"); diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 52590a5970..213f25dfdb 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -32,13 +32,16 @@ #include "base/hash_map.h" #include "base/logging.h" #include "base/stringprintf.h" -#include "class_linker.h" +#include "class_linker-inl.h" #include "dex_file-inl.h" #include "dex_file_verifier.h" #include "globals.h" #include "leb128.h" +#include "mirror/field.h" +#include "mirror/method.h" #include "mirror/string.h" #include "os.h" +#include "reflection.h" #include "safe_map.h" #include "handle_scope-inl.h" #include "thread.h" @@ -1044,6 +1047,918 @@ std::string DexFile::GetDexCanonicalLocation(const char* dex_location) { } } +// Read a signed integer. "zwidth" is the zero-based byte count. +static int32_t ReadSignedInt(const uint8_t* ptr, int zwidth) { + int32_t val = 0; + for (int i = zwidth; i >= 0; --i) { + val = ((uint32_t)val >> 8) | (((int32_t)*ptr++) << 24); + } + val >>= (3 - zwidth) * 8; + return val; +} + +// Read an unsigned integer. "zwidth" is the zero-based byte count, +// "fill_on_right" indicates which side we want to zero-fill from. +static uint32_t ReadUnsignedInt(const uint8_t* ptr, int zwidth, bool fill_on_right) { + uint32_t val = 0; + for (int i = zwidth; i >= 0; --i) { + val = (val >> 8) | (((uint32_t)*ptr++) << 24); + } + if (!fill_on_right) { + val >>= (3 - zwidth) * 8; + } + return val; +} + +// Read a signed long. "zwidth" is the zero-based byte count. +static int64_t ReadSignedLong(const uint8_t* ptr, int zwidth) { + int64_t val = 0; + for (int i = zwidth; i >= 0; --i) { + val = ((uint64_t)val >> 8) | (((int64_t)*ptr++) << 56); + } + val >>= (7 - zwidth) * 8; + return val; +} + +// Read an unsigned long. "zwidth" is the zero-based byte count, +// "fill_on_right" indicates which side we want to zero-fill from. +static uint64_t ReadUnsignedLong(const uint8_t* ptr, int zwidth, bool fill_on_right) { + uint64_t val = 0; + for (int i = zwidth; i >= 0; --i) { + val = (val >> 8) | (((uint64_t)*ptr++) << 56); + } + if (!fill_on_right) { + val >>= (7 - zwidth) * 8; + } + return val; +} + +const DexFile::AnnotationSetItem* DexFile::FindAnnotationSetForField(ArtField* field) const { + mirror::Class* klass = field->GetDeclaringClass(); + const AnnotationsDirectoryItem* annotations_dir = GetAnnotationsDirectory(*klass->GetClassDef()); + if (annotations_dir == nullptr) { + return nullptr; + } + const FieldAnnotationsItem* field_annotations = GetFieldAnnotations(annotations_dir); + if (field_annotations == nullptr) { + return nullptr; + } + uint32_t field_index = field->GetDexFieldIndex(); + uint32_t field_count = annotations_dir->fields_size_; + for (uint32_t i = 0; i < field_count; ++i) { + if (field_annotations[i].field_idx_ == field_index) { + return GetFieldAnnotationSetItem(field_annotations[i]); + } + } + return nullptr; +} + +mirror::Object* DexFile::GetAnnotationForField(ArtField* field, + Handle<mirror::Class> annotation_class) const { + const AnnotationSetItem* annotation_set = FindAnnotationSetForField(field); + if (annotation_set == nullptr) { + return nullptr; + } + StackHandleScope<1> hs(Thread::Current()); + Handle<mirror::Class> field_class(hs.NewHandle(field->GetDeclaringClass())); + return GetAnnotationObjectFromAnnotationSet( + field_class, annotation_set, kDexVisibilityRuntime, annotation_class); +} + +mirror::ObjectArray<mirror::Object>* DexFile::GetAnnotationsForField(ArtField* field) const { + const AnnotationSetItem* annotation_set = FindAnnotationSetForField(field); + StackHandleScope<1> hs(Thread::Current()); + Handle<mirror::Class> field_class(hs.NewHandle(field->GetDeclaringClass())); + return ProcessAnnotationSet(field_class, annotation_set, kDexVisibilityRuntime); +} + +mirror::ObjectArray<mirror::Object>* DexFile::GetSignatureAnnotationForField(ArtField* field) + const { + const AnnotationSetItem* annotation_set = FindAnnotationSetForField(field); + if (annotation_set == nullptr) { + return nullptr; + } + StackHandleScope<1> hs(Thread::Current()); + Handle<mirror::Class> field_class(hs.NewHandle(field->GetDeclaringClass())); + return GetSignatureValue(field_class, annotation_set); +} + +bool DexFile::IsFieldAnnotationPresent(ArtField* field, Handle<mirror::Class> annotation_class) + const { + const AnnotationSetItem* annotation_set = FindAnnotationSetForField(field); + if (annotation_set == nullptr) { + return false; + } + StackHandleScope<1> hs(Thread::Current()); + Handle<mirror::Class> field_class(hs.NewHandle(field->GetDeclaringClass())); + const AnnotationItem* annotation_item = GetAnnotationItemFromAnnotationSet( + field_class, annotation_set, kDexVisibilityRuntime, annotation_class); + return annotation_item != nullptr; +} + +const DexFile::AnnotationSetItem* DexFile::FindAnnotationSetForMethod(ArtMethod* method) const { + mirror::Class* klass = method->GetDeclaringClass(); + const AnnotationsDirectoryItem* annotations_dir = GetAnnotationsDirectory(*klass->GetClassDef()); + if (annotations_dir == nullptr) { + return nullptr; + } + const MethodAnnotationsItem* method_annotations = GetMethodAnnotations(annotations_dir); + if (method_annotations == nullptr) { + return nullptr; + } + uint32_t method_index = method->GetDexMethodIndex(); + uint32_t method_count = annotations_dir->methods_size_; + for (uint32_t i = 0; i < method_count; ++i) { + if (method_annotations[i].method_idx_ == method_index) { + return GetMethodAnnotationSetItem(method_annotations[i]); + } + } + return nullptr; +} + +const DexFile::ParameterAnnotationsItem* DexFile::FindAnnotationsItemForMethod(ArtMethod* method) + const { + mirror::Class* klass = method->GetDeclaringClass(); + const AnnotationsDirectoryItem* annotations_dir = GetAnnotationsDirectory(*klass->GetClassDef()); + if (annotations_dir == nullptr) { + return nullptr; + } + const ParameterAnnotationsItem* parameter_annotations = GetParameterAnnotations(annotations_dir); + if (parameter_annotations == nullptr) { + return nullptr; + } + uint32_t method_index = method->GetDexMethodIndex(); + uint32_t parameter_count = annotations_dir->parameters_size_; + for (uint32_t i = 0; i < parameter_count; ++i) { + if (parameter_annotations[i].method_idx_ == method_index) { + return ¶meter_annotations[i]; + } + } + return nullptr; +} + +mirror::Object* DexFile::GetAnnotationDefaultValue(ArtMethod* method) const { + mirror::Class* klass = method->GetDeclaringClass(); + const AnnotationsDirectoryItem* annotations_dir = GetAnnotationsDirectory(*klass->GetClassDef()); + if (annotations_dir == nullptr) { + return nullptr; + } + const AnnotationSetItem* annotation_set = GetClassAnnotationSet(annotations_dir); + if (annotation_set == nullptr) { + return nullptr; + } + const AnnotationItem* annotation_item = SearchAnnotationSet(annotation_set, + "Ldalvik/annotation/AnnotationDefault;", kDexVisibilitySystem); + if (annotation_item == nullptr) { + return nullptr; + } + const uint8_t* annotation = SearchEncodedAnnotation(annotation_item->annotation_, "value"); + if (annotation == nullptr) { + return nullptr; + } + uint8_t header_byte = *(annotation++); + if ((header_byte & kDexAnnotationValueTypeMask) != kDexAnnotationAnnotation) { + return nullptr; + } + annotation = SearchEncodedAnnotation(annotation, method->GetName()); + if (annotation == nullptr) { + return nullptr; + } + AnnotationValue annotation_value; + StackHandleScope<2> hs(Thread::Current()); + Handle<mirror::Class> h_klass(hs.NewHandle(klass)); + Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType())); + if (!ProcessAnnotationValue(h_klass, &annotation, &annotation_value, return_type, kAllObjects)) { + return nullptr; + } + return annotation_value.value_.GetL(); +} + +mirror::Object* DexFile::GetAnnotationForMethod(ArtMethod* method, + Handle<mirror::Class> annotation_class) const { + const AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); + if (annotation_set == nullptr) { + return nullptr; + } + StackHandleScope<1> hs(Thread::Current()); + Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); + return GetAnnotationObjectFromAnnotationSet(method_class, annotation_set, + kDexVisibilityRuntime, annotation_class); +} + +mirror::ObjectArray<mirror::Object>* DexFile::GetAnnotationsForMethod(ArtMethod* method) const { + const AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); + StackHandleScope<1> hs(Thread::Current()); + Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); + return ProcessAnnotationSet(method_class, annotation_set, kDexVisibilityRuntime); +} + +mirror::ObjectArray<mirror::Object>* DexFile::GetExceptionTypesForMethod(ArtMethod* method) const { + const AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); + if (annotation_set == nullptr) { + return nullptr; + } + StackHandleScope<1> hs(Thread::Current()); + Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); + return GetThrowsValue(method_class, annotation_set); +} + +mirror::ObjectArray<mirror::Object>* DexFile::GetParameterAnnotations(ArtMethod* method) const { + const ParameterAnnotationsItem* parameter_annotations = FindAnnotationsItemForMethod(method); + if (parameter_annotations == nullptr) { + return nullptr; + } + const AnnotationSetRefList* set_ref_list = + GetParameterAnnotationSetRefList(parameter_annotations); + if (set_ref_list == nullptr) { + return nullptr; + } + uint32_t size = set_ref_list->size_; + StackHandleScope<1> hs(Thread::Current()); + Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); + return ProcessAnnotationSetRefList(method_class, set_ref_list, size); +} + +bool DexFile::IsMethodAnnotationPresent(ArtMethod* method, Handle<mirror::Class> annotation_class) + const { + const AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); + if (annotation_set == nullptr) { + return false; + } + StackHandleScope<1> hs(Thread::Current()); + Handle<mirror::Class> method_class(hs.NewHandle(method->GetDeclaringClass())); + const AnnotationItem* annotation_item = GetAnnotationItemFromAnnotationSet( + method_class, annotation_set, kDexVisibilityRuntime, annotation_class); + return (annotation_item != nullptr); +} + +const DexFile::AnnotationSetItem* DexFile::FindAnnotationSetForClass(Handle<mirror::Class> klass) + const { + const AnnotationsDirectoryItem* annotations_dir = GetAnnotationsDirectory(*klass->GetClassDef()); + if (annotations_dir == nullptr) { + return nullptr; + } + return GetClassAnnotationSet(annotations_dir); +} + +mirror::Object* DexFile::GetAnnotationForClass(Handle<mirror::Class> klass, + Handle<mirror::Class> annotation_class) const { + const AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + if (annotation_set == nullptr) { + return nullptr; + } + return GetAnnotationObjectFromAnnotationSet(klass, annotation_set, kDexVisibilityRuntime, + annotation_class); +} + +mirror::ObjectArray<mirror::Object>* DexFile::GetAnnotationsForClass(Handle<mirror::Class> klass) + const { + const AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + return ProcessAnnotationSet(klass, annotation_set, kDexVisibilityRuntime); +} + +bool DexFile::IsClassAnnotationPresent(Handle<mirror::Class> klass, + Handle<mirror::Class> annotation_class) const { + const AnnotationSetItem* annotation_set = FindAnnotationSetForClass(klass); + if (annotation_set == nullptr) { + return false; + } + const AnnotationItem* annotation_item = GetAnnotationItemFromAnnotationSet( + klass, annotation_set, kDexVisibilityRuntime, annotation_class); + return (annotation_item != nullptr); +} + +mirror::Object* DexFile::CreateAnnotationMember(Handle<mirror::Class> klass, + Handle<mirror::Class> annotation_class, const uint8_t** annotation) const { + Thread* self = Thread::Current(); + ScopedObjectAccessUnchecked soa(self); + StackHandleScope<5> hs(self); + uint32_t element_name_index = DecodeUnsignedLeb128(annotation); + const char* name = StringDataByIdx(element_name_index); + Handle<mirror::String> string_name( + hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, name))); + + ArtMethod* annotation_method = + annotation_class->FindDeclaredVirtualMethodByName(name, sizeof(void*)); + if (annotation_method == nullptr) { + return nullptr; + } + Handle<mirror::Class> method_return(hs.NewHandle(annotation_method->GetReturnType())); + + AnnotationValue annotation_value; + if (!ProcessAnnotationValue(klass, annotation, &annotation_value, method_return, kAllObjects)) { + return nullptr; + } + Handle<mirror::Object> value_object(hs.NewHandle(annotation_value.value_.GetL())); + + mirror::Class* annotation_member_class = + WellKnownClasses::ToClass(WellKnownClasses::libcore_reflect_AnnotationMember); + Handle<mirror::Object> new_member(hs.NewHandle(annotation_member_class->AllocObject(self))); + Handle<mirror::Method> method_object( + hs.NewHandle(mirror::Method::CreateFromArtMethod(self, annotation_method))); + + if (new_member.Get() == nullptr || string_name.Get() == nullptr || + method_object.Get() == nullptr || method_return.Get() == nullptr) { + LOG(ERROR) << StringPrintf("Failed creating annotation element (m=%p n=%p a=%p r=%p", + new_member.Get(), string_name.Get(), method_object.Get(), method_return.Get()); + return nullptr; + } + + JValue result; + ArtMethod* annotation_member_init = + soa.DecodeMethod(WellKnownClasses::libcore_reflect_AnnotationMember_init); + uint32_t args[5] = { static_cast<uint32_t>(reinterpret_cast<uintptr_t>(new_member.Get())), + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(string_name.Get())), + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(value_object.Get())), + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(method_return.Get())), + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(method_object.Get())) + }; + annotation_member_init->Invoke(self, args, sizeof(args), &result, "VLLLL"); + if (self->IsExceptionPending()) { + LOG(INFO) << "Exception in AnnotationMember.<init>"; + return nullptr; + } + + return new_member.Get(); +} + +const DexFile::AnnotationItem* DexFile::GetAnnotationItemFromAnnotationSet( + Handle<mirror::Class> klass, const AnnotationSetItem* annotation_set, uint32_t visibility, + Handle<mirror::Class> annotation_class) const { + for (uint32_t i = 0; i < annotation_set->size_; ++i) { + const AnnotationItem* annotation_item = GetAnnotationItem(annotation_set, i); + if (annotation_item->visibility_ != visibility) { + continue; + } + const uint8_t* annotation = annotation_item->annotation_; + uint32_t type_index = DecodeUnsignedLeb128(&annotation); + mirror::Class* resolved_class = Runtime::Current()->GetClassLinker()->ResolveType( + klass->GetDexFile(), type_index, klass.Get()); + if (resolved_class == nullptr) { + std::string temp; + LOG(WARNING) << StringPrintf("Unable to resolve %s annotation class %d", + klass->GetDescriptor(&temp), type_index); + CHECK(Thread::Current()->IsExceptionPending()); + Thread::Current()->ClearException(); + continue; + } + if (resolved_class == annotation_class.Get()) { + return annotation_item; + } + } + + return nullptr; +} + +mirror::Object* DexFile::GetAnnotationObjectFromAnnotationSet(Handle<mirror::Class> klass, + const AnnotationSetItem* annotation_set, uint32_t visibility, + Handle<mirror::Class> annotation_class) const { + const AnnotationItem* annotation_item = + GetAnnotationItemFromAnnotationSet(klass, annotation_set, visibility, annotation_class); + if (annotation_item == nullptr) { + return nullptr; + } + const uint8_t* annotation = annotation_item->annotation_; + return ProcessEncodedAnnotation(klass, &annotation); +} + +mirror::Object* DexFile::GetAnnotationValue(Handle<mirror::Class> klass, + const AnnotationItem* annotation_item, const char* annotation_name, + Handle<mirror::Class> array_class, uint32_t expected_type) const { + const uint8_t* annotation = + SearchEncodedAnnotation(annotation_item->annotation_, annotation_name); + if (annotation == nullptr) { + return nullptr; + } + AnnotationValue annotation_value; + if (!ProcessAnnotationValue(klass, &annotation, &annotation_value, array_class, kAllObjects)) { + return nullptr; + } + if (annotation_value.type_ != expected_type) { + return nullptr; + } + return annotation_value.value_.GetL(); +} + +mirror::ObjectArray<mirror::Object>* DexFile::GetSignatureValue(Handle<mirror::Class> klass, + const AnnotationSetItem* annotation_set) const { + StackHandleScope<1> hs(Thread::Current()); + const AnnotationItem* annotation_item = + SearchAnnotationSet(annotation_set, "Ldalvik/annotation/Signature;", kDexVisibilitySystem); + if (annotation_item == nullptr) { + return nullptr; + } + mirror::Class* string_class = mirror::String::GetJavaLangString(); + Handle<mirror::Class> string_array_class(hs.NewHandle( + Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class))); + mirror::Object* obj = + GetAnnotationValue(klass, annotation_item, "value", string_array_class, kDexAnnotationArray); + if (obj == nullptr) { + return nullptr; + } + return obj->AsObjectArray<mirror::Object>(); +} + +mirror::ObjectArray<mirror::Object>* DexFile::GetThrowsValue(Handle<mirror::Class> klass, + const AnnotationSetItem* annotation_set) const { + StackHandleScope<1> hs(Thread::Current()); + const AnnotationItem* annotation_item = + SearchAnnotationSet(annotation_set, "Ldalvik/annotation/Throws;", kDexVisibilitySystem); + if (annotation_item == nullptr) { + return nullptr; + } + mirror::Class* class_class = mirror::Class::GetJavaLangClass(); + Handle<mirror::Class> class_array_class(hs.NewHandle( + Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &class_class))); + mirror::Object* obj = + GetAnnotationValue(klass, annotation_item, "value", class_array_class, kDexAnnotationArray); + if (obj == nullptr) { + return nullptr; + } + return obj->AsObjectArray<mirror::Object>(); +} + +mirror::ObjectArray<mirror::Object>* DexFile::ProcessAnnotationSet(Handle<mirror::Class> klass, + const AnnotationSetItem* annotation_set, uint32_t visibility) const { + Thread* self = Thread::Current(); + ScopedObjectAccessUnchecked soa(self); + StackHandleScope<2> hs(self); + Handle<mirror::Class> annotation_array_class(hs.NewHandle( + soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_annotation_Annotation__array))); + if (annotation_set == nullptr) { + return mirror::ObjectArray<mirror::Object>::Alloc(self, annotation_array_class.Get(), 0); + } + + uint32_t size = annotation_set->size_; + Handle<mirror::ObjectArray<mirror::Object>> result(hs.NewHandle( + mirror::ObjectArray<mirror::Object>::Alloc(self, annotation_array_class.Get(), size))); + if (result.Get() == nullptr) { + return nullptr; + } + + uint32_t dest_index = 0; + for (uint32_t i = 0; i < size; ++i) { + const AnnotationItem* annotation_item = GetAnnotationItem(annotation_set, i); + if (annotation_item->visibility_ != visibility) { + continue; + } + const uint8_t* annotation = annotation_item->annotation_; + mirror::Object* annotation_obj = ProcessEncodedAnnotation(klass, &annotation); + if (annotation_obj != nullptr) { + result->SetWithoutChecks<false>(dest_index, annotation_obj); + ++dest_index; + } + } + + if (dest_index == size) { + return result.Get(); + } + + mirror::ObjectArray<mirror::Object>* trimmed_result = + mirror::ObjectArray<mirror::Object>::Alloc(self, annotation_array_class.Get(), dest_index); + for (uint32_t i = 0; i < dest_index; ++i) { + mirror::Object* obj = result->GetWithoutChecks(i); + trimmed_result->SetWithoutChecks<false>(i, obj); + } + + return trimmed_result; +} + +mirror::ObjectArray<mirror::Object>* DexFile::ProcessAnnotationSetRefList( + Handle<mirror::Class> klass, const AnnotationSetRefList* set_ref_list, uint32_t size) const { + Thread* self = Thread::Current(); + ScopedObjectAccessUnchecked soa(self); + StackHandleScope<1> hs(self); + mirror::Class* annotation_array_class = + soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_annotation_Annotation__array); + mirror::Class* annotation_array_array_class = + Runtime::Current()->GetClassLinker()->FindArrayClass(self, &annotation_array_class); + Handle<mirror::ObjectArray<mirror::Object>> annotation_array_array(hs.NewHandle( + mirror::ObjectArray<mirror::Object>::Alloc(self, annotation_array_array_class, size))); + if (annotation_array_array.Get() == nullptr) { + LOG(ERROR) << "Annotation set ref array allocation failed"; + return nullptr; + } + for (uint32_t index = 0; index < size; ++index) { + const AnnotationSetRefItem* set_ref_item = &set_ref_list->list_[index]; + const AnnotationSetItem* set_item = GetSetRefItemItem(set_ref_item); + mirror::Object* annotation_set = ProcessAnnotationSet(klass, set_item, kDexVisibilityRuntime); + if (annotation_set == nullptr) { + return nullptr; + } + annotation_array_array->SetWithoutChecks<false>(index, annotation_set); + } + return annotation_array_array.Get(); +} + +bool DexFile::ProcessAnnotationValue(Handle<mirror::Class> klass, const uint8_t** annotation_ptr, + AnnotationValue* annotation_value, Handle<mirror::Class> array_class, + DexFile::AnnotationResultStyle result_style) const { + Thread* self = Thread::Current(); + mirror::Object* element_object = nullptr; + bool set_object = false; + Primitive::Type primitive_type = Primitive::kPrimVoid; + const uint8_t* annotation = *annotation_ptr; + uint8_t header_byte = *(annotation++); + uint8_t value_type = header_byte & kDexAnnotationValueTypeMask; + uint8_t value_arg = header_byte >> kDexAnnotationValueArgShift; + int32_t width = value_arg + 1; + annotation_value->type_ = value_type; + + switch (value_type) { + case kDexAnnotationByte: + annotation_value->value_.SetB(static_cast<int8_t>(ReadSignedInt(annotation, value_arg))); + primitive_type = Primitive::kPrimByte; + break; + case kDexAnnotationShort: + annotation_value->value_.SetS(static_cast<int16_t>(ReadSignedInt(annotation, value_arg))); + primitive_type = Primitive::kPrimShort; + break; + case kDexAnnotationChar: + annotation_value->value_.SetC(static_cast<uint16_t>(ReadUnsignedInt(annotation, value_arg, + false))); + primitive_type = Primitive::kPrimChar; + break; + case kDexAnnotationInt: + annotation_value->value_.SetI(ReadSignedInt(annotation, value_arg)); + primitive_type = Primitive::kPrimInt; + break; + case kDexAnnotationLong: + annotation_value->value_.SetJ(ReadSignedLong(annotation, value_arg)); + primitive_type = Primitive::kPrimLong; + break; + case kDexAnnotationFloat: + annotation_value->value_.SetI(ReadUnsignedInt(annotation, value_arg, true)); + primitive_type = Primitive::kPrimFloat; + break; + case kDexAnnotationDouble: + annotation_value->value_.SetJ(ReadUnsignedLong(annotation, value_arg, true)); + primitive_type = Primitive::kPrimDouble; + break; + case kDexAnnotationBoolean: + annotation_value->value_.SetZ(value_arg != 0); + primitive_type = Primitive::kPrimBoolean; + width = 0; + break; + case kDexAnnotationString: { + uint32_t index = ReadUnsignedInt(annotation, value_arg, false); + if (result_style == kAllRaw) { + annotation_value->value_.SetI(index); + } else { + StackHandleScope<1> hs(self); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); + element_object = Runtime::Current()->GetClassLinker()->ResolveString( + klass->GetDexFile(), index, dex_cache); + set_object = true; + if (element_object == nullptr) { + return false; + } + } + break; + } + case kDexAnnotationType: { + uint32_t index = ReadUnsignedInt(annotation, value_arg, false); + if (result_style == kAllRaw) { + annotation_value->value_.SetI(index); + } else { + element_object = Runtime::Current()->GetClassLinker()->ResolveType( + klass->GetDexFile(), index, klass.Get()); + set_object = true; + if (element_object == nullptr) { + self->ClearException(); + const char* msg = StringByTypeIdx(index); + self->ThrowNewException("Ljava/lang/TypeNotPresentException;", msg); + } + } + break; + } + case kDexAnnotationMethod: { + uint32_t index = ReadUnsignedInt(annotation, value_arg, false); + if (result_style == kAllRaw) { + annotation_value->value_.SetI(index); + } else { + StackHandleScope<2> hs(self); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader())); + ArtMethod* method = Runtime::Current()->GetClassLinker()->ResolveMethodWithoutInvokeType( + klass->GetDexFile(), index, dex_cache, class_loader); + if (method == nullptr) { + return false; + } + set_object = true; + if (method->IsConstructor()) { + element_object = mirror::Constructor::CreateFromArtMethod(self, method); + } else { + element_object = mirror::Method::CreateFromArtMethod(self, method); + } + if (element_object == nullptr) { + return false; + } + } + break; + } + case kDexAnnotationField: { + uint32_t index = ReadUnsignedInt(annotation, value_arg, false); + if (result_style == kAllRaw) { + annotation_value->value_.SetI(index); + } else { + StackHandleScope<2> hs(self); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader())); + ArtField* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS( + klass->GetDexFile(), index, dex_cache, class_loader); + if (field == nullptr) { + return false; + } + set_object = true; + element_object = mirror::Field::CreateFromArtField(self, field, true); + if (element_object == nullptr) { + return false; + } + } + break; + } + case kDexAnnotationEnum: { + uint32_t index = ReadUnsignedInt(annotation, value_arg, false); + if (result_style == kAllRaw) { + annotation_value->value_.SetI(index); + } else { + StackHandleScope<3> hs(self); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader())); + ArtField* enum_field = Runtime::Current()->GetClassLinker()->ResolveField( + klass->GetDexFile(), index, dex_cache, class_loader, true); + Handle<mirror::Class> field_class(hs.NewHandle(enum_field->GetDeclaringClass())); + if (enum_field == nullptr) { + return false; + } else { + Runtime::Current()->GetClassLinker()->EnsureInitialized(self, field_class, true, true); + element_object = enum_field->GetObject(field_class.Get()); + set_object = true; + } + } + break; + } + case kDexAnnotationArray: + if (result_style == kAllRaw || array_class.Get() == nullptr) { + return false; + } else { + ScopedObjectAccessUnchecked soa(self); + StackHandleScope<2> hs(self); + uint32_t size = DecodeUnsignedLeb128(&annotation); + Handle<mirror::Class> component_type(hs.NewHandle(array_class->GetComponentType())); + Handle<mirror::Array> new_array(hs.NewHandle(mirror::Array::Alloc<true>( + self, array_class.Get(), size, array_class->GetComponentSizeShift(), + Runtime::Current()->GetHeap()->GetCurrentAllocator()))); + if (new_array.Get() == nullptr) { + LOG(ERROR) << "Annotation element array allocation failed with size " << size; + return false; + } + AnnotationValue new_annotation_value; + for (uint32_t i = 0; i < size; ++i) { + if (!ProcessAnnotationValue(klass, &annotation, &new_annotation_value, component_type, + kPrimitivesOrObjects)) { + return false; + } + if (!component_type->IsPrimitive()) { + mirror::Object* obj = new_annotation_value.value_.GetL(); + new_array->AsObjectArray<mirror::Object>()->SetWithoutChecks<false>(i, obj); + } else { + switch (new_annotation_value.type_) { + case kDexAnnotationByte: + new_array->AsByteArray()->SetWithoutChecks<false>( + i, new_annotation_value.value_.GetB()); + break; + case kDexAnnotationShort: + new_array->AsShortArray()->SetWithoutChecks<false>( + i, new_annotation_value.value_.GetS()); + break; + case kDexAnnotationChar: + new_array->AsCharArray()->SetWithoutChecks<false>( + i, new_annotation_value.value_.GetC()); + break; + case kDexAnnotationInt: + new_array->AsIntArray()->SetWithoutChecks<false>( + i, new_annotation_value.value_.GetI()); + break; + case kDexAnnotationLong: + new_array->AsLongArray()->SetWithoutChecks<false>( + i, new_annotation_value.value_.GetJ()); + break; + case kDexAnnotationFloat: + new_array->AsFloatArray()->SetWithoutChecks<false>( + i, new_annotation_value.value_.GetF()); + break; + case kDexAnnotationDouble: + new_array->AsDoubleArray()->SetWithoutChecks<false>( + i, new_annotation_value.value_.GetD()); + break; + case kDexAnnotationBoolean: + new_array->AsBooleanArray()->SetWithoutChecks<false>( + i, new_annotation_value.value_.GetZ()); + break; + default: + LOG(FATAL) << "Found invalid annotation value type while building annotation array"; + return false; + } + } + } + element_object = new_array.Get(); + set_object = true; + width = 0; + } + break; + case kDexAnnotationAnnotation: + if (result_style == kAllRaw) { + return false; + } + element_object = ProcessEncodedAnnotation(klass, &annotation); + if (element_object == nullptr) { + return false; + } + set_object = true; + width = 0; + break; + case kDexAnnotationNull: + if (result_style == kAllRaw) { + annotation_value->value_.SetI(0); + } else { + CHECK(element_object == nullptr); + set_object = true; + } + width = 0; + break; + default: + LOG(ERROR) << StringPrintf("Bad annotation element value type 0x%02x", value_type); + return false; + } + + annotation += width; + *annotation_ptr = annotation; + + if (result_style == kAllObjects && primitive_type != Primitive::kPrimVoid) { + element_object = BoxPrimitive(primitive_type, annotation_value->value_); + set_object = true; + } + + if (set_object) { + annotation_value->value_.SetL(element_object); + } + + return true; +} + +mirror::Object* DexFile::ProcessEncodedAnnotation(Handle<mirror::Class> klass, + const uint8_t** annotation) const { + uint32_t type_index = DecodeUnsignedLeb128(annotation); + uint32_t size = DecodeUnsignedLeb128(annotation); + + Thread* self = Thread::Current(); + ScopedObjectAccessUnchecked soa(self); + StackHandleScope<2> hs(self); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + Handle<mirror::Class> annotation_class(hs.NewHandle( + class_linker->ResolveType(klass->GetDexFile(), type_index, klass.Get()))); + if (annotation_class.Get() == nullptr) { + LOG(INFO) << "Unable to resolve " << PrettyClass(klass.Get()) << " annotation class " + << type_index; + DCHECK(Thread::Current()->IsExceptionPending()); + Thread::Current()->ClearException(); + return nullptr; + } + + mirror::Class* annotation_member_class = + soa.Decode<mirror::Class*>(WellKnownClasses::libcore_reflect_AnnotationMember); + mirror::Class* annotation_member_array_class = + class_linker->FindArrayClass(self, &annotation_member_class); + mirror::ObjectArray<mirror::Object>* element_array = nullptr; + + if (size > 0) { + element_array = + mirror::ObjectArray<mirror::Object>::Alloc(self, annotation_member_array_class, size); + if (element_array == nullptr) { + LOG(ERROR) << "Failed to allocate annotation member array (" << size << " elements)"; + return nullptr; + } + } + + Handle<mirror::ObjectArray<mirror::Object>> h_element_array(hs.NewHandle(element_array)); + for (uint32_t i = 0; i < size; ++i) { + mirror::Object* new_member = CreateAnnotationMember(klass, annotation_class, annotation); + if (new_member == nullptr) { + return nullptr; + } + h_element_array->SetWithoutChecks<false>(i, new_member); + } + + JValue result; + ArtMethod* create_annotation_method = + soa.DecodeMethod(WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation); + uint32_t args[2] = { static_cast<uint32_t>(reinterpret_cast<uintptr_t>(annotation_class.Get())), + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(h_element_array.Get())) }; + create_annotation_method->Invoke(self, args, sizeof(args), &result, "LLL"); + if (self->IsExceptionPending()) { + LOG(INFO) << "Exception in AnnotationFactory.createAnnotation"; + return nullptr; + } + + return result.GetL(); +} + +const DexFile::AnnotationItem* DexFile::SearchAnnotationSet(const AnnotationSetItem* annotation_set, + const char* descriptor, uint32_t visibility) const { + const AnnotationItem* result = nullptr; + for (uint32_t i = 0; i < annotation_set->size_; ++i) { + const AnnotationItem* annotation_item = GetAnnotationItem(annotation_set, i); + if (annotation_item->visibility_ != visibility) { + continue; + } + const uint8_t* annotation = annotation_item->annotation_; + uint32_t type_index = DecodeUnsignedLeb128(&annotation); + + if (strcmp(descriptor, StringByTypeIdx(type_index)) == 0) { + result = annotation_item; + break; + } + } + return result; +} + +const uint8_t* DexFile::SearchEncodedAnnotation(const uint8_t* annotation, const char* name) const { + DecodeUnsignedLeb128(&annotation); // unused type_index + uint32_t size = DecodeUnsignedLeb128(&annotation); + + while (size != 0) { + uint32_t element_name_index = DecodeUnsignedLeb128(&annotation); + const char* element_name = GetStringData(GetStringId(element_name_index)); + if (strcmp(name, element_name) == 0) { + return annotation; + } + SkipAnnotationValue(&annotation); + size--; + } + return nullptr; +} + +bool DexFile::SkipAnnotationValue(const uint8_t** annotation_ptr) const { + const uint8_t* annotation = *annotation_ptr; + uint8_t header_byte = *(annotation++); + uint8_t value_type = header_byte & kDexAnnotationValueTypeMask; + uint8_t value_arg = header_byte >> kDexAnnotationValueArgShift; + int32_t width = value_arg + 1; + + switch (value_type) { + case kDexAnnotationByte: + case kDexAnnotationShort: + case kDexAnnotationChar: + case kDexAnnotationInt: + case kDexAnnotationLong: + case kDexAnnotationFloat: + case kDexAnnotationDouble: + case kDexAnnotationString: + case kDexAnnotationType: + case kDexAnnotationMethod: + case kDexAnnotationField: + case kDexAnnotationEnum: + break; + case kDexAnnotationArray: + { + uint32_t size = DecodeUnsignedLeb128(&annotation); + while (size--) { + if (!SkipAnnotationValue(&annotation)) { + return false; + } + } + width = 0; + break; + } + case kDexAnnotationAnnotation: + { + DecodeUnsignedLeb128(&annotation); // unused type_index + uint32_t size = DecodeUnsignedLeb128(&annotation); + while (size--) { + DecodeUnsignedLeb128(&annotation); // unused element_name_index + if (!SkipAnnotationValue(&annotation)) { + return false; + } + } + width = 0; + break; + } + case kDexAnnotationBoolean: + case kDexAnnotationNull: + width = 0; + break; + default: + LOG(FATAL) << StringPrintf("Bad annotation element value byte 0x%02x", value_type); + return false; + } + + annotation += width; + *annotation_ptr = annotation; + return true; +} + std::ostream& operator<<(std::ostream& os, const DexFile& dex_file) { os << StringPrintf("[DexFile: %s dex-checksum=%08x location-checksum=%08x %p-%p]", dex_file.GetLocation().c_str(), @@ -1127,60 +2042,6 @@ void ClassDataItemIterator::ReadClassDataMethod() { } } -// Read a signed integer. "zwidth" is the zero-based byte count. -static int32_t ReadSignedInt(const uint8_t* ptr, int zwidth) { - int32_t val = 0; - for (int i = zwidth; i >= 0; --i) { - val = ((uint32_t)val >> 8) | (((int32_t)*ptr++) << 24); - } - val >>= (3 - zwidth) * 8; - return val; -} - -// Read an unsigned integer. "zwidth" is the zero-based byte count, -// "fill_on_right" indicates which side we want to zero-fill from. -static uint32_t ReadUnsignedInt(const uint8_t* ptr, int zwidth, bool fill_on_right) { - uint32_t val = 0; - if (!fill_on_right) { - for (int i = zwidth; i >= 0; --i) { - val = (val >> 8) | (((uint32_t)*ptr++) << 24); - } - val >>= (3 - zwidth) * 8; - } else { - for (int i = zwidth; i >= 0; --i) { - val = (val >> 8) | (((uint32_t)*ptr++) << 24); - } - } - return val; -} - -// Read a signed long. "zwidth" is the zero-based byte count. -static int64_t ReadSignedLong(const uint8_t* ptr, int zwidth) { - int64_t val = 0; - for (int i = zwidth; i >= 0; --i) { - val = ((uint64_t)val >> 8) | (((int64_t)*ptr++) << 56); - } - val >>= (7 - zwidth) * 8; - return val; -} - -// Read an unsigned long. "zwidth" is the zero-based byte count, -// "fill_on_right" indicates which side we want to zero-fill from. -static uint64_t ReadUnsignedLong(const uint8_t* ptr, int zwidth, bool fill_on_right) { - uint64_t val = 0; - if (!fill_on_right) { - for (int i = zwidth; i >= 0; --i) { - val = (val >> 8) | (((uint64_t)*ptr++) << 56); - } - val >>= (7 - zwidth) * 8; - } else { - for (int i = zwidth; i >= 0; --i) { - val = (val >> 8) | (((uint64_t)*ptr++) << 56); - } - } - return val; -} - EncodedStaticFieldValueIterator::EncodedStaticFieldValueIterator( const DexFile& dex_file, Handle<mirror::DexCache>* dex_cache, Handle<mirror::ClassLoader>* class_loader, ClassLinker* linker, diff --git a/runtime/dex_file.h b/runtime/dex_file.h index a1ddbc7d61..89283213a8 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -28,6 +28,8 @@ #include "globals.h" #include "invoke_type.h" #include "jni.h" +#include "jvalue.h" +#include "mirror/object_array.h" #include "modifiers.h" #include "utf.h" @@ -384,6 +386,17 @@ class DexFile { DISALLOW_COPY_AND_ASSIGN(AnnotationItem); }; + struct AnnotationValue { + JValue value_; + uint8_t type_; + }; + + enum AnnotationResultStyle { // private + kAllObjects, + kPrimitivesOrObjects, + kAllRaw + }; + // Returns the checksum of a file for comparison with GetLocationChecksum(). // For .dex files, this is the header checksum. // For zip files, this is the classes.dex zip entry CRC32 checksum. @@ -643,6 +656,11 @@ class DexFile { return StringDataByIdx(method_id.name_idx_); } + // Returns the shorty of a method by its index. + const char* GetMethodShorty(uint32_t idx) const { + return StringDataByIdx(GetProtoId(GetMethodId(idx).proto_idx_).shorty_idx_); + } + // Returns the shorty of a method id. const char* GetMethodShorty(const MethodId& method_id) const { return StringDataByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_); @@ -812,6 +830,187 @@ class DexFile { static bool LineNumForPcCb(void* context, uint32_t address, uint32_t line_num); + const AnnotationsDirectoryItem* GetAnnotationsDirectory(const ClassDef& class_def) const { + if (class_def.annotations_off_ == 0) { + return nullptr; + } else { + return reinterpret_cast<const AnnotationsDirectoryItem*>(begin_ + class_def.annotations_off_); + } + } + + const AnnotationSetItem* GetClassAnnotationSet(const AnnotationsDirectoryItem* anno_dir) const { + if (anno_dir->class_annotations_off_ == 0) { + return nullptr; + } else { + return reinterpret_cast<const AnnotationSetItem*>(begin_ + anno_dir->class_annotations_off_); + } + } + + const FieldAnnotationsItem* GetFieldAnnotations(const AnnotationsDirectoryItem* anno_dir) const { + if (anno_dir->fields_size_ == 0) { + return nullptr; + } else { + return reinterpret_cast<const FieldAnnotationsItem*>(&anno_dir[1]); + } + } + + const MethodAnnotationsItem* GetMethodAnnotations(const AnnotationsDirectoryItem* anno_dir) + const { + if (anno_dir->methods_size_ == 0) { + return nullptr; + } else { + // Skip past the header and field annotations. + const uint8_t* addr = reinterpret_cast<const uint8_t*>(&anno_dir[1]); + addr += anno_dir->fields_size_ * sizeof(FieldAnnotationsItem); + return reinterpret_cast<const MethodAnnotationsItem*>(addr); + } + } + + const ParameterAnnotationsItem* GetParameterAnnotations(const AnnotationsDirectoryItem* anno_dir) + const { + if (anno_dir->parameters_size_ == 0) { + return nullptr; + } else { + // Skip past the header, field annotations, and method annotations. + const uint8_t* addr = reinterpret_cast<const uint8_t*>(&anno_dir[1]); + addr += anno_dir->fields_size_ * sizeof(FieldAnnotationsItem); + addr += anno_dir->methods_size_ * sizeof(MethodAnnotationsItem); + return reinterpret_cast<const ParameterAnnotationsItem*>(addr); + } + } + + const AnnotationSetItem* GetFieldAnnotationSetItem(const FieldAnnotationsItem& anno_item) const { + uint32_t offset = anno_item.annotations_off_; + if (offset == 0) { + return nullptr; + } else { + return reinterpret_cast<const AnnotationSetItem*>(begin_ + offset); + } + } + + const AnnotationSetItem* GetMethodAnnotationSetItem(const MethodAnnotationsItem& anno_item) + const { + uint32_t offset = anno_item.annotations_off_; + if (offset == 0) { + return nullptr; + } else { + return reinterpret_cast<const AnnotationSetItem*>(begin_ + offset); + } + } + + const AnnotationSetRefList* GetParameterAnnotationSetRefList( + const ParameterAnnotationsItem* anno_item) const { + uint32_t offset = anno_item->annotations_off_; + if (offset == 0) { + return nullptr; + } + return reinterpret_cast<const AnnotationSetRefList*>(begin_ + offset); + } + + const AnnotationItem* GetAnnotationItem(const AnnotationSetItem* set_item, uint32_t index) const { + DCHECK_LE(index, set_item->size_); + uint32_t offset = set_item->entries_[index]; + if (offset == 0) { + return nullptr; + } else { + return reinterpret_cast<const AnnotationItem*>(begin_ + offset); + } + } + + const AnnotationSetItem* GetSetRefItemItem(const AnnotationSetRefItem* anno_item) const { + uint32_t offset = anno_item->annotations_off_; + if (offset == 0) { + return nullptr; + } + return reinterpret_cast<const AnnotationSetItem*>(begin_ + offset); + } + + const AnnotationSetItem* FindAnnotationSetForField(ArtField* field) const + SHARED_REQUIRES(Locks::mutator_lock_); + mirror::Object* GetAnnotationForField(ArtField* field, Handle<mirror::Class> annotation_class) + const SHARED_REQUIRES(Locks::mutator_lock_); + mirror::ObjectArray<mirror::Object>* GetAnnotationsForField(ArtField* field) const + SHARED_REQUIRES(Locks::mutator_lock_); + mirror::ObjectArray<mirror::Object>* GetSignatureAnnotationForField(ArtField* field) const + SHARED_REQUIRES(Locks::mutator_lock_); + bool IsFieldAnnotationPresent(ArtField* field, Handle<mirror::Class> annotation_class) const + SHARED_REQUIRES(Locks::mutator_lock_); + + const AnnotationSetItem* FindAnnotationSetForMethod(ArtMethod* method) const + SHARED_REQUIRES(Locks::mutator_lock_); + const ParameterAnnotationsItem* FindAnnotationsItemForMethod(ArtMethod* method) const + SHARED_REQUIRES(Locks::mutator_lock_); + mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) const + SHARED_REQUIRES(Locks::mutator_lock_); + mirror::Object* GetAnnotationForMethod(ArtMethod* method, Handle<mirror::Class> annotation_class) + const SHARED_REQUIRES(Locks::mutator_lock_); + mirror::ObjectArray<mirror::Object>* GetAnnotationsForMethod(ArtMethod* method) const + SHARED_REQUIRES(Locks::mutator_lock_); + mirror::ObjectArray<mirror::Object>* GetExceptionTypesForMethod(ArtMethod* method) const + SHARED_REQUIRES(Locks::mutator_lock_); + mirror::ObjectArray<mirror::Object>* GetParameterAnnotations(ArtMethod* method) const + SHARED_REQUIRES(Locks::mutator_lock_); + bool IsMethodAnnotationPresent(ArtMethod* method, Handle<mirror::Class> annotation_class) const + SHARED_REQUIRES(Locks::mutator_lock_); + + const AnnotationSetItem* FindAnnotationSetForClass(Handle<mirror::Class> klass) const + SHARED_REQUIRES(Locks::mutator_lock_); + mirror::Object* GetAnnotationForClass(Handle<mirror::Class> klass, + Handle<mirror::Class> annotation_class) const + SHARED_REQUIRES(Locks::mutator_lock_); + mirror::ObjectArray<mirror::Object>* GetAnnotationsForClass(Handle<mirror::Class> klass) const + SHARED_REQUIRES(Locks::mutator_lock_); + bool IsClassAnnotationPresent(Handle<mirror::Class> klass, Handle<mirror::Class> annotation_class) + const SHARED_REQUIRES(Locks::mutator_lock_); + + mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass, + Handle<mirror::Class> annotation_class, + const uint8_t** annotation) const + SHARED_REQUIRES(Locks::mutator_lock_); + const AnnotationItem* GetAnnotationItemFromAnnotationSet(Handle<mirror::Class> klass, + const AnnotationSetItem* annotation_set, + uint32_t visibility, + Handle<mirror::Class> annotation_class) + const SHARED_REQUIRES(Locks::mutator_lock_); + mirror::Object* GetAnnotationObjectFromAnnotationSet(Handle<mirror::Class> klass, + const AnnotationSetItem* annotation_set, + uint32_t visibility, + Handle<mirror::Class> annotation_class) const + SHARED_REQUIRES(Locks::mutator_lock_); + mirror::Object* GetAnnotationValue(Handle<mirror::Class> klass, + const AnnotationItem* annotation_item, + const char* annotation_name, + Handle<mirror::Class> array_class, + uint32_t expected_type) const + SHARED_REQUIRES(Locks::mutator_lock_); + mirror::ObjectArray<mirror::Object>* GetSignatureValue(Handle<mirror::Class> klass, + const AnnotationSetItem* annotation_set) + const SHARED_REQUIRES(Locks::mutator_lock_); + mirror::ObjectArray<mirror::Object>* GetThrowsValue(Handle<mirror::Class> klass, + const AnnotationSetItem* annotation_set) const + SHARED_REQUIRES(Locks::mutator_lock_); + mirror::ObjectArray<mirror::Object>* ProcessAnnotationSet(Handle<mirror::Class> klass, + const AnnotationSetItem* annotation_set, + uint32_t visibility) const + SHARED_REQUIRES(Locks::mutator_lock_); + mirror::ObjectArray<mirror::Object>* ProcessAnnotationSetRefList(Handle<mirror::Class> klass, + const AnnotationSetRefList* set_ref_list, uint32_t size) const + SHARED_REQUIRES(Locks::mutator_lock_); + bool ProcessAnnotationValue(Handle<mirror::Class> klass, const uint8_t** annotation_ptr, + AnnotationValue* annotation_value, Handle<mirror::Class> return_class, + DexFile::AnnotationResultStyle result_style) const + SHARED_REQUIRES(Locks::mutator_lock_); + mirror::Object* ProcessEncodedAnnotation(Handle<mirror::Class> klass, + const uint8_t** annotation) const + SHARED_REQUIRES(Locks::mutator_lock_); + const AnnotationItem* SearchAnnotationSet(const AnnotationSetItem* annotation_set, + const char* descriptor, uint32_t visibility) const + SHARED_REQUIRES(Locks::mutator_lock_); + const uint8_t* SearchEncodedAnnotation(const uint8_t* annotation, const char* name) const + SHARED_REQUIRES(Locks::mutator_lock_); + bool SkipAnnotationValue(const uint8_t** annotation_ptr) const + SHARED_REQUIRES(Locks::mutator_lock_); + // Debug info opcodes and constants enum { DBG_END_SEQUENCE = 0x00, diff --git a/runtime/gc/collector/mark_sweep-inl.h b/runtime/gc/collector/mark_sweep-inl.h index a3cc83132f..56edcc9d09 100644 --- a/runtime/gc/collector/mark_sweep-inl.h +++ b/runtime/gc/collector/mark_sweep-inl.h @@ -35,10 +35,17 @@ inline void MarkSweep::ScanObjectVisit(mirror::Object* obj, const MarkVisitor& v obj->VisitReferences(visitor, ref_visitor); if (kCountScannedTypes) { mirror::Class* klass = obj->GetClass<kVerifyNone>(); - if (UNLIKELY(klass == mirror::Class::GetJavaLangClass())) { + uint32_t class_flags = klass->GetClassFlags(); + if ((class_flags & mirror::kClassFlagNoReferenceFields) != 0) { + ++no_reference_class_count_; + } else if (class_flags == mirror::kClassFlagNormal) { + ++normal_count_; + } else if (class_flags == mirror::kClassFlagObjectArray) { + ++object_array_count_; + } else if (class_flags == mirror::kClassFlagClass) { ++class_count_; - } else if (UNLIKELY(klass->IsArrayClass<kVerifyNone>())) { - ++array_count_; + } else if ((class_flags & mirror::kClassFlagReference) != 0) { + ++reference_count_; } else { ++other_count_; } diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index b0a8a5bf2b..7ddc7ccc63 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -70,7 +70,6 @@ static constexpr bool kParallelProcessMarkStack = true; static constexpr bool kProfileLargeObjects = false; static constexpr bool kMeasureOverhead = false; static constexpr bool kCountTasks = false; -static constexpr bool kCountJavaLangRefs = false; static constexpr bool kCountMarkedObjects = false; // Turn off kCheckLocks when profiling the GC since it slows the GC down by up to 40%. @@ -114,15 +113,17 @@ void MarkSweep::InitializePhase() { mark_stack_ = heap_->GetMarkStack(); DCHECK(mark_stack_ != nullptr); immune_region_.Reset(); + no_reference_class_count_.StoreRelaxed(0); + normal_count_.StoreRelaxed(0); class_count_.StoreRelaxed(0); - array_count_.StoreRelaxed(0); + object_array_count_.StoreRelaxed(0); other_count_.StoreRelaxed(0); + reference_count_.StoreRelaxed(0); large_object_test_.StoreRelaxed(0); large_object_mark_.StoreRelaxed(0); overhead_time_ .StoreRelaxed(0); work_chunks_created_.StoreRelaxed(0); work_chunks_deleted_.StoreRelaxed(0); - reference_count_.StoreRelaxed(0); mark_null_count_.StoreRelaxed(0); mark_immune_count_.StoreRelaxed(0); mark_fastpath_count_.StoreRelaxed(0); @@ -1265,9 +1266,6 @@ void MarkSweep::SweepLargeObjects(bool swap_bitmaps) { // Process the "referent" field in a java.lang.ref.Reference. If the referent has not yet been // marked, put it on the appropriate list in the heap for later processing. void MarkSweep::DelayReferenceReferent(mirror::Class* klass, mirror::Reference* ref) { - if (kCountJavaLangRefs) { - ++reference_count_; - } heap_->GetReferenceProcessor()->DelayReferenceReferent(klass, ref, this); } @@ -1386,8 +1384,14 @@ inline mirror::Object* MarkSweep::IsMarked(mirror::Object* object) { void MarkSweep::FinishPhase() { TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); if (kCountScannedTypes) { - VLOG(gc) << "MarkSweep scanned classes=" << class_count_.LoadRelaxed() - << " arrays=" << array_count_.LoadRelaxed() << " other=" << other_count_.LoadRelaxed(); + VLOG(gc) + << "MarkSweep scanned" + << " no reference objects=" << no_reference_class_count_.LoadRelaxed() + << " normal objects=" << normal_count_.LoadRelaxed() + << " classes=" << class_count_.LoadRelaxed() + << " object arrays=" << object_array_count_.LoadRelaxed() + << " references=" << reference_count_.LoadRelaxed() + << " other=" << other_count_.LoadRelaxed(); } if (kCountTasks) { VLOG(gc) << "Total number of work chunks allocated: " << work_chunks_created_.LoadRelaxed(); @@ -1399,9 +1403,6 @@ void MarkSweep::FinishPhase() { VLOG(gc) << "Large objects tested " << large_object_test_.LoadRelaxed() << " marked " << large_object_mark_.LoadRelaxed(); } - if (kCountJavaLangRefs) { - VLOG(gc) << "References scanned " << reference_count_.LoadRelaxed(); - } if (kCountMarkedObjects) { VLOG(gc) << "Marked: null=" << mark_null_count_.LoadRelaxed() << " immune=" << mark_immune_count_.LoadRelaxed() diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index 8bd1dc7cd5..371bba531d 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -245,7 +245,7 @@ class MarkSweep : public GarbageCollector { void RevokeAllThreadLocalBuffers(); // Whether or not we count how many of each type of object were scanned. - static const bool kCountScannedTypes = false; + static constexpr bool kCountScannedTypes = false; // Current space, we check this space first to avoid searching for the appropriate space for an // object. @@ -260,18 +260,23 @@ class MarkSweep : public GarbageCollector { // Parallel finger. AtomicInteger atomic_finger_; + + AtomicInteger no_reference_class_count_; + AtomicInteger normal_count_; // Number of classes scanned, if kCountScannedTypes. AtomicInteger class_count_; - // Number of arrays scanned, if kCountScannedTypes. - AtomicInteger array_count_; + // Number of object arrays scanned, if kCountScannedTypes. + AtomicInteger object_array_count_; // Number of non-class/arrays scanned, if kCountScannedTypes. AtomicInteger other_count_; + // Number of java.lang.ref.Reference instances. + AtomicInteger reference_count_; + AtomicInteger large_object_test_; AtomicInteger large_object_mark_; AtomicInteger overhead_time_; AtomicInteger work_chunks_created_; AtomicInteger work_chunks_deleted_; - AtomicInteger reference_count_; AtomicInteger mark_null_count_; AtomicInteger mark_immune_count_; AtomicInteger mark_fastpath_count_; diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index d7f918b4ff..b8c44781a3 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -3707,7 +3707,7 @@ void Heap::AddModUnionTable(accounting::ModUnionTable* mod_union_table) { void Heap::CheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count) { CHECK(c == nullptr || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) || - (c->IsVariableSize() || c->GetObjectSize() == byte_count)); + (c->IsVariableSize() || c->GetObjectSize() == byte_count)) << c->GetClassFlags(); CHECK_GE(byte_count, sizeof(mirror::Object)); } diff --git a/runtime/image.cc b/runtime/image.cc index 2586959e55..8df17c6929 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -24,7 +24,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '1', '9', '\0' }; +const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '0', '\0' }; ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index cd678f670b..b2c6e4da12 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -547,6 +547,7 @@ inline uint32_t Class::GetAccessFlags() { inline String* Class::GetName() { return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(Class, name_)); } + inline void Class::SetName(String* name) { if (Runtime::Current()->IsActiveTransaction()) { SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, name_), name); @@ -784,9 +785,17 @@ inline void Class::InitializeClassVisitor::operator()( inline void Class::SetAccessFlags(uint32_t new_access_flags) { // Called inside a transaction when setting pre-verified flag during boot image compilation. if (Runtime::Current()->IsActiveTransaction()) { - SetField32<true>(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), new_access_flags); + SetField32<true>(AccessFlagsOffset(), new_access_flags); + } else { + SetField32<false>(AccessFlagsOffset(), new_access_flags); + } +} + +inline void Class::SetClassFlags(uint32_t new_flags) { + if (Runtime::Current()->IsActiveTransaction()) { + SetField32<true>(OFFSET_OF_OBJECT_MEMBER(Class, class_flags_), new_flags); } else { - SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), new_access_flags); + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_flags_), new_flags); } } diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 055b3e5110..949ff5fe3d 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -44,6 +44,7 @@ void Class::SetClassClass(Class* java_lang_Class) { << java_lang_Class_.Read() << " " << java_lang_Class; CHECK(java_lang_Class != nullptr); + java_lang_Class->SetClassFlags(java_lang_Class->GetClassFlags() | mirror::kClassFlagClass); java_lang_Class_ = GcRoot<Class>(java_lang_Class); } @@ -504,6 +505,16 @@ ArtMethod* Class::FindDeclaredVirtualMethod(const DexCache* dex_cache, uint32_t return nullptr; } +ArtMethod* Class::FindDeclaredVirtualMethodByName(const StringPiece& name, size_t pointer_size) { + for (auto& method : GetVirtualMethods(pointer_size)) { + ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size); + if (name == np_method->GetName()) { + return &method; + } + } + return nullptr; +} + ArtMethod* Class::FindVirtualMethod( const StringPiece& name, const StringPiece& signature, size_t pointer_size) { for (Class* klass = this; klass != nullptr; klass = klass->GetSuperClass()) { diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 3f375be9ca..ef257ae457 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -19,6 +19,7 @@ #include "base/iteration_range.h" #include "dex_file.h" +#include "class_flags.h" #include "gc_root.h" #include "gc/allocator_type.h" #include "invoke_type.h" @@ -201,6 +202,12 @@ class MANAGED Class FINAL : public Object { return OFFSET_OF_OBJECT_MEMBER(Class, access_flags_); } + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + ALWAYS_INLINE uint32_t GetClassFlags() SHARED_REQUIRES(Locks::mutator_lock_) { + return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, class_flags_)); + } + void SetClassFlags(uint32_t new_flags) SHARED_REQUIRES(Locks::mutator_lock_); + void SetAccessFlags(uint32_t new_access_flags) SHARED_REQUIRES(Locks::mutator_lock_); // Returns true if the class is an interface. @@ -228,21 +235,19 @@ class MANAGED Class FINAL : public Object { } ALWAYS_INLINE bool IsStringClass() SHARED_REQUIRES(Locks::mutator_lock_) { - return (GetField32(AccessFlagsOffset()) & kAccClassIsStringClass) != 0; + return (GetClassFlags() & kClassFlagString) != 0; } ALWAYS_INLINE void SetStringClass() SHARED_REQUIRES(Locks::mutator_lock_) { - uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_)); - SetAccessFlags(flags | kAccClassIsStringClass); + SetClassFlags(GetClassFlags() | kClassFlagString | kClassFlagNoReferenceFields); } ALWAYS_INLINE bool IsClassLoaderClass() SHARED_REQUIRES(Locks::mutator_lock_) { - return (GetField32(AccessFlagsOffset()) & kAccClassIsClassLoaderClass) != 0; + return (GetClassFlags() & kClassFlagClassLoader) != 0; } ALWAYS_INLINE void SetClassLoaderClass() SHARED_REQUIRES(Locks::mutator_lock_) { - uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_)); - SetAccessFlags(flags | kAccClassIsClassLoaderClass); + SetClassFlags(GetClassFlags() | kClassFlagClassLoader); } // Returns true if the class is abstract. @@ -272,27 +277,27 @@ class MANAGED Class FINAL : public Object { template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsTypeOfReferenceClass() SHARED_REQUIRES(Locks::mutator_lock_) { - return (GetAccessFlags<kVerifyFlags>() & kAccClassIsReference) != 0; + return (GetClassFlags<kVerifyFlags>() & kClassFlagReference) != 0; } template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsWeakReferenceClass() SHARED_REQUIRES(Locks::mutator_lock_) { - return (GetAccessFlags<kVerifyFlags>() & kAccClassIsWeakReference) != 0; + return (GetClassFlags<kVerifyFlags>() & kClassFlagWeakReference) != 0; } template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsSoftReferenceClass() SHARED_REQUIRES(Locks::mutator_lock_) { - return (GetAccessFlags<kVerifyFlags>() & kAccReferenceFlagsMask) == kAccClassIsReference; + return (GetClassFlags<kVerifyFlags>() & kClassFlagSoftReference) != 0; } template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsFinalizerReferenceClass() SHARED_REQUIRES(Locks::mutator_lock_) { - return (GetAccessFlags<kVerifyFlags>() & kAccClassIsFinalizerReference) != 0; + return (GetClassFlags<kVerifyFlags>() & kClassFlagFinalizerReference) != 0; } template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsPhantomReferenceClass() SHARED_REQUIRES(Locks::mutator_lock_) { - return (GetAccessFlags<kVerifyFlags>() & kAccClassIsPhantomReference) != 0; + return (GetClassFlags<kVerifyFlags>() & kClassFlagPhantomReference) != 0; } // Can references of this type be assigned to by things of another type? For non-array types @@ -826,6 +831,9 @@ class MANAGED Class FINAL : public Object { size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_); + ArtMethod* FindDeclaredVirtualMethodByName(const StringPiece& name, size_t pointer_size) + SHARED_REQUIRES(Locks::mutator_lock_); + ArtMethod* FindVirtualMethod(const StringPiece& name, const StringPiece& signature, size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_); @@ -862,7 +870,8 @@ class MANAGED Class FINAL : public Object { uint32_t NumInstanceFields() SHARED_REQUIRES(Locks::mutator_lock_); ArtField* GetInstanceField(uint32_t i) SHARED_REQUIRES(Locks::mutator_lock_); - // Returns the number of instance fields containing reference types. + // Returns the number of instance fields containing reference types not counting fields in the + // super class. uint32_t NumReferenceInstanceFields() SHARED_REQUIRES(Locks::mutator_lock_) { DCHECK(IsResolved() || IsErroneous()); return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_)); @@ -1225,6 +1234,9 @@ class MANAGED Class FINAL : public Object { // length-prefixed array. uint64_t virtual_methods_; + // Class flags to help speed up visiting object references. + uint32_t class_flags_; + // Total size of the Class instance; used when allocating storage on gc heap. // See also object_size_. uint32_t class_size_; diff --git a/runtime/mirror/class_flags.h b/runtime/mirror/class_flags.h new file mode 100644 index 0000000000..6c15639177 --- /dev/null +++ b/runtime/mirror/class_flags.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_MIRROR_CLASS_FLAGS_H_ +#define ART_RUNTIME_MIRROR_CLASS_FLAGS_H_ + +#include <stdint.h> + +namespace art { +namespace mirror { + +// Object types stored in class to help GC with faster object marking. +static constexpr uint32_t kClassFlagNormal = 0x00000000; +// Only normal objects which have no reference fields, e.g. string or primitive array or normal +// class instance. +static constexpr uint32_t kClassFlagNoReferenceFields = 0x00000001; +static constexpr uint32_t kClassFlagString = 0x00000004; +static constexpr uint32_t kClassFlagObjectArray = 0x00000008; +static constexpr uint32_t kClassFlagClass = 0x00000010; + +// class is ClassLoader or one of its subclasses +static constexpr uint32_t kClassFlagClassLoader = 0x00000020; + +// class is a soft/weak/phantom ref +static constexpr uint32_t kClassFlagSoftReference = 0x00000040; +// class is a weak reference +static constexpr uint32_t kClassFlagWeakReference = 0x00000080; +// class is a finalizer reference +static constexpr uint32_t kClassFlagFinalizerReference = 0x00000100; +// class is a phantom reference +static constexpr uint32_t kClassFlagPhantomReference = 0x00000200; + +static constexpr uint32_t kClassFlagReference = + kClassFlagSoftReference | + kClassFlagWeakReference | + kClassFlagFinalizerReference | + kClassFlagPhantomReference; + +} // namespace mirror +} // namespace art + +#endif // ART_RUNTIME_MIRROR_CLASS_FLAGS_H_ + diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 586ae30d19..702a0f49ea 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -24,6 +24,7 @@ #include "atomic.h" #include "array-inl.h" #include "class.h" +#include "class_flags.h" #include "class_linker.h" #include "class_loader-inl.h" #include "lock_word-inl.h" @@ -1010,20 +1011,43 @@ inline void Object::VisitReferences(const Visitor& visitor, const JavaLangRefVisitor& ref_visitor) { mirror::Class* klass = GetClass<kVerifyFlags>(); visitor(this, ClassOffset(), false); - if (klass == Class::GetJavaLangClass()) { - AsClass<kVerifyNone>()->VisitReferences(klass, visitor); - } else if (klass->IsArrayClass() || klass->IsStringClass()) { - if (klass->IsObjectArrayClass<kVerifyNone>()) { - AsObjectArray<mirror::Object, kVerifyNone>()->VisitReferences(visitor); - } - } else if (klass->IsClassLoaderClass()) { - mirror::ClassLoader* class_loader = AsClassLoader<kVerifyFlags>(); - class_loader->VisitReferences<kVerifyFlags>(klass, visitor); - } else { + const uint32_t class_flags = klass->GetClassFlags<kVerifyNone>(); + if (LIKELY(class_flags == kClassFlagNormal)) { DCHECK(!klass->IsVariableSize()); VisitInstanceFieldsReferences(klass, visitor); - if (UNLIKELY(klass->IsTypeOfReferenceClass<kVerifyNone>())) { - ref_visitor(klass, AsReference()); + DCHECK(!klass->IsClassClass()); + } else { + if ((class_flags & kClassFlagNoReferenceFields) == 0) { + DCHECK(!klass->IsStringClass()); + if (class_flags == kClassFlagClass) { + DCHECK(klass->IsClassClass()); + AsClass<kVerifyNone>()->VisitReferences(klass, visitor); + } else if (class_flags == kClassFlagObjectArray) { + DCHECK(klass->IsObjectArrayClass()); + AsObjectArray<mirror::Object, kVerifyNone>()->VisitReferences(visitor); + } else if ((class_flags & kClassFlagReference) != 0) { + VisitInstanceFieldsReferences(klass, visitor); + ref_visitor(klass, AsReference()); + } else { + mirror::ClassLoader* const class_loader = AsClassLoader<kVerifyFlags>(); + class_loader->VisitReferences<kVerifyFlags>(klass, visitor); + } + } else if (kIsDebugBuild) { + CHECK(!klass->IsClassClass()); + CHECK(!klass->IsObjectArrayClass()); + // String still has instance fields for reflection purposes but these don't exist in + // actual string instances. + if (!klass->IsStringClass()) { + size_t total_reference_instance_fields = 0; + mirror::Class* super_class = klass; + do { + total_reference_instance_fields += super_class->NumReferenceInstanceFields(); + super_class = super_class->GetSuperClass(); + } while (super_class != nullptr); + // The only reference field should be the object's class. This field is handled at the + // beginning of the function. + CHECK_EQ(total_reference_instance_fields, 1u); + } } } } diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc index b6236b1eb3..45610dccc8 100644 --- a/runtime/mirror/string.cc +++ b/runtime/mirror/string.cc @@ -55,6 +55,7 @@ int32_t String::FastIndexOf(int32_t ch, int32_t start) { void String::SetClass(Class* java_lang_String) { CHECK(java_lang_String_.IsNull()); CHECK(java_lang_String != nullptr); + CHECK(java_lang_String->IsStringClass()); java_lang_String_ = GcRoot<Class>(java_lang_String); } diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index eb2e1f6977..fbee2d7bf3 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -157,10 +157,9 @@ class MANAGED String FINAL : public Object { return java_lang_String_.Read(); } - static void SetClass(Class* java_lang_String); - static void ResetClass(); - static void VisitRoots(RootVisitor* visitor) - SHARED_REQUIRES(Locks::mutator_lock_); + static void SetClass(Class* java_lang_String) SHARED_REQUIRES(Locks::mutator_lock_); + static void ResetClass() SHARED_REQUIRES(Locks::mutator_lock_); + static void VisitRoots(RootVisitor* visitor) SHARED_REQUIRES(Locks::mutator_lock_); private: void SetHashCode(int32_t new_hash_code) SHARED_REQUIRES(Locks::mutator_lock_) { diff --git a/runtime/modifiers.h b/runtime/modifiers.h index 0d9ec29db3..f7ab10be9d 100644 --- a/runtime/modifiers.h +++ b/runtime/modifiers.h @@ -19,6 +19,8 @@ #include <stdint.h> +namespace art { + static constexpr uint32_t kAccPublic = 0x0001; // class, field, method, ic static constexpr uint32_t kAccPrivate = 0x0002; // field, method, ic static constexpr uint32_t kAccProtected = 0x0004; // field, method, ic @@ -49,28 +51,8 @@ static constexpr uint32_t kAccFastNative = 0x00080000; // method (dex static constexpr uint32_t kAccMiranda = 0x00200000; // method (dex only) // Special runtime-only flags. -// Note: if only kAccClassIsReference is set, we have a soft reference. - -// class is ClassLoader or one of its subclasses -static constexpr uint32_t kAccClassIsClassLoaderClass = 0x10000000; - // class/ancestor overrides finalize() static constexpr uint32_t kAccClassIsFinalizable = 0x80000000; -// class is a soft/weak/phantom ref -static constexpr uint32_t kAccClassIsReference = 0x08000000; -// class is a weak reference -static constexpr uint32_t kAccClassIsWeakReference = 0x04000000; -// class is a finalizer reference -static constexpr uint32_t kAccClassIsFinalizerReference = 0x02000000; -// class is a phantom reference -static constexpr uint32_t kAccClassIsPhantomReference = 0x01000000; -// class is the string class -static constexpr uint32_t kAccClassIsStringClass = 0x00800000; - -static constexpr uint32_t kAccReferenceFlagsMask = (kAccClassIsReference - | kAccClassIsWeakReference - | kAccClassIsFinalizerReference - | kAccClassIsPhantomReference); // Valid (meaningful) bits for a field. static constexpr uint32_t kAccValidFieldFlags = kAccPublic | kAccPrivate | kAccProtected | @@ -95,5 +77,7 @@ static constexpr uint32_t kAccValidClassFlags = kAccPublic | kAccFinal | kAccSup static constexpr uint32_t kAccValidInterfaceFlags = kAccPublic | kAccInterface | kAccAbstract | kAccSynthetic | kAccAnnotation; +} // namespace art + #endif // ART_RUNTIME_MODIFIERS_H_ diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index c337e91cf8..b9cc219a85 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -426,6 +426,45 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT return soa.AddLocalReference<jobjectArray>(ret.Get()); } +static jobject Class_getDeclaredAnnotation(JNIEnv* env, jobject javaThis, jclass annotationType) { + ScopedFastNativeObjectAccess soa(env); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis))); + if (klass->IsProxyClass()) { + return nullptr; + } + Handle<mirror::Class> annotation_class(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType))); + return soa.AddLocalReference<jobject>( + klass->GetDexFile().GetAnnotationForClass(klass, annotation_class)); +} + +static jobjectArray Class_getDeclaredAnnotations(JNIEnv* env, jobject javaThis) { + ScopedFastNativeObjectAccess soa(env); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis))); + if (klass->IsProxyClass()) { + // Return an empty array instead of a null pointer + mirror::Class* annotation_array_class = + soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_annotation_Annotation__array); + mirror::ObjectArray<mirror::Object>* empty_array = + mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class, 0); + return soa.AddLocalReference<jobjectArray>(empty_array); + } + return soa.AddLocalReference<jobjectArray>(klass->GetDexFile().GetAnnotationsForClass(klass)); +} + +static jboolean Class_isDeclaredAnnotationPresent(JNIEnv* env, jobject javaThis, + jclass annotationType) { + ScopedFastNativeObjectAccess soa(env); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis))); + if (klass->IsProxyClass()) { + return false; + } + Handle<mirror::Class> annotation_class(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType))); + return klass->GetDexFile().IsClassAnnotationPresent(klass, annotation_class); +} + static jobject Class_newInstance(JNIEnv* env, jobject javaThis) { ScopedFastNativeObjectAccess soa(env); StackHandleScope<4> hs(soa.Self()); @@ -508,6 +547,9 @@ static jobject Class_newInstance(JNIEnv* env, jobject javaThis) { static JNINativeMethod gMethods[] = { NATIVE_METHOD(Class, classForName, "!(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"), + NATIVE_METHOD(Class, getDeclaredAnnotation, + "!(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"), + NATIVE_METHOD(Class, getDeclaredAnnotations, "!()[Ljava/lang/annotation/Annotation;"), NATIVE_METHOD(Class, getDeclaredConstructorInternal, "!([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"), NATIVE_METHOD(Class, getDeclaredConstructorsInternal, "!(Z)[Ljava/lang/reflect/Constructor;"), @@ -518,10 +560,11 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(Class, getDeclaredMethodInternal, "!(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"), NATIVE_METHOD(Class, getDeclaredMethodsUnchecked, - "!(Z)[Ljava/lang/reflect/Method;"), + "!(Z)[Ljava/lang/reflect/Method;"), NATIVE_METHOD(Class, getNameNative, "!()Ljava/lang/String;"), NATIVE_METHOD(Class, getProxyInterfaces, "!()[Ljava/lang/Class;"), NATIVE_METHOD(Class, getPublicDeclaredFields, "!()[Ljava/lang/reflect/Field;"), + NATIVE_METHOD(Class, isDeclaredAnnotationPresent, "!(Ljava/lang/Class;)Z"), NATIVE_METHOD(Class, newInstance, "!()Ljava/lang/Object;"), }; diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc index 93ba84a8c3..cf3346dd1c 100644 --- a/runtime/native/java_lang_reflect_Constructor.cc +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -18,6 +18,7 @@ #include "art_method-inl.h" #include "class_linker.h" +#include "class_linker-inl.h" #include "jni_internal.h" #include "mirror/class-inl.h" #include "mirror/method.h" @@ -28,6 +29,55 @@ namespace art { +static jobject Constructor_getAnnotationNative(JNIEnv* env, jobject javaMethod, + jclass annotationType) { + ScopedFastNativeObjectAccess soa(env); + StackHandleScope<1> hs(soa.Self()); + ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); + Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType))); + return soa.AddLocalReference<jobject>( + method->GetDexFile()->GetAnnotationForMethod(method, klass)); +} + +static jobjectArray Constructor_getDeclaredAnnotations(JNIEnv* env, jobject javaMethod) { + ScopedFastNativeObjectAccess soa(env); + ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); + return soa.AddLocalReference<jobjectArray>(method->GetDexFile()->GetAnnotationsForMethod(method)); +} + +static jobjectArray Constructor_getExceptionTypes(JNIEnv* env, jobject javaMethod) { + ScopedFastNativeObjectAccess soa(env); + ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); + mirror::ObjectArray<mirror::Object>* result_array = + method->GetDexFile()->GetExceptionTypesForMethod(method); + if (result_array == nullptr) { + // Return an empty array instead of a null pointer + mirror::Class* class_class = mirror::Class::GetJavaLangClass(); + mirror::Class* class_array_class = + Runtime::Current()->GetClassLinker()->FindArrayClass(soa.Self(), &class_class); + mirror::ObjectArray<mirror::Object>* empty_array = + mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), class_array_class, 0); + return soa.AddLocalReference<jobjectArray>(empty_array); + } else { + return soa.AddLocalReference<jobjectArray>(result_array); + } +} + +static jobjectArray Constructor_getParameterAnnotationsNative(JNIEnv* env, jobject javaMethod) { + ScopedFastNativeObjectAccess soa(env); + ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); + return soa.AddLocalReference<jobjectArray>(method->GetDexFile()->GetParameterAnnotations(method)); +} + +static jboolean Constructor_isAnnotationPresentNative(JNIEnv* env, jobject javaMethod, + jclass annotationType) { + ScopedFastNativeObjectAccess soa(env); + StackHandleScope<1> hs(soa.Self()); + ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); + Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType))); + return method->GetDexFile()->IsMethodAnnotationPresent(method, klass); +} + /* * We can also safely assume the constructor isn't associated * with an interface, array, or primitive class. If this is coming from @@ -82,6 +132,13 @@ static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectA } static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Constructor, getAnnotationNative, + "!(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"), + NATIVE_METHOD(Constructor, getDeclaredAnnotations, "!()[Ljava/lang/annotation/Annotation;"), + NATIVE_METHOD(Constructor, getExceptionTypes, "!()[Ljava/lang/Class;"), + NATIVE_METHOD(Constructor, getParameterAnnotationsNative, + "!()[[Ljava/lang/annotation/Annotation;"), + NATIVE_METHOD(Constructor, isAnnotationPresentNative, "!(Ljava/lang/Class;)Z"), NATIVE_METHOD(Constructor, newInstance, "!([Ljava/lang/Object;)Ljava/lang/Object;"), }; diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 5bbb0dc45f..cc60fe0a91 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -415,16 +415,51 @@ static void Field_setShort(JNIEnv* env, jobject javaField, jobject javaObj, jsho SetPrimitiveField<Primitive::kPrimShort>(env, javaField, javaObj, value); } +static jobject Field_getAnnotationNative(JNIEnv* env, jobject javaField, jclass annotationType) { + ScopedFastNativeObjectAccess soa(env); + StackHandleScope<1> hs(soa.Self()); + ArtField* field = soa.Decode<mirror::Field*>(javaField)->GetArtField(); + Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType))); + return soa.AddLocalReference<jobject>(field->GetDexFile()->GetAnnotationForField(field, klass)); +} + +static jobjectArray Field_getDeclaredAnnotations(JNIEnv* env, jobject javaField) { + ScopedFastNativeObjectAccess soa(env); + ArtField* field = soa.Decode<mirror::Field*>(javaField)->GetArtField(); + return soa.AddLocalReference<jobjectArray>(field->GetDexFile()->GetAnnotationsForField(field)); +} + +static jobjectArray Field_getSignatureAnnotation(JNIEnv* env, jobject javaField) { + ScopedFastNativeObjectAccess soa(env); + ArtField* field = soa.Decode<mirror::Field*>(javaField)->GetArtField(); + return soa.AddLocalReference<jobjectArray>( + field->GetDexFile()->GetSignatureAnnotationForField(field)); +} + +static jboolean Field_isAnnotationPresentNative(JNIEnv* env, jobject javaField, + jclass annotationType) { + ScopedFastNativeObjectAccess soa(env); + StackHandleScope<1> hs(soa.Self()); + ArtField* field = soa.Decode<mirror::Field*>(javaField)->GetArtField(); + Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType))); + return field->GetDexFile()->IsFieldAnnotationPresent(field, klass); +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(Field, get, "!(Ljava/lang/Object;)Ljava/lang/Object;"), NATIVE_METHOD(Field, getBoolean, "!(Ljava/lang/Object;)Z"), NATIVE_METHOD(Field, getByte, "!(Ljava/lang/Object;)B"), NATIVE_METHOD(Field, getChar, "!(Ljava/lang/Object;)C"), + NATIVE_METHOD(Field, getAnnotationNative, + "!(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"), + NATIVE_METHOD(Field, getDeclaredAnnotations, "!()[Ljava/lang/annotation/Annotation;"), + NATIVE_METHOD(Field, getSignatureAnnotation, "!()[Ljava/lang/String;"), NATIVE_METHOD(Field, getDouble, "!(Ljava/lang/Object;)D"), NATIVE_METHOD(Field, getFloat, "!(Ljava/lang/Object;)F"), NATIVE_METHOD(Field, getInt, "!(Ljava/lang/Object;)I"), NATIVE_METHOD(Field, getLong, "!(Ljava/lang/Object;)J"), NATIVE_METHOD(Field, getShort, "!(Ljava/lang/Object;)S"), + NATIVE_METHOD(Field, isAnnotationPresentNative, "!(Ljava/lang/Class;)Z"), NATIVE_METHOD(Field, set, "!(Ljava/lang/Object;Ljava/lang/Object;)V"), NATIVE_METHOD(Field, setBoolean, "!(Ljava/lang/Object;Z)V"), NATIVE_METHOD(Field, setByte, "!(Ljava/lang/Object;B)V"), diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc index 9533b4dc8a..2f0e7fe1f1 100644 --- a/runtime/native/java_lang_reflect_Method.cc +++ b/runtime/native/java_lang_reflect_Method.cc @@ -18,6 +18,7 @@ #include "art_method-inl.h" #include "class_linker.h" +#include "class_linker-inl.h" #include "jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" @@ -28,35 +29,111 @@ namespace art { +static jobject Method_getAnnotationNative(JNIEnv* env, jobject javaMethod, jclass annotationType) { + ScopedFastNativeObjectAccess soa(env); + ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); + if (method->GetDeclaringClass()->IsProxyClass()) { + return nullptr; + } + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType))); + return soa.AddLocalReference<jobject>( + method->GetDexFile()->GetAnnotationForMethod(method, klass)); +} + +static jobjectArray Method_getDeclaredAnnotations(JNIEnv* env, jobject javaMethod) { + ScopedFastNativeObjectAccess soa(env); + ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); + if (method->GetDeclaringClass()->IsProxyClass()) { + // Return an empty array instead of a null pointer + mirror::Class* annotation_array_class = + soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_annotation_Annotation__array); + mirror::ObjectArray<mirror::Object>* empty_array = + mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class, 0); + return soa.AddLocalReference<jobjectArray>(empty_array); + } + return soa.AddLocalReference<jobjectArray>(method->GetDexFile()->GetAnnotationsForMethod(method)); +} + +static jobject Method_getDefaultValue(JNIEnv* env, jobject javaMethod) { + ScopedFastNativeObjectAccess soa(env); + ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); + if (!method->GetDeclaringClass()->IsAnnotation()) { + return nullptr; + } + return soa.AddLocalReference<jobject>(method->GetDexFile()->GetAnnotationDefaultValue(method)); +} + +static jobjectArray Method_getExceptionTypes(JNIEnv* env, jobject javaMethod) { + ScopedFastNativeObjectAccess soa(env); + ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); + if (method->GetDeclaringClass()->IsProxyClass()) { + mirror::Class* klass = method->GetDeclaringClass(); + int throws_index = -1; + size_t i = 0; + for (const auto& m : klass->GetVirtualMethods(sizeof(void*))) { + if (&m == method) { + throws_index = i; + break; + } + ++i; + } + CHECK_NE(throws_index, -1); + mirror::ObjectArray<mirror::Class>* declared_exceptions = klass->GetThrows()->Get(throws_index); + return soa.AddLocalReference<jobjectArray>(declared_exceptions->Clone(soa.Self())); + } else { + mirror::ObjectArray<mirror::Object>* result_array = + method->GetDexFile()->GetExceptionTypesForMethod(method); + if (result_array == nullptr) { + // Return an empty array instead of a null pointer + mirror::Class* class_class = mirror::Class::GetJavaLangClass(); + mirror::Class* class_array_class = + Runtime::Current()->GetClassLinker()->FindArrayClass(soa.Self(), &class_class); + mirror::ObjectArray<mirror::Object>* empty_array = + mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), class_array_class, 0); + return soa.AddLocalReference<jobjectArray>(empty_array); + } else { + return soa.AddLocalReference<jobjectArray>(result_array); + } + } +} + +static jobjectArray Method_getParameterAnnotationsNative(JNIEnv* env, jobject javaMethod) { + ScopedFastNativeObjectAccess soa(env); + ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); + if (method->GetDeclaringClass()->IsProxyClass()) { + return nullptr; + } + return soa.AddLocalReference<jobjectArray>(method->GetDexFile()->GetParameterAnnotations(method)); +} + static jobject Method_invoke(JNIEnv* env, jobject javaMethod, jobject javaReceiver, jobject javaArgs) { ScopedFastNativeObjectAccess soa(env); return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs); } -static jobject Method_getExceptionTypesNative(JNIEnv* env, jobject javaMethod) { +static jboolean Method_isAnnotationPresentNative(JNIEnv* env, jobject javaMethod, + jclass annotationType) { ScopedFastNativeObjectAccess soa(env); - ArtMethod* proxy_method = ArtMethod::FromReflectedMethod(soa, javaMethod); - CHECK(proxy_method->GetDeclaringClass()->IsProxyClass()); - mirror::Class* proxy_class = proxy_method->GetDeclaringClass(); - int throws_index = -1; - size_t i = 0; - for (const auto& m : proxy_class->GetVirtualMethods(sizeof(void*))) { - if (&m == proxy_method) { - throws_index = i; - break; - } - ++i; + ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); + if (method->GetDeclaringClass()->IsProxyClass()) { + return false; } - CHECK_NE(throws_index, -1); - mirror::ObjectArray<mirror::Class>* declared_exceptions = - proxy_class->GetThrows()->Get(throws_index); - return soa.AddLocalReference<jobject>(declared_exceptions->Clone(soa.Self())); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType))); + return method->GetDexFile()->IsMethodAnnotationPresent(method, klass); } static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Method, getAnnotationNative, + "!(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"), + NATIVE_METHOD(Method, getDeclaredAnnotations, "!()[Ljava/lang/annotation/Annotation;"), + NATIVE_METHOD(Method, getDefaultValue, "!()Ljava/lang/Object;"), + NATIVE_METHOD(Method, getExceptionTypes, "!()[Ljava/lang/Class;"), + NATIVE_METHOD(Method, getParameterAnnotationsNative, "!()[[Ljava/lang/annotation/Annotation;"), NATIVE_METHOD(Method, invoke, "!(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"), - NATIVE_METHOD(Method, getExceptionTypesNative, "!()[Ljava/lang/Class;"), + NATIVE_METHOD(Method, isAnnotationPresentNative, "!(Ljava/lang/Class;)Z"), }; void register_java_lang_reflect_Method(JNIEnv* env) { diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 1828b91e2a..a50607105f 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -1956,6 +1956,32 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } case Instruction::MONITOR_ENTER: work_line_->PushMonitor(this, inst->VRegA_11x(), work_insn_idx_); + // Check whether the previous instruction is a move-object with vAA as a source, creating + // untracked lock aliasing. + if (0 != work_insn_idx_ && !insn_flags_[work_insn_idx_].IsBranchTarget()) { + uint32_t prev_idx = work_insn_idx_ - 1; + while (0 != prev_idx && !insn_flags_[prev_idx].IsOpcode()) { + prev_idx--; + } + const Instruction* prev_inst = Instruction::At(code_item_->insns_ + prev_idx); + switch (prev_inst->Opcode()) { + case Instruction::MOVE_OBJECT: + case Instruction::MOVE_OBJECT_16: + case Instruction::MOVE_OBJECT_FROM16: + if (prev_inst->VRegB() == inst->VRegA_11x()) { + // Redo the copy. This won't change the register types, but update the lock status + // for the aliased register. + work_line_->CopyRegister1(this, + prev_inst->VRegA(), + prev_inst->VRegB(), + kTypeCategoryRef); + } + break; + + default: // Other instruction types ignored. + break; + } + } break; case Instruction::MONITOR_EXIT: /* diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h index f61e51fb23..a9c4c951fd 100644 --- a/runtime/verifier/register_line.h +++ b/runtime/verifier/register_line.h @@ -344,6 +344,14 @@ class RegisterLine { } else { reg_to_lock_depths_.erase(it); } + // Need to unlock every register at the same lock depth. These are aliased locks. + uint32_t mask = 1 << depth; + for (auto& pair : reg_to_lock_depths_) { + if ((pair.second & mask) != 0) { + VLOG(verifier) << "Also unlocking " << pair.first; + pair.second ^= mask; + } + } } void ClearAllRegToLockDepths(size_t reg) { diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 0c7cce908c..e2c3afbcbc 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -35,6 +35,7 @@ jclass WellKnownClasses::dalvik_system_DexPathList; jclass WellKnownClasses::dalvik_system_DexPathList__Element; jclass WellKnownClasses::dalvik_system_PathClassLoader; jclass WellKnownClasses::dalvik_system_VMRuntime; +jclass WellKnownClasses::java_lang_annotation_Annotation__array; jclass WellKnownClasses::java_lang_BootClassLoader; jclass WellKnownClasses::java_lang_ClassLoader; jclass WellKnownClasses::java_lang_ClassNotFoundException; @@ -59,6 +60,8 @@ jclass WellKnownClasses::java_lang_Throwable; jclass WellKnownClasses::java_nio_DirectByteBuffer; jclass WellKnownClasses::java_util_ArrayList; jclass WellKnownClasses::java_util_Collections; +jclass WellKnownClasses::libcore_reflect_AnnotationFactory; +jclass WellKnownClasses::libcore_reflect_AnnotationMember; jclass WellKnownClasses::libcore_util_EmptyArray; jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk; jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer; @@ -120,6 +123,8 @@ jmethodID WellKnownClasses::java_lang_Thread_run; jmethodID WellKnownClasses::java_lang_Thread__UncaughtExceptionHandler_uncaughtException; jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread; jmethodID WellKnownClasses::java_nio_DirectByteBuffer_init; +jmethodID WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation; +jmethodID WellKnownClasses::libcore_reflect_AnnotationMember_init; jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch; @@ -213,6 +218,7 @@ void WellKnownClasses::Init(JNIEnv* env) { dalvik_system_PathClassLoader = CacheClass(env, "dalvik/system/PathClassLoader"); dalvik_system_VMRuntime = CacheClass(env, "dalvik/system/VMRuntime"); + java_lang_annotation_Annotation__array = CacheClass(env, "[Ljava/lang/annotation/Annotation;"); java_lang_BootClassLoader = CacheClass(env, "java/lang/BootClassLoader"); java_lang_ClassLoader = CacheClass(env, "java/lang/ClassLoader"); java_lang_ClassNotFoundException = CacheClass(env, "java/lang/ClassNotFoundException"); @@ -238,6 +244,8 @@ void WellKnownClasses::Init(JNIEnv* env) { java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer"); java_util_ArrayList = CacheClass(env, "java/util/ArrayList"); java_util_Collections = CacheClass(env, "java/util/Collections"); + libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory"); + libcore_reflect_AnnotationMember = CacheClass(env, "libcore/reflect/AnnotationMember"); libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray"); org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk"); org_apache_harmony_dalvik_ddmc_DdmServer = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer"); @@ -262,6 +270,8 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Thread__UncaughtExceptionHandler_uncaughtException = CacheMethod(env, java_lang_Thread__UncaughtExceptionHandler, false, "uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V"); java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "removeThread", "(Ljava/lang/Thread;)V"); java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "<init>", "(JI)V"); + libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(env, libcore_reflect_AnnotationFactory, true, "createAnnotation", "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;"); + libcore_reflect_AnnotationMember_init = CacheMethod(env, libcore_reflect_AnnotationMember, false, "<init>", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V"); org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V"); org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 6dd8168cbf..c8562917e9 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -46,6 +46,7 @@ struct WellKnownClasses { static jclass dalvik_system_DexPathList__Element; static jclass dalvik_system_PathClassLoader; static jclass dalvik_system_VMRuntime; + static jclass java_lang_annotation_Annotation__array; static jclass java_lang_BootClassLoader; static jclass java_lang_ClassLoader; static jclass java_lang_ClassNotFoundException; @@ -70,6 +71,8 @@ struct WellKnownClasses { static jclass java_util_ArrayList; static jclass java_util_Collections; static jclass java_nio_DirectByteBuffer; + static jclass libcore_reflect_AnnotationFactory; + static jclass libcore_reflect_AnnotationMember; static jclass libcore_util_EmptyArray; static jclass org_apache_harmony_dalvik_ddmc_Chunk; static jclass org_apache_harmony_dalvik_ddmc_DdmServer; @@ -131,6 +134,8 @@ struct WellKnownClasses { static jmethodID java_lang_Thread__UncaughtExceptionHandler_uncaughtException; static jmethodID java_lang_ThreadGroup_removeThread; static jmethodID java_nio_DirectByteBuffer_init; + static jmethodID libcore_reflect_AnnotationFactory_createAnnotation; + static jmethodID libcore_reflect_AnnotationMember_init; static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_dispatch; diff --git a/test/004-SignalTest/signaltest.cc b/test/004-SignalTest/signaltest.cc index 1414715bbf..34e331b901 100644 --- a/test/004-SignalTest/signaltest.cc +++ b/test/004-SignalTest/signaltest.cc @@ -93,13 +93,16 @@ extern "C" JNIEXPORT void JNICALL Java_Main_terminateSignalTest(JNIEnv*, jclass) char *go_away_compiler = nullptr; extern "C" JNIEXPORT jint JNICALL Java_Main_testSignal(JNIEnv*, jclass) { -#if defined(__arm__) || defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) +#if defined(__arm__) || defined(__i386__) || defined(__aarch64__) // On supported architectures we cause a real SEGV. *go_away_compiler = 'a'; +#elif defined(__x86_64__) + // Cause a SEGV using an instruction known to be 3 bytes long to account for hardcoded jump + // in the signal handler + asm volatile("movl $0, %%eax;" "movb $1, (%%al);" : : : "%eax"); #else // On other architectures we simulate SEGV. kill(getpid(), SIGSEGV); #endif return 1234; } - diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc index 04326b3028..702e779bb1 100644 --- a/test/115-native-bridge/nativebridge.cc +++ b/test/115-native-bridge/nativebridge.cc @@ -206,8 +206,9 @@ static jint trampoline_Java_Main_testSignal(JNIEnv*, jclass) { #if defined(__arm__) || defined(__i386__) || defined(__aarch64__) *go_away_compiler = 'a'; #elif defined(__x86_64__) - // Cause a SEGV using an instruction known to be 3 bytes long - asm volatile("movl $0, %%eax;" "movb $1, (%%eax);" : : : "%eax"); + // Cause a SEGV using an instruction known to be 3 bytes long to account for hardcoded jump + // in the signal handler + asm volatile("movl $0, %%eax;" "movb $1, (%%al);" : : : "%eax"); #else // On other architectures we simulate SEGV. kill(getpid(), SIGSEGV); diff --git a/test/529-long-split/expected.txt b/test/529-long-split/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/529-long-split/expected.txt diff --git a/test/529-long-split/info.txt b/test/529-long-split/info.txt new file mode 100644 index 0000000000..39b5b96eb6 --- /dev/null +++ b/test/529-long-split/info.txt @@ -0,0 +1,2 @@ +Regression test for optimizing that used to trip +during register allocation. diff --git a/test/529-long-split/src/Main.java b/test/529-long-split/src/Main.java new file mode 100644 index 0000000000..dc52d88486 --- /dev/null +++ b/test/529-long-split/src/Main.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2015 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 { + if (testOddLow1(5L)) { + throw new Error(); + } + + if (testNonFollowingHigh(5)) { + throw new Error(); + } + + if (testOddLow2()) { + throw new Error(); + } + } + + public static boolean testOddLow1(long a /* ECX-EDX */) { + // class instance is in EBP + long b = myLongField1; // ESI-EDI + int f = myField1; // EBX + int e = myField2; // EAX + int g = myField3; // ESI (by spilling ESI-EDI, see below) + int h = myField4; // EDI + myLongField2 = a; // Make sure ESI-EDI gets spilled and not ECX-EDX + myField2 = f; // use of EBX + myField1 = e; // use of EAX + myField3 = h; // use of ESI + myField4 = g; // use if EDI + + // At this point `b` has been spilled and needs to have a pair. The ordering + // in the register allocator triggers the allocation of `res` before `b`. + // `res` being used after the `doCall`, we want a callee saved register. + // + // EBP is taken by the class instance and EDI is taken by `g` (both used in the `myField4` + // assignment below). So we end up allocating ESI for `res`. + // + // When we try to allocate a pair for `b` we're in the following situation: + // EAX is free + // ECX is taken + // EDX is taken + // EBX is free + // ESP is blocked + // EBP could be spilled + // ESI is taken + // EDI could be spilled + // + // So there is no consecutive registers available to please the register allocator. + // The compiler used to trip then because of a bogus implementation of trying to split + // an unaligned register pair (here ECX and EDX). The implementation would not find + // a register and the register allocator would then complain about not having + // enough registers for the operation. + boolean res = a == b; + $noinline$doCall(); + myField4 = g; + return res; + } + + public static boolean testNonFollowingHigh(int i) { + // class instance is in EBP + long b = myLongField1; // ESI-EDI + long a = (long)i; // EAX-EDX + int f = myField1; // EBX + int e = myField2; // ECX + int g = myField3; // ESI (by spilling ESI-EDI, see below) + int h = myField4; // EDI + myLongField2 = a; // Make sure ESI-EDI gets spilled and not ECX-EDX + myField2 = f; // use of EBX + myField1 = e; // use of ECX + myField3 = h; // use of EDI + myField4 = g; // use of ESI + + // At this point `b` has been spilled and needs to have a pair. The ordering + // in the register allocator triggers the allocation of `res` before `b`. + // `res` being used after the `doCall`, we want a callee saved register. + // + // EBP is taken by the class instance and ESI is taken by `g` (both used in the `myField4` + // assignment below). So we end up allocating EDI for `res`. + // + // When we try to allocate a pair for `b` we're in the following situation: + // EAX is taken + // ECX is free + // EDX is taken + // EBX is free + // ESP is blocked + // EBP could be spilled + // ESI is taken + // EDI could be spilled + // + // So there is no consecutive registers available to please the register allocator. + // The compiler used to be in a bad state because of a bogus implementation of trying + // to split an unaligned register pair (here EAX and EDX). + boolean res = a == b; + $noinline$doCall(); + myField4 = g; + return res; + } + + public static boolean testOddLow2() { + // class instance is in EBP + long b = myLongField1; // ECX-EDX (hint due to call below). + long a = myLongField2; // ESI-EDI + int f = myField1; // EBX + int e = myField2; // EAX + int g = myField3; // ECX + int h = myField4; // EDX + int i = myField5; // ESI - callee saved due to assignment after call to $noinline$doCall. + myField2 = f; // use of EBX + myField1 = e; // use of EAX + myField3 = h; // use of EDX + myField4 = i; // use of ESI + myField5 = g; // use of ECX + + // At this point `a` and `b` have been spilled and need to have a pairs. The ordering + // in the register allocator triggers the allocation of `res` before `a` and `b`. + // `res` being used after the `doCall`, we want a callee saved register. + // + // EBP is taken by the class instance and ESI is taken by `i` (both used in the `myField4` + // assignment below). So we end up allocating EDI for `res`. + // + // We first try to allocator a pair for `b`. We're in the following situation: + // EAX is free + // ECX is free + // EDX is free + // EBX is free + // ESP is blocked + // EBP could be spilled + // ESI could be spilled + // EDI is taken + // + // Because `b` is used as a first argument to a call, we take its hint and allocate + // ECX-EDX to it. + // + // We then try to allocate a pair for `a`. We're in the following situation: + // EAX is free + // ECX could be spilled + // EDX could be spilled + // EBX is free + // ESP is blocked + // EBP could be spilled + // ESI could be spilled + // EDI is taken + // + // So no consecutive two free registers are available. When trying to find a slot, we pick + // the first unaligned or non-pair interval. In this case, this is the unaligned ECX-EDX. + // The compiler used to then trip because it forgot to remove the high interval containing + // the pair from the active list. + + boolean res = a == b; + $noinline$doCall(b); + myField4 = i; // use of ESI + return res; + } + + public static void $noinline$doCall() { + if (doThrow) throw new Error(); + } + + public static void $noinline$doCall(long e) { + if (doThrow) throw new Error(); + } + + public static boolean doThrow; + public static int myField1; + public static int myField2; + public static int myField3; + public static int myField4; + public static int myField5; + public static long myLongField1; + public static long myLongField2; +} diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt index dd37cdbaf5..6a452eb0c0 100644 --- a/test/800-smali/expected.txt +++ b/test/800-smali/expected.txt @@ -1,6 +1,5 @@ PackedSwitch b/17790197 -b/17978759 FloatBadArgReg negLong sameFieldNames @@ -41,4 +40,6 @@ b/22881413 b/20843113 b/23201502 (float) b/23201502 (double) +b/23300986 +b/23300986 (2) Done! diff --git a/test/800-smali/smali/b_17978759.smali b/test/800-smali/smali/b_17978759.smali deleted file mode 100644 index 07bcae5bb4..0000000000 --- a/test/800-smali/smali/b_17978759.smali +++ /dev/null @@ -1,28 +0,0 @@ -.class public LB17978759; -.super Ljava/lang/Object; - - .method public constructor <init>()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;-><init>()V - return-void - .end method - - .method public test()V - .registers 2 - - move-object v0, p0 - # v0 and p0 alias - monitor-enter p0 - # monitor-enter on p0 - monitor-exit v0 - # monitor-exit on v0, however, verifier doesn't track this and so this is - # a warning. Verifier will still think p0 is locked. - - move-object v0, p0 - # v0 will now appear locked. - monitor-enter v0 - # Attempt to lock v0 twice is a verifier failure. - monitor-exit v0 - - return-void - .end method diff --git a/test/800-smali/smali/b_23300986.smali b/test/800-smali/smali/b_23300986.smali new file mode 100644 index 0000000000..f008b92925 --- /dev/null +++ b/test/800-smali/smali/b_23300986.smali @@ -0,0 +1,23 @@ +.class public LB23300986; + +.super Ljava/lang/Object; + +.method public static runAliasAfterEnter(Ljava/lang/Object;)V + .registers 3 + monitor-enter v2 # Lock on parameter + move-object v1, v2 # Copy parameter into v1, establishing an alias. + monitor-exit v1 # Unlock on alias + monitor-enter v2 # Do it again. + monitor-exit v1 + return-void +.end method + +.method public static runAliasBeforeEnter(Ljava/lang/Object;)V + .registers 3 + move-object v1, v2 # Copy parameter into v1, establishing an alias. + monitor-enter v2 # Lock on parameter + monitor-exit v1 # Unlock on alias + monitor-enter v2 # Do it again. + monitor-exit v1 + return-void +.end method diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java index b481a1dbc4..183958aef6 100644 --- a/test/800-smali/src/Main.java +++ b/test/800-smali/src/Main.java @@ -53,8 +53,6 @@ public class Main { new Object[]{123}, null, 123)); testCases.add(new TestCase("b/17790197", "B17790197", "getInt", null, null, 100)); - testCases.add(new TestCase("b/17978759", "B17978759", "test", null, new VerifyError(), - null)); testCases.add(new TestCase("FloatBadArgReg", "FloatBadArgReg", "getInt", new Object[]{100}, null, 100)); testCases.add(new TestCase("negLong", "negLong", "negLong", null, null, 122142L)); @@ -127,6 +125,10 @@ public class Main { new NullPointerException(), null)); testCases.add(new TestCase("b/23201502 (double)", "B23201502", "runDouble", null, new NullPointerException(), null)); + testCases.add(new TestCase("b/23300986", "B23300986", "runAliasAfterEnter", + new Object[] { new Object() }, null, null)); + testCases.add(new TestCase("b/23300986 (2)", "B23300986", "runAliasBeforeEnter", + new Object[] { new Object() }, null, null)); } public void runTests() { diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh index 62fd67bfd7..1dced32e96 100755 --- a/tools/buildbot-build.sh +++ b/tools/buildbot-build.sh @@ -24,6 +24,7 @@ android_root="/data/local/tmp/system" linker="linker" mode="target" j_arg="-j$(nproc)" +showcommands= make_command= if [[ "$TARGET_PRODUCT" == "armv8" ]]; then @@ -54,6 +55,9 @@ while true; do elif [[ "$1" == -j* ]]; then j_arg=$1 shift + elif [[ "$1" == "--showcommands" ]]; then + showcommands="showcommands" + shift elif [[ "$1" == "" ]]; then break fi @@ -70,7 +74,7 @@ elif [[ $mode == "target" ]]; then # Use '-e' to force the override of TARGET_GLOBAL_LDFLAGS. # Also, we build extra tools that will be used by tests, so that # they are compiled with our own linker. - make_command="make -e $j_arg build-art-target-tests $common_targets libjavacrypto libjavacoretests linker toybox toolbox sh" + make_command="make -e $j_arg $showcommands build-art-target-tests $common_targets libjavacrypto libjavacoretests linker toybox toolbox sh" echo "Executing env $env $make_command" env $env $make_command fi diff --git a/tools/checker/README b/tools/checker/README index 259691e500..65f5bd25a5 100644 --- a/tools/checker/README +++ b/tools/checker/README @@ -2,10 +2,10 @@ Checker is a testing tool which compiles a given test file and compares the state of the control-flow graph before and after each optimization pass against a set of assertions specified alongside the tests. -Tests are written in Java, turned into DEX and compiled with the Optimizing -compiler. "Check lines" are assertions formatted as comments of the Java file. -They begin with prefix 'CHECK' followed by a pattern that the engine attempts -to match in the compiler-generated output. +Tests are written in Java or Smali, turned into DEX and compiled with the +Optimizing compiler. "Check lines" are assertions formatted as comments of the +source file. They begin with prefix "/// CHECK" or "## CHECK", respectively, +followed by a pattern that the engine attempts to match in the compiler output. Assertions are tested in groups which correspond to the individual compiler passes. Each group of check lines therefore must start with a 'CHECK-START' @@ -15,19 +15,23 @@ be listed with the '--list-passes' command-line flag). Matching of check lines is carried out in the order of appearance in the source file. There are three types of check lines: - - CHECK: Must match an output line which appears in the output group - later than lines matched against any preceeding checks. Output - lines must therefore match the check lines in the same order. - These are referred to as "in-order" checks in the code. - - CHECK-DAG: Must match an output line which appears in the output group - later than lines matched against any preceeding in-order checks. - In other words, the order of output lines does not matter - between consecutive DAG checks. - - CHECK-NOT: Must not match any output line which appears in the output group - later than lines matched against any preceeding checks and - earlier than lines matched against any subsequent checks. - Surrounding non-negative checks (or boundaries of the group) - therefore create a scope within which the assertion is verified. + - CHECK: Must match an output line which appears in the output group + later than lines matched against any preceeding checks. Output + lines must therefore match the check lines in the same order. + These are referred to as "in-order" checks in the code. + - CHECK-DAG: Must match an output line which appears in the output group + later than lines matched against any preceeding in-order checks. + In other words, the order of output lines does not matter + between consecutive DAG checks. + - CHECK-NOT: Must not match any output line which appears in the output group + later than lines matched against any preceeding checks and + earlier than lines matched against any subsequent checks. + Surrounding non-negative checks (or boundaries of the group) + therefore create a scope within which the assertion is verified. + - CHECK-NEXT: Must match the output line which comes right after the line which + matched the previous check. Cannot be used after any but the + in-order CHECK. + - CHECK-EVAL: Specifies a Python expression which must evaluate to 'True'. Check-line patterns are treated as plain text rather than regular expressions but are whitespace agnostic. @@ -45,18 +49,30 @@ be redefined or used undefined. Example: The following assertions can be placed in a Java source file: - // CHECK-START: int MyClass.MyMethod() constant_folding (after) - // CHECK: <<ID:i\d+>> IntConstant {{11|22}} - // CHECK: Return [<<ID>>] + /// CHECK-START: int MyClass.MyMethod() constant_folding (after) + /// CHECK: <<ID:i\d+>> IntConstant {{11|22}} + /// CHECK: Return [<<ID>>] The engine will attempt to match the check lines against the output of the group named on the first line. Together they verify that the CFG after constant folding returns an integer constant with value either 11 or 22. + +Of the language constructs above, 'CHECK-EVAL' lines support only referencing of +variables. Any other surrounding text will be passed to Python's `eval` as is. + +Example: + /// CHECK-START: int MyClass.MyMethod() liveness (after) + /// CHECK: InstructionA liveness:<<VarA:\d+>> + /// CHECK: InstructionB liveness:<<VarB:\d+>> + /// CHECK-EVAL: <<VarA>> != <<VarB>> + + A group of check lines can be made architecture-specific by inserting '-<arch>' after the 'CHECK-START' keyword. The previous example can be updated to run for arm64 only with: - // CHECK-START-ARM64: int MyClass.MyMethod() constant_folding (after) - // CHECK: <<ID:i\d+>> IntConstant {{11|22}} - // CHECK: Return [<<ID>>] +Example: + /// CHECK-START-ARM64: int MyClass.MyMethod() constant_folding (after) + /// CHECK: <<ID:i\d+>> IntConstant {{11|22}} + /// CHECK: Return [<<ID>>] diff --git a/tools/checker/file_format/checker/parser.py b/tools/checker/file_format/checker/parser.py index 001f72a225..446302fed2 100644 --- a/tools/checker/file_format/checker/parser.py +++ b/tools/checker/file_format/checker/parser.py @@ -15,7 +15,7 @@ from common.archs import archs_list from common.logger import Logger from file_format.common import SplitStream -from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, RegexExpression +from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, TestExpression import re @@ -81,6 +81,11 @@ def __processLine(line, lineNo, prefix, fileName): if notLine is not None: return (notLine, TestAssertion.Variant.Not, lineNo), None, None + # 'CHECK-EVAL' lines evaluate a Python expression. + evalLine = __extractLine(prefix + "-EVAL", line) + if evalLine is not None: + return (evalLine, TestAssertion.Variant.Eval, lineNo), None, None + Logger.fail("Checker assertion could not be parsed: '" + line + "'", fileName, lineNo) def __isMatchAtStart(match): @@ -97,16 +102,24 @@ def __firstMatch(matches, string): def ParseCheckerAssertion(parent, line, variant, lineNo): """ This method parses the content of a check line stripped of the initial - comment symbol and the CHECK keyword. + comment symbol and the CHECK-* keyword. """ assertion = TestAssertion(parent, variant, line, lineNo) + isEvalLine = (variant == TestAssertion.Variant.Eval) + # Loop as long as there is something to parse. while line: # Search for the nearest occurrence of the special markers. - matchWhitespace = re.search(r"\s+", line) - matchPattern = re.search(RegexExpression.Regex.regexPattern, line) - matchVariableReference = re.search(RegexExpression.Regex.regexVariableReference, line) - matchVariableDefinition = re.search(RegexExpression.Regex.regexVariableDefinition, line) + if isEvalLine: + # The following constructs are not supported in CHECK-EVAL lines + matchWhitespace = None + matchPattern = None + matchVariableDefinition = None + else: + matchWhitespace = re.search(r"\s+", line) + matchPattern = re.search(TestExpression.Regex.regexPattern, line) + matchVariableDefinition = re.search(TestExpression.Regex.regexVariableDefinition, line) + matchVariableReference = re.search(TestExpression.Regex.regexVariableReference, line) # If one of the above was identified at the current position, extract them # from the line, parse them and add to the list of line parts. @@ -114,24 +127,24 @@ def ParseCheckerAssertion(parent, line, variant, lineNo): # A whitespace in the check line creates a new separator of line parts. # This allows for ignored output between the previous and next parts. line = line[matchWhitespace.end():] - assertion.addExpression(RegexExpression.createSeparator()) + assertion.addExpression(TestExpression.createSeparator()) elif __isMatchAtStart(matchPattern): pattern = line[0:matchPattern.end()] pattern = pattern[2:-2] line = line[matchPattern.end():] - assertion.addExpression(RegexExpression.createPattern(pattern)) + assertion.addExpression(TestExpression.createPattern(pattern)) elif __isMatchAtStart(matchVariableReference): var = line[0:matchVariableReference.end()] line = line[matchVariableReference.end():] name = var[2:-2] - assertion.addExpression(RegexExpression.createVariableReference(name)) + assertion.addExpression(TestExpression.createVariableReference(name)) elif __isMatchAtStart(matchVariableDefinition): var = line[0:matchVariableDefinition.end()] line = line[matchVariableDefinition.end():] colonPos = var.find(":") name = var[2:colonPos] body = var[colonPos+1:-2] - assertion.addExpression(RegexExpression.createVariableDefinition(name, body)) + assertion.addExpression(TestExpression.createVariableDefinition(name, body)) else: # If we're not currently looking at a special marker, this is a plain # text match all the way until the first special marker (or the end @@ -143,7 +156,10 @@ def ParseCheckerAssertion(parent, line, variant, lineNo): line) text = line[0:firstMatch] line = line[firstMatch:] - assertion.addExpression(RegexExpression.createText(text)) + if isEvalLine: + assertion.addExpression(TestExpression.createPlainText(text)) + else: + assertion.addExpression(TestExpression.createPatternFromPlainText(text)) return assertion def ParseCheckerStream(fileName, prefix, stream): diff --git a/tools/checker/file_format/checker/struct.py b/tools/checker/file_format/checker/struct.py index 2b2e4429e7..7ee09cdb84 100644 --- a/tools/checker/file_format/checker/struct.py +++ b/tools/checker/file_format/checker/struct.py @@ -74,7 +74,7 @@ class TestAssertion(PrintableMixin): class Variant(object): """Supported types of assertions.""" - InOrder, NextLine, DAG, Not = range(4) + InOrder, NextLine, DAG, Not, Eval = range(5) def __init__(self, parent, variant, originalText, lineNo): assert isinstance(parent, TestCase) @@ -92,9 +92,9 @@ class TestAssertion(PrintableMixin): return self.parent.fileName def addExpression(self, new_expression): - assert isinstance(new_expression, RegexExpression) + assert isinstance(new_expression, TestExpression) if self.variant == TestAssertion.Variant.Not: - if new_expression.variant == RegexExpression.Variant.VarDef: + if new_expression.variant == TestExpression.Variant.VarDef: Logger.fail("CHECK-NOT lines cannot define variables", self.fileName, self.lineNo) self.expressions.append(new_expression) @@ -102,10 +102,10 @@ class TestAssertion(PrintableMixin): """ Returns a regex pattern for this entire assertion. Only used in tests. """ regex = "" for expression in self.expressions: - if expression.variant == RegexExpression.Variant.Separator: + if expression.variant == TestExpression.Variant.Separator: regex = regex + ", " else: - regex = regex + "(" + expression.pattern + ")" + regex = regex + "(" + expression.text + ")" return regex def __eq__(self, other): @@ -114,11 +114,11 @@ class TestAssertion(PrintableMixin): and self.expressions == other.expressions -class RegexExpression(EqualityMixin, PrintableMixin): +class TestExpression(EqualityMixin, PrintableMixin): class Variant(object): """Supported language constructs.""" - Text, Pattern, VarRef, VarDef, Separator = range(5) + PlainText, Pattern, VarRef, VarDef, Separator = range(5) class Regex(object): rName = r"([a-zA-Z][a-zA-Z0-9]*)" @@ -128,40 +128,45 @@ class RegexExpression(EqualityMixin, PrintableMixin): rVariableStartSym = r"(<<)" rVariableEndSym = r"(>>)" rVariableSeparator = r"(:)" + rVariableDefinitionBody = rName + rVariableSeparator + rRegex regexPattern = rPatternStartSym + rRegex + rPatternEndSym regexVariableReference = rVariableStartSym + rName + rVariableEndSym - regexVariableDefinition = rVariableStartSym + rName + rVariableSeparator + rRegex + rVariableEndSym + regexVariableDefinition = rVariableStartSym + rVariableDefinitionBody + rVariableEndSym - def __init__(self, variant, name, pattern): + def __init__(self, variant, name, text): self.variant = variant self.name = name - self.pattern = pattern + self.text = text def __eq__(self, other): return isinstance(other, self.__class__) \ and self.variant == other.variant \ and self.name == other.name \ - and self.pattern == other.pattern + and self.text == other.text @staticmethod def createSeparator(): - return RegexExpression(RegexExpression.Variant.Separator, None, None) + return TestExpression(TestExpression.Variant.Separator, None, None) @staticmethod - def createText(text): - return RegexExpression(RegexExpression.Variant.Text, None, re.escape(text)) + def createPlainText(text): + return TestExpression(TestExpression.Variant.PlainText, None, text) + + @staticmethod + def createPatternFromPlainText(text): + return TestExpression(TestExpression.Variant.Pattern, None, re.escape(text)) @staticmethod def createPattern(pattern): - return RegexExpression(RegexExpression.Variant.Pattern, None, pattern) + return TestExpression(TestExpression.Variant.Pattern, None, pattern) @staticmethod def createVariableReference(name): - assert re.match(RegexExpression.Regex.rName, name) - return RegexExpression(RegexExpression.Variant.VarRef, name, None) + assert re.match(TestExpression.Regex.rName, name) + return TestExpression(TestExpression.Variant.VarRef, name, None) @staticmethod def createVariableDefinition(name, pattern): - assert re.match(RegexExpression.Regex.rName, name) - return RegexExpression(RegexExpression.Variant.VarDef, name, pattern) + assert re.match(TestExpression.Regex.rName, name) + return TestExpression(TestExpression.Variant.VarDef, name, pattern) diff --git a/tools/checker/file_format/checker/test.py b/tools/checker/file_format/checker/test.py index 36ed4b1592..495dabc588 100644 --- a/tools/checker/file_format/checker/test.py +++ b/tools/checker/file_format/checker/test.py @@ -17,7 +17,7 @@ from common.archs import archs_list from common.testing import ToUnicode from file_format.checker.parser import ParseCheckerStream -from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, RegexExpression +from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, TestExpression import io import unittest @@ -73,10 +73,11 @@ class CheckerParser_PrefixTest(unittest.TestCase): self.assertParses(" ///CHECK: foo") self.assertParses("/// CHECK: foo") -class CheckerParser_RegexExpressionTest(unittest.TestCase): +class CheckerParser_TestExpressionTest(unittest.TestCase): def parseAssertion(self, string, variant=""): - checkerText = u"/// CHECK-START: pass\n/// CHECK" + ToUnicode(variant) + u": " + ToUnicode(string) + checkerText = (u"/// CHECK-START: pass\n" + + u"/// CHECK" + ToUnicode(variant) + u": " + ToUnicode(string)) checkerFile = ParseCheckerStream("<test-file>", "CHECK", io.StringIO(checkerText)) self.assertEqual(len(checkerFile.testCases), 1) testCase = checkerFile.testCases[0] @@ -92,17 +93,17 @@ class CheckerParser_RegexExpressionTest(unittest.TestCase): self.assertEqual(expected, self.parseAssertion(string).toRegex()) def assertEqualsText(self, string, text): - self.assertEqual(self.parseExpression(string), RegexExpression.createText(text)) + self.assertEqual(self.parseExpression(string), TestExpression.createPatternFromPlainText(text)) def assertEqualsPattern(self, string, pattern): - self.assertEqual(self.parseExpression(string), RegexExpression.createPattern(pattern)) + self.assertEqual(self.parseExpression(string), TestExpression.createPattern(pattern)) def assertEqualsVarRef(self, string, name): - self.assertEqual(self.parseExpression(string), RegexExpression.createVariableReference(name)) + self.assertEqual(self.parseExpression(string), TestExpression.createVariableReference(name)) def assertEqualsVarDef(self, string, name, pattern): self.assertEqual(self.parseExpression(string), - RegexExpression.createVariableDefinition(name, pattern)) + TestExpression.createVariableDefinition(name, pattern)) def assertVariantNotEqual(self, string, variant): self.assertNotEqual(variant, self.parseExpression(string).variant) @@ -166,17 +167,17 @@ class CheckerParser_RegexExpressionTest(unittest.TestCase): self.assertEqualsVarDef("<<ABC:(a[bc])>>", "ABC", "(a[bc])") def test_Empty(self): - self.assertVariantNotEqual("{{}}", RegexExpression.Variant.Pattern) - self.assertVariantNotEqual("<<>>", RegexExpression.Variant.VarRef) - self.assertVariantNotEqual("<<:>>", RegexExpression.Variant.VarDef) + self.assertEqualsText("{{}}", "{{}}") + self.assertVariantNotEqual("<<>>", TestExpression.Variant.VarRef) + self.assertVariantNotEqual("<<:>>", TestExpression.Variant.VarDef) def test_InvalidVarName(self): - self.assertVariantNotEqual("<<0ABC>>", RegexExpression.Variant.VarRef) - self.assertVariantNotEqual("<<AB=C>>", RegexExpression.Variant.VarRef) - self.assertVariantNotEqual("<<ABC=>>", RegexExpression.Variant.VarRef) - self.assertVariantNotEqual("<<0ABC:abc>>", RegexExpression.Variant.VarDef) - self.assertVariantNotEqual("<<AB=C:abc>>", RegexExpression.Variant.VarDef) - self.assertVariantNotEqual("<<ABC=:abc>>", RegexExpression.Variant.VarDef) + self.assertVariantNotEqual("<<0ABC>>", TestExpression.Variant.VarRef) + self.assertVariantNotEqual("<<AB=C>>", TestExpression.Variant.VarRef) + self.assertVariantNotEqual("<<ABC=>>", TestExpression.Variant.VarRef) + self.assertVariantNotEqual("<<0ABC:abc>>", TestExpression.Variant.VarDef) + self.assertVariantNotEqual("<<AB=C:abc>>", TestExpression.Variant.VarDef) + self.assertVariantNotEqual("<<ABC=:abc>>", TestExpression.Variant.VarDef) def test_BodyMatchNotGreedy(self): self.assertEqualsRegex("{{abc}}{{def}}", "(abc)(def)") @@ -201,7 +202,7 @@ class CheckerParser_FileLayoutTest(unittest.TestCase): content = assertionEntry[0] variant = assertionEntry[1] assertion = TestAssertion(testCase, variant, content, 0) - assertion.addExpression(RegexExpression.createText(content)) + assertion.addExpression(TestExpression.createPatternFromPlainText(content)) return testFile def assertParsesTo(self, checkerText, expectedData): @@ -279,9 +280,15 @@ class CheckerParser_FileLayoutTest(unittest.TestCase): self.parse( """ /// CHECK-START: Example Group + /// CHECK-EVAL: foo + /// CHECK-NEXT: bar + """) + with self.assertRaises(CheckerException): + self.parse( + """ + /// CHECK-START: Example Group /// CHECK-NEXT: bar """) - class CheckerParser_ArchTests(unittest.TestCase): @@ -329,3 +336,61 @@ class CheckerParser_ArchTests(unittest.TestCase): self.assertEqual(len(checkerFile.testCases), 1) self.assertEqual(len(checkerFile.testCasesForArch(arch)), 1) self.assertEqual(len(checkerFile.testCases[0].assertions), 4) + + +class CheckerParser_EvalTests(unittest.TestCase): + def parseTestCase(self, string): + checkerText = u"/// CHECK-START: pass\n" + ToUnicode(string) + checkerFile = ParseCheckerStream("<test-file>", "CHECK", io.StringIO(checkerText)) + self.assertEqual(len(checkerFile.testCases), 1) + return checkerFile.testCases[0] + + def parseExpressions(self, string): + testCase = self.parseTestCase("/// CHECK-EVAL: " + string) + self.assertEqual(len(testCase.assertions), 1) + assertion = testCase.assertions[0] + self.assertEqual(assertion.variant, TestAssertion.Variant.Eval) + self.assertEqual(assertion.originalText, string) + return assertion.expressions + + def assertParsesToPlainText(self, text): + testCase = self.parseTestCase("/// CHECK-EVAL: " + text) + self.assertEqual(len(testCase.assertions), 1) + assertion = testCase.assertions[0] + self.assertEqual(assertion.variant, TestAssertion.Variant.Eval) + self.assertEqual(assertion.originalText, text) + self.assertEqual(len(assertion.expressions), 1) + expression = assertion.expressions[0] + self.assertEqual(expression.variant, TestExpression.Variant.PlainText) + self.assertEqual(expression.text, text) + + def test_PlainText(self): + self.assertParsesToPlainText("XYZ") + self.assertParsesToPlainText("True") + self.assertParsesToPlainText("{{abc}}") + self.assertParsesToPlainText("<<ABC:abc>>") + self.assertParsesToPlainText("<<ABC=>>") + + def test_VariableReference(self): + self.assertEqual(self.parseExpressions("<<ABC>>"), + [ TestExpression.createVariableReference("ABC") ]) + self.assertEqual(self.parseExpressions("123<<ABC>>"), + [ TestExpression.createPlainText("123"), + TestExpression.createVariableReference("ABC") ]) + self.assertEqual(self.parseExpressions("123 <<ABC>>"), + [ TestExpression.createPlainText("123 "), + TestExpression.createVariableReference("ABC") ]) + self.assertEqual(self.parseExpressions("<<ABC>>XYZ"), + [ TestExpression.createVariableReference("ABC"), + TestExpression.createPlainText("XYZ") ]) + self.assertEqual(self.parseExpressions("<<ABC>> XYZ"), + [ TestExpression.createVariableReference("ABC"), + TestExpression.createPlainText(" XYZ") ]) + self.assertEqual(self.parseExpressions("123<<ABC>>XYZ"), + [ TestExpression.createPlainText("123"), + TestExpression.createVariableReference("ABC"), + TestExpression.createPlainText("XYZ") ]) + self.assertEqual(self.parseExpressions("123 <<ABC>> XYZ"), + [ TestExpression.createPlainText("123 "), + TestExpression.createVariableReference("ABC"), + TestExpression.createPlainText(" XYZ") ]) diff --git a/tools/checker/match/file.py b/tools/checker/match/file.py index 42ca7df439..6601a1e965 100644 --- a/tools/checker/match/file.py +++ b/tools/checker/match/file.py @@ -17,7 +17,7 @@ from common.immutables import ImmutableDict from common.logger import Logger from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass from file_format.checker.struct import CheckerFile, TestCase, TestAssertion -from match.line import MatchLines +from match.line import MatchLines, EvaluateLine MatchScope = namedtuple("MatchScope", ["start", "end"]) MatchInfo = namedtuple("MatchInfo", ["scope", "variables"]) @@ -94,6 +94,11 @@ def testNotGroup(assertions, c1Pass, scope, variables): if MatchLines(assertion, line, variables) is not None: raise MatchFailedException(assertion, i) +def testEvalGroup(assertions, scope, variables): + for assertion in assertions: + if not EvaluateLine(assertion, variables): + raise MatchFailedException(assertion, scope.start) + def MatchTestCase(testCase, c1Pass): """ Runs a test case against a C1visualizer graph dump. @@ -132,11 +137,15 @@ def MatchTestCase(testCase, c1Pass): assert len(assertionGroup) == 1 scope = MatchScope(matchFrom, matchFrom + 1) match = findMatchingLine(assertionGroup[0], c1Pass, scope, variables) - else: + elif assertionGroup[0].variant == TestAssertion.Variant.DAG: # A group of DAG assertions. Match them all starting from the same point. - assert assertionGroup[0].variant == TestAssertion.Variant.DAG scope = MatchScope(matchFrom, c1Length) match = matchDagGroup(assertionGroup, c1Pass, scope, variables) + else: + assert assertionGroup[0].variant == TestAssertion.Variant.Eval + scope = MatchScope(matchFrom, c1Length) + testEvalGroup(assertionGroup, scope, variables) + continue if pendingNotAssertions: # Previous group were NOT assertions. Make sure they don't match any lines diff --git a/tools/checker/match/line.py b/tools/checker/match/line.py index ce11e2a528..08f001f660 100644 --- a/tools/checker/match/line.py +++ b/tools/checker/match/line.py @@ -13,7 +13,7 @@ # limitations under the License. from common.logger import Logger -from file_format.checker.struct import RegexExpression +from file_format.checker.struct import TestExpression, TestAssertion import re @@ -21,30 +21,40 @@ def headAndTail(list): return list[0], list[1:] def splitAtSeparators(expressions): - """ Splits a list of RegexExpressions at separators. """ + """ Splits a list of TestExpressions at separators. """ splitExpressions = [] wordStart = 0 for index, expression in enumerate(expressions): - if expression.variant == RegexExpression.Variant.Separator: + if expression.variant == TestExpression.Variant.Separator: splitExpressions.append(expressions[wordStart:index]) wordStart = index + 1 splitExpressions.append(expressions[wordStart:]) return splitExpressions +def getVariable(name, variables, pos): + if name in variables: + return variables[name] + else: + Logger.testFailed("Missing definition of variable \"{}\"".format(name), + pos.fileName, pos.lineNo) + +def setVariable(name, value, variables, pos): + if name not in variables: + return variables.copyWith(name, value) + else: + Logger.testFailed("Multiple definitions of variable \"{}\"".format(name), + pos.fileName, pos.lineNo) + def matchWords(checkerWord, stringWord, variables, pos): - """ Attempts to match a list of RegexExpressions against a string. + """ Attempts to match a list of TestExpressions against a string. Returns updated variable dictionary if successful and None otherwise. """ for expression in checkerWord: # If `expression` is a variable reference, replace it with the value. - if expression.variant == RegexExpression.Variant.VarRef: - if expression.name in variables: - pattern = re.escape(variables[expression.name]) - else: - Logger.testFailed("Missing definition of variable \"{}\"".format(expression.name), - pos.fileName, pos.lineNo) + if expression.variant == TestExpression.Variant.VarRef: + pattern = re.escape(getVariable(expression.name, variables, pos)) else: - pattern = expression.pattern + pattern = expression.text # Match the expression's regex pattern against the remainder of the word. # Note: re.match will succeed only if matched from the beginning. @@ -53,12 +63,8 @@ def matchWords(checkerWord, stringWord, variables, pos): return None # If `expression` was a variable definition, set the variable's value. - if expression.variant == RegexExpression.Variant.VarDef: - if expression.name not in variables: - variables = variables.copyWith(expression.name, stringWord[:match.end()]) - else: - Logger.testFailed("Multiple definitions of variable \"{}\"".format(expression.name), - pos.fileName, pos.lineNo) + if expression.variant == TestExpression.Variant.VarDef: + variables = setVariable(expression.name, stringWord[:match.end()], variables, pos) # Move cursor by deleting the matched characters. stringWord = stringWord[match.end():] @@ -73,11 +79,13 @@ def MatchLines(checkerLine, stringLine, variables): """ Attempts to match a CHECK line against a string. Returns variable state after the match if successful and None otherwise. """ + assert checkerLine.variant != TestAssertion.Variant.Eval + checkerWords = splitAtSeparators(checkerLine.expressions) stringWords = stringLine.split() while checkerWords: - # Get the next run of RegexExpressions which must match one string word. + # Get the next run of TestExpressions which must match one string word. checkerWord, checkerWords = headAndTail(checkerWords) # Keep reading words until a match is found. @@ -92,5 +100,18 @@ def MatchLines(checkerLine, stringLine, variables): if not wordMatched: return None - # All RegexExpressions matched. Return new variable state. + # All TestExpressions matched. Return new variable state. return variables + +def getEvalText(expression, variables, pos): + if expression.variant == TestExpression.Variant.PlainText: + return expression.text + else: + assert expression.variant == TestExpression.Variant.VarRef + return getVariable(expression.name, variables, pos) + +def EvaluateLine(checkerLine, variables): + assert checkerLine.variant == TestAssertion.Variant.Eval + eval_string = "".join(map(lambda expr: getEvalText(expr, variables, checkerLine), + checkerLine.expressions)) + return eval(eval_string) diff --git a/tools/checker/match/test.py b/tools/checker/match/test.py index ca748c7726..5144ca9dc2 100644 --- a/tools/checker/match/test.py +++ b/tools/checker/match/test.py @@ -17,7 +17,7 @@ from common.testing import ToUnicode from file_format.c1visualizer.parser import ParseC1visualizerStream from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass from file_format.checker.parser import ParseCheckerStream, ParseCheckerAssertion -from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, RegexExpression +from file_format.checker.struct import CheckerFile, TestCase, TestAssertion from match.file import MatchTestCase, MatchFailedException from match.line import MatchLines @@ -386,3 +386,17 @@ class MatchFiles_Test(unittest.TestCase): abc bar """) + + def test_EvalAssertions(self): + self.assertMatches("/// CHECK-EVAL: True", "foo") + self.assertDoesNotMatch("/// CHECK-EVAL: False", "foo") + + self.assertMatches("/// CHECK-EVAL: 1 + 2 == 3", "foo") + self.assertDoesNotMatch("/// CHECK-EVAL: 1 + 2 == 4", "foo") + + twoVarTestCase = """ + /// CHECK-DAG: <<X:\d+>> <<Y:\d+>> + /// CHECK-EVAL: <<X>> > <<Y>> + """ + self.assertMatches(twoVarTestCase, "42 41"); + self.assertDoesNotMatch(twoVarTestCase, "42 43") diff --git a/tools/checker/run_unit_tests.py b/tools/checker/run_unit_tests.py index 2f5b1feaa6..2e8f2083b5 100755 --- a/tools/checker/run_unit_tests.py +++ b/tools/checker/run_unit_tests.py @@ -17,9 +17,10 @@ from common.logger import Logger from file_format.c1visualizer.test import C1visualizerParser_Test from file_format.checker.test import CheckerParser_PrefixTest, \ - CheckerParser_RegexExpressionTest, \ + CheckerParser_TestExpressionTest, \ CheckerParser_FileLayoutTest, \ - CheckerParser_ArchTests + CheckerParser_ArchTests, \ + CheckerParser_EvalTests from match.test import MatchLines_Test, \ MatchFiles_Test diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh index 7faf86ed5c..8466bb314c 100755 --- a/tools/setup-buildbot-device.sh +++ b/tools/setup-buildbot-device.sh @@ -30,3 +30,6 @@ adb shell ifconfig echo -e "${green}List properties${nc}" adb shell getprop + +echo -e "${green}Stopping framework${nc}" +adb shell stop |