Author: cmtice Date: 2024-06-13T09:14:17-07:00 New Revision: 9f70cd83897b36e5628057dfef2855a0b9045766
URL: https://github.com/llvm/llvm-project/commit/9f70cd83897b36e5628057dfef2855a0b9045766 DIFF: https://github.com/llvm/llvm-project/commit/9f70cd83897b36e5628057dfef2855a0b9045766.diff LOG: [LLDB] Add more helper functions to ValueObject class. (#87197) Create additional helper functions for the ValueObject class, for: - returning the value as an APSInt or APFloat - additional type casting options - additional ways to create ValueObjects from various types of data - dereferencing a ValueObject These helper functions are needed for implementing the Data Inspection Language, described in https://discourse.llvm.org/t/rfc-data-inspection-language/69893 Added: Modified: lldb/include/lldb/Core/ValueObject.h lldb/include/lldb/Utility/Scalar.h lldb/source/API/SBValue.cpp lldb/source/Core/ValueObject.cpp Removed: ################################################################################ diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h index 1e8c7c9c00536..db1fdae170ed0 100644 --- a/lldb/include/lldb/Core/ValueObject.h +++ b/lldb/include/lldb/Core/ValueObject.h @@ -441,6 +441,30 @@ class ValueObject { virtual int64_t GetValueAsSigned(int64_t fail_value, bool *success = nullptr); + /// If the current ValueObject is of an appropriate type, convert the + /// value to an APSInt and return that. Otherwise return an error. + llvm::Expected<llvm::APSInt> GetValueAsAPSInt(); + + /// If the current ValueObject is of an appropriate type, convert the + /// value to an APFloat and return that. Otherwise return an error. + llvm::Expected<llvm::APFloat> GetValueAsAPFloat(); + + /// If the current ValueObject is of an appropriate type, convert the + /// value to a boolean and return that. Otherwise return an error. + llvm::Expected<bool> GetValueAsBool(); + + /// Update an existing integer ValueObject with a new integer value. This + /// is only intended to be used with 'temporary' ValueObjects, i.e. ones that + /// are not associated with program variables. It does not update program + /// memory, registers, stack, etc. + void SetValueFromInteger(const llvm::APInt &value, Status &error); + + /// Update an existing integer ValueObject with an integer value created + /// frome 'new_val_sp'. This is only intended to be used with 'temporary' + /// ValueObjects, i.e. ones that are not associated with program variables. + /// It does not update program memory, registers, stack, etc. + void SetValueFromInteger(lldb::ValueObjectSP new_val_sp, Status &error); + virtual bool SetValueFromCString(const char *value_str, Status &error); /// Return the module associated with this value object in case the value is @@ -618,6 +642,32 @@ class ValueObject { virtual lldb::ValueObjectSP CastPointerType(const char *name, lldb::TypeSP &type_sp); + /// Return the target load address associated with this value object. + lldb::addr_t GetLoadAddress(); + + /// Take a ValueObject whose type is an inherited class, and cast it to + /// 'type', which should be one of its base classes. 'base_type_indices' + /// contains the indices of direct base classes on the path from the + /// ValueObject's current type to 'type' + llvm::Expected<lldb::ValueObjectSP> + CastDerivedToBaseType(CompilerType type, + const llvm::ArrayRef<uint32_t> &base_type_indices); + + /// Take a ValueObject whose type is a base class, and cast it to 'type', + /// which should be one of its derived classes. 'base_type_indices' + /// contains the indices of direct base classes on the path from the + /// ValueObject's current type to 'type' + llvm::Expected<lldb::ValueObjectSP> CastBaseToDerivedType(CompilerType type, + uint64_t offset); + + // Take a ValueObject that contains a scalar, enum or pointer type, and + // cast it to a "basic" type (integer, float or boolean). + lldb::ValueObjectSP CastToBasicType(CompilerType type); + + // Take a ValueObject that contain an integer, float or enum, and cast it + // to an enum. + lldb::ValueObjectSP CastToEnumType(CompilerType type); + /// If this object represents a C++ class with a vtable, return an object /// that represents the virtual function table. If the object isn't a class /// with a vtable, return a valid ValueObject with the error set correctly. @@ -659,15 +709,41 @@ class ValueObject { const ExecutionContext &exe_ctx, const EvaluateExpressionOptions &options); + /// Given an address either create a value object containing the value at + /// that address, or create a value object containing the address itself + /// (pointer value), depending on whether the parameter 'do_deref' is true or + /// false. static lldb::ValueObjectSP CreateValueObjectFromAddress(llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx, - CompilerType type); + CompilerType type, bool do_deref = true); static lldb::ValueObjectSP CreateValueObjectFromData(llvm::StringRef name, const DataExtractor &data, const ExecutionContext &exe_ctx, CompilerType type); + /// Create a value object containing the given APInt value. + static lldb::ValueObjectSP CreateValueObjectFromAPInt(lldb::TargetSP target, + const llvm::APInt &v, + CompilerType type, + llvm::StringRef name); + + /// Create a value object containing the given APFloat value. + static lldb::ValueObjectSP + CreateValueObjectFromAPFloat(lldb::TargetSP target, const llvm::APFloat &v, + CompilerType type, llvm::StringRef name); + + /// Create a value object containing the given boolean value. + static lldb::ValueObjectSP CreateValueObjectFromBool(lldb::TargetSP target, + bool value, + llvm::StringRef name); + + /// Create a nullptr value object with the specified type (must be a + /// nullptr type). + static lldb::ValueObjectSP CreateValueObjectFromNullptr(lldb::TargetSP target, + CompilerType type, + llvm::StringRef name); + lldb::ValueObjectSP Persist(); /// Returns true if this is a char* or a char[] if it is a char* and @@ -719,6 +795,10 @@ class ValueObject { ClearUserVisibleData(eClearUserVisibleDataItemsSummary); } + void SetDerefValobj(ValueObject *deref) { m_deref_valobj = deref; } + + ValueObject *GetDerefValobj() { return m_deref_valobj; } + void SetValueFormat(lldb::TypeFormatImplSP format) { m_type_format_sp = std::move(format); ClearUserVisibleData(eClearUserVisibleDataItemsValue); diff --git a/lldb/include/lldb/Utility/Scalar.h b/lldb/include/lldb/Utility/Scalar.h index d7155884c6d1b..0d8eba3c9726d 100644 --- a/lldb/include/lldb/Utility/Scalar.h +++ b/lldb/include/lldb/Utility/Scalar.h @@ -181,6 +181,10 @@ class Scalar { long double LongDouble(long double fail_value = 0.0) const; + llvm::APSInt GetAPSInt() const { return m_integer; } + + llvm::APFloat GetAPFloat() const { return m_float; } + Status SetValueFromCString(const char *s, lldb::Encoding encoding, size_t byte_size); diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp index c53ec5a746482..9d7efba024d11 100644 --- a/lldb/source/API/SBValue.cpp +++ b/lldb/source/API/SBValue.cpp @@ -1281,26 +1281,8 @@ lldb::addr_t SBValue::GetLoadAddress() { lldb::addr_t value = LLDB_INVALID_ADDRESS; ValueLocker locker; lldb::ValueObjectSP value_sp(GetSP(locker)); - if (value_sp) { - TargetSP target_sp(value_sp->GetTargetSP()); - if (target_sp) { - const bool scalar_is_load_address = true; - AddressType addr_type; - value = value_sp->GetAddressOf(scalar_is_load_address, &addr_type); - if (addr_type == eAddressTypeFile) { - ModuleSP module_sp(value_sp->GetModule()); - if (!module_sp) - value = LLDB_INVALID_ADDRESS; - else { - Address addr; - module_sp->ResolveFileAddress(value, addr); - value = addr.GetLoadAddress(target_sp.get()); - } - } else if (addr_type == eAddressTypeHost || - addr_type == eAddressTypeInvalid) - value = LLDB_INVALID_ADDRESS; - } - } + if (value_sp) + return value_sp->GetLoadAddress(); return value; } diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index f32cb82faa006..1c69145560de2 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -1116,6 +1116,153 @@ int64_t ValueObject::GetValueAsSigned(int64_t fail_value, bool *success) { return fail_value; } +llvm::Expected<llvm::APSInt> ValueObject::GetValueAsAPSInt() { + // Make sure the type can be converted to an APSInt. + if (!GetCompilerType().IsInteger() && + !GetCompilerType().IsScopedEnumerationType() && + !GetCompilerType().IsEnumerationType() && + !GetCompilerType().IsPointerType() && + !GetCompilerType().IsNullPtrType() && + !GetCompilerType().IsReferenceType() && !GetCompilerType().IsBoolean()) + return llvm::make_error<llvm::StringError>( + "type cannot be converted to APSInt", llvm::inconvertibleErrorCode()); + + if (CanProvideValue()) { + Scalar scalar; + if (ResolveValue(scalar)) + return scalar.GetAPSInt(); + } + + return llvm::make_error<llvm::StringError>( + "error occurred; unable to convert to APSInt", + llvm::inconvertibleErrorCode()); +} + +llvm::Expected<llvm::APFloat> ValueObject::GetValueAsAPFloat() { + if (!GetCompilerType().IsFloat()) + return llvm::make_error<llvm::StringError>( + "type cannot be converted to APFloat", llvm::inconvertibleErrorCode()); + + if (CanProvideValue()) { + Scalar scalar; + if (ResolveValue(scalar)) + return scalar.GetAPFloat(); + } + + return llvm::make_error<llvm::StringError>( + "error occurred; unable to convert to APFloat", + llvm::inconvertibleErrorCode()); +} + +llvm::Expected<bool> ValueObject::GetValueAsBool() { + CompilerType val_type = GetCompilerType(); + if (val_type.IsInteger() || val_type.IsUnscopedEnumerationType() || + val_type.IsPointerType()) { + auto value_or_err = GetValueAsAPSInt(); + if (value_or_err) + return value_or_err->getBoolValue(); + } + if (val_type.IsFloat()) { + auto value_or_err = GetValueAsAPFloat(); + if (value_or_err) + return value_or_err->isNonZero(); + } + if (val_type.IsArrayType()) + return GetAddressOf() != 0; + + return llvm::make_error<llvm::StringError>("type cannot be converted to bool", + llvm::inconvertibleErrorCode()); +} + +void ValueObject::SetValueFromInteger(const llvm::APInt &value, Status &error) { + // Verify the current object is an integer object + CompilerType val_type = GetCompilerType(); + if (!val_type.IsInteger() && !val_type.IsUnscopedEnumerationType() && + !val_type.IsFloat() && !val_type.IsPointerType() && + !val_type.IsScalarType()) { + error.SetErrorString("current value object is not an integer objet"); + return; + } + + // Verify the current object is not actually associated with any program + // variable. + if (GetVariable()) { + error.SetErrorString("current value object is not a temporary object"); + return; + } + + // Verify the proposed new value is the right size. + lldb::TargetSP target = GetTargetSP(); + uint64_t byte_size = 0; + if (auto temp = GetCompilerType().GetByteSize(target.get())) + byte_size = temp.value(); + if (value.getBitWidth() != byte_size * CHAR_BIT) { + error.SetErrorString( + "illegal argument: new value should be of the same size"); + return; + } + + lldb::DataExtractorSP data_sp; + data_sp->SetData(value.getRawData(), byte_size, + target->GetArchitecture().GetByteOrder()); + data_sp->SetAddressByteSize( + static_cast<uint8_t>(target->GetArchitecture().GetAddressByteSize())); + SetData(*data_sp, error); +} + +void ValueObject::SetValueFromInteger(lldb::ValueObjectSP new_val_sp, + Status &error) { + // Verify the current object is an integer object + CompilerType val_type = GetCompilerType(); + if (!val_type.IsInteger() && !val_type.IsUnscopedEnumerationType() && + !val_type.IsFloat() && !val_type.IsPointerType() && + !val_type.IsScalarType()) { + error.SetErrorString("current value object is not an integer objet"); + return; + } + + // Verify the current object is not actually associated with any program + // variable. + if (GetVariable()) { + error.SetErrorString("current value object is not a temporary object"); + return; + } + + // Verify the proposed new value is the right type. + CompilerType new_val_type = new_val_sp->GetCompilerType(); + if (!new_val_type.IsInteger() && !new_val_type.IsFloat() && + !new_val_type.IsPointerType()) { + error.SetErrorString( + "illegal argument: new value should be of the same size"); + return; + } + + if (new_val_type.IsInteger()) { + auto value_or_err = new_val_sp->GetValueAsAPSInt(); + if (value_or_err) + SetValueFromInteger(*value_or_err, error); + else + error.SetErrorString("error getting APSInt from new_val_sp"); + } else if (new_val_type.IsFloat()) { + auto value_or_err = new_val_sp->GetValueAsAPFloat(); + if (value_or_err) + SetValueFromInteger(value_or_err->bitcastToAPInt(), error); + else + error.SetErrorString("error getting APFloat from new_val_sp"); + } else if (new_val_type.IsPointerType()) { + bool success = true; + uint64_t int_val = new_val_sp->GetValueAsUnsigned(0, &success); + if (success) { + lldb::TargetSP target = GetTargetSP(); + uint64_t num_bits = 0; + if (auto temp = new_val_sp->GetCompilerType().GetBitSize(target.get())) + num_bits = temp.value(); + SetValueFromInteger(llvm::APInt(num_bits, int_val), error); + } else + error.SetErrorString("error converting new_val_sp to integer"); + } +} + // if any more "special cases" are added to // ValueObject::DumpPrintableRepresentation() please keep this call up to date // by returning true for your new special cases. We will eventually move to @@ -2843,6 +2990,374 @@ ValueObjectSP ValueObject::CastPointerType(const char *name, TypeSP &type_sp) { return valobj_sp; } +lldb::addr_t ValueObject::GetLoadAddress() { + lldb::addr_t addr_value = LLDB_INVALID_ADDRESS; + if (auto target_sp = GetTargetSP()) { + const bool scalar_is_load_address = true; + AddressType addr_type; + addr_value = GetAddressOf(scalar_is_load_address, &addr_type); + if (addr_type == eAddressTypeFile) { + lldb::ModuleSP module_sp(GetModule()); + if (!module_sp) + addr_value = LLDB_INVALID_ADDRESS; + else { + Address tmp_addr; + module_sp->ResolveFileAddress(addr_value, tmp_addr); + addr_value = tmp_addr.GetLoadAddress(target_sp.get()); + } + } else if (addr_type == eAddressTypeHost || + addr_type == eAddressTypeInvalid) + addr_value = LLDB_INVALID_ADDRESS; + } + return addr_value; +} + +llvm::Expected<lldb::ValueObjectSP> ValueObject::CastDerivedToBaseType( + CompilerType type, const llvm::ArrayRef<uint32_t> &base_type_indices) { + // Make sure the starting type and the target type are both valid for this + // type of cast; otherwise return the shared pointer to the original + // (unchanged) ValueObject. + if (!type.IsPointerType() && !type.IsReferenceType()) + return llvm::make_error<llvm::StringError>( + "Invalid target type: should be a pointer or a reference", + llvm::inconvertibleErrorCode()); + + CompilerType start_type = GetCompilerType(); + if (start_type.IsReferenceType()) + start_type = start_type.GetNonReferenceType(); + + auto target_record_type = + type.IsPointerType() ? type.GetPointeeType() : type.GetNonReferenceType(); + auto start_record_type = + start_type.IsPointerType() ? start_type.GetPointeeType() : start_type; + + if (!target_record_type.IsRecordType() || !start_record_type.IsRecordType()) + return llvm::make_error<llvm::StringError>( + "Underlying start & target types should be record types", + llvm::inconvertibleErrorCode()); + + if (target_record_type.CompareTypes(start_record_type)) + return llvm::make_error<llvm::StringError>( + "Underlying start & target types should be diff erent", + llvm::inconvertibleErrorCode()); + + if (base_type_indices.empty()) + return llvm::make_error<llvm::StringError>( + "Children sequence must be non-empty", llvm::inconvertibleErrorCode()); + + // Both the starting & target types are valid for the cast, and the list of + // base class indices is non-empty, so we can proceed with the cast. + + lldb::TargetSP target = GetTargetSP(); + // The `value` can be a pointer, but GetChildAtIndex works for pointers too. + lldb::ValueObjectSP inner_value = GetSP(); + + for (const uint32_t i : base_type_indices) + // Create synthetic value if needed. + inner_value = + inner_value->GetChildAtIndex(i, /*can_create_synthetic*/ true); + + // At this point type of `inner_value` should be the dereferenced target + // type. + CompilerType inner_value_type = inner_value->GetCompilerType(); + if (type.IsPointerType()) { + if (!inner_value_type.CompareTypes(type.GetPointeeType())) + return llvm::make_error<llvm::StringError>( + "casted value doesn't match the desired type", + llvm::inconvertibleErrorCode()); + + uintptr_t addr = inner_value->GetLoadAddress(); + llvm::StringRef name = ""; + ExecutionContext exe_ctx(target.get(), false); + return ValueObject::CreateValueObjectFromAddress(name, addr, exe_ctx, type, + /* do deref */ false); + } + + // At this point the target type should be a reference. + if (!inner_value_type.CompareTypes(type.GetNonReferenceType())) + return llvm::make_error<llvm::StringError>( + "casted value doesn't match the desired type", + llvm::inconvertibleErrorCode()); + + return lldb::ValueObjectSP(inner_value->Cast(type.GetNonReferenceType())); +} + +llvm::Expected<lldb::ValueObjectSP> +ValueObject::CastBaseToDerivedType(CompilerType type, uint64_t offset) { + // Make sure the starting type and the target type are both valid for this + // type of cast; otherwise return the shared pointer to the original + // (unchanged) ValueObject. + if (!type.IsPointerType() && !type.IsReferenceType()) + return llvm::make_error<llvm::StringError>( + "Invalid target type: should be a pointer or a reference", + llvm::inconvertibleErrorCode()); + + CompilerType start_type = GetCompilerType(); + if (start_type.IsReferenceType()) + start_type = start_type.GetNonReferenceType(); + + auto target_record_type = + type.IsPointerType() ? type.GetPointeeType() : type.GetNonReferenceType(); + auto start_record_type = + start_type.IsPointerType() ? start_type.GetPointeeType() : start_type; + + if (!target_record_type.IsRecordType() || !start_record_type.IsRecordType()) + return llvm::make_error<llvm::StringError>( + "Underlying start & target types should be record types", + llvm::inconvertibleErrorCode()); + + if (target_record_type.CompareTypes(start_record_type)) + return llvm::make_error<llvm::StringError>( + "Underlying start & target types should be diff erent", + llvm::inconvertibleErrorCode()); + + CompilerType virtual_base; + if (target_record_type.IsVirtualBase(start_record_type, &virtual_base)) { + if (!virtual_base.IsValid()) + return llvm::make_error<llvm::StringError>( + "virtual base should be valid", llvm::inconvertibleErrorCode()); + return llvm::make_error<llvm::StringError>( + llvm::Twine("cannot cast " + start_type.TypeDescription() + " to " + + type.TypeDescription() + " via virtual base " + + virtual_base.TypeDescription()), + llvm::inconvertibleErrorCode()); + } + + // Both the starting & target types are valid for the cast, so we can + // proceed with the cast. + + lldb::TargetSP target = GetTargetSP(); + auto pointer_type = + type.IsPointerType() ? type : type.GetNonReferenceType().GetPointerType(); + + uintptr_t addr = + type.IsPointerType() ? GetValueAsUnsigned(0) : GetLoadAddress(); + + llvm::StringRef name = ""; + ExecutionContext exe_ctx(target.get(), false); + lldb::ValueObjectSP value = ValueObject::CreateValueObjectFromAddress( + name, addr - offset, exe_ctx, pointer_type, /* do_deref */ false); + + if (type.IsPointerType()) + return value; + + // At this point the target type is a reference. Since `value` is a pointer, + // it has to be dereferenced. + Status error; + return value->Dereference(error); +} + +lldb::ValueObjectSP ValueObject::CastToBasicType(CompilerType type) { + bool is_scalar = GetCompilerType().IsScalarType(); + bool is_enum = GetCompilerType().IsEnumerationType(); + bool is_pointer = + GetCompilerType().IsPointerType() || GetCompilerType().IsNullPtrType(); + bool is_float = GetCompilerType().IsFloat(); + bool is_integer = GetCompilerType().IsInteger(); + + if (!type.IsScalarType()) { + m_error.SetErrorString("target type must be a scalar"); + return GetSP(); + } + + if (!is_scalar && !is_enum && !is_pointer) { + m_error.SetErrorString("argument must be a scalar, enum, or pointer"); + return GetSP(); + } + + lldb::TargetSP target = GetTargetSP(); + uint64_t type_byte_size = 0; + uint64_t val_byte_size = 0; + if (auto temp = type.GetByteSize(target.get())) + type_byte_size = temp.value(); + if (auto temp = GetCompilerType().GetByteSize(target.get())) + val_byte_size = temp.value(); + + if (is_pointer) { + if (!type.IsInteger() && !type.IsBoolean()) { + m_error.SetErrorString("target type must be an integer or boolean"); + return GetSP(); + } + if (!type.IsBoolean() && type_byte_size < val_byte_size) { + m_error.SetErrorString( + "target type cannot be smaller than the pointer type"); + return GetSP(); + } + } + + if (type.IsBoolean()) { + if (!is_scalar || is_integer) + return ValueObject::CreateValueObjectFromBool( + target, GetValueAsUnsigned(0) != 0, "result"); + else if (is_scalar && is_float) { + auto float_value_or_err = GetValueAsAPFloat(); + if (float_value_or_err) + return ValueObject::CreateValueObjectFromBool( + target, !float_value_or_err->isZero(), "result"); + else { + m_error.SetErrorStringWithFormat( + "cannot get value as APFloat: %s", + llvm::toString(float_value_or_err.takeError()).c_str()); + return GetSP(); + } + } + } + + if (type.IsInteger()) { + if (!is_scalar || is_integer) { + auto int_value_or_err = GetValueAsAPSInt(); + if (int_value_or_err) { + // Get the value as APSInt and extend or truncate it to the requested + // size. + llvm::APSInt ext = + int_value_or_err->extOrTrunc(type_byte_size * CHAR_BIT); + return ValueObject::CreateValueObjectFromAPInt(target, ext, type, + "result"); + } else { + m_error.SetErrorStringWithFormat( + "cannot get value as APSInt: %s", + llvm::toString(int_value_or_err.takeError()).c_str()); + ; + return GetSP(); + } + } else if (is_scalar && is_float) { + llvm::APSInt integer(type_byte_size * CHAR_BIT, !type.IsSigned()); + bool is_exact; + auto float_value_or_err = GetValueAsAPFloat(); + if (float_value_or_err) { + llvm::APFloatBase::opStatus status = + float_value_or_err->convertToInteger( + integer, llvm::APFloat::rmTowardZero, &is_exact); + + // Casting floating point values that are out of bounds of the target + // type is undefined behaviour. + if (status & llvm::APFloatBase::opInvalidOp) { + m_error.SetErrorStringWithFormat( + "invalid type cast detected: %s", + llvm::toString(float_value_or_err.takeError()).c_str()); + return GetSP(); + } + return ValueObject::CreateValueObjectFromAPInt(target, integer, type, + "result"); + } + } + } + + if (type.IsFloat()) { + if (!is_scalar) { + auto int_value_or_err = GetValueAsAPSInt(); + if (int_value_or_err) { + llvm::APSInt ext = + int_value_or_err->extOrTrunc(type_byte_size * CHAR_BIT); + Scalar scalar_int(ext); + llvm::APFloat f = scalar_int.CreateAPFloatFromAPSInt( + type.GetCanonicalType().GetBasicTypeEnumeration()); + return ValueObject::CreateValueObjectFromAPFloat(target, f, type, + "result"); + } else { + m_error.SetErrorStringWithFormat( + "cannot get value as APSInt: %s", + llvm::toString(int_value_or_err.takeError()).c_str()); + return GetSP(); + } + } else { + if (is_integer) { + auto int_value_or_err = GetValueAsAPSInt(); + if (int_value_or_err) { + Scalar scalar_int(*int_value_or_err); + llvm::APFloat f = scalar_int.CreateAPFloatFromAPSInt( + type.GetCanonicalType().GetBasicTypeEnumeration()); + return ValueObject::CreateValueObjectFromAPFloat(target, f, type, + "result"); + } else { + m_error.SetErrorStringWithFormat( + "cannot get value as APSInt: %s", + llvm::toString(int_value_or_err.takeError()).c_str()); + return GetSP(); + } + } + if (is_float) { + auto float_value_or_err = GetValueAsAPFloat(); + if (float_value_or_err) { + Scalar scalar_float(*float_value_or_err); + llvm::APFloat f = scalar_float.CreateAPFloatFromAPFloat( + type.GetCanonicalType().GetBasicTypeEnumeration()); + return ValueObject::CreateValueObjectFromAPFloat(target, f, type, + "result"); + } else { + m_error.SetErrorStringWithFormat( + "cannot get value as APFloat: %s", + llvm::toString(float_value_or_err.takeError()).c_str()); + return GetSP(); + } + } + } + } + + m_error.SetErrorString("Unable to perform requested cast"); + return GetSP(); +} + +lldb::ValueObjectSP ValueObject::CastToEnumType(CompilerType type) { + bool is_enum = GetCompilerType().IsEnumerationType(); + bool is_integer = GetCompilerType().IsInteger(); + bool is_float = GetCompilerType().IsFloat(); + + if (!is_enum && !is_integer && !is_float) { + m_error.SetErrorString("argument must be an integer, a float, or an enum"); + return GetSP(); + } + + if (!type.IsEnumerationType()) { + m_error.SetErrorString("target type must be an enum"); + return GetSP(); + } + + lldb::TargetSP target = GetTargetSP(); + uint64_t byte_size = 0; + if (auto temp = type.GetByteSize(target.get())) + byte_size = temp.value(); + + if (is_float) { + llvm::APSInt integer(byte_size * CHAR_BIT, !type.IsSigned()); + bool is_exact; + auto value_or_err = GetValueAsAPFloat(); + if (value_or_err) { + llvm::APFloatBase::opStatus status = value_or_err->convertToInteger( + integer, llvm::APFloat::rmTowardZero, &is_exact); + + // Casting floating point values that are out of bounds of the target + // type is undefined behaviour. + if (status & llvm::APFloatBase::opInvalidOp) { + m_error.SetErrorStringWithFormat( + "invalid type cast detected: %s", + llvm::toString(value_or_err.takeError()).c_str()); + return GetSP(); + } + return ValueObject::CreateValueObjectFromAPInt(target, integer, type, + "result"); + } else { + m_error.SetErrorString("cannot get value as APFloat"); + return GetSP(); + } + } else { + // Get the value as APSInt and extend or truncate it to the requested size. + auto value_or_err = GetValueAsAPSInt(); + if (value_or_err) { + llvm::APSInt ext = value_or_err->extOrTrunc(byte_size * CHAR_BIT); + return ValueObject::CreateValueObjectFromAPInt(target, ext, type, + "result"); + } else { + m_error.SetErrorStringWithFormat( + "cannot get value as APSInt: %s", + llvm::toString(value_or_err.takeError()).c_str()); + return GetSP(); + } + } + m_error.SetErrorString("Cannot perform requested cast"); + return GetSP(); +} + ValueObject::EvaluationPoint::EvaluationPoint() : m_mod_id(), m_exe_ctx_ref() {} ValueObject::EvaluationPoint::EvaluationPoint(ExecutionContextScope *exe_scope, @@ -3028,9 +3543,11 @@ lldb::ValueObjectSP ValueObject::CreateValueObjectFromExpression( lldb::ValueObjectSP ValueObject::CreateValueObjectFromAddress( llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx, - CompilerType type) { + CompilerType type, bool do_deref) { if (type) { CompilerType pointer_type(type.GetPointerType()); + if (!do_deref) + pointer_type = type; if (pointer_type) { lldb::DataBufferSP buffer( new lldb_private::DataBufferHeap(&address, sizeof(lldb::addr_t))); @@ -3039,10 +3556,12 @@ lldb::ValueObjectSP ValueObject::CreateValueObjectFromAddress( ConstString(name), buffer, exe_ctx.GetByteOrder(), exe_ctx.GetAddressByteSize())); if (ptr_result_valobj_sp) { - ptr_result_valobj_sp->GetValue().SetValueType( - Value::ValueType::LoadAddress); + if (do_deref) + ptr_result_valobj_sp->GetValue().SetValueType( + Value::ValueType::LoadAddress); Status err; - ptr_result_valobj_sp = ptr_result_valobj_sp->Dereference(err); + if (do_deref) + ptr_result_valobj_sp = ptr_result_valobj_sp->Dereference(err); if (ptr_result_valobj_sp && !name.empty()) ptr_result_valobj_sp->SetName(ConstString(name)); } @@ -3065,6 +3584,66 @@ lldb::ValueObjectSP ValueObject::CreateValueObjectFromData( return new_value_sp; } +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromAPInt(lldb::TargetSP target, + const llvm::APInt &v, CompilerType type, + llvm::StringRef name) { + ExecutionContext exe_ctx(target.get(), false); + uint64_t byte_size = 0; + if (auto temp = type.GetByteSize(target.get())) + byte_size = temp.value(); + lldb::DataExtractorSP data_sp = std::make_shared<DataExtractor>( + reinterpret_cast<const void *>(v.getRawData()), byte_size, + exe_ctx.GetByteOrder(), exe_ctx.GetAddressByteSize()); + return ValueObject::CreateValueObjectFromData(name, *data_sp, exe_ctx, type); +} + +lldb::ValueObjectSP ValueObject::CreateValueObjectFromAPFloat( + lldb::TargetSP target, const llvm::APFloat &v, CompilerType type, + llvm::StringRef name) { + return CreateValueObjectFromAPInt(target, v.bitcastToAPInt(), type, name); +} + +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromBool(lldb::TargetSP target, bool value, + llvm::StringRef name) { + CompilerType target_type; + if (target) { + for (auto type_system_sp : target->GetScratchTypeSystems()) + if (auto compiler_type = + type_system_sp->GetBasicTypeFromAST(lldb::eBasicTypeBool)) { + target_type = compiler_type; + break; + } + } + ExecutionContext exe_ctx(target.get(), false); + uint64_t byte_size = 0; + if (auto temp = target_type.GetByteSize(target.get())) + byte_size = temp.value(); + lldb::DataExtractorSP data_sp = std::make_shared<DataExtractor>( + reinterpret_cast<const void *>(&value), byte_size, exe_ctx.GetByteOrder(), + exe_ctx.GetAddressByteSize()); + return ValueObject::CreateValueObjectFromData(name, *data_sp, exe_ctx, + target_type); +} + +lldb::ValueObjectSP ValueObject::CreateValueObjectFromNullptr( + lldb::TargetSP target, CompilerType type, llvm::StringRef name) { + if (!type.IsNullPtrType()) { + lldb::ValueObjectSP ret_val; + return ret_val; + } + uintptr_t zero = 0; + ExecutionContext exe_ctx(target.get(), false); + uint64_t byte_size = 0; + if (auto temp = type.GetByteSize(target.get())) + byte_size = temp.value(); + lldb::DataExtractorSP data_sp = std::make_shared<DataExtractor>( + reinterpret_cast<const void *>(zero), byte_size, exe_ctx.GetByteOrder(), + exe_ctx.GetAddressByteSize()); + return ValueObject::CreateValueObjectFromData(name, *data_sp, exe_ctx, type); +} + ModuleSP ValueObject::GetModule() { ValueObject *root(GetRoot()); if (root != this) _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits