| // 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 "garnet/bin/zxdb/expr/eval_operators.h" |
| |
| #include "garnet/bin/zxdb/common/err.h" |
| #include "garnet/bin/zxdb/expr/cast.h" |
| #include "garnet/bin/zxdb/expr/expr_eval_context.h" |
| #include "garnet/bin/zxdb/expr/expr_node.h" |
| #include "garnet/bin/zxdb/expr/expr_token.h" |
| #include "garnet/bin/zxdb/expr/expr_value.h" |
| #include "garnet/bin/zxdb/symbols/symbol_data_provider.h" |
| |
| namespace zxdb { |
| |
| namespace { |
| |
| using EvalCallback = std::function<void(const Err& err, ExprValue value)>; |
| |
| void DoAssignment(fxl::RefPtr<ExprEvalContext> context, |
| const ExprValue& left_value, const ExprValue& right_value, |
| EvalCallback cb) { |
| // Note: the calling code will have evaluated the value of the left node. |
| // Often this isn't strictly necessary: we only need the "source", but |
| // optimizing in that way would complicate things. |
| const ExprValueSource& dest = left_value.source(); |
| if (dest.type() == ExprValueSource::Type::kTemporary) { |
| cb(Err("Can't assign to a temporary."), ExprValue()); |
| return; |
| } |
| |
| // The coerced value will be the result. It should have the "source" of the |
| // left-hand-side since the location being assigned to doesn't change. |
| ExprValue coerced; |
| Err err = CoerceValueTo(right_value, left_value.type_ref(), dest, &coerced); |
| if (err.has_error()) { |
| cb(err, ExprValue()); |
| return; |
| } |
| |
| // Make a copy to avoid ambiguity of copying and moving the value below. |
| std::vector<uint8_t> data = coerced.data(); |
| |
| // Update the memory with the new data. The result of the expression is |
| // the coerced value. |
| context->GetDataProvider()->WriteMemory( |
| dest.address(), std::move(data), |
| [ coerced = std::move(coerced), cb = std::move(cb) ](const Err& err) { |
| if (err.has_error()) |
| cb(err, ExprValue()); |
| else |
| cb(Err(), coerced); |
| }); |
| } |
| |
| void DoBinaryOperator(fxl::RefPtr<ExprEvalContext> context, |
| const ExprValue& left_value, const ExprToken& op, |
| const ExprValue& right_value, EvalCallback cb) { |
| switch (op.type()) { |
| case ExprToken::kEquals: |
| DoAssignment(std::move(context), left_value, right_value, std::move(cb)); |
| break; |
| |
| case ExprToken::kEquality: |
| default: |
| cb(Err("Unsupported binary operator '%s', sorry!", op.value().c_str()), |
| ExprValue()); |
| break; |
| } |
| } |
| |
| } // namespace |
| |
| void EvalBinaryOperator(fxl::RefPtr<ExprEvalContext> context, |
| const fxl::RefPtr<ExprNode>& left, const ExprToken& op, |
| const fxl::RefPtr<ExprNode>& right, EvalCallback cb) { |
| left->Eval(context, [ context, op, right, cb = std::move(cb) ]( |
| const Err& err, ExprValue left_value) { |
| if (err.has_error()) { |
| cb(err, ExprValue()); |
| return; |
| } |
| |
| // Note: if we implement ||, need to special-case here so evaluation |
| // short-circuits if the "left" is true. |
| right->Eval(context, [ |
| context, left_value = std::move(left_value), op, cb = std::move(cb) |
| ](const Err& err, ExprValue right_value) { |
| DoBinaryOperator(std::move(context), left_value, op, right_value, |
| std::move(cb)); |
| }); |
| }); |
| } |
| |
| } // namespace zxdb |