[debugger] Parse C-style casts.
Adds C-style cast support to the parser.
Since symbol lookup is not yet hooked up to the parser, this is not
currently usable. It will be hooked up in a follow-up.
This leaves some refactoring needs for the existing casting code: (a)
the existing reinterpret_cast support (which is special-cased as a
function call) should generate the new CastExprNode, and (b) the
existing "Coerce" function should be folded into the new general-purpose
casting function. These changes are noted in the code.
Change-Id: I62ac8753ef25886497bdfbe300f15c195a8b0489
diff --git a/garnet/bin/zxdb/expr/cast.cc b/garnet/bin/zxdb/expr/cast.cc
index 478f7fe..0afcf85 100644
--- a/garnet/bin/zxdb/expr/cast.cc
+++ b/garnet/bin/zxdb/expr/cast.cc
@@ -260,6 +260,54 @@
} // namespace
+const char* CastTypeToString(CastType type) {
+ switch (type) {
+ case CastType::kImplicit:
+ return "implicit";
+ case CastType::kC:
+ return "C";
+ case CastType::kReinterpret:
+ return "reinterpret_cast";
+ case CastType::kStatic:
+ return "static_cast";
+ }
+ return "<invalid>";
+}
+
+Err CastExprValue(CastType cast_type, const ExprValue& source,
+ const fxl::RefPtr<Type>& dest_type, ExprValue* result) {
+ switch (cast_type) {
+ case CastType::kImplicit:
+ return CoerceValueTo(source, dest_type, ExprValueSource(), result);
+ case CastType::kC: {
+ // A C-style cast can do the following things.
+ // - const_cast
+ // - static_cast
+ // - static_cast followed by a const_cast
+ // - reinterpret_cast
+ // - reinterpret_cast followed by a const_cast
+ //
+ // Since the debugger ignores const in debugging, this ends up being
+ // a static cast falling back to a reinterpret cast.
+ //
+ // TODO(DX-1178) this should be a static cast when it exists. Currently
+ // "coerce" implements the things we're willing to implicitly cast
+ // and doesn't handle things like derived type conversions.
+ if (!CoerceValueTo(source, dest_type, ExprValueSource(), result)
+ .has_error())
+ return Err();
+ return ReinterpretCast(source, dest_type, result);
+ }
+ case CastType::kReinterpret:
+ return ReinterpretCast(source, dest_type, result);
+ case CastType::kStatic:
+ // TODO(DX-1178) write a real static cast.
+ return CoerceValueTo(source, dest_type, ExprValueSource(), result);
+ }
+ FXL_NOTREACHED();
+ return Err("Internal error.");
+}
+
Err CoerceValueTo(const ExprValue& source, const fxl::RefPtr<Type>& dest_type,
const ExprValueSource& dest_source, ExprValue* result) {
// There are several fundamental types of things that can be casted:
diff --git a/garnet/bin/zxdb/expr/cast.h b/garnet/bin/zxdb/expr/cast.h
index 76ec1a1..c9d9889 100644
--- a/garnet/bin/zxdb/expr/cast.h
+++ b/garnet/bin/zxdb/expr/cast.h
@@ -13,6 +13,21 @@
class Type;
class ExprValueSource;
+enum class CastType {
+ kImplicit, // Things like double d = (float)f;
+ kC, // C-style cast: (int)foo;
+ kReinterpret,
+ kStatic
+ // We don't bother implementing const_cast and dynamic_cast yet because
+ // they're less useful in a debugger.
+};
+
+const char* CastTypeToString(CastType);
+
+// TODO(brettw) replace the other cast calls with calls to this one:
+Err CastExprValue(CastType cast_type, const ExprValue& source,
+ const fxl::RefPtr<Type>& dest_type, ExprValue* result);
+
// Attempts to convert the "source" value to the given type. This attempts
// to be as permissive as possible. In a debugger context, people want to be
// able to make arbitrary binary assignments without being told to do an
@@ -26,6 +41,13 @@
//
// This does not implement static-cast-like conversions of derived classes
// where the cast involves adjusting the value of the pointer.
+//
+// The dest_source will be set as the "source" of the result ExprValue. When
+// generating temporaries, this should be a default-constructed
+// ExprValueSource, but this is useful when doing implicit casts for assignment
+// where the destination location is given.
+//
+// TODO(brettw) this should be renamed "ImplicitCast".
Err CoerceValueTo(const ExprValue& source, const fxl::RefPtr<Type>& dest_type,
const ExprValueSource& dest_source, ExprValue* result);
diff --git a/garnet/bin/zxdb/expr/expr_node.cc b/garnet/bin/zxdb/expr/expr_node.cc
index e59a976..0c41864 100644
--- a/garnet/bin/zxdb/expr/expr_node.cc
+++ b/garnet/bin/zxdb/expr/expr_node.cc
@@ -228,6 +228,29 @@
right_->Print(out, indent + 1);
}
+void CastExprNode::Eval(fxl::RefPtr<ExprEvalContext> context,
+ EvalCallback cb) const {
+ from_->Eval(context, [cast_type = cast_type_, to_type = to_type_->type(),
+ cb = std::move(cb)](const Err& err, ExprValue value) {
+ if (err.has_error()) {
+ cb(err, value);
+ } else {
+ ExprValue cast_result;
+ Err cast_err = CastExprValue(cast_type, value, to_type, &cast_result);
+ if (cast_err.has_error())
+ cb(cast_err, ExprValue());
+ else
+ cb(Err(), std::move(cast_result));
+ }
+ });
+}
+
+void CastExprNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "CAST(" << CastTypeToString(cast_type_) << ")\n";
+ to_type_->Print(out, indent + 1);
+ from_->Print(out, indent + 1);
+}
+
void DereferenceExprNode::Eval(fxl::RefPtr<ExprEvalContext> context,
EvalCallback cb) const {
expr_->EvalFollowReferences(
@@ -243,6 +266,8 @@
void FunctionCallExprNode::Eval(fxl::RefPtr<ExprEvalContext> context,
EvalCallback cb) const {
+ // TODO(brettw) parse reinterpret_cast as a cast instead.
+
// Handle reinterpret_cast calls since this is currently the only function
// that can be called (this will need to be enhanced significantly when we
// add more).
diff --git a/garnet/bin/zxdb/expr/expr_node.h b/garnet/bin/zxdb/expr/expr_node.h
index f1eb8c9..a51d405 100644
--- a/garnet/bin/zxdb/expr/expr_node.h
+++ b/garnet/bin/zxdb/expr/expr_node.h
@@ -8,6 +8,7 @@
#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"
@@ -24,6 +25,7 @@
class AddressOfExprNode;
class ArrayAccessExprNode;
class BinaryOpExprNode;
+class CastExprNode;
class DereferenceExprNode;
class FunctionCallExprNode;
class IdentifierExprNode;
@@ -43,6 +45,7 @@
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; }
@@ -167,6 +170,31 @@
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:
diff --git a/garnet/bin/zxdb/expr/expr_parser.cc b/garnet/bin/zxdb/expr/expr_parser.cc
index f279de0..f325a97e 100644
--- a/garnet/bin/zxdb/expr/expr_parser.cc
+++ b/garnet/bin/zxdb/expr/expr_parser.cc
@@ -596,6 +596,18 @@
Consume(ExprToken::kRightParen, token, "Expected ')' to match.");
if (has_error())
return nullptr;
+
+ if (const TypeExprNode* type_expr = expr->AsType()) {
+ // Convert "(TypeName)..." into a cast.
+ auto cast_expr = ParseExpression(kPrecedenceCallAccess);
+ if (has_error())
+ return nullptr;
+
+ fxl::RefPtr<TypeExprNode> type_ref(const_cast<TypeExprNode*>(type_expr));
+ return fxl::MakeRefCounted<CastExprNode>(CastType::kC, std::move(type_ref),
+ std::move(cast_expr));
+ }
+
return expr;
}
diff --git a/garnet/bin/zxdb/expr/expr_parser_unittest.cc b/garnet/bin/zxdb/expr/expr_parser_unittest.cc
index 2569f49..f2195fc 100644
--- a/garnet/bin/zxdb/expr/expr_parser_unittest.cc
+++ b/garnet/bin/zxdb/expr/expr_parser_unittest.cc
@@ -558,4 +558,26 @@
GetParseString("sizeof(foo)", &TestLookupName));
}
+TEST_F(ExprParserTest, Casts) {
+ EXPECT_EQ(
+ "CAST(C)\n"
+ " TYPE(Type)\n"
+ " IDENTIFIER(\"a\")\n",
+ GetParseString("(Type)(a)", &TestLookupName));
+
+ EXPECT_EQ(
+ "BINARY_OP(&&)\n"
+ " CAST(C)\n"
+ " TYPE(Type*)\n"
+ " IDENTIFIER(\"a\")\n"
+ " IDENTIFIER(\"b\")\n",
+ GetParseString("(Type*)a && b", &TestLookupName));
+
+ // Looks like a cast but it's not a type.
+ auto result = Parse("(NotType)a", &TestLookupName);
+ EXPECT_FALSE(result);
+ EXPECT_EQ("Unexpected input, did you forget an operator?",
+ parser().err().msg());
+}
+
} // namespace zxdb