blob: 3be53bb74eb8d400adbd5a71b6cc42e918739a6e [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/zxdb/expr/expr_value.h"
#include "src/developer/debug/zxdb/common/err.h"
#include "src/developer/debug/zxdb/common/string_util.h"
#include "src/developer/debug/zxdb/expr/eval_context.h"
#include "src/developer/debug/zxdb/symbols/base_type.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace zxdb {
ExprValue::ExprValue() = default;
ExprValue::ExprValue(bool value, fxl::RefPtr<Type> type, const ExprValueSource& source)
: ExprValue(std::move(type), BaseType::kBaseTypeBoolean, "bool", &value, sizeof(bool), source) {
}
ExprValue::ExprValue(int8_t value, fxl::RefPtr<Type> type, const ExprValueSource& source)
: ExprValue(std::move(type), BaseType::kBaseTypeSigned, "int8_t", &value, sizeof(int8_t),
source) {}
ExprValue::ExprValue(uint8_t value, fxl::RefPtr<Type> type, const ExprValueSource& source)
: ExprValue(std::move(type), BaseType::kBaseTypeUnsigned, "uint8_t", &value, sizeof(uint8_t),
source) {}
ExprValue::ExprValue(int16_t value, fxl::RefPtr<Type> type, const ExprValueSource& source)
: ExprValue(std::move(type), BaseType::kBaseTypeSigned, "int16_t", &value, sizeof(int16_t),
source) {}
ExprValue::ExprValue(uint16_t value, fxl::RefPtr<Type> type, const ExprValueSource& source)
: ExprValue(std::move(type), BaseType::kBaseTypeUnsigned, "uint16_t", &value, sizeof(uint16_t),
source) {}
ExprValue::ExprValue(int32_t value, fxl::RefPtr<Type> type, const ExprValueSource& source)
: ExprValue(std::move(type), BaseType::kBaseTypeSigned, "int32_t", &value, sizeof(int32_t),
source) {}
ExprValue::ExprValue(uint32_t value, fxl::RefPtr<Type> type, const ExprValueSource& source)
: ExprValue(std::move(type), BaseType::kBaseTypeUnsigned, "uint32_t", &value, sizeof(uint32_t),
source) {}
ExprValue::ExprValue(int64_t value, fxl::RefPtr<Type> type, const ExprValueSource& source)
: ExprValue(std::move(type), BaseType::kBaseTypeSigned, "int64_t", &value, sizeof(int64_t),
source) {}
ExprValue::ExprValue(uint64_t value, fxl::RefPtr<Type> type, const ExprValueSource& source)
: ExprValue(std::move(type), BaseType::kBaseTypeUnsigned, "uint64_t", &value, sizeof(uint64_t),
source) {}
ExprValue::ExprValue(float value, fxl::RefPtr<Type> type, const ExprValueSource& source)
: ExprValue(std::move(type), BaseType::kBaseTypeFloat, "float", &value, sizeof(float), source) {
}
ExprValue::ExprValue(double value, fxl::RefPtr<Type> type, const ExprValueSource& source)
: ExprValue(std::move(type), BaseType::kBaseTypeFloat, "double", &value, sizeof(double),
source) {}
ExprValue::ExprValue(fxl::RefPtr<Type> optional_type, int base_type, const char* type_name,
void* data, uint32_t data_size, const ExprValueSource& source)
: type_(optional_type ? std::move(optional_type)
: fxl::RefPtr<Type>(
fxl::MakeRefCounted<BaseType>(base_type, data_size, type_name))),
source_(source) {
// The type that we made or were given should match the size of the input data. But also allow
// 0-sized types since the input type may not be concrete.
FX_DCHECK(type_->byte_size() == data_size || type_->byte_size() == 0);
TaggedData::DataBuffer bytes;
bytes.resize(data_size);
memcpy(bytes.data(), data, data_size);
data_ = TaggedData(std::move(bytes));
}
ExprValue::ExprValue(fxl::RefPtr<Type> type, std::vector<uint8_t> data,
const ExprValueSource& source)
: type_(std::move(type)), source_(source), data_(std::move(data)) {}
ExprValue::ExprValue(fxl::RefPtr<Type> type, TaggedData buffer, const ExprValueSource& source)
: type_(std::move(type)), source_(source), data_(std::move(buffer)) {}
bool ExprValue::operator==(const ExprValue& other) const {
// Currently this does a comparison of the raw bytes of the value. This will be fine for most
// primitive values but will be incorrect for some composite structs.
return data_ == other.data_;
}
int ExprValue::GetBaseType() const {
if (!type_)
return BaseType::kBaseTypeNone;
// Remove "const", etc. and see if it's a base type.
const BaseType* base_type = type_->StripCVT()->As<BaseType>();
if (!base_type)
return BaseType::kBaseTypeNone;
return base_type->base_type();
}
Err ExprValue::EnsureAllValid() const {
if (!data_.all_valid())
return Err::OptimizedOut();
return Err();
}
Err ExprValue::EnsureSizeIs(size_t size) const {
if (data_.size() != size) {
return Err(
fxl::StringPrintf("The value of type '%s' is the incorrect size "
"(expecting %zu, got %zu). Please file a bug.",
type_ ? type_->GetFullName().c_str() : "<unknown>", size, data_.size()));
}
return Err();
}
template <>
int8_t ExprValue::GetAs<int8_t>() const {
FX_DCHECK(data_.size() == sizeof(int8_t)) << "Got size of " << data_.size();
int8_t result;
memcpy(&result, data_.bytes().data(), sizeof(int8_t));
return result;
}
template <>
uint8_t ExprValue::GetAs<uint8_t>() const {
FX_DCHECK(data_.size() == sizeof(uint8_t)) << "Got size of " << data_.size();
uint8_t result;
memcpy(&result, data_.bytes().data(), sizeof(uint8_t));
return result;
}
template <>
int16_t ExprValue::GetAs<int16_t>() const {
FX_DCHECK(data_.size() == sizeof(int16_t)) << "Got size of " << data_.size();
int16_t result;
memcpy(&result, data_.bytes().data(), sizeof(int16_t));
return result;
}
template <>
uint16_t ExprValue::GetAs<uint16_t>() const {
FX_DCHECK(data_.size() == sizeof(uint16_t)) << "Got size of " << data_.size();
uint16_t result;
memcpy(&result, data_.bytes().data(), sizeof(uint16_t));
return result;
}
template <>
int32_t ExprValue::GetAs<int32_t>() const {
FX_DCHECK(data_.size() == sizeof(int32_t)) << "Got size of " << data_.size();
int32_t result;
memcpy(&result, data_.bytes().data(), sizeof(int32_t));
return result;
}
template <>
uint32_t ExprValue::GetAs<uint32_t>() const {
FX_DCHECK(data_.size() == sizeof(uint32_t)) << "Got size of " << data_.size();
uint32_t result;
memcpy(&result, data_.bytes().data(), sizeof(uint32_t));
return result;
}
template <>
int64_t ExprValue::GetAs<int64_t>() const {
FX_DCHECK(data_.size() == sizeof(int64_t)) << "Got size of " << data_.size();
int64_t result;
memcpy(&result, data_.bytes().data(), sizeof(int64_t));
return result;
}
template <>
uint64_t ExprValue::GetAs<uint64_t>() const {
FX_DCHECK(data_.size() == sizeof(uint64_t)) << "Got size of " << data_.size();
uint64_t result;
memcpy(&result, data_.bytes().data(), sizeof(uint64_t));
return result;
}
template <>
int128_t ExprValue::GetAs<int128_t>() const {
FX_DCHECK(data_.size() == sizeof(int128_t)) << "Got size of " << data_.size();
int128_t result;
memcpy(&result, data_.bytes().data(), sizeof(int128_t));
return result;
}
template <>
uint128_t ExprValue::GetAs<uint128_t>() const {
FX_DCHECK(data_.size() == sizeof(uint128_t)) << "Got size of " << data_.size();
uint128_t result;
memcpy(&result, data_.bytes().data(), sizeof(uint128_t));
return result;
}
template <>
float ExprValue::GetAs<float>() const {
FX_DCHECK(data_.size() == sizeof(float)) << "Got size of " << data_.size();
float result;
memcpy(&result, data_.bytes().data(), sizeof(float));
return result;
}
template <>
double ExprValue::GetAs<double>() const {
FX_DCHECK(data_.size() == sizeof(double)) << "Got size of " << data_.size();
double result;
memcpy(&result, data_.bytes().data(), sizeof(double));
return result;
}
Err ExprValue::PromoteTo64(int64_t* output) const {
if (data_.empty())
return Err("Value has no data.");
if (!data_.all_valid())
return Err::OptimizedOut();
switch (data_.size()) {
case sizeof(int8_t):
*output = GetAs<int8_t>();
break;
case sizeof(int16_t):
*output = GetAs<int16_t>();
break;
case sizeof(int32_t):
*output = GetAs<int32_t>();
break;
case sizeof(int64_t):
*output = GetAs<int64_t>();
break;
default:
return Err(
fxl::StringPrintf("Unexpected value size (%zu), please file a bug.", data_.size()));
}
return Err();
}
Err ExprValue::PromoteTo64(uint64_t* output) const {
if (data_.empty())
return Err("Value has no data.");
if (!data_.all_valid())
return Err::OptimizedOut();
switch (data_.size()) {
case sizeof(uint8_t):
*output = GetAs<uint8_t>();
break;
case sizeof(uint16_t):
*output = GetAs<uint16_t>();
break;
case sizeof(uint32_t):
*output = GetAs<uint32_t>();
break;
case sizeof(uint64_t):
*output = GetAs<uint64_t>();
break;
default:
return Err(
fxl::StringPrintf("Unexpected value size (%zu), please file a bug.", data_.size()));
}
return Err();
}
Err ExprValue::PromoteTo128(int128_t* output) const {
if (!data_.all_valid())
return Err::OptimizedOut();
if (data_.size() == sizeof(int128_t)) {
*output = GetAs<int128_t>();
return Err();
}
// Use PromoteTo64 to handle all other cases.
int64_t out64 = 0;
if (Err err = PromoteTo64(&out64); err.has_error())
return err;
*output = out64;
return Err();
}
Err ExprValue::PromoteTo128(uint128_t* output) const {
if (!data_.all_valid())
return Err::OptimizedOut();
if (data_.size() == sizeof(uint128_t)) {
*output = GetAs<uint128_t>();
return Err();
}
// Use PromoteTo64 to handle all other cases.
uint64_t out64 = 0;
if (Err err = PromoteTo64(&out64); err.has_error())
return err;
*output = out64;
return Err();
}
Err ExprValue::PromoteToDouble(double* output) const {
if (data_.empty())
return Err("Value has no data.");
if (!data_.all_valid())
return Err::OptimizedOut();
switch (data_.size()) {
case sizeof(float):
*output = GetAs<float>();
break;
case sizeof(double):
*output = GetAs<double>();
break;
default:
return Err(
fxl::StringPrintf("Unexpected value size (%zu), please file a bug.", data_.size()));
}
return Err();
}
std::ostream& operator<<(std::ostream& out, const ExprValue& value) {
if (!value.type())
return out << "{null ExprValue}";
out << value.type()->GetFullName() << "(";
std::string value_str;
const Type* type = value.type()->StripCVT();
if (const BaseType* base = type->As<BaseType>()) {
switch (base->base_type()) {
case BaseType::kBaseTypeBoolean: {
uint64_t as_unsigned = 0;
if (value.PromoteTo64(&as_unsigned).ok())
value_str = as_unsigned ? "true" : "false";
break;
}
// Ignore kBaseTypeAddress which we don't use (pointers are "ModifiedTypes").
case BaseType::kBaseTypeFloat: {
double as_double = 0.0;
if (value.PromoteToDouble(&as_double).ok())
value_str = std::to_string(as_double);
break;
}
case BaseType::kBaseTypeSigned:
case BaseType::kBaseTypeSignedChar: {
int64_t as_signed = 0;
if (value.PromoteTo64(&as_signed).ok())
value_str = std::to_string(as_signed);
break;
}
case BaseType::kBaseTypeUnsigned:
case BaseType::kBaseTypeUnsignedChar: {
uint64_t as_unsigned = 0;
if (value.PromoteTo64(&as_unsigned).ok())
value_str = std::to_string(as_unsigned);
break;
}
}
}
if (value_str.empty()) {
// This catches anything that's not a base type and anything that errored out above. Output as a
// hex dump.
const auto& bytes = value.data().bytes();
for (size_t i = 0; i < bytes.size(); i++) {
if (i > 0)
value_str.push_back(' ');
value_str.append(to_hex_string(bytes[i], 2, true));
}
}
out << value_str << ")";
return out;
}
} // namespace zxdb