| // 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.h" | |
| #include <lib/syslog/cpp/macros.h> | |
| #include "src/developer/debug/zxdb/expr/eval_context.h" | |
| #include "src/developer/debug/zxdb/expr/expr_node.h" | |
| #include "src/developer/debug/zxdb/expr/expr_parser.h" | |
| #include "src/developer/debug/zxdb/expr/expr_tokenizer.h" | |
| #include "src/developer/debug/zxdb/symbols/modified_type.h" | |
| namespace zxdb { | |
| namespace { | |
| class MultiEvalTracking { | |
| public: | |
| using OutputVector = std::vector<ErrOrValue>; | |
| using Completion = fit::callback<void(OutputVector)>; | |
| MultiEvalTracking(size_t count, Completion cb) : remaining_(count), completion_(std::move(cb)) { | |
| data_.resize(count, ErrOrValue(ExprValue())); | |
| } | |
| void SetResult(size_t index, ErrOrValue value) { | |
| FX_DCHECK(index < data_.size()); | |
| FX_DCHECK(remaining_ > 0); | |
| // Nothing should be set on this slot yet. | |
| FX_DCHECK(!data_[index].has_error()); | |
| FX_DCHECK(data_[index].value() == ExprValue()); | |
| data_[index] = std::move(value); | |
| remaining_--; | |
| if (remaining_ == 0) | |
| completion_(std::move(data_)); | |
| } | |
| private: | |
| size_t remaining_; // # callbacks remaining before completion. | |
| OutputVector data_; | |
| Completion completion_; | |
| }; | |
| } // namespace | |
| void EvalExpression(const std::string& input, const fxl::RefPtr<EvalContext>& context, | |
| bool follow_references, EvalCallback cb) { | |
| ExprTokenizer tokenizer(input, context->GetLanguage()); | |
| if (!tokenizer.Tokenize()) | |
| return cb(tokenizer.err()); | |
| ExprParser parser(tokenizer.TakeTokens(), tokenizer.language(), | |
| context->GetSymbolNameLookupCallback()); | |
| auto node = parser.ParseExpression(); | |
| if (parser.err().has_error()) { | |
| // Add context information since we have the original input string (the | |
| // parser doesn't have this). | |
| ExprToken error_token = parser.error_token(); | |
| if (error_token.type() != ExprTokenType::kInvalid) { | |
| Err context_err(parser.err().type(), | |
| parser.err().msg() + "\n" + | |
| ExprTokenizer::GetErrorContext(input, error_token.byte_offset())); | |
| cb(context_err); | |
| } else { | |
| cb(parser.err()); | |
| } | |
| return; | |
| } | |
| if (follow_references) | |
| node->Eval(context, std::move(cb)); | |
| else | |
| node->EvalFollowReferences(context, std::move(cb)); | |
| } | |
| void EvalExpressions(const std::vector<std::string>& inputs, | |
| const fxl::RefPtr<EvalContext>& context, bool follow_references, | |
| fit::callback<void(std::vector<ErrOrValue>)> cb) { | |
| FX_DCHECK(!inputs.empty()); | |
| auto tracking = std::make_shared<MultiEvalTracking>(inputs.size(), std::move(cb)); | |
| for (size_t i = 0; i < inputs.size(); i++) { | |
| EvalExpression(inputs[i], context, follow_references, | |
| [tracking, i](ErrOrValue value) { tracking->SetResult(i, std::move(value)); }); | |
| } | |
| } | |
| // TODO(bug 44074) support non-pointer values and take their address implicitly. | |
| Err ValueToAddressAndSize(const fxl::RefPtr<EvalContext>& eval_context, const ExprValue& value, | |
| uint64_t* address, std::optional<uint32_t>* size) { | |
| fxl::RefPtr<Type> concrete_type = value.GetConcreteType(eval_context.get()); | |
| if (concrete_type->AsCollection()) { | |
| // Don't allow structs and classes that are <= 64 bits to be converted | |
| // to addresses. | |
| return Err("Can't convert '%s' to an address.", concrete_type->GetFullName().c_str()); | |
| } | |
| // See if there's an intrinsic size to the object being pointed to. This is true for pointers. | |
| // References should have been followed and stripped before here. | |
| if (auto modified = concrete_type->AsModifiedType(); | |
| modified && modified->tag() == DwarfTag::kPointerType) { | |
| if (auto modified_type = modified->modified().Get()->AsType()) | |
| *size = modified_type->byte_size(); | |
| } | |
| // Convert anything else <= 64 bits to a number. | |
| return value.PromoteTo64(address); | |
| } | |
| } // namespace zxdb |