As of #193120, `eExpressionPathOptionsNoFragileObjcIvar` and `eExpressionPathOptionsNoSyntheticArrayRange` are no longer used anywhere, and aren't expected to be used again.
1412 lines
54 KiB
C++
1412 lines
54 KiB
C++
//===-- 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 <memory>
|
|
|
|
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<lldb::TypeSystemSP>
|
|
GetTypeSystemFromCU(std::shared_ptr<StackFrame> 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<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(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<uint64_t> int_bit_size =
|
|
int_type.GetBitSize(m_exe_ctx_scope.get());
|
|
if (!int_bit_size)
|
|
return int_bit_size.takeError();
|
|
llvm::Expected<uint64_t> 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<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_scope.get());
|
|
if (!lhs_size)
|
|
return lhs_size.takeError();
|
|
llvm::Expected<uint64_t> rhs_size =
|
|
rhs_type.GetBitSize(m_exe_ctx_scope.get());
|
|
if (!rhs_size)
|
|
return rhs_size.takeError();
|
|
|
|
if (*rhs_size == *lhs_size) {
|
|
llvm::Expected<lldb::TypeSystemSP> 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<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, std::shared_ptr<StackFrame> 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<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(stack_frame.get(), var_sp);
|
|
|
|
if (value_sp)
|
|
return value_sp;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
lldb::ValueObjectSP LookupIdentifier(llvm::StringRef name_ref,
|
|
std::shared_ptr<StackFrame> 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<StackFrame> 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<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_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<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_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<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;
|
|
}
|
|
}
|
|
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_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<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_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<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_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<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(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<DILDiagnosticError>(
|
|
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<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);
|
|
}
|
|
|
|
llvm::Expected<lldb::ValueObjectSP>
|
|
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<DILDiagnosticError>(
|
|
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<DILDiagnosticError>(
|
|
m_expr, "unimplemented binary operation", node.GetLocation());
|
|
}
|
|
|
|
llvm::Expected<lldb::ValueObjectSP>
|
|
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<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);
|
|
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)) {
|
|
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());
|
|
|
|
// 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("<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,
|
|
std::shared_ptr<ExecutionContextScope> 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(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<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(m_exe_ctx_scope);
|
|
if (!type_system)
|
|
return type_system.takeError();
|
|
|
|
llvm::Expected<CompilerType> 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<uint64_t> 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<lldb::ValueObjectSP>
|
|
Interpreter::Visit(const FloatLiteralNode &node) {
|
|
llvm::Expected<lldb::TypeSystemSP> 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<DILDiagnosticError>(
|
|
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<lldb::ValueObjectSP>
|
|
Interpreter::Visit(const BooleanLiteralNode &node) {
|
|
bool value = node.GetValue();
|
|
llvm::Expected<lldb::TypeSystemSP> 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<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_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<DILDiagnosticError>(
|
|
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<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_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<DILDiagnosticError>(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<DILDiagnosticError>(m_expr, std::move(errMsg),
|
|
node.GetLocation());
|
|
}
|
|
|
|
} // namespace lldb_private::dil
|