blob: a51d405897724959bbb3a07ae9dd09ef891fde11 [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.
#pragma once
#include <functional>
#include <iosfwd>
#include <memory>
#include "garnet/bin/zxdb/expr/cast.h"
#include "garnet/bin/zxdb/expr/expr_token.h"
#include "garnet/bin/zxdb/expr/expr_value.h"
#include "garnet/bin/zxdb/expr/identifier.h"
#include "lib/fxl/memory/ref_counted.h"
#include "lib/fxl/memory/ref_ptr.h"
namespace zxdb {
class Err;
class ExprEvalContext;
class Type;
// Node subclasses.
class AddressOfExprNode;
class ArrayAccessExprNode;
class BinaryOpExprNode;
class CastExprNode;
class DereferenceExprNode;
class FunctionCallExprNode;
class IdentifierExprNode;
class LiteralExprNode;
class MemberAccessExprNode;
class TypeExprNode;
class UnaryOpExprNode;
// Represents one node in the abstract syntax tree.
class ExprNode : public fxl::RefCountedThreadSafe<ExprNode> {
public:
using EvalCallback = std::function<void(const Err& err, ExprValue value)>;
ExprNode() = default;
virtual ~ExprNode() = default;
virtual const AddressOfExprNode* AsAddressOf() const { return nullptr; }
virtual const ArrayAccessExprNode* AsArrayAccess() const { return nullptr; }
virtual const BinaryOpExprNode* AsBinaryOp() const { return nullptr; }
virtual const CastExprNode* AsCast() const { return nullptr; }
virtual const DereferenceExprNode* AsDereference() const { return nullptr; }
virtual const FunctionCallExprNode* AsFunctionCall() const { return nullptr; }
virtual const IdentifierExprNode* AsIdentifier() const { return nullptr; }
virtual const LiteralExprNode* AsLiteral() const { return nullptr; }
virtual const MemberAccessExprNode* AsMemberAccess() const { return nullptr; }
virtual const TypeExprNode* AsType() const { return nullptr; }
virtual const UnaryOpExprNode* AsUnaryOp() const { return nullptr; }
// Evaluates the expression and calls the callback with the result. The
// callback may be called reentrantly (meaning from within the callstack
// of Eval itself).
//
// Some eval operations are asynchronous because they require reading data
// from the remote system. Many are not. Since we expect relatively few
// evals (from user typing) and that they are quite simple (most are one
// value or a simple dereference), we opt for simplicity and make all
// evals require a callback.
//
// For larger expressions this can be quite heavyweight because not only
// will the expression be recursively executed, but returning the result
// will double the depth of the recursion (not to mention a heavyweight
// lambda bind for each).
//
// One thing that might cause expression eval speed to be an issue is when
// they are automatically executed as in a conditional breakpoint. If we
// start using expressions in conditional breakpoints and find that
// performance is unacceptable, this should be optimized to support evals
// that do not require callbacks unless necessary.
//
// The caller is responsible for ensuring the tree of nodes is in scope for
// the duration of this call until the callback is executed. This would
// normally be done by having the tree be owned by the callback itself. If
// this is causing memory lifetime problems, we should switch nodes to be
// reference counted.
//
// See also EvalFollowReferences below.
virtual void Eval(fxl::RefPtr<ExprEvalContext> context,
EvalCallback cb) const = 0;
// Like "Eval" but expands all references to the values they point to. When
// evaluating a subexpression this is the variant you want because without
// it the ExprValue in the callback will be the reference, which just
// contains the address of the value you want.
//
// The time you wouldn't want this is when calling externally where the
// caller wants to know the actual type the expression evaluated to.
void EvalFollowReferences(fxl::RefPtr<ExprEvalContext> context,
EvalCallback cb) const;
// Dumps the tree to a stream with the given indent. Used for unit testing
// and debugging.
virtual void Print(std::ostream& out, int indent) const = 0;
};
// Implements taking an address of n expression ("&" in C).
class AddressOfExprNode : public ExprNode {
public:
const AddressOfExprNode* AsAddressOf() const override { return this; }
void Eval(fxl::RefPtr<ExprEvalContext> context,
EvalCallback cb) const override;
void Print(std::ostream& out, int indent) const override;
private:
FRIEND_REF_COUNTED_THREAD_SAFE(AddressOfExprNode);
FRIEND_MAKE_REF_COUNTED(AddressOfExprNode);
AddressOfExprNode();
AddressOfExprNode(fxl::RefPtr<ExprNode> expr) : expr_(std::move(expr)) {}
~AddressOfExprNode() override = default;
fxl::RefPtr<ExprNode> expr_;
};
// Implements an array access: foo[bar].
class ArrayAccessExprNode : public ExprNode {
public:
const ArrayAccessExprNode* AsArrayAccess() const override { return this; }
void Eval(fxl::RefPtr<ExprEvalContext> context,
EvalCallback cb) const override;
void Print(std::ostream& out, int indent) const override;
private:
FRIEND_REF_COUNTED_THREAD_SAFE(ArrayAccessExprNode);
FRIEND_MAKE_REF_COUNTED(ArrayAccessExprNode);
ArrayAccessExprNode();
ArrayAccessExprNode(fxl::RefPtr<ExprNode> left, fxl::RefPtr<ExprNode> inner)
: left_(std::move(left)), inner_(std::move(inner)) {}
~ArrayAccessExprNode() override = default;
// Converts the given value which is the result of executing the "inner"
// expression and converts it to an integer if possible.
static Err InnerValueToOffset(const ExprValue& inner, int64_t* offset);
static void DoAccess(fxl::RefPtr<ExprEvalContext> context, ExprValue left,
int64_t offset, EvalCallback cb);
fxl::RefPtr<ExprNode> left_;
fxl::RefPtr<ExprNode> inner_;
};
// Implements all binary operators.
class BinaryOpExprNode : public ExprNode {
public:
const BinaryOpExprNode* AsBinaryOp() const override { return this; }
void Eval(fxl::RefPtr<ExprEvalContext> context,
EvalCallback cb) const override;
void Print(std::ostream& out, int indent) const override;
private:
FRIEND_REF_COUNTED_THREAD_SAFE(BinaryOpExprNode);
FRIEND_MAKE_REF_COUNTED(BinaryOpExprNode);
BinaryOpExprNode();
BinaryOpExprNode(fxl::RefPtr<ExprNode> left, ExprToken op,
fxl::RefPtr<ExprNode> right)
: left_(std::move(left)), op_(std::move(op)), right_(std::move(right)) {}
~BinaryOpExprNode() override = default;
fxl::RefPtr<ExprNode> left_;
ExprToken op_;
fxl::RefPtr<ExprNode> right_;
};
// Implements all types of casts.
class CastExprNode : public ExprNode {
public:
const CastExprNode* AsCast() const override { return this; }
void Eval(fxl::RefPtr<ExprEvalContext> context,
EvalCallback cb) const override;
void Print(std::ostream& out, int indent) const override;
private:
FRIEND_REF_COUNTED_THREAD_SAFE(CastExprNode);
FRIEND_MAKE_REF_COUNTED(CastExprNode);
CastExprNode();
CastExprNode(CastType cast_type, fxl::RefPtr<TypeExprNode> to_type,
fxl::RefPtr<ExprNode> from)
: cast_type_(cast_type),
to_type_(std::move(to_type)),
from_(std::move(from)) {}
~CastExprNode() override = default;
CastType cast_type_;
fxl::RefPtr<TypeExprNode> to_type_;
fxl::RefPtr<ExprNode> from_;
};
// Implements dereferencing a pointer ("*" in C).
class DereferenceExprNode : public ExprNode {
public:
const DereferenceExprNode* AsDereference() const override { return this; }
void Eval(fxl::RefPtr<ExprEvalContext> context,
EvalCallback cb) const override;
void Print(std::ostream& out, int indent) const override;
private:
FRIEND_REF_COUNTED_THREAD_SAFE(DereferenceExprNode);
FRIEND_MAKE_REF_COUNTED(DereferenceExprNode);
DereferenceExprNode();
DereferenceExprNode(fxl::RefPtr<ExprNode> expr) : expr_(std::move(expr)) {}
~DereferenceExprNode() override = default;
fxl::RefPtr<ExprNode> expr_;
};
// Function calls include things like: "Foo()", "ns::Foo<int>(6, 5)".
class FunctionCallExprNode : public ExprNode {
public:
const FunctionCallExprNode* AsFunctionCall() const override { return this; }
void Eval(fxl::RefPtr<ExprEvalContext> context,
EvalCallback cb) const override;
void Print(std::ostream& out, int indent) const override;
const Identifier& name() const { return name_; }
const std::vector<fxl::RefPtr<ExprNode>>& args() const { return args_; }
private:
FRIEND_REF_COUNTED_THREAD_SAFE(FunctionCallExprNode);
FRIEND_MAKE_REF_COUNTED(FunctionCallExprNode);
FunctionCallExprNode();
FunctionCallExprNode(Identifier name, std::vector<fxl::RefPtr<ExprNode>> args)
: name_(std::move(name)), args_(std::move(args)) {}
~FunctionCallExprNode() override = default;
Identifier name_;
std::vector<fxl::RefPtr<ExprNode>> args_;
};
// Implements a bare identifier.
class IdentifierExprNode : public ExprNode {
public:
const IdentifierExprNode* AsIdentifier() const override { return this; }
void Eval(fxl::RefPtr<ExprEvalContext> context,
EvalCallback cb) const override;
void Print(std::ostream& out, int indent) const override;
Identifier& ident() { return ident_; }
const Identifier& ident() const { return ident_; }
// Destructively moves the identifier out of this class. This unusual
// mutating getter is implemented because the expression parser is also used
// to parse identifiers, and this will hold the result which we would prefer
// not to copy to get out.
Identifier TakeIdentifier() { return std::move(ident_); }
private:
FRIEND_REF_COUNTED_THREAD_SAFE(IdentifierExprNode);
FRIEND_MAKE_REF_COUNTED(IdentifierExprNode);
IdentifierExprNode() = default;
// Simple one-name identifier.
IdentifierExprNode(const ExprToken& name) : ident_(name){};
IdentifierExprNode(Identifier id) : ident_(std::move(id)) {}
~IdentifierExprNode() override = default;
Identifier ident_;
};
// Implements a literal like a number or a boolean.
class LiteralExprNode : public ExprNode {
public:
const LiteralExprNode* AsLiteral() const override { return this; }
void Eval(fxl::RefPtr<ExprEvalContext> context,
EvalCallback cb) const override;
void Print(std::ostream& out, int indent) const override;
// The token's value won't have been checked that it's valid, only
// that it starts like the type of literal it is. This checking will be done
// at evaluation time.
const ExprToken& token() const { return token_; }
private:
FRIEND_REF_COUNTED_THREAD_SAFE(LiteralExprNode);
FRIEND_MAKE_REF_COUNTED(LiteralExprNode);
LiteralExprNode() = default;
explicit LiteralExprNode(const ExprToken& token) : token_(token) {}
~LiteralExprNode() override = default;
ExprToken token_;
};
// Implements both "." and "->" struct/class/union data member accesses.
class MemberAccessExprNode : public ExprNode {
public:
const MemberAccessExprNode* AsMemberAccess() const override { return this; }
void Eval(fxl::RefPtr<ExprEvalContext> context,
EvalCallback cb) const override;
void Print(std::ostream& out, int indent) const override;
// Expression on the left side of the "." or "->".
const ExprNode* left() const { return left_.get(); }
// The "." or "->" token itself.
const ExprToken& accessor() const { return accessor_; }
// The name of the data member.
const Identifier& member() const { return member_; }
private:
FRIEND_REF_COUNTED_THREAD_SAFE(MemberAccessExprNode);
FRIEND_MAKE_REF_COUNTED(MemberAccessExprNode);
MemberAccessExprNode() = default;
MemberAccessExprNode(fxl::RefPtr<ExprNode> left, const ExprToken& access,
const Identifier& member)
: left_(std::move(left)), accessor_(access), member_(member) {}
~MemberAccessExprNode() override = default;
fxl::RefPtr<ExprNode> left_;
ExprToken accessor_;
Identifier member_;
};
// Implements references to type names. This mostly appears in casts.
class TypeExprNode : public ExprNode {
public:
const TypeExprNode* AsType() const override { return this; }
void Eval(fxl::RefPtr<ExprEvalContext> context,
EvalCallback cb) const override;
void Print(std::ostream& out, int indent) const override;
fxl::RefPtr<Type>& type() { return type_; }
const fxl::RefPtr<Type>& type() const { return type_; }
private:
FRIEND_REF_COUNTED_THREAD_SAFE(TypeExprNode);
FRIEND_MAKE_REF_COUNTED(TypeExprNode);
TypeExprNode();
TypeExprNode(fxl::RefPtr<Type> type) : type_(std::move(type)) {}
~TypeExprNode() override = default;
fxl::RefPtr<Type> type_;
};
// Implements unary mathematical operators (the operation depends on the
// operator token).
class UnaryOpExprNode : public ExprNode {
public:
const UnaryOpExprNode* AsUnaryOp() const override { return this; }
void Eval(fxl::RefPtr<ExprEvalContext> context,
EvalCallback cb) const override;
void Print(std::ostream& out, int indent) const override;
private:
FRIEND_REF_COUNTED_THREAD_SAFE(UnaryOpExprNode);
FRIEND_MAKE_REF_COUNTED(UnaryOpExprNode);
UnaryOpExprNode();
UnaryOpExprNode(const ExprToken& op, fxl::RefPtr<ExprNode> expr)
: op_(op), expr_(std::move(expr)) {}
~UnaryOpExprNode() override = default;
ExprToken op_;
fxl::RefPtr<ExprNode> expr_;
};
} // namespace zxdb