blob: 08d2a33b5b0d09fe4aa2f3c1f362401c0a6441f9 [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/resolve_ptr_ref.h"
#include "src/developer/debug/zxdb/common/err.h"
#include "src/developer/debug/zxdb/expr/eval_context.h"
#include "src/developer/debug/zxdb/expr/expr_value.h"
#include "src/developer/debug/zxdb/expr/resolve_base.h"
#include "src/developer/debug/zxdb/symbols/arch.h"
#include "src/developer/debug/zxdb/symbols/modified_type.h"
#include "src/developer/debug/zxdb/symbols/symbol_data_provider.h"
#include "src/developer/debug/zxdb/symbols/type.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace zxdb {
namespace {
// Extracts the value from the ExprValue, assuming it's a pointer. If not,
// return the error, otherwise fill in *pointer_value.
Err GetPointerValue(const ExprValue& value, TargetPointer* pointer_value) {
Err err = value.EnsureSizeIs(kTargetPointerSize);
if (err.has_error())
return err;
*pointer_value = value.GetAs<TargetPointer>();
return Err();
}
// Backend for the higher-level variant of ResolvePointer that does not handle upcasting to derived
// types.
void DoResolvePointer(const fxl::RefPtr<EvalContext>& eval_context, const ExprValue& pointer,
EvalCallback cb) {
fxl::RefPtr<Type> pointed_to;
if (Err err = GetPointedToType(eval_context, pointer.type(), &pointed_to); err.has_error())
return cb(err);
TargetPointer pointer_value = 0;
if (Err err = GetPointerValue(pointer, &pointer_value); err.has_error())
return cb(err);
// Forward to low-level pointer resolution.
ResolvePointer(std::move(eval_context), pointer_value, std::move(pointed_to), std::move(cb));
}
// Backend for EnsureResolveReference that does not handle upcasting to derived types.
void DoEnsureResolveReference(const fxl::RefPtr<EvalContext>& eval_context, ExprValue value,
EvalCallback cb) {
Type* type = value.type();
if (!type) {
// Untyped input, pass the value forward and let the callback handle the problem.
return cb(std::move(value));
}
// The computed type will have the same const, etc. on it as the original, so we need to
// make it concrete.
fxl::RefPtr<Type> concrete = eval_context->GetConcreteType(type);
if (concrete->tag() != DwarfTag::kReferenceType &&
concrete->tag() != DwarfTag::kRvalueReferenceType) {
// Not a reference, nothing to do.
return cb(std::move(value));
}
// The symbol provider should have created the right object type.
const ModifiedType* reference = concrete->AsModifiedType();
FX_DCHECK(reference);
const Type* underlying_type = reference->modified().Get()->AsType();
TargetPointer pointer_value = 0;
if (Err err = GetPointerValue(value, &pointer_value); err.has_error()) {
cb(err);
} else {
ResolvePointer(eval_context, pointer_value, RefPtrTo(underlying_type), std::move(cb));
}
}
} // namespace
void ResolvePointer(const fxl::RefPtr<EvalContext>& eval_context, uint64_t address,
fxl::RefPtr<Type> type, EvalCallback cb) {
// We need to be careful to construct the return type with the original type given since it may
// have const qualifiers, etc., but to use the concrete one (no const, with forward-definitions
// resolved) for size computation.
fxl::RefPtr<Type> concrete = eval_context->GetConcreteType(type.get());
if (!concrete)
return cb(Err("Missing pointer type."));
uint32_t type_size = concrete->byte_size();
eval_context->GetDataProvider()->GetMemoryAsync(
address, type_size,
[type = std::move(type), address, type_size, cb = std::move(cb)](
const Err& err, std::vector<uint8_t> data) mutable {
// Watch out, "type" may be non-concrete (we need to preserve "const", etc.). Use
// "type_size" for the concrete size.
if (err.has_error()) {
cb(err);
} else if (data.size() != type_size) {
// Short read, memory is invalid.
cb(Err(fxl::StringPrintf("Invalid pointer 0x%" PRIx64, address)));
} else {
cb(ExprValue(std::move(type), std::move(data), ExprValueSource(address)));
}
});
}
void ResolvePointer(const fxl::RefPtr<EvalContext>& eval_context, const ExprValue& pointer,
EvalCallback cb) {
if (eval_context->ShouldPromoteToDerived()) {
// Check to see if this is a pointer to a base class that we can convert to a derived class.
PromotePtrRefToDerived(eval_context, PromoteToDerived::kPtrOnly, pointer,
[eval_context, cb = std::move(cb)](ErrOrValue result) mutable {
if (result.has_error())
cb(result);
else
DoResolvePointer(eval_context, result.take_value(), std::move(cb));
});
} else {
// No magic base-class resolution is required, just check the reference.
DoResolvePointer(eval_context, pointer, std::move(cb));
}
}
void EnsureResolveReference(const fxl::RefPtr<EvalContext>& eval_context, ExprValue value,
EvalCallback cb) {
if (eval_context->ShouldPromoteToDerived()) {
// Check to see if this is a reference to a base class that we can convert to a derived class.
PromotePtrRefToDerived(eval_context, PromoteToDerived::kRefOnly, std::move(value),
[eval_context, cb = std::move(cb)](ErrOrValue result) mutable {
if (result.has_error()) {
cb(result);
} else {
DoEnsureResolveReference(eval_context, result.take_value(),
std::move(cb));
}
});
} else {
// No magic base-class resolution is required, just check the reference.
DoEnsureResolveReference(eval_context, std::move(value), std::move(cb));
}
}
Err GetPointedToType(const fxl::RefPtr<EvalContext>& eval_context, const Type* input,
fxl::RefPtr<Type>* pointed_to) {
if (!input)
return Err("No type information.");
// Convert to a pointer. GetConcreteType() here is more theoretical since current C compilers
// won't forward-declare pointer types. But it's nice to be sure and this will also strip
// CV-qualifiers which we do need.
fxl::RefPtr<Type> input_concrete = eval_context->GetConcreteType(input);
const ModifiedType* mod_type = input_concrete->AsModifiedType();
if (!mod_type || mod_type->tag() != DwarfTag::kPointerType) {
return Err(fxl::StringPrintf("Attempting to dereference '%s' which is not a pointer.",
input->GetFullName().c_str()));
}
*pointed_to = fxl::RefPtr<Type>(const_cast<Type*>(mod_type->modified().Get()->AsType()));
if (!*pointed_to)
return Err("Can not dereference a pointer to 'void'.");
return Err();
}
} // namespace zxdb