//===-- 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/Symbol/CompileUnit.h" #include "lldb/Symbol/TypeSystem.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/RegisterContext.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/FormatAdapters.h" #include 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, ExecutionContextScope &ctx, llvm::StringRef name) { uint64_t addr = valobj.GetLoadAddress(); ExecutionContext exe_ctx; ctx.CalculateExecutionContext(exe_ctx); return ValueObject::CreateValueObjectFromAddress( name, addr, exe_ctx, valobj.GetCompilerType().GetArrayElementType(&ctx).GetPointerType(), /* do_deref */ false); } static llvm::Expected GetTypeSystemFromCU(std::shared_ptr ctx) { SymbolContext symbol_context = ctx->GetSymbolContext(lldb::eSymbolContextCompUnit); lldb::LanguageType language = symbol_context.comp_unit->GetLanguage(); symbol_context = ctx->GetSymbolContext(lldb::eSymbolContextModule); return symbol_context.module_sp->GetTypeSystemForLanguage(language); } llvm::Expected Interpreter::UnaryConversion(lldb::ValueObjectSP valobj, uint32_t location) { if (!valobj) return llvm::make_error(m_expr, "invalid value object", location); llvm::Expected type_system = GetTypeSystemFromCU(m_exe_ctx_scope); 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 int_bit_size = int_type.GetBitSize(m_exe_ctx_scope.get()); if (!int_bit_size) return int_bit_size.takeError(); llvm::Expected uint_bit_size = uint_type.GetBitSize(m_exe_ctx_scope.get()); if (!uint_bit_size) return int_bit_size.takeError(); if (bitfield_size < *int_bit_size || (in_type.IsSigned() && bitfield_size == *int_bit_size)) return valobj->CastToBasicType(int_type); if (bitfield_size <= *uint_bit_size) return valobj->CastToBasicType(uint_type); // 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_scope, scalar, in_type, "result"); } } if (in_type.IsArrayType()) valobj = ArrayToPointerConversion(*valobj, *m_exe_ctx_scope, "result"); CompilerType promoted_type = valobj->GetCompilerType().GetPromotedIntegerType(); if (promoted_type) return valobj->CastToBasicType(promoted_type); 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 Interpreter::PromoteSignedInteger(CompilerType &lhs_type, CompilerType &rhs_type) { assert(lhs_type.IsInteger() && rhs_type.IsInteger()); if (!lhs_type.IsSigned() && rhs_type.IsSigned()) { llvm::Expected lhs_size = lhs_type.GetBitSize(m_exe_ctx_scope.get()); if (!lhs_size) return lhs_size.takeError(); llvm::Expected rhs_size = rhs_type.GetBitSize(m_exe_ctx_scope.get()); if (!rhs_size) return rhs_size.takeError(); if (*rhs_size == *lhs_size) { llvm::Expected type_system = GetTypeSystemFromCU(m_exe_ctx_scope); 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 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( 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; 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 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, std::shared_ptr stack_frame, lldb::TargetSP target_sp, lldb::DynamicValueType use_dynamic) { // Get a global variables list without the locals from the current frame SymbolContext symbol_context = stack_frame->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 = stack_frame->GetValueObjectForFrameVariable(var_sp, use_dynamic); } if (value_sp) return value_sp; // Check for match in modules global variables. VariableList modules_var_list; target_sp->GetImages().FindGlobalVariables( ConstString(name_ref), std::numeric_limits::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(stack_frame.get(), var_sp); if (value_sp) return value_sp; } return nullptr; } lldb::ValueObjectSP LookupIdentifier(llvm::StringRef name_ref, std::shared_ptr stack_frame, 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. if (name_ref.consume_front("$")) { lldb::RegisterContextSP reg_ctx(stack_frame->GetRegisterContext()); if (!reg_ctx) return nullptr; if (const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name_ref)) return ValueObjectRegister::Create(stack_frame.get(), 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( stack_frame->GetInScopeVariableList(false)); lldb::ValueObjectSP value_sp; if (variable_list) { lldb::VariableSP var_sp = variable_list->FindVariable(ConstString(name_ref)); if (var_sp) value_sp = stack_frame->GetValueObjectForFrameVariable(var_sp, use_dynamic); } if (value_sp) return value_sp; // Try looking for an instance variable (class member). SymbolContext sc = stack_frame->GetSymbolContext( lldb::eSymbolContextFunction | lldb::eSymbolContextBlock); llvm::StringRef ivar_name = sc.GetInstanceVariableName(); value_sp = stack_frame->FindVariable(ConstString(ivar_name)); if (value_sp) value_sp = value_sp->GetChildMemberWithName(name_ref); if (value_sp) return value_sp; } return nullptr; } Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr, std::shared_ptr frame_sp, lldb::DynamicValueType use_dynamic, uint32_t options) : m_target(std::move(target)), m_expr(expr), m_exe_ctx_scope(frame_sp), 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 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(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 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 Interpreter::Visit(const IdentifierNode &node) { lldb::DynamicValueType use_dynamic = m_use_dynamic; lldb::ValueObjectSP identifier = LookupIdentifier(node.GetName(), m_exe_ctx_scope, use_dynamic); if (!identifier && m_allow_globals) identifier = LookupGlobalIdentifier(node.GetName(), m_exe_ctx_scope, m_target, use_dynamic); if (!identifier) { std::string errMsg = llvm::formatv("use of undeclared identifier '{0}'", node.GetName()); return llvm::make_error( m_expr, errMsg, node.GetLocation(), node.GetName().size()); } return identifier; } llvm::Expected 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(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(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 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(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_scope, scalar, operand->GetCompilerType(), "result"); break; } case UnaryOpKind::Plus: { if (operand->GetCompilerType().IsReferenceType()) { operand = operand->Dereference(error); if (error.Fail()) return error.ToError(); } llvm::Expected 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(m_expr, errMsg, node.GetLocation()); } return operand; } } return llvm::make_error(m_expr, "invalid unary operation", node.GetLocation()); } llvm::Expected 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( m_expr, "arithmetic on a pointer to void", location); if (ptr->GetValueAsUnsigned(0) == 0 && offset != 0) return llvm::make_error( 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(m_expr, std::move(errMsg), location); } llvm::Expected byte_size = ptr->GetCompilerType().GetPointeeType().GetByteSize( m_exe_ctx_scope.get()); 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); ExecutionContext exe_ctx(m_target.get(), false); Scalar scalar(ptr_addr); return ValueObject::CreateValueObjectFromScalar( m_exe_ctx_scope, scalar, ptr->GetCompilerType(), "result"); } llvm::Expected 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(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(m_expr, errMsg, location); } auto value_object = [this, result_type](Scalar scalar) { return ValueObject::CreateValueObjectFromScalar(m_exe_ctx_scope, 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); } return llvm::make_error( m_expr, "invalid arithmetic operation", location); } llvm::Expected 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(m_expr, std::move(errMsg), location); } return PointerOffset(ptr, offset, BinaryOpKind::Add, location); } llvm::Expected 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( 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(m_expr, errMsg, location); } llvm::Expected lhs_byte_size = lhs_type.GetPointeeType().GetByteSize(m_exe_ctx_scope.get()); 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(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( m_expr, "undefined pointer arithmetic", location); } diff /= item_size; llvm::Expected type_system = GetTypeSystemFromCU(m_exe_ctx_scope); if (!type_system) return type_system.takeError(); CompilerType ptrdiff_type = type_system.get()->GetPointerDiffType(true); if (!ptrdiff_type) return llvm::make_error( m_expr, "unable to determine pointer diff type", location); Scalar scalar(diff); return ValueObject::CreateValueObjectFromScalar(m_exe_ctx_scope, 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(m_expr, std::move(errMsg), location); } llvm::Expected 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(m_expr, std::move(errMsg), location); } return EvaluateScalarOp(BinaryOpKind::Mul, lhs, rhs, result_type, location); } llvm::Expected 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(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( m_expr, "division by zero is undefined", location); } return EvaluateScalarOp(BinaryOpKind::Div, lhs, rhs, result_type, location); } llvm::Expected 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(m_expr, std::move(errMsg), location); } if (rhs->GetValueAsSigned(-1) == 0) { return llvm::make_error( m_expr, "division by zero is undefined", location); } return EvaluateScalarOp(BinaryOpKind::Rem, lhs, rhs, result_type, location); } llvm::Expected Interpreter::Visit(const BinaryOpNode &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; 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( m_expr, "operands have different type systems", node.GetLocation()); } switch (node.GetKind()) { 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()); } return llvm::make_error( m_expr, "unimplemented binary operation", node.GetLocation()); } llvm::Expected Interpreter::Visit(const MemberOfNode &node) { 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( 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( 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( 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( 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(""), base->GetName()); return llvm::make_error( 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(""), base->GetName()); return llvm::make_error( m_expr, errMsg, node.GetLocation(), node.GetFieldName().size()); } llvm::Expected 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( 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); 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(""), var_expr_path_strm.GetData()); return llvm::make_error(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(""), var_expr_path_strm.GetData()); return llvm::make_error(m_expr, std::move(err_msg), node.GetLocation()); } if (static_cast(child_idx) >= synthetic->GetNumChildrenIgnoringErrors()) { std::string err_msg = llvm::formatv( "array index {0} is not valid for \"({1}) {2}\"", child_idx, base->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); return llvm::make_error(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(""), var_expr_path_strm.GetData()); return llvm::make_error(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(""), var_expr_path_strm.GetData()); if (base_type.IsPointerToVoid()) err_msg = "subscript of pointer to incomplete type 'void'"; return llvm::make_error(m_expr, std::move(err_msg), node.GetLocation()); } } else if (base_type.IsArrayType(nullptr, nullptr, &is_incomplete_array)) { 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(""), var_expr_path_strm.GetData()); return llvm::make_error(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(""), var_expr_path_strm.GetData()); return llvm::make_error(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("")); return llvm::make_error(m_expr, std::move(err_msg), node.GetLocation(), 1); } if (static_cast(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(""), var_expr_path_strm.GetData()); return llvm::make_error(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(""), var_expr_path_strm.GetData()); return llvm::make_error(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( m_expr, "could not get the index as an integer", node.GetIndex().GetLocation()); return base->GetSyntheticArrayMember(signed_child_idx, true); } llvm::Expected 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( 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( m_expr, "could not get the index as an integer", node.GetLocation()); // if the format given is [high-low], swap range if (first_index > last_index) std::swap(first_index, last_index); auto base_or_err = EvaluateAndDereference(node.GetBase()); if (!base_or_err) return base_or_err; lldb::ValueObjectSP base = *base_or_err; 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(""), base->GetName().GetStringRef()); return llvm::make_error(m_expr, message, node.GetLocation()); } return child_valobj_sp; } llvm::Expected Interpreter::PickIntegerType(lldb::TypeSystemSP type_system, std::shared_ptr 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, 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 size = signed_type.GetBitSize(ctx.get()); 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( m_expr, "integer literal is too large to be represented in any integer type", literal.GetLocation()); } llvm::Expected Interpreter::Visit(const IntegerLiteralNode &node) { llvm::Expected type_system = GetTypeSystemFromCU(m_exe_ctx_scope); if (!type_system) return type_system.takeError(); llvm::Expected type = PickIntegerType(*type_system, m_exe_ctx_scope, 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 type_bitsize = type->GetBitSize(m_exe_ctx_scope.get()); if (!type_bitsize) return type_bitsize.takeError(); scalar.TruncOrExtendTo(*type_bitsize, false); return ValueObject::CreateValueObjectFromScalar(m_exe_ctx_scope, scalar, *type, "result"); } llvm::Expected Interpreter::Visit(const FloatLiteralNode &node) { llvm::Expected type_system = GetTypeSystemFromCU(m_exe_ctx_scope); 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( m_expr, "unable to create a const literal", node.GetLocation()); Scalar scalar = node.GetValue(); return ValueObject::CreateValueObjectFromScalar(m_exe_ctx_scope, scalar, type, "result"); } llvm::Expected Interpreter::Visit(const BooleanLiteralNode &node) { bool value = node.GetValue(); llvm::Expected type_system = GetTypeSystemFromCU(m_exe_ctx_scope); if (!type_system) return type_system.takeError(); return ValueObject::CreateValueObjectFromBool(m_exe_ctx_scope, *type_system, value, "result"); } llvm::Expected 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( 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_scope.get())) { type_byte_size = *temp; } else { std::string errMsg = llvm::formatv("unable to get byte size for type {0}", target_type.TypeDescription()); return llvm::make_error( m_expr, std::move(errMsg), location, target_type.TypeDescription().length()); } if (auto temp = source_type.GetByteSize(m_exe_ctx_scope.get())) { rhs_type_byte_size = *temp; } else { std::string errMsg = llvm::formatv("unable to get byte size for type {0}", source_type.TypeDescription()); return llvm::make_error( 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( 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( m_expr, std::move(errMsg), location, source_type.TypeDescription().length()); } return CastKind::eArithmetic; } llvm::Expected 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( 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( 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( m_expr, std::move(errMsg), location, source_type.TypeDescription().length()); } llvm::Expected 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_scope, 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(m_expr, error.AsCString(), node.GetLocation()); } 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()) return operand->CastToEnumType(target_type); break; } case CastKind::eArithmetic: { if (op_type.IsPointerType() || op_type.IsNullPtrType() || op_type.IsScalarType() || op_type.IsEnumerationType()) return 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)); llvm::StringRef name = "result"; ExecutionContext exe_ctx(m_target.get(), false); return ValueObject::CreateValueObjectFromAddress(name, addr, exe_ctx, target_type, /* do_deref */ false); } case CastKind::eNone: { return lldb::ValueObjectSP(); } } // switch std::string errMsg = llvm::formatv("unable to cast from '{0}' to '{1}'", op_type.TypeDescription(), target_type.TypeDescription()); return llvm::make_error(m_expr, std::move(errMsg), node.GetLocation()); } } // namespace lldb_private::dil