| //===-- DILEval.cpp -------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "lldb/ValueObject/DILEval.h" | |
| #include "lldb/Core/Module.h" | |
| #include "lldb/Expression/ExpressionVariable.h" | |
| #include "lldb/Symbol/CompileUnit.h" | |
| #include "lldb/Symbol/TypeSystem.h" | |
| #include "lldb/Symbol/VariableList.h" | |
| #include "lldb/Target/ExecutionContext.h" | |
| #include "lldb/Target/RegisterContext.h" | |
| #include "lldb/Target/Target.h" | |
| #include "lldb/Utility/LLDBLog.h" | |
| #include "lldb/ValueObject/DILAST.h" | |
| #include "lldb/ValueObject/DILParser.h" | |
| #include "lldb/ValueObject/ValueObject.h" | |
| #include "lldb/ValueObject/ValueObjectRegister.h" | |
| #include "lldb/ValueObject/ValueObjectVariable.h" | |
| #include "llvm/Support/ErrorExtras.h" | |
| #include "llvm/Support/FormatAdapters.h" | |
| #include <memory> | |
| #include <optional> | |
| namespace lldb_private::dil { | |
| static CompilerType GetBasicType(lldb::TypeSystemSP type_system, | |
| lldb::BasicType basic_type) { | |
| if (type_system) | |
| return type_system.get()->GetBasicTypeFromAST(basic_type); | |
| return CompilerType(); | |
| } | |
| static lldb::ValueObjectSP ArrayToPointerConversion(ValueObject &valobj, | |
| ExecutionContext &exe_ctx, | |
| llvm::StringRef name) { | |
| uint64_t addr = valobj.GetLoadAddress(); | |
| return ValueObject::CreateValueObjectFromAddress( | |
| name, addr, exe_ctx, | |
| valobj.GetCompilerType() | |
| .GetArrayElementType(exe_ctx.GetBestExecutionContextScope()) | |
| .GetPointerType(), | |
| /* do_deref */ false); | |
| } | |
| static llvm::Expected<SymbolContext> | |
| GetSymbolContexFromFrame(ExecutionContext &exe_ctx) { | |
| auto frame_sp = exe_ctx.GetFrameSP(); | |
| if (!frame_sp) | |
| return llvm::createStringError("no stack frame in execution context"); | |
| SymbolContext symbol_context = frame_sp->GetSymbolContext( | |
| lldb::eSymbolContextCompUnit | lldb::eSymbolContextModule); | |
| if (!symbol_context.comp_unit) | |
| return llvm::createStringErrorV("no compile unit for frame: {0}", | |
| frame_sp->GetFunctionName()); | |
| if (!symbol_context.module_sp) | |
| return llvm::createStringErrorV("no module for frame: {0}", | |
| frame_sp->GetFunctionName()); | |
| return symbol_context; | |
| } | |
| llvm::Expected<lldb::LanguageType> Interpreter::GetSourceLanguageFromCU() { | |
| llvm::Expected<SymbolContext> symbol_context = | |
| GetSymbolContexFromFrame(m_exe_ctx); | |
| if (!symbol_context) | |
| return symbol_context.takeError(); | |
| return symbol_context->comp_unit->GetLanguage(); | |
| } | |
| llvm::Expected<lldb::TypeSystemSP> Interpreter::GetTypeSystemFromCU() { | |
| lldb::LanguageType language = lldb::eLanguageTypeUnknown; | |
| if (auto frame_sp = m_exe_ctx.GetFrameSP()) { | |
| SymbolContext symbol_context = | |
| frame_sp->GetSymbolContext(lldb::eSymbolContextCompUnit); | |
| if (symbol_context.comp_unit) { | |
| language = symbol_context.comp_unit->GetLanguage(); | |
| symbol_context = frame_sp->GetSymbolContext(lldb::eSymbolContextModule); | |
| if (symbol_context.module_sp) | |
| return symbol_context.module_sp->GetTypeSystemForLanguage(language); | |
| } | |
| } | |
| lldb::TargetSP target_sp = m_exe_ctx.GetTargetSP(); | |
| if (!target_sp) | |
| return llvm::createStringError("no target for frame"); | |
| if (language == lldb::eLanguageTypeUnknown) | |
| language = target_sp->GetLanguage().AsLanguageType(); | |
| return target_sp->GetScratchTypeSystemForLanguage(language); | |
| } | |
| // DIL normally treats '.' as member access, but register names may contain | |
| // periods. Reconstruct the candidate so an exact register name takes | |
| // precedence. | |
| static std::optional<std::string> GetRegisterPathName(const ASTNode &node) { | |
| if (node.GetKind() == NodeKind::eIdentifierNode) { | |
| std::string name_storage = | |
| static_cast<const IdentifierNode &>(node).GetName(); | |
| llvm::StringRef name = name_storage; | |
| if (name.consume_front("$")) | |
| return name.str(); | |
| return std::nullopt; | |
| } | |
| if (node.GetKind() != NodeKind::eMemberOfNode) | |
| return std::nullopt; | |
| const auto &member = static_cast<const MemberOfNode &>(node); | |
| if (member.GetIsArrow()) | |
| return std::nullopt; | |
| std::optional<std::string> base_name = GetRegisterPathName(member.GetBase()); | |
| if (!base_name) | |
| return std::nullopt; | |
| return *base_name + "." + member.GetFieldName().str(); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::UnaryConversion(lldb::ValueObjectSP valobj, uint32_t location) { | |
| if (!valobj) | |
| return llvm::make_error<DILDiagnosticError>(m_expr, "invalid value object", | |
| location); | |
| llvm::Expected<lldb::TypeSystemSP> type_system = GetTypeSystemFromCU(); | |
| if (!type_system) | |
| return type_system.takeError(); | |
| CompilerType in_type = valobj->GetCompilerType(); | |
| if (valobj->IsBitfield()) { | |
| // Promote bitfields. If `int` can represent the bitfield value, it is | |
| // converted to `int`. Otherwise, if `unsigned int` can represent it, it | |
| // is converted to `unsigned int`. Otherwise, it is treated as its | |
| // underlying type. | |
| uint32_t bitfield_size = valobj->GetBitfieldBitSize(); | |
| // Some bitfields have undefined size (e.g. result of ternary operation). | |
| // The AST's `bitfield_size` of those is 0, and no promotion takes place. | |
| if (bitfield_size > 0 && in_type.IsInteger()) { | |
| CompilerType int_type = GetBasicType(*type_system, lldb::eBasicTypeInt); | |
| CompilerType uint_type = | |
| GetBasicType(*type_system, lldb::eBasicTypeUnsignedInt); | |
| llvm::Expected<uint64_t> int_bit_size = | |
| int_type.GetBitSize(m_exe_ctx.GetBestExecutionContextScope()); | |
| if (!int_bit_size) | |
| return int_bit_size.takeError(); | |
| llvm::Expected<uint64_t> uint_bit_size = | |
| uint_type.GetBitSize(m_exe_ctx.GetBestExecutionContextScope()); | |
| if (!uint_bit_size) | |
| return uint_bit_size.takeError(); | |
| if (bitfield_size < *int_bit_size || | |
| (in_type.IsSigned() && bitfield_size == *int_bit_size)) { | |
| auto result = valobj->CastToBasicType(int_type); | |
| if (result->GetError().Fail()) | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, result->GetError().AsCString(), location); | |
| return result; | |
| } | |
| if (bitfield_size <= *uint_bit_size) { | |
| auto result = valobj->CastToBasicType(uint_type); | |
| if (result->GetError().Fail()) | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, result->GetError().AsCString(), location); | |
| return result; | |
| } | |
| // Re-create as a const value with the same underlying type | |
| Scalar scalar; | |
| bool resolved = valobj->ResolveValue(scalar); | |
| if (!resolved) | |
| return llvm::createStringError("invalid scalar value"); | |
| return ValueObject::CreateValueObjectFromScalar(m_exe_ctx, scalar, | |
| in_type, "result"); | |
| } | |
| } | |
| if (in_type.IsArrayType()) | |
| valobj = ArrayToPointerConversion(*valobj, m_exe_ctx, "result"); | |
| CompilerType promoted_type = | |
| valobj->GetCompilerType().GetPromotedIntegerType(); | |
| if (promoted_type) { | |
| auto result = valobj->CastToBasicType(promoted_type); | |
| if (result->GetError().Fail()) | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, result->GetError().AsCString(), location); | |
| return result; | |
| } | |
| return valobj; | |
| } | |
| /// Basic types with a lower rank are converted to the basic type | |
| /// with a higher rank. | |
| static size_t ConversionRank(CompilerType type) { | |
| switch (type.GetCanonicalType().GetBasicTypeEnumeration()) { | |
| case lldb::eBasicTypeBool: | |
| return 1; | |
| case lldb::eBasicTypeChar: | |
| case lldb::eBasicTypeSignedChar: | |
| case lldb::eBasicTypeUnsignedChar: | |
| return 2; | |
| case lldb::eBasicTypeShort: | |
| case lldb::eBasicTypeUnsignedShort: | |
| return 3; | |
| case lldb::eBasicTypeInt: | |
| case lldb::eBasicTypeUnsignedInt: | |
| return 4; | |
| case lldb::eBasicTypeLong: | |
| case lldb::eBasicTypeUnsignedLong: | |
| return 5; | |
| case lldb::eBasicTypeLongLong: | |
| case lldb::eBasicTypeUnsignedLongLong: | |
| return 6; | |
| case lldb::eBasicTypeInt128: | |
| case lldb::eBasicTypeUnsignedInt128: | |
| return 7; | |
| case lldb::eBasicTypeHalf: | |
| return 8; | |
| case lldb::eBasicTypeFloat: | |
| return 9; | |
| case lldb::eBasicTypeDouble: | |
| return 10; | |
| case lldb::eBasicTypeLongDouble: | |
| return 11; | |
| default: | |
| break; | |
| } | |
| return 0; | |
| } | |
| static lldb::BasicType BasicTypeToUnsigned(lldb::BasicType basic_type) { | |
| switch (basic_type) { | |
| case lldb::eBasicTypeChar: | |
| case lldb::eBasicTypeSignedChar: | |
| return lldb::eBasicTypeUnsignedChar; | |
| case lldb::eBasicTypeShort: | |
| return lldb::eBasicTypeUnsignedShort; | |
| case lldb::eBasicTypeInt: | |
| return lldb::eBasicTypeUnsignedInt; | |
| case lldb::eBasicTypeLong: | |
| return lldb::eBasicTypeUnsignedLong; | |
| case lldb::eBasicTypeLongLong: | |
| return lldb::eBasicTypeUnsignedLongLong; | |
| case lldb::eBasicTypeInt128: | |
| return lldb::eBasicTypeUnsignedInt128; | |
| default: | |
| return basic_type; | |
| } | |
| } | |
| llvm::Expected<CompilerType> | |
| Interpreter::PromoteSignedInteger(CompilerType &lhs_type, | |
| CompilerType &rhs_type) { | |
| assert(lhs_type.IsInteger() && rhs_type.IsInteger()); | |
| if (!lhs_type.IsSigned() && rhs_type.IsSigned()) { | |
| llvm::Expected<uint64_t> lhs_size = | |
| lhs_type.GetBitSize(m_exe_ctx.GetBestExecutionContextScope()); | |
| if (!lhs_size) | |
| return lhs_size.takeError(); | |
| llvm::Expected<uint64_t> rhs_size = | |
| rhs_type.GetBitSize(m_exe_ctx.GetBestExecutionContextScope()); | |
| if (!rhs_size) | |
| return rhs_size.takeError(); | |
| if (*rhs_size == *lhs_size) { | |
| llvm::Expected<lldb::TypeSystemSP> type_system = GetTypeSystemFromCU(); | |
| if (!type_system) | |
| return type_system.takeError(); | |
| CompilerType r_type_unsigned = GetBasicType( | |
| *type_system, | |
| BasicTypeToUnsigned( | |
| rhs_type.GetCanonicalType().GetBasicTypeEnumeration())); | |
| return r_type_unsigned; | |
| } | |
| } | |
| return rhs_type; | |
| } | |
| llvm::Expected<CompilerType> | |
| Interpreter::ArithmeticConversion(lldb::ValueObjectSP &lhs, | |
| lldb::ValueObjectSP &rhs, uint32_t location) { | |
| // Apply unary conversion for both operands. | |
| auto lhs_or_err = UnaryConversion(lhs, location); | |
| if (!lhs_or_err) | |
| return lhs_or_err.takeError(); | |
| lhs = *lhs_or_err; | |
| auto rhs_or_err = UnaryConversion(rhs, location); | |
| if (!rhs_or_err) | |
| return rhs_or_err.takeError(); | |
| rhs = *rhs_or_err; | |
| CompilerType lhs_type = lhs->GetCompilerType(); | |
| CompilerType rhs_type = rhs->GetCompilerType(); | |
| // If types already match, no need for further conversions. | |
| if (lhs_type.CompareTypes(rhs_type)) | |
| return lhs_type; | |
| // If either of the operands is not arithmetic (e.g. pointer), we're done. | |
| if (!lhs_type.IsScalarType() || !rhs_type.IsScalarType()) | |
| return CompilerType(); | |
| size_t l_rank = ConversionRank(lhs_type); | |
| size_t r_rank = ConversionRank(rhs_type); | |
| if (l_rank == 0 || r_rank == 0) | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "unexpected basic type in arithmetic operation", location); | |
| // If both operands are integer, check if we need to promote | |
| // the higher ranked signed type. | |
| if (lhs_type.IsInteger() && rhs_type.IsInteger()) { | |
| using Rank = std::tuple<size_t, bool>; | |
| Rank int_l_rank = {l_rank, !lhs_type.IsSigned()}; | |
| Rank int_r_rank = {r_rank, !rhs_type.IsSigned()}; | |
| if (int_l_rank < int_r_rank) { | |
| auto type_or_err = PromoteSignedInteger(lhs_type, rhs_type); | |
| if (!type_or_err) | |
| return type_or_err.takeError(); | |
| return *type_or_err; | |
| } | |
| if (int_l_rank > int_r_rank) { | |
| auto type_or_err = PromoteSignedInteger(rhs_type, lhs_type); | |
| if (!type_or_err) | |
| return type_or_err.takeError(); | |
| return *type_or_err; | |
| } | |
| return lhs_type; | |
| } | |
| // Handle other combinations of integer and floating point operands. | |
| if (l_rank < r_rank) | |
| return rhs_type; | |
| return lhs_type; | |
| } | |
| static lldb::VariableSP DILFindVariable(ConstString name, | |
| VariableList &variable_list) { | |
| lldb::VariableSP exact_match; | |
| std::vector<lldb::VariableSP> possible_matches; | |
| for (lldb::VariableSP var_sp : variable_list) { | |
| llvm::StringRef str_ref_name = var_sp->GetName().GetStringRef(); | |
| str_ref_name.consume_front("::"); | |
| // Check for the exact same match | |
| if (str_ref_name == name.GetStringRef()) | |
| return var_sp; | |
| // Check for possible matches by base name | |
| if (var_sp->NameMatches(name)) | |
| possible_matches.push_back(var_sp); | |
| } | |
| // If there's a non-exact match, take it. | |
| if (possible_matches.size() > 0) | |
| return possible_matches[0]; | |
| return nullptr; | |
| } | |
| lldb::ValueObjectSP LookupGlobalIdentifier(llvm::StringRef name_ref, | |
| ExecutionContext &exe_ctx, | |
| lldb::DynamicValueType use_dynamic) { | |
| // Get a global variables list without the locals from the current frame | |
| auto frame_sp = exe_ctx.GetFrameSP(); | |
| if (!frame_sp) | |
| return nullptr; | |
| SymbolContext symbol_context = | |
| frame_sp->GetSymbolContext(lldb::eSymbolContextCompUnit); | |
| lldb::VariableListSP variable_list; | |
| if (symbol_context.comp_unit) | |
| variable_list = symbol_context.comp_unit->GetVariableList(true); | |
| name_ref.consume_front("::"); | |
| lldb::ValueObjectSP value_sp; | |
| if (variable_list) { | |
| lldb::VariableSP var_sp = | |
| DILFindVariable(ConstString(name_ref), *variable_list); | |
| if (var_sp) | |
| value_sp = frame_sp->GetValueObjectForFrameVariable(var_sp, use_dynamic); | |
| } | |
| if (value_sp) | |
| return value_sp; | |
| // Check for match in modules global variables. | |
| auto target_sp = exe_ctx.GetTargetSP(); | |
| if (!target_sp) | |
| return nullptr; | |
| VariableList modules_var_list; | |
| target_sp->GetImages().FindGlobalVariables( | |
| ConstString(name_ref), std::numeric_limits<uint32_t>::max(), | |
| modules_var_list); | |
| if (!modules_var_list.Empty()) { | |
| lldb::VariableSP var_sp = | |
| DILFindVariable(ConstString(name_ref), modules_var_list); | |
| if (var_sp) | |
| value_sp = ValueObjectVariable::Create( | |
| exe_ctx.GetBestExecutionContextScope(), var_sp); | |
| if (value_sp) | |
| return value_sp; | |
| } | |
| return nullptr; | |
| } | |
| lldb::ValueObjectSP LookupPersistentIdentifier(llvm::StringRef name_ref, | |
| ExecutionContext &exe_ctx, | |
| lldb::LanguageType language) { | |
| auto target_sp = exe_ctx.GetTargetSP(); | |
| if (!target_sp) | |
| return nullptr; | |
| if (name_ref.starts_with("$")) { | |
| if (auto *state = | |
| target_sp->GetPersistentExpressionStateForLanguage(language)) | |
| if (auto var_sp = state->GetVariable(name_ref)) | |
| if (auto valobj_sp = var_sp->GetValueObject()) | |
| return valobj_sp; | |
| } | |
| return nullptr; | |
| } | |
| lldb::ValueObjectSP LookupIdentifier(llvm::StringRef name_ref, | |
| ExecutionContext &exe_ctx, | |
| lldb::DynamicValueType use_dynamic) { | |
| // Support $rax as a special syntax for accessing registers. | |
| // Will return an invalid value in case the requested register doesn't exist. | |
| auto frame_sp = exe_ctx.GetFrameSP(); | |
| if (!frame_sp) | |
| return nullptr; | |
| if (name_ref.consume_front("$")) { | |
| lldb::RegisterContextSP reg_ctx(frame_sp->GetRegisterContext()); | |
| if (!reg_ctx) | |
| return nullptr; | |
| if (const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name_ref)) | |
| return ValueObjectRegister::Create(exe_ctx.GetBestExecutionContextScope(), | |
| reg_ctx, reg_info); | |
| return nullptr; | |
| } | |
| if (!name_ref.contains("::")) { | |
| // Lookup in the current frame. | |
| // Try looking for a local variable in current scope. | |
| lldb::VariableListSP variable_list(frame_sp->GetInScopeVariableList(false)); | |
| lldb::ValueObjectSP value_sp; | |
| if (variable_list) { | |
| lldb::VariableSP var_sp = | |
| variable_list->FindVariable(ConstString(name_ref)); | |
| if (var_sp) | |
| value_sp = | |
| frame_sp->GetValueObjectForFrameVariable(var_sp, use_dynamic); | |
| } | |
| if (value_sp) | |
| return value_sp; | |
| // Try looking for an instance variable (class member). | |
| SymbolContext sc = frame_sp->GetSymbolContext(lldb::eSymbolContextFunction | | |
| lldb::eSymbolContextBlock); | |
| llvm::StringRef instance_name = sc.GetInstanceName(); | |
| value_sp = frame_sp->FindVariable(ConstString(instance_name)); | |
| if (value_sp) | |
| value_sp = value_sp->GetChildMemberWithName(name_ref); | |
| if (value_sp) | |
| return value_sp; | |
| } | |
| return nullptr; | |
| } | |
| lldb::ValueObjectSP LookupEnumValue(llvm::StringRef name_ref, | |
| ExecutionContext &exe_ctx) { | |
| if (name_ref.contains("::")) { | |
| llvm::StringRef enum_typename, enumerator_name; | |
| // FIXME: Change this to a structured binding for lambda capturing | |
| // once we have C++20. | |
| std::tie(enum_typename, enumerator_name) = name_ref.rsplit("::"); | |
| CompilerType enum_type = ResolveTypeByName(enum_typename.str(), exe_ctx); | |
| lldb::ValueObjectSP result; | |
| enum_type.ForEachEnumerator([&](const CompilerType &integer_type, | |
| ConstString name, | |
| const llvm::APSInt &value) -> bool { | |
| if (name == enumerator_name) { | |
| Scalar scalar(value); | |
| result = ValueObject::CreateValueObjectFromScalar(exe_ctx, scalar, | |
| enum_type, "result"); | |
| return false; // Stop iterating | |
| } | |
| return true; | |
| }); | |
| return result; | |
| } | |
| return nullptr; | |
| } | |
| Interpreter::Interpreter(ExecutionContext &exe_ctx, llvm::StringRef expr, | |
| lldb::DynamicValueType use_dynamic, uint32_t options) | |
| : m_exe_ctx(exe_ctx), m_expr(expr), m_use_dynamic(use_dynamic) { | |
| const bool check_ptr_vs_member = | |
| (options & StackFrame::eExpressionPathOptionCheckPtrVsMember) != 0; | |
| const bool no_synth_child = | |
| (options & StackFrame::eExpressionPathOptionsNoSyntheticChildren) != 0; | |
| const bool allow_var_updates = | |
| (options & StackFrame::eExpressionPathOptionsAllowVarUpdates) != 0; | |
| const bool disallow_globals = | |
| (options & StackFrame::eExpressionPathOptionsDisallowGlobals) != 0; | |
| m_use_synthetic = !no_synth_child; | |
| m_check_ptr_vs_member = check_ptr_vs_member; | |
| m_allow_var_updates = allow_var_updates; | |
| m_allow_globals = !disallow_globals; | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::EvaluateTree(const ASTNodeUP &tree) { | |
| assert(tree && "ASTNodeUP must not contain a nullptr"); | |
| auto value_or_error = Interpreter::Evaluate(*tree); | |
| if (!value_or_error) { | |
| auto error = value_or_error.takeError(); | |
| LLDB_LOG(GetLog(LLDBLog::Expressions), | |
| "[Interpreter::Evaluate] DIL interpreter failed:\n{0}", | |
| llvm::toStringWithoutConsuming(error)); | |
| return error; | |
| } | |
| return value_or_error; | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode &node) { | |
| // Evaluate an AST. | |
| auto value_or_error = node.Accept(this); | |
| // Convert SP with a nullptr to an error. | |
| if (value_or_error && !*value_or_error) | |
| return llvm::make_error<DILDiagnosticError>(m_expr, "invalid value object", | |
| node.GetLocation()); | |
| // Return the computed value-or-error. The caller is responsible for | |
| // checking if an error occurred during the evaluation. | |
| return value_or_error; | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::EvaluateAndDereference(const ASTNode &node) { | |
| auto valobj_or_err = Evaluate(node); | |
| if (!valobj_or_err) | |
| return valobj_or_err; | |
| lldb::ValueObjectSP valobj = *valobj_or_err; | |
| Status error; | |
| if (valobj->GetCompilerType().IsReferenceType()) { | |
| valobj = valobj->Dereference(error); | |
| if (error.Fail()) | |
| return error.ToError(); | |
| } | |
| return valobj; | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::Visit(const IdentifierNode &node) { | |
| lldb::DynamicValueType use_dynamic = m_use_dynamic; | |
| lldb::ValueObjectSP identifier = | |
| LookupIdentifier(node.GetName(), m_exe_ctx, use_dynamic); | |
| if (!identifier && m_allow_globals) | |
| identifier = LookupGlobalIdentifier(node.GetName(), m_exe_ctx, use_dynamic); | |
| if (!identifier) | |
| identifier = LookupEnumValue(node.GetName(), m_exe_ctx); | |
| if (!identifier && node.GetName()[0] == '$') { | |
| auto language = GetSourceLanguageFromCU(); | |
| if (language) | |
| identifier = | |
| LookupPersistentIdentifier(node.GetName(), m_exe_ctx, language.get()); | |
| else | |
| // Fall through to the unknown identifier diagnostic without a CU. | |
| llvm::consumeError(language.takeError()); | |
| } | |
| if (!identifier && node.GetName() == "nullptr") { | |
| // If we got a "nullptr" identifier, and there is no defined variable with | |
| // this name, resolve it as a null pointer. | |
| llvm::Expected<lldb::TypeSystemSP> type_system = GetTypeSystemFromCU(); | |
| if (!type_system) | |
| return type_system.takeError(); | |
| llvm::APInt value(type_system.get()->GetPointerByteSize() * CHAR_BIT, 0); | |
| Scalar scalar(value); | |
| CompilerType type = GetBasicType(*type_system, lldb::eBasicTypeNullPtr); | |
| return ValueObject::CreateValueObjectFromScalar(m_exe_ctx, scalar, type, | |
| "result"); | |
| } | |
| if (!identifier) { | |
| std::string errMsg = | |
| llvm::formatv("use of undeclared identifier '{0}'", node.GetName()); | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, errMsg, node.GetLocation(), node.GetName().size()); | |
| } | |
| return identifier; | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::Visit(const UnaryOpNode &node) { | |
| Status error; | |
| auto op_or_err = Evaluate(node.GetOperand()); | |
| if (!op_or_err) | |
| return op_or_err; | |
| lldb::ValueObjectSP operand = *op_or_err; | |
| switch (node.GetKind()) { | |
| case UnaryOpKind::Deref: { | |
| lldb::ValueObjectSP dynamic_op = operand->GetDynamicValue(m_use_dynamic); | |
| if (dynamic_op) | |
| operand = dynamic_op; | |
| lldb::ValueObjectSP child_sp = operand->Dereference(error); | |
| if (!child_sp && m_use_synthetic) { | |
| if (lldb::ValueObjectSP synth_obj_sp = operand->GetSyntheticValue()) { | |
| error.Clear(); | |
| child_sp = synth_obj_sp->Dereference(error); | |
| } | |
| } | |
| if (error.Fail()) | |
| return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(), | |
| node.GetLocation()); | |
| return child_sp; | |
| } | |
| case UnaryOpKind::AddrOf: { | |
| Status error; | |
| lldb::ValueObjectSP value = operand->AddressOf(error); | |
| if (error.Fail()) | |
| return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(), | |
| node.GetLocation()); | |
| return value; | |
| } | |
| case UnaryOpKind::Minus: { | |
| if (operand->GetCompilerType().IsReferenceType()) { | |
| operand = operand->Dereference(error); | |
| if (error.Fail()) | |
| return error.ToError(); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> conv_op = | |
| UnaryConversion(operand, node.GetOperand().GetLocation()); | |
| if (!conv_op) | |
| return conv_op; | |
| operand = *conv_op; | |
| CompilerType operand_type = operand->GetCompilerType(); | |
| if (!operand_type.IsScalarType()) { | |
| std::string errMsg = | |
| llvm::formatv("invalid argument type '{0}' to unary expression", | |
| operand_type.GetTypeName()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, | |
| node.GetLocation()); | |
| } | |
| Scalar scalar; | |
| bool resolved = operand->ResolveValue(scalar); | |
| if (!resolved) | |
| break; | |
| bool negated = scalar.UnaryNegate(); | |
| if (negated) | |
| return ValueObject::CreateValueObjectFromScalar( | |
| m_exe_ctx, scalar, operand->GetCompilerType(), "result"); | |
| break; | |
| } | |
| case UnaryOpKind::Plus: { | |
| if (operand->GetCompilerType().IsReferenceType()) { | |
| operand = operand->Dereference(error); | |
| if (error.Fail()) | |
| return error.ToError(); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> conv_op = | |
| UnaryConversion(operand, node.GetOperand().GetLocation()); | |
| if (!conv_op) | |
| return conv_op; | |
| operand = *conv_op; | |
| CompilerType operand_type = operand->GetCompilerType(); | |
| if (!operand_type.IsScalarType() && | |
| // Unary plus is allowed for pointers. | |
| !operand_type.IsPointerType()) { | |
| std::string errMsg = | |
| llvm::formatv("invalid argument type '{0}' to unary expression", | |
| operand_type.GetTypeName()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, | |
| node.GetLocation()); | |
| } | |
| return operand; | |
| } | |
| case UnaryOpKind::Not: { | |
| if (operand->GetCompilerType().IsReferenceType()) { | |
| operand = operand->Dereference(error); | |
| if (error.Fail()) | |
| return error.ToError(); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> conv_op = | |
| UnaryConversion(operand, node.GetLocation()); | |
| if (!conv_op) | |
| return conv_op; | |
| operand = *conv_op; | |
| CompilerType operand_type = operand->GetCompilerType(); | |
| if (!operand_type.IsInteger()) { | |
| std::string errMsg = | |
| llvm::formatv("invalid argument type '{0}' to unary expression", | |
| operand_type.GetTypeName()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, | |
| node.GetLocation()); | |
| } | |
| Scalar scalar; | |
| bool resolved = operand->ResolveValue(scalar); | |
| if (!resolved) { | |
| std::string errMsg = llvm::formatv("invalid operand value: {0}", | |
| operand->GetError().AsCString()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, | |
| node.GetLocation()); | |
| } | |
| bool flipped = scalar.OnesComplement(); | |
| if (flipped) | |
| return ValueObject::CreateValueObjectFromScalar( | |
| m_exe_ctx, scalar, operand->GetCompilerType(), "result"); | |
| break; | |
| } | |
| case UnaryOpKind::LNot: { | |
| if (operand->GetCompilerType().IsReferenceType()) { | |
| operand = operand->Dereference(error); | |
| if (error.Fail()) | |
| return error.ToError(); | |
| } | |
| CompilerType operand_type = operand->GetCompilerType(); | |
| if (!operand_type.IsContextuallyConvertibleToBool()) { | |
| std::string errMsg = | |
| llvm::formatv("invalid argument type '{0}' to unary expression", | |
| operand_type.GetTypeName()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, | |
| node.GetLocation()); | |
| } | |
| llvm::Expected<lldb::TypeSystemSP> type_system = GetTypeSystemFromCU(); | |
| if (!type_system) | |
| return type_system.takeError(); | |
| auto value_or_err = operand->GetValueAsBool(); | |
| if (!value_or_err) | |
| return value_or_err.takeError(); | |
| return ValueObject::CreateValueObjectFromBool(m_exe_ctx, *type_system, | |
| !(*value_or_err), "result"); | |
| } | |
| } | |
| return llvm::make_error<DILDiagnosticError>(m_expr, "invalid unary operation", | |
| node.GetLocation()); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::PointerOffset(lldb::ValueObjectSP ptr, lldb::ValueObjectSP offset, | |
| BinaryOpKind operation, uint32_t location) { | |
| assert(operation == BinaryOpKind::Add || operation == BinaryOpKind::Sub); | |
| if (ptr->GetCompilerType().IsPointerToVoid()) | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "arithmetic on a pointer to void", location); | |
| if (ptr->GetValueAsUnsigned(0) == 0 && offset != 0) | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "arithmetic on a nullptr is undefined", location); | |
| bool success; | |
| int64_t offset_int = offset->GetValueAsSigned(0, &success); | |
| if (!success) { | |
| std::string errMsg = llvm::formatv("could not get the offset: {0}", | |
| offset->GetError().AsCString()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg), | |
| location); | |
| } | |
| llvm::Expected<uint64_t> byte_size = | |
| ptr->GetCompilerType().GetPointeeType().GetByteSize( | |
| m_exe_ctx.GetBestExecutionContextScope()); | |
| if (!byte_size) | |
| return byte_size.takeError(); | |
| uint64_t ptr_addr = ptr->GetValueAsUnsigned(0); | |
| if (operation == BinaryOpKind::Sub) | |
| ptr_addr -= offset_int * (*byte_size); | |
| else | |
| ptr_addr += offset_int * (*byte_size); | |
| Scalar scalar(ptr_addr); | |
| return ValueObject::CreateValueObjectFromScalar( | |
| m_exe_ctx, scalar, ptr->GetCompilerType(), "result"); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::EvaluateScalarOp(BinaryOpKind kind, lldb::ValueObjectSP lhs, | |
| lldb::ValueObjectSP rhs, CompilerType result_type, | |
| uint32_t location) { | |
| Scalar l, r; | |
| bool l_resolved = lhs->ResolveValue(l); | |
| if (!l_resolved) { | |
| std::string errMsg = | |
| llvm::formatv("invalid lhs value: {0}", lhs->GetError().AsCString()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location); | |
| } | |
| bool r_resolved = rhs->ResolveValue(r); | |
| if (!r_resolved) { | |
| std::string errMsg = | |
| llvm::formatv("invalid rhs value: {0}", rhs->GetError().AsCString()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location); | |
| } | |
| auto value_object = [this, result_type](Scalar scalar) { | |
| return ValueObject::CreateValueObjectFromScalar(m_exe_ctx, scalar, | |
| result_type, "result"); | |
| }; | |
| switch (kind) { | |
| case BinaryOpKind::Add: | |
| return value_object(l + r); | |
| case BinaryOpKind::Sub: | |
| return value_object(l - r); | |
| case BinaryOpKind::Mul: | |
| return value_object(l * r); | |
| case BinaryOpKind::Div: | |
| return value_object(l / r); | |
| case BinaryOpKind::Rem: | |
| return value_object(l % r); | |
| case BinaryOpKind::And: | |
| return value_object(l & r); | |
| case BinaryOpKind::Xor: | |
| return value_object(l ^ r); | |
| case BinaryOpKind::Or: | |
| return value_object(l | r); | |
| case BinaryOpKind::Shl: | |
| return value_object(l << r); | |
| case BinaryOpKind::Shr: | |
| return value_object(l >> r); | |
| case BinaryOpKind::LT: | |
| return value_object(l < r); | |
| case BinaryOpKind::GT: | |
| return value_object(l > r); | |
| case BinaryOpKind::LE: | |
| return value_object(l <= r); | |
| case BinaryOpKind::GE: | |
| return value_object(l >= r); | |
| case BinaryOpKind::EQ: | |
| return value_object(l == r); | |
| case BinaryOpKind::NE: | |
| return value_object(l != r); | |
| default: | |
| break; | |
| } | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "invalid arithmetic operation", location); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryAddition( | |
| lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) { | |
| // Operation '+' works for: | |
| // {scalar,unscoped_enum} <-> {scalar,unscoped_enum} | |
| // {integer,unscoped_enum} <-> pointer | |
| // pointer <-> {integer,unscoped_enum} | |
| auto orig_lhs_type = lhs->GetCompilerType(); | |
| auto orig_rhs_type = rhs->GetCompilerType(); | |
| auto type_or_err = ArithmeticConversion(lhs, rhs, location); | |
| if (!type_or_err) | |
| return type_or_err.takeError(); | |
| CompilerType result_type = *type_or_err; | |
| if (result_type.IsScalarType()) | |
| return EvaluateScalarOp(BinaryOpKind::Add, lhs, rhs, result_type, location); | |
| // Check for pointer arithmetics. | |
| // One of the operands must be a pointer and the other one an integer. | |
| lldb::ValueObjectSP ptr, offset; | |
| if (lhs->GetCompilerType().IsPointerType()) { | |
| ptr = lhs; | |
| offset = rhs; | |
| } else if (rhs->GetCompilerType().IsPointerType()) { | |
| ptr = rhs; | |
| offset = lhs; | |
| } | |
| if (!ptr || !offset->GetCompilerType().IsInteger()) { | |
| std::string errMsg = | |
| llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')", | |
| orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg), | |
| location); | |
| } | |
| return PointerOffset(ptr, offset, BinaryOpKind::Add, location); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinarySubtraction( | |
| lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) { | |
| // Operation '-' works for: | |
| // {scalar,unscoped_enum} <-> {scalar,unscoped_enum} | |
| // pointer <-> {integer,unscoped_enum} | |
| // pointer <-> pointer (if pointee types are compatible) | |
| auto orig_lhs_type = lhs->GetCompilerType(); | |
| auto orig_rhs_type = rhs->GetCompilerType(); | |
| auto type_or_err = ArithmeticConversion(lhs, rhs, location); | |
| if (!type_or_err) | |
| return type_or_err.takeError(); | |
| CompilerType result_type = *type_or_err; | |
| if (result_type.IsScalarType()) | |
| return EvaluateScalarOp(BinaryOpKind::Sub, lhs, rhs, result_type, location); | |
| auto lhs_type = lhs->GetCompilerType(); | |
| auto rhs_type = rhs->GetCompilerType(); | |
| // "pointer - integer" operation. | |
| if (lhs_type.IsPointerType() && rhs_type.IsInteger()) | |
| return PointerOffset(lhs, rhs, BinaryOpKind::Sub, location); | |
| // "pointer - pointer" operation. | |
| if (lhs_type.IsPointerType() && rhs_type.IsPointerType()) { | |
| if (lhs_type.IsPointerToVoid() && rhs_type.IsPointerToVoid()) { | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "arithmetic on pointers to void", location); | |
| } | |
| // Compare canonical unqualified pointer types. | |
| CompilerType lhs_unqualified_type = lhs_type.GetCanonicalType(); | |
| CompilerType rhs_unqualified_type = rhs_type.GetCanonicalType(); | |
| if (!lhs_unqualified_type.CompareTypes(rhs_unqualified_type)) { | |
| std::string errMsg = llvm::formatv( | |
| "'{0}' and '{1}' are not pointers to compatible types", | |
| orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location); | |
| } | |
| llvm::Expected<uint64_t> lhs_byte_size = | |
| lhs_type.GetPointeeType().GetByteSize( | |
| m_exe_ctx.GetBestExecutionContextScope()); | |
| if (!lhs_byte_size) | |
| return lhs_byte_size.takeError(); | |
| // Since pointers have compatible types, both have the same pointee size. | |
| int64_t item_size = *lhs_byte_size; | |
| int64_t diff = static_cast<int64_t>(lhs->GetValueAsUnsigned(0) - | |
| rhs->GetValueAsUnsigned(0)); | |
| assert(item_size > 0 && "Pointee size cannot be 0"); | |
| if (diff % item_size != 0) { | |
| // If address difference isn't divisible by pointee size then performing | |
| // the operation is undefined behaviour. | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "undefined pointer arithmetic", location); | |
| } | |
| diff /= item_size; | |
| llvm::Expected<lldb::TypeSystemSP> type_system = GetTypeSystemFromCU(); | |
| if (!type_system) | |
| return type_system.takeError(); | |
| CompilerType ptrdiff_type = type_system.get()->GetPointerDiffType(true); | |
| if (!ptrdiff_type) | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "unable to determine pointer diff type", location); | |
| Scalar scalar(diff); | |
| return ValueObject::CreateValueObjectFromScalar(m_exe_ctx, scalar, | |
| ptrdiff_type, "result"); | |
| } | |
| std::string errMsg = | |
| llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')", | |
| orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg), | |
| location); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryMultiplication( | |
| lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) { | |
| // Operation '*' works for: | |
| // {scalar,unscoped_enum} <-> {scalar,unscoped_enum} | |
| auto orig_lhs_type = lhs->GetCompilerType(); | |
| auto orig_rhs_type = rhs->GetCompilerType(); | |
| auto type_or_err = ArithmeticConversion(lhs, rhs, location); | |
| if (!type_or_err) | |
| return type_or_err.takeError(); | |
| CompilerType result_type = *type_or_err; | |
| if (!result_type.IsScalarType()) { | |
| std::string errMsg = | |
| llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')", | |
| orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg), | |
| location); | |
| } | |
| return EvaluateScalarOp(BinaryOpKind::Mul, lhs, rhs, result_type, location); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryDivision( | |
| lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) { | |
| // Operation '/' works for: | |
| // {scalar,unscoped_enum} <-> {scalar,unscoped_enum} | |
| auto orig_lhs_type = lhs->GetCompilerType(); | |
| auto orig_rhs_type = rhs->GetCompilerType(); | |
| auto type_or_err = ArithmeticConversion(lhs, rhs, location); | |
| if (!type_or_err) | |
| return type_or_err.takeError(); | |
| CompilerType result_type = *type_or_err; | |
| if (!result_type.IsScalarType()) { | |
| std::string errMsg = | |
| llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')", | |
| orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg), | |
| location); | |
| } | |
| // Check for zero only for integer division. | |
| if (result_type.IsInteger() && rhs->GetValueAsSigned(-1) == 0) { | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "division by zero is undefined", location); | |
| } | |
| return EvaluateScalarOp(BinaryOpKind::Div, lhs, rhs, result_type, location); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryRemainder( | |
| lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs, uint32_t location) { | |
| // Operation '%' works for: | |
| // {integer,unscoped_enum} <-> {integer,unscoped_enum} | |
| auto orig_lhs_type = lhs->GetCompilerType(); | |
| auto orig_rhs_type = rhs->GetCompilerType(); | |
| auto type_or_err = ArithmeticConversion(lhs, rhs, location); | |
| if (!type_or_err) | |
| return type_or_err.takeError(); | |
| CompilerType result_type = *type_or_err; | |
| if (!result_type.IsInteger()) { | |
| std::string errMsg = | |
| llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')", | |
| orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg), | |
| location); | |
| } | |
| if (rhs->GetValueAsSigned(-1) == 0) { | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "division by zero is undefined", location); | |
| } | |
| return EvaluateScalarOp(BinaryOpKind::Rem, lhs, rhs, result_type, location); | |
| } | |
| static bool HasFloatingRepresentation(CompilerType ct) { | |
| return ct.GetTypeInfo() & lldb::eTypeIsFloat; | |
| } | |
| static llvm::Expected<bool> VerifyAssignmentTypes(CompilerType lhs_type, | |
| CompilerType rhs_type) { | |
| // Make sure lhs is a legal type for DIL assignment. | |
| if (!lhs_type.IsInteger() && !lhs_type.IsUnscopedEnumerationType() && | |
| !HasFloatingRepresentation(lhs_type) && !lhs_type.IsPointerType() && | |
| !lhs_type.IsScalarType()) | |
| return llvm::createStringError( | |
| "Illegal type for lhs of assignment (not scalar numeric type)"); | |
| // Make sure rhs is a legal type for DIL assignment. | |
| if (!rhs_type.IsInteger() && !rhs_type.IsUnscopedEnumerationType() && | |
| !HasFloatingRepresentation(rhs_type) && !rhs_type.IsPointerType()) | |
| return llvm::createStringError( | |
| "Illegal type for rhs of assignment (not scalar numeric type)"); | |
| // Only allow assigning pointers to pointers. | |
| if ((lhs_type.IsPointerType() && !rhs_type.IsPointerType()) || | |
| (!lhs_type.IsPointerType() && rhs_type.IsPointerType())) | |
| return llvm::createStringError( | |
| "Invalid assignment: Can only assign pointers to pointers"); | |
| // For "real numbers", the types must match exactly. | |
| if ((HasFloatingRepresentation(rhs_type) || | |
| HasFloatingRepresentation(lhs_type)) && | |
| lhs_type != rhs_type) { | |
| std::string err_msg = | |
| llvm::formatv("Incompatible types for assignment: Cannot assign {0} " | |
| "to {1}", | |
| rhs_type.TypeDescription(), lhs_type.TypeDescription()); | |
| return llvm::createStringError(err_msg); | |
| } | |
| return true; | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::EvaluateAssignment(lldb::ValueObjectSP lhs, | |
| lldb::ValueObjectSP rhs, uint32_t location) { | |
| // Verify that lhs can accept an assignment. | |
| if (llvm::Error err = lhs->CanSetValue()) | |
| return err; | |
| auto all_ok = | |
| VerifyAssignmentTypes(lhs->GetCompilerType(), rhs->GetCompilerType()); | |
| if (!all_ok) | |
| return all_ok.takeError(); | |
| if (llvm::Error e = lhs->SetValueFromInteger(rhs, m_allow_var_updates)) | |
| return e; | |
| return lhs; | |
| } | |
| static bool IsLiteralZero(lldb::ValueObjectSP &val, bool is_literal) { | |
| bool is_zero = val->GetValueAsUnsigned(-1) == 0; | |
| bool is_boolean = val->GetCompilerType().IsBoolean(); | |
| return is_zero && !is_boolean && is_literal; | |
| } | |
| llvm::Error | |
| Interpreter::ValidateComparison(BinaryOpKind kind, lldb::ValueObjectSP &lhs, | |
| lldb::ValueObjectSP &rhs, bool lhs_is_literal, | |
| bool rhs_is_literal, uint32_t location) { | |
| auto orig_lhs_type = lhs->GetCompilerType(); | |
| auto orig_rhs_type = rhs->GetCompilerType(); | |
| bool is_ordered = (kind == BinaryOpKind::LT || kind == BinaryOpKind::LE || | |
| kind == BinaryOpKind::GT || kind == BinaryOpKind::GE); | |
| bool lhs_nullptr_or_zero = | |
| orig_lhs_type.IsNullPtrType() || IsLiteralZero(lhs, lhs_is_literal); | |
| bool rhs_nullptr_or_zero = | |
| orig_rhs_type.IsNullPtrType() || IsLiteralZero(rhs, rhs_is_literal); | |
| if (orig_lhs_type.IsArrayType()) | |
| lhs = ArrayToPointerConversion(*lhs, m_exe_ctx, "result"); | |
| if (orig_rhs_type.IsArrayType()) | |
| rhs = ArrayToPointerConversion(*rhs, m_exe_ctx, "result"); | |
| CompilerType lhs_type = lhs->GetCompilerType(); | |
| CompilerType rhs_type = rhs->GetCompilerType(); | |
| if (lhs_type == rhs_type) | |
| return llvm::Error::success(); | |
| lldb::ValueObjectSP lhs_child; | |
| lldb::ValueObjectSP rhs_child; | |
| bool is_signed; | |
| if (!lhs_nullptr_or_zero && !lhs_type.IsPointerType() && | |
| !lhs_type.IsIntegerOrEnumerationType(is_signed)) { | |
| // lhs is not a nullptr, pointer, enum or integer. Check to see if its | |
| // first child could be a pointer. If so, update lhs_type accordingly. | |
| lhs_child = lhs->GetChildAtIndex(0); | |
| if (lhs_child && (lhs_child->IsPointerType() || | |
| lhs_child->GetCompilerType().IsNullPtrType())) | |
| lhs_type = lhs_child->GetCompilerType(); | |
| } | |
| if (!rhs_nullptr_or_zero && !rhs_type.IsPointerType() && | |
| !rhs_type.IsIntegerOrEnumerationType(is_signed)) { | |
| // rhs is not a nullptr, pointer, enum or integer. Check to see if its | |
| // first child could be a pointer. If so, update rhs_type accordingly. | |
| rhs_child = rhs->GetChildAtIndex(0); | |
| if (rhs_child && (rhs_child->IsPointerType() || | |
| rhs_child->GetCompilerType().IsNullPtrType())) | |
| rhs_type = rhs_child->GetCompilerType(); | |
| } | |
| if ((lhs_type != orig_lhs_type) || (rhs_type != orig_rhs_type)) { | |
| if (lhs_type.IsNullPtrType() || rhs_type.IsNullPtrType()) | |
| return llvm::Error::success(); | |
| // May be an integer or enum. | |
| if (!lhs_type.IsPointerType() || !rhs_type.IsPointerType()) | |
| return llvm::Error::success(); | |
| CompilerType lhs_unqualified = | |
| lhs_type.GetCanonicalType().GetFullyUnqualifiedType(); | |
| CompilerType rhs_unqualified = | |
| rhs_type.GetCanonicalType().GetFullyUnqualifiedType(); | |
| if (lhs_unqualified.IsPointerToVoid() || rhs_unqualified.IsPointerToVoid()) | |
| return llvm::Error::success(); | |
| // We have two pointers, neither of which is nullptr or void *. Make | |
| // sure their types are compatible. | |
| bool comparable = lhs_unqualified.CompareTypes(rhs_unqualified); | |
| if (comparable) | |
| return llvm::Error::success(); | |
| std::string errMsg = llvm::formatv( | |
| "comparison of distinct pointer types ({0} and {1})", | |
| orig_lhs_type.TypeDescription(), orig_rhs_type.TypeDescription()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location); | |
| } | |
| if (!is_ordered && ((orig_lhs_type.IsNullPtrType() && rhs_nullptr_or_zero) || | |
| (lhs_nullptr_or_zero && orig_rhs_type.IsNullPtrType()))) | |
| return llvm::Error::success(); | |
| // If the operands has arithmetic or enumeration type (scoped or unscoped), | |
| // usual arithmetic conversions are performed on both operands following the | |
| // rules for arithmetic operators. | |
| auto type_or_err = ArithmeticConversion(lhs, rhs, location); | |
| if (!type_or_err) | |
| return type_or_err.takeError(); | |
| lhs_type = lhs->GetCompilerType(); | |
| rhs_type = rhs->GetCompilerType(); | |
| if (lhs_type.IsScalarOrUnscopedEnumerationType() && | |
| rhs_type.IsScalarOrUnscopedEnumerationType()) | |
| return llvm::Error::success(); | |
| // Scoped enums can be compared only to the instances of the same type. | |
| if (lhs_type.IsScopedEnumerationType() || | |
| rhs_type.IsScopedEnumerationType()) { | |
| if (lhs_type.CompareTypes(rhs_type)) | |
| return llvm::Error::success(); | |
| std::string errMsg = llvm::formatv( | |
| "invalid operands to binary expression ({0} and {1})", | |
| orig_lhs_type.TypeDescription(), orig_rhs_type.TypeDescription()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location); | |
| } | |
| // Check if the value can be compared to a pointer. We allow all pointers, | |
| // integers, unscoped enumerations and a `nullptr` literal if it's an | |
| // equality/inequality comparison, including comparing a pointer with an | |
| // integer representing an address. This also allows comparing `nullptr` and | |
| // any integer, not just literal zero, e.g. `nullptr == 1` is false. | |
| auto comparable_to_pointer = [&](CompilerType t) { | |
| return t.IsPointerType() || t.IsInteger() || | |
| t.IsUnscopedEnumerationType() || (!is_ordered && t.IsNullPtrType()); | |
| }; | |
| if ((lhs_type.IsPointerType() && comparable_to_pointer(rhs_type)) || | |
| (comparable_to_pointer(lhs_type) && rhs_type.IsPointerType())) { | |
| // If both are pointers, check if they have comparable types. | |
| if ((lhs_type.IsPointerType() && !lhs_type.IsPointerToVoid()) && | |
| (rhs_type.IsPointerType() && !rhs_type.IsPointerToVoid())) { | |
| // Compare canonical unqualified pointer types. | |
| CompilerType lhs_unqualified_type = | |
| lhs_type.GetCanonicalType().GetFullyUnqualifiedType(); | |
| CompilerType rhs_unqualified_type = | |
| rhs_type.GetCanonicalType().GetFullyUnqualifiedType(); | |
| bool comparable = lhs_unqualified_type.CompareTypes(rhs_unqualified_type); | |
| if (!comparable) { | |
| std::string errMsg = llvm::formatv( | |
| "comparison of distinct pointer types ({0} and {1})", | |
| orig_lhs_type.TypeDescription(), orig_rhs_type.TypeDescription()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location); | |
| } | |
| } | |
| // Comparing pointers to void is always allowed. | |
| return llvm::Error::success(); | |
| } | |
| std::string errMsg = llvm::formatv( | |
| "invalid operands to binary expression ({0} and {1})", | |
| orig_lhs_type.TypeDescription(), orig_rhs_type.TypeDescription()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::EvaluateComparison(BinaryOpKind kind, lldb::ValueObjectSP lhs, | |
| lldb::ValueObjectSP rhs, bool lhs_is_literal, | |
| bool rhs_is_literal, uint32_t location) { | |
| // Comparison works for: | |
| // nullptr_t <-> {nullptr_t,integer} (if integer is literal zero) | |
| // {nullptr_t,integer} <-> nullptr_t (if integer is literal zero) | |
| // {scalar,unscoped_enum} <-> {scalar,unscoped_enum} | |
| // scoped_enum <-> scoped_enum (if the same type) | |
| // pointer <-> pointer (if pointee types are compatible) | |
| // pointer <-> {integer,unscoped_enum,nullptr_t} | |
| // {integer,unscoped_enum,nullptr_t} <-> pointer | |
| if (auto error = ValidateComparison(kind, lhs, rhs, lhs_is_literal, | |
| rhs_is_literal, location)) | |
| return error; | |
| llvm::Expected<lldb::TypeSystemSP> type_system = GetTypeSystemFromCU(); | |
| if (!type_system) | |
| return type_system.takeError(); | |
| CompilerType boolean_type = GetBasicType(*type_system, lldb::eBasicTypeBool); | |
| return EvaluateScalarOp(kind, lhs, rhs, boolean_type, location); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::EvaluateBinaryBitwise(BinaryOpKind kind, lldb::ValueObjectSP lhs, | |
| lldb::ValueObjectSP rhs, uint32_t location) { | |
| // Operations {'&', '|', '^'} work for: | |
| // {integer,unscoped_enum} <-> {integer,unscoped_enum} | |
| auto orig_lhs_type = lhs->GetCompilerType(); | |
| auto orig_rhs_type = rhs->GetCompilerType(); | |
| auto type_or_err = ArithmeticConversion(lhs, rhs, location); | |
| if (!type_or_err) | |
| return type_or_err.takeError(); | |
| CompilerType result_type = *type_or_err; | |
| if (!result_type.IsInteger()) { | |
| std::string errMsg = | |
| llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')", | |
| orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location); | |
| } | |
| return EvaluateScalarOp(kind, lhs, rhs, result_type, location); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::EvaluateBinaryShift(BinaryOpKind kind, lldb::ValueObjectSP lhs, | |
| lldb::ValueObjectSP rhs, uint32_t location) { | |
| // Operations {'>>', '<<'} work for: | |
| // {integer,unscoped_enum} <-> {integer,unscoped_enum} | |
| CompilerType orig_lhs_type = lhs->GetCompilerType(); | |
| CompilerType orig_rhs_type = rhs->GetCompilerType(); | |
| auto lhs_or_err = UnaryConversion(lhs, location); | |
| if (!lhs_or_err) | |
| return lhs_or_err.takeError(); | |
| lhs = *lhs_or_err; | |
| auto rhs_or_err = UnaryConversion(rhs, location); | |
| if (!rhs_or_err) | |
| return rhs_or_err.takeError(); | |
| rhs = *rhs_or_err; | |
| CompilerType lhs_type = lhs->GetCompilerType(); | |
| CompilerType rhs_type = rhs->GetCompilerType(); | |
| if (!lhs_type.IsInteger() || !rhs_type.IsInteger()) { | |
| std::string errMsg = | |
| llvm::formatv("invalid operands to binary expression ('{0}' and '{1}')", | |
| orig_lhs_type.GetTypeName(), orig_rhs_type.GetTypeName()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, location); | |
| } | |
| bool success; | |
| uint64_t amount = rhs->GetValueAsUnsigned(0, &success); | |
| if (!success) | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "could not get the shift amount as an integer", location); | |
| llvm::Expected<uint64_t> lhs_size = | |
| lhs_type.GetBitSize(m_exe_ctx.GetBestExecutionContextScope()); | |
| if (!lhs_size) | |
| return lhs_size.takeError(); | |
| if (amount >= *lhs_size) | |
| return llvm::make_error<DILDiagnosticError>(m_expr, "invalid shift amount", | |
| location); | |
| return EvaluateScalarOp(kind, lhs, rhs, lhs_type, location); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::EvaluateLogical(const BinaryOpNode &node) { | |
| // Operations {'&&', '||'} work for: | |
| // {IsContextuallyConvertibleToBool} <-> {IsContextuallyConvertibleToBool} | |
| // Note: These operators will not evaluate or check the type of RHS | |
| // if the result is determined after evaluating LHS. | |
| auto lhs_or_err = EvaluateAndDereference(node.GetLHS()); | |
| if (!lhs_or_err) | |
| return lhs_or_err; | |
| lldb::ValueObjectSP lhs = *lhs_or_err; | |
| auto lhs_type = lhs->GetCompilerType(); | |
| if (!lhs_type.IsContextuallyConvertibleToBool()) { | |
| std::string errMsg = llvm::formatv( | |
| "value of type {0} is not contextually convertible to 'bool'", | |
| lhs_type.TypeDescription()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, | |
| node.GetLocation()); | |
| } | |
| llvm::Expected<lldb::TypeSystemSP> type_system = GetTypeSystemFromCU(); | |
| if (!type_system) | |
| return type_system.takeError(); | |
| // For "&&", exit early if LHS is "false" | |
| // For "||", exit early if LHS is "true". | |
| auto lvalue_or_err = lhs->GetValueAsBool(); | |
| if (!lvalue_or_err) | |
| return lvalue_or_err.takeError(); | |
| bool lhs_val = *lvalue_or_err; | |
| bool exit_early = node.GetKind() == BinaryOpKind::LAnd ? !lhs_val : lhs_val; | |
| if (exit_early) | |
| return ValueObject::CreateValueObjectFromBool(m_exe_ctx, *type_system, | |
| lhs_val, "result"); | |
| // If the result is to be determined, evaluate the RHS. | |
| auto rhs_or_err = EvaluateAndDereference(node.GetRHS()); | |
| if (!rhs_or_err) | |
| return rhs_or_err; | |
| lldb::ValueObjectSP rhs = *rhs_or_err; | |
| auto rhs_type = rhs->GetCompilerType(); | |
| if (!rhs_type.IsContextuallyConvertibleToBool()) { | |
| std::string errMsg = llvm::formatv( | |
| "value of type {0} is not contextually convertible to 'bool'", | |
| rhs_type.TypeDescription()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, | |
| node.GetLocation()); | |
| } | |
| auto rvalue_or_err = rhs->GetValueAsBool(); | |
| if (!rvalue_or_err) | |
| return rvalue_or_err.takeError(); | |
| return ValueObject::CreateValueObjectFromBool(m_exe_ctx, *type_system, | |
| *rvalue_or_err, "result"); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::Visit(const BinaryOpNode &node) { | |
| // Handle logical operators separately. They may or may not evaluate RHS. | |
| if (node.GetKind() == BinaryOpKind::LAnd || | |
| node.GetKind() == BinaryOpKind::LOr) | |
| return EvaluateLogical(node); | |
| auto lhs_or_err = EvaluateAndDereference(node.GetLHS()); | |
| if (!lhs_or_err) | |
| return lhs_or_err; | |
| lldb::ValueObjectSP lhs = *lhs_or_err; | |
| auto rhs_or_err = EvaluateAndDereference(node.GetRHS()); | |
| if (!rhs_or_err) | |
| return rhs_or_err; | |
| lldb::ValueObjectSP rhs = *rhs_or_err; | |
| bool lhs_is_literal = node.GetLHS().IsConstLiteral(); | |
| bool rhs_is_literal = node.GetRHS().IsConstLiteral(); | |
| lldb::TypeSystemSP lhs_system = | |
| lhs->GetCompilerType().GetTypeSystem().GetSharedPointer(); | |
| lldb::TypeSystemSP rhs_system = | |
| rhs->GetCompilerType().GetTypeSystem().GetSharedPointer(); | |
| if (lhs_system->GetPluginName() != rhs_system->GetPluginName()) { | |
| // TODO: Attempt to convert values to current CU's type system | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "operands have different type systems", node.GetLocation()); | |
| } | |
| switch (node.GetKind()) { | |
| case BinaryOpKind::Assign: | |
| return EvaluateAssignment(lhs, rhs, node.GetLocation()); | |
| case BinaryOpKind::Add: | |
| return EvaluateBinaryAddition(lhs, rhs, node.GetLocation()); | |
| case BinaryOpKind::Sub: | |
| return EvaluateBinarySubtraction(lhs, rhs, node.GetLocation()); | |
| case BinaryOpKind::Mul: | |
| return EvaluateBinaryMultiplication(lhs, rhs, node.GetLocation()); | |
| case BinaryOpKind::Div: | |
| return EvaluateBinaryDivision(lhs, rhs, node.GetLocation()); | |
| case BinaryOpKind::Rem: | |
| return EvaluateBinaryRemainder(lhs, rhs, node.GetLocation()); | |
| case BinaryOpKind::And: | |
| case BinaryOpKind::Xor: | |
| case BinaryOpKind::Or: | |
| return EvaluateBinaryBitwise(node.GetKind(), lhs, rhs, node.GetLocation()); | |
| case BinaryOpKind::Shl: | |
| case BinaryOpKind::Shr: | |
| return EvaluateBinaryShift(node.GetKind(), lhs, rhs, node.GetLocation()); | |
| case BinaryOpKind::AddAssign: { | |
| auto ret_or_err = EvaluateBinaryAddition(lhs, rhs, node.GetLocation()); | |
| if (!ret_or_err) | |
| return ret_or_err; | |
| return EvaluateAssignment(lhs, *ret_or_err, node.GetLocation()); | |
| } | |
| case BinaryOpKind::SubAssign: { | |
| auto ret_or_err = EvaluateBinarySubtraction(lhs, rhs, node.GetLocation()); | |
| if (!ret_or_err) | |
| return ret_or_err; | |
| return EvaluateAssignment(lhs, *ret_or_err, node.GetLocation()); | |
| } | |
| case BinaryOpKind::MulAssign: { | |
| auto ret_or_err = | |
| EvaluateBinaryMultiplication(lhs, rhs, node.GetLocation()); | |
| if (!ret_or_err) | |
| return ret_or_err; | |
| return EvaluateAssignment(lhs, *ret_or_err, node.GetLocation()); | |
| } | |
| case BinaryOpKind::DivAssign: { | |
| auto ret_or_err = EvaluateBinaryDivision(lhs, rhs, node.GetLocation()); | |
| if (!ret_or_err) | |
| return ret_or_err; | |
| return EvaluateAssignment(lhs, *ret_or_err, node.GetLocation()); | |
| } | |
| case BinaryOpKind::RemAssign: { | |
| auto ret_or_err = EvaluateBinaryRemainder(lhs, rhs, node.GetLocation()); | |
| if (!ret_or_err) | |
| return ret_or_err; | |
| return EvaluateAssignment(lhs, *ret_or_err, node.GetLocation()); | |
| } | |
| case BinaryOpKind::AndAssign: { | |
| auto ret_or_err = | |
| EvaluateBinaryBitwise(BinaryOpKind::And, lhs, rhs, node.GetLocation()); | |
| if (!ret_or_err) | |
| return ret_or_err; | |
| return EvaluateAssignment(lhs, *ret_or_err, node.GetLocation()); | |
| } | |
| case BinaryOpKind::XorAssign: { | |
| auto ret_or_err = | |
| EvaluateBinaryBitwise(BinaryOpKind::Xor, lhs, rhs, node.GetLocation()); | |
| if (!ret_or_err) | |
| return ret_or_err; | |
| return EvaluateAssignment(lhs, *ret_or_err, node.GetLocation()); | |
| } | |
| case BinaryOpKind::OrAssign: { | |
| auto ret_or_err = | |
| EvaluateBinaryBitwise(BinaryOpKind::Or, lhs, rhs, node.GetLocation()); | |
| if (!ret_or_err) | |
| return ret_or_err; | |
| return EvaluateAssignment(lhs, *ret_or_err, node.GetLocation()); | |
| } | |
| case BinaryOpKind::ShlAssign: { | |
| auto ret_or_err = | |
| EvaluateBinaryShift(BinaryOpKind::Shl, lhs, rhs, node.GetLocation()); | |
| if (!ret_or_err) | |
| return ret_or_err; | |
| return EvaluateAssignment(lhs, *ret_or_err, node.GetLocation()); | |
| } | |
| case BinaryOpKind::ShrAssign: { | |
| auto ret_or_err = | |
| EvaluateBinaryShift(BinaryOpKind::Shr, lhs, rhs, node.GetLocation()); | |
| if (!ret_or_err) | |
| return ret_or_err; | |
| return EvaluateAssignment(lhs, *ret_or_err, node.GetLocation()); | |
| } | |
| case BinaryOpKind::EQ: | |
| case BinaryOpKind::NE: | |
| case BinaryOpKind::LT: | |
| case BinaryOpKind::LE: | |
| case BinaryOpKind::GT: | |
| case BinaryOpKind::GE: | |
| return EvaluateComparison(node.GetKind(), lhs, rhs, lhs_is_literal, | |
| rhs_is_literal, node.GetLocation()); | |
| default: | |
| break; | |
| } | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "unimplemented binary operation", node.GetLocation()); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::Visit(const MemberOfNode &node) { | |
| if (std::optional<std::string> register_name = GetRegisterPathName(node)) { | |
| auto frame_sp = m_exe_ctx.GetFrameSP(); | |
| lldb::RegisterContextSP reg_ctx_sp = | |
| frame_sp ? frame_sp->GetRegisterContext() : nullptr; | |
| if (reg_ctx_sp) { | |
| if (const RegisterInfo *reg_info = | |
| reg_ctx_sp->GetRegisterInfoByName(*register_name)) | |
| return ValueObjectRegister::Create( | |
| m_exe_ctx.GetBestExecutionContextScope(), reg_ctx_sp, reg_info); | |
| } | |
| } | |
| auto base_or_err = Evaluate(node.GetBase()); | |
| if (!base_or_err) | |
| return base_or_err; | |
| bool expr_is_ptr = node.GetIsArrow(); | |
| lldb::ValueObjectSP base = *base_or_err; | |
| // Perform some basic type & correctness checking. | |
| if (node.GetIsArrow()) { | |
| // If we have a non-pointer type with a synthetic value then lets check | |
| // if we have a synthetic dereference specified. | |
| if (!base->IsPointerType() && base->HasSyntheticValue()) { | |
| Status deref_error; | |
| if (lldb::ValueObjectSP synth_deref_sp = | |
| base->GetSyntheticValue()->Dereference(deref_error); | |
| synth_deref_sp && deref_error.Success()) { | |
| base = std::move(synth_deref_sp); | |
| } | |
| if (!base || deref_error.Fail()) { | |
| std::string errMsg = llvm::formatv( | |
| "Failed to dereference synthetic value: {0}", deref_error); | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, errMsg, node.GetLocation(), node.GetFieldName().size()); | |
| } | |
| // Some synthetic plug-ins fail to set the error in Dereference | |
| if (!base) { | |
| std::string errMsg = "Failed to dereference synthetic value"; | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, errMsg, node.GetLocation(), node.GetFieldName().size()); | |
| } | |
| expr_is_ptr = false; | |
| } | |
| } | |
| if (m_check_ptr_vs_member) { | |
| bool base_is_ptr = base->IsPointerType(); | |
| if (expr_is_ptr != base_is_ptr) { | |
| if (base_is_ptr) { | |
| std::string errMsg = | |
| llvm::formatv("member reference type {0} is a pointer; " | |
| "did you mean to use '->'?", | |
| base->GetCompilerType().TypeDescription()); | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, errMsg, node.GetLocation(), node.GetFieldName().size()); | |
| } else { | |
| std::string errMsg = | |
| llvm::formatv("member reference type {0} is not a pointer; " | |
| "did you mean to use '.'?", | |
| base->GetCompilerType().TypeDescription()); | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, errMsg, node.GetLocation(), node.GetFieldName().size()); | |
| } | |
| } | |
| } | |
| lldb::ValueObjectSP field_obj = | |
| base->GetChildMemberWithName(node.GetFieldName()); | |
| if (!field_obj) { | |
| if (m_use_synthetic) { | |
| field_obj = base->GetSyntheticValue(); | |
| if (field_obj) | |
| field_obj = field_obj->GetChildMemberWithName(node.GetFieldName()); | |
| } | |
| if (!m_use_synthetic || !field_obj) { | |
| std::string errMsg = llvm::formatv( | |
| "\"{0}\" is not a member of \"({1}) {2}\"", node.GetFieldName(), | |
| base->GetTypeName().AsCString("<invalid type>"), base->GetName()); | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, errMsg, node.GetLocation(), node.GetFieldName().size()); | |
| } | |
| } | |
| if (field_obj) { | |
| if (m_use_dynamic != lldb::eNoDynamicValues) { | |
| lldb::ValueObjectSP dynamic_val_sp = | |
| field_obj->GetDynamicValue(m_use_dynamic); | |
| if (dynamic_val_sp) | |
| field_obj = dynamic_val_sp; | |
| } | |
| return field_obj; | |
| } | |
| CompilerType base_type = base->GetCompilerType(); | |
| if (node.GetIsArrow() && base->IsPointerType()) | |
| base_type = base_type.GetPointeeType(); | |
| std::string errMsg = llvm::formatv( | |
| "\"{0}\" is not a member of \"({1}) {2}\"", node.GetFieldName(), | |
| base->GetTypeName().AsCString("<invalid type>"), base->GetName()); | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, errMsg, node.GetLocation(), node.GetFieldName().size()); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::Visit(const ArraySubscriptNode &node) { | |
| auto idx_or_err = EvaluateAndDereference(node.GetIndex()); | |
| if (!idx_or_err) | |
| return idx_or_err; | |
| lldb::ValueObjectSP idx = *idx_or_err; | |
| if (!idx->GetCompilerType().IsIntegerOrUnscopedEnumerationType()) { | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "array subscript is not an integer", node.GetLocation()); | |
| } | |
| StreamString var_expr_path_strm; | |
| uint64_t child_idx = idx->GetValueAsUnsigned(0); | |
| lldb::ValueObjectSP child_valobj_sp; | |
| auto base_or_err = Evaluate(node.GetBase()); | |
| if (!base_or_err) | |
| return base_or_err; | |
| lldb::ValueObjectSP base = *base_or_err; | |
| CompilerType base_type = base->GetCompilerType().GetNonReferenceType(); | |
| base->GetExpressionPath(var_expr_path_strm); | |
| if (base->GetValueType() == lldb::eValueTypeRegister && | |
| !base_type.IsPointerType() && | |
| child_idx > std::numeric_limits<uint32_t>::max()) { | |
| std::string err_msg = llvm::formatv( | |
| "array index {0} is not valid for \"({1}) {2}\"", child_idx, | |
| base->GetTypeName().AsCString("<invalid type>"), | |
| var_expr_path_strm.GetData()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), | |
| node.GetLocation()); | |
| } | |
| bool is_incomplete_array = false; | |
| if (base_type.IsPointerType()) { | |
| bool is_objc_pointer = true; | |
| if (base->GetCompilerType().GetMinimumLanguage() != lldb::eLanguageTypeObjC) | |
| is_objc_pointer = false; | |
| else if (!base->GetCompilerType().IsPointerType()) | |
| is_objc_pointer = false; | |
| if (!m_use_synthetic && is_objc_pointer) { | |
| std::string err_msg = llvm::formatv( | |
| "\"({0}) {1}\" is an Objective-C pointer, and cannot be subscripted", | |
| base->GetTypeName().AsCString("<invalid type>"), | |
| var_expr_path_strm.GetData()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), | |
| node.GetLocation()); | |
| } | |
| if (is_objc_pointer) { | |
| lldb::ValueObjectSP synthetic = base->GetSyntheticValue(); | |
| if (!synthetic || synthetic == base) { | |
| std::string err_msg = | |
| llvm::formatv("\"({0}) {1}\" is not an array type", | |
| base->GetTypeName().AsCString("<invalid type>"), | |
| var_expr_path_strm.GetData()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), | |
| node.GetLocation()); | |
| } | |
| if (static_cast<uint32_t>(child_idx) >= | |
| synthetic->GetNumChildrenIgnoringErrors()) { | |
| std::string err_msg = llvm::formatv( | |
| "array index {0} is not valid for \"({1}) {2}\"", child_idx, | |
| base->GetTypeName().AsCString("<invalid type>"), | |
| var_expr_path_strm.GetData()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), | |
| node.GetLocation()); | |
| } | |
| child_valobj_sp = synthetic->GetChildAtIndex(child_idx); | |
| if (!child_valobj_sp) { | |
| std::string err_msg = llvm::formatv( | |
| "array index {0} is not valid for \"({1}) {2}\"", child_idx, | |
| base->GetTypeName().AsCString("<invalid type>"), | |
| var_expr_path_strm.GetData()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), | |
| node.GetLocation()); | |
| } | |
| if (m_use_dynamic != lldb::eNoDynamicValues) { | |
| if (auto dynamic_sp = child_valobj_sp->GetDynamicValue(m_use_dynamic)) | |
| child_valobj_sp = std::move(dynamic_sp); | |
| } | |
| return child_valobj_sp; | |
| } | |
| child_valobj_sp = base->GetSyntheticArrayMember(child_idx, true); | |
| if (!child_valobj_sp) { | |
| std::string err_msg = llvm::formatv( | |
| "failed to use pointer as array for index {0} for " | |
| "\"({1}) {2}\"", | |
| child_idx, base->GetTypeName().AsCString("<invalid type>"), | |
| var_expr_path_strm.GetData()); | |
| if (base_type.IsPointerToVoid()) | |
| err_msg = "subscript of pointer to incomplete type 'void'"; | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), | |
| node.GetLocation()); | |
| } | |
| } else if (base_type.IsArrayType(nullptr, nullptr, &is_incomplete_array) || | |
| base_type.IsVectorType()) { | |
| child_valobj_sp = base->GetChildAtIndex(child_idx); | |
| if (!child_valobj_sp && (is_incomplete_array || m_use_synthetic)) | |
| child_valobj_sp = base->GetSyntheticArrayMember(child_idx, true); | |
| if (!child_valobj_sp) { | |
| std::string err_msg = llvm::formatv( | |
| "array index {0} is not valid for \"({1}) {2}\"", child_idx, | |
| base->GetTypeName().AsCString("<invalid type>"), | |
| var_expr_path_strm.GetData()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), | |
| node.GetLocation()); | |
| } | |
| } else if (base_type.IsScalarType()) { | |
| child_valobj_sp = | |
| base->GetSyntheticBitFieldChild(child_idx, child_idx, true); | |
| if (!child_valobj_sp) { | |
| std::string err_msg = llvm::formatv( | |
| "bitfield range {0}:{1} is not valid for \"({2}) {3}\"", child_idx, | |
| child_idx, base->GetTypeName().AsCString("<invalid type>"), | |
| var_expr_path_strm.GetData()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), | |
| node.GetLocation(), 1); | |
| } | |
| } else { | |
| lldb::ValueObjectSP synthetic = base->GetSyntheticValue(); | |
| if (!m_use_synthetic || !synthetic || synthetic == base) { | |
| std::string err_msg = | |
| llvm::formatv("\"{0}\" is not an array type", | |
| base->GetTypeName().AsCString("<invalid type>")); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), | |
| node.GetLocation(), 1); | |
| } | |
| if (static_cast<uint32_t>(child_idx) >= | |
| synthetic->GetNumChildrenIgnoringErrors(child_idx + 1)) { | |
| std::string err_msg = llvm::formatv( | |
| "array index {0} is not valid for \"({1}) {2}\"", child_idx, | |
| base->GetTypeName().AsCString("<invalid type>"), | |
| var_expr_path_strm.GetData()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), | |
| node.GetLocation(), 1); | |
| } | |
| child_valobj_sp = synthetic->GetChildAtIndex(child_idx); | |
| if (!child_valobj_sp) { | |
| std::string err_msg = llvm::formatv( | |
| "array index {0} is not valid for \"({1}) {2}\"", child_idx, | |
| base->GetTypeName().AsCString("<invalid type>"), | |
| var_expr_path_strm.GetData()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), | |
| node.GetLocation(), 1); | |
| } | |
| } | |
| if (child_valobj_sp) { | |
| if (m_use_dynamic != lldb::eNoDynamicValues) { | |
| if (auto dynamic_sp = child_valobj_sp->GetDynamicValue(m_use_dynamic)) | |
| child_valobj_sp = std::move(dynamic_sp); | |
| } | |
| return child_valobj_sp; | |
| } | |
| bool success; | |
| int64_t signed_child_idx = idx->GetValueAsSigned(0, &success); | |
| if (!success) | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "could not get the index as an integer", | |
| node.GetIndex().GetLocation()); | |
| return base->GetSyntheticArrayMember(signed_child_idx, true); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::Visit(const BitFieldExtractionNode &node) { | |
| auto first_idx_or_err = EvaluateAndDereference(node.GetFirstIndex()); | |
| if (!first_idx_or_err) | |
| return first_idx_or_err; | |
| lldb::ValueObjectSP first_idx = *first_idx_or_err; | |
| auto last_idx_or_err = EvaluateAndDereference(node.GetLastIndex()); | |
| if (!last_idx_or_err) | |
| return last_idx_or_err; | |
| lldb::ValueObjectSP last_idx = *last_idx_or_err; | |
| if (!first_idx->GetCompilerType().IsIntegerOrUnscopedEnumerationType() || | |
| !last_idx->GetCompilerType().IsIntegerOrUnscopedEnumerationType()) { | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "bit index is not an integer", node.GetLocation()); | |
| } | |
| bool success_first, success_last; | |
| int64_t first_index = first_idx->GetValueAsSigned(0, &success_first); | |
| int64_t last_index = last_idx->GetValueAsSigned(0, &success_last); | |
| if (!success_first || !success_last) | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "could not get the index as an integer", node.GetLocation()); | |
| // Reject negative indices before the swap below, so the diagnostic reports | |
| // the range as the user wrote it. A negative index would also wrap to a huge | |
| // offset in the uint32_t GetSyntheticBitFieldChild call below. | |
| if (first_index < 0 || last_index < 0) { | |
| std::string message = | |
| llvm::formatv("bitfield range {0}:{1} is not valid (negative index)", | |
| first_index, last_index); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, message, | |
| node.GetLocation()); | |
| } | |
| // if the format given is [high-low], swap range | |
| if (first_index > last_index) | |
| std::swap(first_index, last_index); | |
| // GetMaxU64Bitfield in the data layer only supports up to 64 bits (it asserts | |
| // bitfield_bit_size <= 64 and otherwise shifts out of bounds), so reject a | |
| // wider range here. | |
| if (last_index - first_index >= 64) { | |
| std::string message = | |
| llvm::formatv("bitfield range {0}:{1} is not valid (more than 64 bits)", | |
| first_index, last_index); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, message, | |
| node.GetLocation()); | |
| } | |
| auto base_or_err = EvaluateAndDereference(node.GetBase()); | |
| if (!base_or_err) | |
| return base_or_err; | |
| lldb::ValueObjectSP base = *base_or_err; | |
| // The high index must lie within the base object's storage; a bit index past | |
| // its bit size shifts out of bounds when the child is later read or formatted | |
| // (GetMaxU64Bitfield). | |
| llvm::Expected<uint64_t> base_bit_size = base->GetCompilerType().GetBitSize( | |
| m_exe_ctx.GetBestExecutionContextScope()); | |
| if (!base_bit_size) | |
| return base_bit_size.takeError(); | |
| if (static_cast<uint64_t>(last_index) >= *base_bit_size) { | |
| std::string message = llvm::formatv( | |
| "bitfield range {0}:{1} is not valid for \"({2}) {3}\"", first_index, | |
| last_index, base->GetTypeName().AsCString("<invalid type>"), | |
| base->GetName().GetStringRef()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, message, | |
| node.GetLocation()); | |
| } | |
| lldb::ValueObjectSP child_valobj_sp = | |
| base->GetSyntheticBitFieldChild(first_index, last_index, true); | |
| if (!child_valobj_sp) { | |
| std::string message = llvm::formatv( | |
| "bitfield range {0}:{1} is not valid for \"({2}) {3}\"", first_index, | |
| last_index, base->GetTypeName().AsCString("<invalid type>"), | |
| base->GetName().GetStringRef()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, message, | |
| node.GetLocation()); | |
| } | |
| return child_valobj_sp; | |
| } | |
| llvm::Expected<CompilerType> | |
| Interpreter::PickIntegerType(lldb::TypeSystemSP type_system, | |
| ExecutionContext &exe_ctx, | |
| const IntegerLiteralNode &literal) { | |
| // Binary, Octal, Hexadecimal and literals with a U suffix are allowed to be | |
| // an unsigned integer. | |
| bool unsigned_is_allowed = literal.IsUnsigned() || literal.GetRadix() != 10; | |
| llvm::APInt apint = literal.GetValue(); | |
| llvm::SmallVector<std::pair<lldb::BasicType, lldb::BasicType>, 3> candidates; | |
| if (literal.GetTypeSuffix() <= IntegerTypeSuffix::None) | |
| candidates.emplace_back(lldb::eBasicTypeInt, | |
| unsigned_is_allowed ? lldb::eBasicTypeUnsignedInt | |
| : lldb::eBasicTypeInvalid); | |
| if (literal.GetTypeSuffix() <= IntegerTypeSuffix::Long) | |
| candidates.emplace_back(lldb::eBasicTypeLong, | |
| unsigned_is_allowed ? lldb::eBasicTypeUnsignedLong | |
| : lldb::eBasicTypeInvalid); | |
| candidates.emplace_back(lldb::eBasicTypeLongLong, | |
| lldb::eBasicTypeUnsignedLongLong); | |
| for (auto [signed_, unsigned_] : candidates) { | |
| CompilerType signed_type = type_system->GetBasicTypeFromAST(signed_); | |
| if (!signed_type) | |
| continue; | |
| llvm::Expected<uint64_t> size = | |
| signed_type.GetBitSize(exe_ctx.GetBestExecutionContextScope()); | |
| if (!size) | |
| return size.takeError(); | |
| if (!literal.IsUnsigned() && apint.isIntN(*size - 1)) | |
| return signed_type; | |
| if (unsigned_ != lldb::eBasicTypeInvalid && apint.isIntN(*size)) | |
| return type_system->GetBasicTypeFromAST(unsigned_); | |
| } | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, | |
| "integer literal is too large to be represented in any integer type", | |
| literal.GetLocation()); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::Visit(const IntegerLiteralNode &node) { | |
| llvm::Expected<lldb::TypeSystemSP> type_system = GetTypeSystemFromCU(); | |
| if (!type_system) | |
| return type_system.takeError(); | |
| llvm::Expected<CompilerType> type = | |
| PickIntegerType(*type_system, m_exe_ctx, node); | |
| if (!type) | |
| return type.takeError(); | |
| Scalar scalar = node.GetValue(); | |
| // APInt from StringRef::getAsInteger comes with just enough bitwidth to | |
| // hold the value. This adjusts APInt bitwidth to match the compiler type. | |
| llvm::Expected<uint64_t> type_bitsize = | |
| type->GetBitSize(m_exe_ctx.GetBestExecutionContextScope()); | |
| if (!type_bitsize) | |
| return type_bitsize.takeError(); | |
| // Literal itself cannot be a negative value, so we do an unsigned extension. | |
| scalar.TruncOrExtendTo(*type_bitsize, false); | |
| // If the picked compiler type is signed, make the scalar signed as well. | |
| if (type->IsSigned()) | |
| scalar.MakeSigned(); | |
| return ValueObject::CreateValueObjectFromScalar(m_exe_ctx, scalar, *type, | |
| "result"); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::Visit(const FloatLiteralNode &node) { | |
| llvm::Expected<lldb::TypeSystemSP> type_system = GetTypeSystemFromCU(); | |
| if (!type_system) | |
| return type_system.takeError(); | |
| bool isFloat = | |
| &node.GetValue().getSemantics() == &llvm::APFloat::IEEEsingle(); | |
| lldb::BasicType basic_type = | |
| isFloat ? lldb::eBasicTypeFloat : lldb::eBasicTypeDouble; | |
| CompilerType type = GetBasicType(*type_system, basic_type); | |
| if (!type) | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "unable to create a const literal", node.GetLocation()); | |
| Scalar scalar = node.GetValue(); | |
| return ValueObject::CreateValueObjectFromScalar(m_exe_ctx, scalar, type, | |
| "result"); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::Visit(const BooleanLiteralNode &node) { | |
| bool value = node.GetValue(); | |
| llvm::Expected<lldb::TypeSystemSP> type_system = GetTypeSystemFromCU(); | |
| if (!type_system) | |
| return type_system.takeError(); | |
| return ValueObject::CreateValueObjectFromBool(m_exe_ctx, *type_system, value, | |
| "result"); | |
| } | |
| llvm::Expected<CastKind> | |
| Interpreter::VerifyArithmeticCast(CompilerType source_type, | |
| CompilerType target_type, int location) { | |
| if (source_type.IsPointerType() || source_type.IsNullPtrType()) { | |
| // Cast from pointer to float/double is not allowed. | |
| if (target_type.GetTypeInfo() & lldb::eTypeIsFloat) { | |
| std::string errMsg = llvm::formatv("Cast from {0} to {1} is not allowed", | |
| source_type.TypeDescription(), | |
| target_type.TypeDescription()); | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, std::move(errMsg), location, | |
| source_type.TypeDescription().length()); | |
| } | |
| // Casting from pointer to bool is always valid. | |
| if (target_type.IsBoolean()) | |
| return CastKind::eArithmetic; | |
| // Otherwise check if the result type is at least as big as the pointer | |
| // size. | |
| uint64_t type_byte_size = 0; | |
| uint64_t rhs_type_byte_size = 0; | |
| if (auto temp = | |
| target_type.GetByteSize(m_exe_ctx.GetBestExecutionContextScope())) { | |
| type_byte_size = *temp; | |
| } else { | |
| std::string errMsg = llvm::formatv("unable to get byte size for type {0}", | |
| target_type.TypeDescription()); | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), temp.takeError(), | |
| "GetByteSize failed: {0}"); | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, std::move(errMsg), location, | |
| target_type.TypeDescription().length()); | |
| } | |
| if (auto temp = | |
| source_type.GetByteSize(m_exe_ctx.GetBestExecutionContextScope())) { | |
| rhs_type_byte_size = *temp; | |
| } else { | |
| std::string errMsg = llvm::formatv("unable to get byte size for type {0}", | |
| source_type.TypeDescription()); | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), temp.takeError(), | |
| "GetByteSize failed: {0}"); | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, std::move(errMsg), location, | |
| source_type.TypeDescription().length()); | |
| } | |
| if (type_byte_size < rhs_type_byte_size) { | |
| std::string errMsg = llvm::formatv( | |
| "cast from pointer to smaller type {0} loses information", | |
| target_type.TypeDescription()); | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, std::move(errMsg), location, | |
| source_type.TypeDescription().length()); | |
| } | |
| } else if (!source_type.IsScalarType() && !source_type.IsEnumerationType()) { | |
| // Otherwise accept only arithmetic types and enums. | |
| std::string errMsg = llvm::formatv("cannot convert {0} to {1}", | |
| source_type.TypeDescription(), | |
| target_type.TypeDescription()); | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, std::move(errMsg), location, | |
| source_type.TypeDescription().length()); | |
| } | |
| return CastKind::eArithmetic; | |
| } | |
| llvm::Expected<CastKind> | |
| Interpreter::VerifyCastType(lldb::ValueObjectSP operand, | |
| CompilerType source_type, CompilerType target_type, | |
| int location) { | |
| if (target_type.IsScalarType()) | |
| return VerifyArithmeticCast(source_type, target_type, location); | |
| if (target_type.IsEnumerationType()) { | |
| // Cast to enum type. | |
| if (!source_type.IsScalarType() && !source_type.IsEnumerationType()) { | |
| std::string errMsg = llvm::formatv("Cast from {0} to {1} is not allowed", | |
| source_type.TypeDescription(), | |
| target_type.TypeDescription()); | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, std::move(errMsg), location, | |
| source_type.TypeDescription().length()); | |
| } | |
| return CastKind::eEnumeration; | |
| } | |
| if (target_type.IsPointerType()) { | |
| if (!source_type.IsInteger() && !source_type.IsEnumerationType() && | |
| !source_type.IsArrayType() && !source_type.IsPointerType() && | |
| !source_type.IsNullPtrType()) { | |
| std::string errMsg = llvm::formatv( | |
| "cannot cast from type {0} to pointer type {1}", | |
| source_type.TypeDescription(), target_type.TypeDescription()); | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, std::move(errMsg), location, | |
| source_type.TypeDescription().length()); | |
| } | |
| return CastKind::ePointer; | |
| } | |
| // Unsupported cast. | |
| std::string errMsg = llvm::formatv( | |
| "casting of {0} to {1} is not implemented yet", | |
| source_type.TypeDescription(), target_type.TypeDescription()); | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, std::move(errMsg), location, | |
| source_type.TypeDescription().length()); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> Interpreter::Visit(const CastNode &node) { | |
| auto operand_or_err = Evaluate(node.GetOperand()); | |
| if (!operand_or_err) | |
| return operand_or_err; | |
| lldb::ValueObjectSP operand = *operand_or_err; | |
| CompilerType op_type = operand->GetCompilerType(); | |
| CompilerType target_type = node.GetType(); | |
| if (op_type.IsReferenceType()) | |
| op_type = op_type.GetNonReferenceType(); | |
| if (target_type.IsScalarType() && op_type.IsArrayType()) { | |
| operand = ArrayToPointerConversion(*operand, m_exe_ctx, | |
| operand->GetName().GetStringRef()); | |
| op_type = operand->GetCompilerType(); | |
| } | |
| auto type_or_err = | |
| VerifyCastType(operand, op_type, target_type, node.GetLocation()); | |
| if (!type_or_err) | |
| return type_or_err.takeError(); | |
| CastKind cast_kind = *type_or_err; | |
| if (operand->GetCompilerType().IsReferenceType()) { | |
| Status error; | |
| operand = operand->Dereference(error); | |
| if (error.Fail()) | |
| return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(), | |
| node.GetLocation()); | |
| } | |
| lldb::ValueObjectSP result; | |
| switch (cast_kind) { | |
| case CastKind::eEnumeration: { | |
| // FIXME: is this correct for float vector types? | |
| if (op_type.GetTypeInfo() & lldb::eTypeIsFloat || op_type.IsInteger() || | |
| op_type.IsEnumerationType()) | |
| result = operand->CastToEnumType(target_type); | |
| break; | |
| } | |
| case CastKind::eArithmetic: { | |
| if (op_type.IsPointerType() || op_type.IsNullPtrType() || | |
| op_type.IsScalarType() || op_type.IsEnumerationType()) | |
| result = operand->CastToBasicType(target_type); | |
| break; | |
| } | |
| case CastKind::ePointer: { | |
| uint64_t addr = op_type.IsArrayType() | |
| ? operand->GetLoadAddress() | |
| : (op_type.IsSigned() ? operand->GetValueAsSigned(0) | |
| : operand->GetValueAsUnsigned(0)); | |
| result = ValueObject::CreateValueObjectFromAddress("result", addr, | |
| m_exe_ctx, target_type, | |
| /* do_deref */ false); | |
| break; | |
| } | |
| case CastKind::eNone: { | |
| return lldb::ValueObjectSP(); | |
| } | |
| } // switch | |
| if (result) { | |
| // If cast failed, retrieve the error message from the result. | |
| if (result->GetError().Fail()) | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, result->GetError().AsCString(), node.GetLocation()); | |
| return result; | |
| } | |
| std::string errMsg = | |
| llvm::formatv("unable to cast from '{0}' to '{1}'", | |
| op_type.TypeDescription(), target_type.TypeDescription()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, std::move(errMsg), | |
| node.GetLocation()); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> | |
| Interpreter::Visit(const ConditionalNode &node) { | |
| auto cond_or_err = EvaluateAndDereference(node.GetCondition()); | |
| if (!cond_or_err) | |
| return cond_or_err; | |
| lldb::ValueObjectSP condition = *cond_or_err; | |
| CompilerType cond_type = condition->GetCompilerType(); | |
| if (!cond_type.IsContextuallyConvertibleToBool()) { | |
| std::string errMsg = llvm::formatv( | |
| "value of type {0} is not contextually convertible to 'bool'", | |
| cond_type.TypeDescription()); | |
| return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, | |
| node.GetLocation()); | |
| } | |
| // Note: DIL evaluates only the operand chosen by the condition, | |
| // and doesn't check the type or evaluate the other operand. | |
| auto value_or_err = condition->GetValueAsBool(); | |
| if (value_or_err) { | |
| if (*value_or_err) { | |
| auto true_or_err = EvaluateAndDereference(node.GetTrueOperand()); | |
| if (!true_or_err) | |
| return true_or_err; | |
| return *true_or_err; | |
| } | |
| auto false_or_err = EvaluateAndDereference(node.GetFalseOperand()); | |
| if (!false_or_err) | |
| return false_or_err; | |
| return *false_or_err; | |
| } | |
| return value_or_err.takeError(); | |
| } | |
| llvm::Expected<lldb::ValueObjectSP> Interpreter::Visit(const SizeOfNode &node) { | |
| CompilerType typearg = node.GetTypeArg(); | |
| Scalar size; | |
| if (typearg.IsValid()) { | |
| if (typearg.IsReferenceType()) | |
| typearg = typearg.GetNonReferenceType(); | |
| llvm::Expected<uint64_t> byte_size = | |
| typearg.GetByteSize(m_exe_ctx.GetTargetSP().get()); | |
| if (!byte_size) | |
| return byte_size.takeError(); | |
| size = *byte_size; | |
| } else { | |
| auto arg_or_err = EvaluateAndDereference(node.GetNodeArg()); | |
| if (!arg_or_err) | |
| return arg_or_err; | |
| lldb::ValueObjectSP arg = *arg_or_err; | |
| if (arg->IsBitfield()) | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "invalid application of 'sizeof' to bit-field", | |
| node.GetLocation()); | |
| llvm::Expected<uint64_t> byte_size = arg->GetByteSize(); | |
| if (!byte_size) | |
| return byte_size.takeError(); | |
| size = *byte_size; | |
| } | |
| llvm::Expected<lldb::TypeSystemSP> type_system = GetTypeSystemFromCU(); | |
| if (!type_system) | |
| return type_system.takeError(); | |
| CompilerType size_type = type_system.get()->GetSizeType(); | |
| if (!size_type) | |
| return llvm::make_error<DILDiagnosticError>( | |
| m_expr, "unable to determine size type", node.GetLocation()); | |
| return ValueObject::CreateValueObjectFromScalar(m_exe_ctx, size, size_type, | |
| "result"); | |
| } | |
| } // namespace lldb_private::dil |