[LLDB] Add assignment to DIL. (#190223)
Add the ability for DIL to recognize and process assignment, updating
program variables. Recognizes '=', '+=' and '-=' operators. Increment
and decrement ('++' and '--') will be added in a separate (future) PR.
"*=" and "/=" need to wait until DIL handles multiply and divide
operators.
This commit is contained in:
parent
4169b17acf
commit
0b9dc1bdd3
15 changed files with 498 additions and 73 deletions
|
|
@ -3,20 +3,27 @@
|
|||
(* This is currently a subset of the final DIL Language, matching the current
|
||||
DIL implementation. *)
|
||||
|
||||
expression = shift_expression ;
|
||||
expression = assignment_expression ;
|
||||
|
||||
assignment_expression = shift_expression
|
||||
| shift_expression assignment_operator assignment_expression ;
|
||||
|
||||
assignment_operator = "="
|
||||
| "+="
|
||||
| "-=" ;
|
||||
|
||||
shift_expression = additive_expression {"<<" additive_expression}
|
||||
| additive_expression {">>" additive_expression} ;
|
||||
|
||||
additive_expression = multiplicative_expression {"+" multiplicative_expression}
|
||||
multiplicative_expression {"-" multiplicative_expression} ;
|
||||
| multiplicative_expression {"-" multiplicative_expression} ;
|
||||
|
||||
multiplicative_expression = cast_expression {"*" cast_expression}
|
||||
| cast_expression {"/" cast_expression}
|
||||
| cast_expression {"%" cast_expression} ;
|
||||
|
||||
cast_expression = unary_expression
|
||||
| "(" type_id ")" cast_expression;
|
||||
| "(" type_id ")" cast_expression ;
|
||||
|
||||
unary_expression = postfix_expression
|
||||
| unary_operator cast_expression ;
|
||||
|
|
|
|||
|
|
@ -34,21 +34,24 @@ enum class NodeKind {
|
|||
|
||||
/// The Unary operators recognized by DIL.
|
||||
enum class UnaryOpKind {
|
||||
AddrOf, // "&"
|
||||
Deref, // "*"
|
||||
Minus, // "-"
|
||||
Plus, // "+"
|
||||
AddrOf, ///< "&"
|
||||
Deref, ///< "*"
|
||||
Minus, ///< "-"
|
||||
Plus, ///< "+"
|
||||
};
|
||||
|
||||
/// The binary operators recognized by DIL.
|
||||
enum class BinaryOpKind {
|
||||
Add, // "+"
|
||||
Sub, // "-"
|
||||
Mul, // "*"
|
||||
Div, // "/"
|
||||
Rem, // "%"
|
||||
Shl, // "<<"
|
||||
Shr, // ">>"
|
||||
Add, ///< "+"
|
||||
AddAssign, ///< "+="
|
||||
Assign, ///< "="
|
||||
Div, ///< "/"
|
||||
Mul, ///< "*"
|
||||
Rem, ///< "%"
|
||||
Shl, ///< "<<"
|
||||
Shr, ///< ">>"
|
||||
Sub, ///< "-"
|
||||
SubAssign, ///< "-="
|
||||
};
|
||||
|
||||
/// Translates DIL tokens to BinaryOpKind.
|
||||
|
|
|
|||
|
|
@ -89,10 +89,10 @@ private:
|
|||
|
||||
/// Perform an arithmetic conversion on two values from an arithmetic
|
||||
/// operation.
|
||||
/// \returns The result type of an arithmetic operation.
|
||||
llvm::Expected<CompilerType> ArithmeticConversion(lldb::ValueObjectSP &lhs,
|
||||
lldb::ValueObjectSP &rhs,
|
||||
uint32_t location);
|
||||
|
||||
/// Add or subtract the offset to the pointer according to the pointee type
|
||||
/// byte size.
|
||||
/// \returns A new `ValueObject` with a new pointer value.
|
||||
|
|
@ -100,6 +100,7 @@ private:
|
|||
lldb::ValueObjectSP offset,
|
||||
BinaryOpKind operation,
|
||||
uint32_t location);
|
||||
|
||||
llvm::Expected<lldb::ValueObjectSP> EvaluateScalarOp(BinaryOpKind kind,
|
||||
lldb::ValueObjectSP lhs,
|
||||
lldb::ValueObjectSP rhs,
|
||||
|
|
@ -111,9 +112,11 @@ private:
|
|||
llvm::Expected<lldb::ValueObjectSP>
|
||||
EvaluateBinaryAddition(lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs,
|
||||
uint32_t location);
|
||||
|
||||
llvm::Expected<lldb::ValueObjectSP>
|
||||
EvaluateBinarySubtraction(lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs,
|
||||
uint32_t location);
|
||||
|
||||
llvm::Expected<lldb::ValueObjectSP>
|
||||
EvaluateBinaryMultiplication(lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs,
|
||||
uint32_t location);
|
||||
|
|
@ -128,6 +131,10 @@ private:
|
|||
std::shared_ptr<ExecutionContextScope> ctx,
|
||||
const IntegerLiteralNode &literal);
|
||||
|
||||
llvm::Expected<lldb::ValueObjectSP>
|
||||
EvaluateAssignment(lldb::ValueObjectSP lhs, lldb::ValueObjectSP rhs,
|
||||
uint32_t location);
|
||||
|
||||
/// A helper function for VerifyCastType (below). This performs
|
||||
/// arithmetic-specific checks. It should only be called if the target_type
|
||||
/// is a scalar type.
|
||||
|
|
@ -152,8 +159,7 @@ private:
|
|||
lldb::DynamicValueType m_use_dynamic;
|
||||
bool m_use_synthetic;
|
||||
bool m_check_ptr_vs_member;
|
||||
// TODO: Remove 'maybe_unused' when next PR, using this, gets submitted.
|
||||
[[maybe_unused]] bool m_allow_var_updates;
|
||||
bool m_allow_var_updates;
|
||||
bool m_allow_globals = true;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ public:
|
|||
colon,
|
||||
coloncolon,
|
||||
eof,
|
||||
equal,
|
||||
float_constant,
|
||||
greatergreater,
|
||||
identifier,
|
||||
|
|
@ -40,9 +41,11 @@ public:
|
|||
l_square,
|
||||
lessless,
|
||||
minus,
|
||||
minusequal,
|
||||
percent,
|
||||
period,
|
||||
plus,
|
||||
plusequal,
|
||||
r_paren,
|
||||
r_square,
|
||||
slash,
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ private:
|
|||
ASTNodeUP Run();
|
||||
|
||||
ASTNodeUP ParseExpression();
|
||||
|
||||
ASTNodeUP ParseAssignmentExpression();
|
||||
ASTNodeUP ParseShiftExpression();
|
||||
ASTNodeUP ParseAdditiveExpression();
|
||||
ASTNodeUP ParseMultiplicativeExpression();
|
||||
|
|
|
|||
|
|
@ -454,14 +454,14 @@ public:
|
|||
/// Update an existing integer ValueObject with a new integer value. If
|
||||
/// can_update_var is true, will allow updating objects associated with
|
||||
/// program variables; otherwise not.
|
||||
void SetValueFromInteger(const llvm::APInt &value, Status &error,
|
||||
bool can_update_var = true);
|
||||
llvm::Error SetValueFromInteger(const llvm::APInt &value,
|
||||
bool can_update_var = true);
|
||||
|
||||
/// Update an existing integer ValueObject with an integer value created
|
||||
/// frome 'new_val_sp'. If can_update_var is true, will allow updating objects
|
||||
/// associated with program variables; otherwise not.
|
||||
void SetValueFromInteger(lldb::ValueObjectSP new_val_sp, Status &error,
|
||||
bool can_update_var = true);
|
||||
llvm::Error SetValueFromInteger(lldb::ValueObjectSP new_val_sp,
|
||||
bool can_update_var = true);
|
||||
|
||||
virtual bool SetValueFromCString(const char *value_str, Status &error);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,16 @@ namespace lldb_private::dil {
|
|||
|
||||
BinaryOpKind GetBinaryOpKindFromToken(Token::Kind token_kind) {
|
||||
switch (token_kind) {
|
||||
case Token::plus:
|
||||
return BinaryOpKind::Add;
|
||||
case Token::equal:
|
||||
return BinaryOpKind::Assign;
|
||||
case Token::minus:
|
||||
return BinaryOpKind::Sub;
|
||||
case Token::minusequal:
|
||||
return BinaryOpKind::SubAssign;
|
||||
case Token::plus:
|
||||
return BinaryOpKind::Add;
|
||||
case Token::plusequal:
|
||||
return BinaryOpKind::AddAssign;
|
||||
case Token::star:
|
||||
return BinaryOpKind::Mul;
|
||||
case Token::slash:
|
||||
|
|
|
|||
|
|
@ -640,6 +640,8 @@ Interpreter::EvaluateScalarOp(BinaryOpKind kind, lldb::ValueObjectSP lhs,
|
|||
return value_object(l << r);
|
||||
case BinaryOpKind::Shr:
|
||||
return value_object(l >> r);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return llvm::make_error<DILDiagnosticError>(
|
||||
m_expr, "invalid arithmetic operation", location);
|
||||
|
|
@ -837,6 +839,60 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::EvaluateBinaryRemainder(
|
|||
return EvaluateScalarOp(BinaryOpKind::Rem, lhs, rhs, result_type, location);
|
||||
}
|
||||
|
||||
static bool HasFloatingRepresentation(CompilerType ct) {
|
||||
return ct.GetTypeInfo() & lldb::eTypeIsFloat;
|
||||
}
|
||||
|
||||
static llvm::Expected<bool> VerifyAssignmentTypes(CompilerType lhs_type,
|
||||
CompilerType rhs_type) {
|
||||
// Make sure lhs is a legal type for DIL assignment.
|
||||
if (!lhs_type.IsInteger() && !lhs_type.IsUnscopedEnumerationType() &&
|
||||
!HasFloatingRepresentation(lhs_type) && !lhs_type.IsPointerType() &&
|
||||
!lhs_type.IsScalarType())
|
||||
return llvm::createStringError(
|
||||
"Illegal type for lhs of assignment (not scalar numeric type)");
|
||||
|
||||
// Make sure rhs is a legal type for DIL assignment.
|
||||
if (!rhs_type.IsInteger() && !rhs_type.IsUnscopedEnumerationType() &&
|
||||
!HasFloatingRepresentation(rhs_type) && !rhs_type.IsPointerType())
|
||||
return llvm::createStringError(
|
||||
"Illegal type for rhs of assignment (not scalar numeric type)");
|
||||
|
||||
// Only allow assigning pointers to pointers.
|
||||
if ((lhs_type.IsPointerType() && !rhs_type.IsPointerType()) ||
|
||||
(!lhs_type.IsPointerType() && rhs_type.IsPointerType()))
|
||||
return llvm::createStringError(
|
||||
"Invalid assignment: Can only assign pointers to pointers");
|
||||
|
||||
// For "real numbers", the types must match exactly.
|
||||
if ((HasFloatingRepresentation(rhs_type) ||
|
||||
HasFloatingRepresentation(lhs_type)) &&
|
||||
lhs_type != rhs_type) {
|
||||
std::string err_msg =
|
||||
llvm::formatv("Incompatible types for assignment: Cannot assign {0} "
|
||||
"to {1}",
|
||||
rhs_type.TypeDescription(), lhs_type.TypeDescription());
|
||||
return llvm::createStringError(err_msg);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
llvm::Expected<lldb::ValueObjectSP>
|
||||
Interpreter::EvaluateAssignment(lldb::ValueObjectSP lhs,
|
||||
lldb::ValueObjectSP rhs, uint32_t location) {
|
||||
|
||||
auto all_ok =
|
||||
VerifyAssignmentTypes(lhs->GetCompilerType(), rhs->GetCompilerType());
|
||||
if (!all_ok)
|
||||
return all_ok.takeError();
|
||||
|
||||
if (llvm::Error e = lhs->SetValueFromInteger(rhs, m_allow_var_updates))
|
||||
return e;
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
llvm::Expected<lldb::ValueObjectSP>
|
||||
Interpreter::EvaluateBinaryShift(BinaryOpKind kind, lldb::ValueObjectSP lhs,
|
||||
lldb::ValueObjectSP rhs, uint32_t location) {
|
||||
|
|
@ -902,8 +958,22 @@ Interpreter::Visit(const BinaryOpNode &node) {
|
|||
switch (node.GetKind()) {
|
||||
case BinaryOpKind::Add:
|
||||
return EvaluateBinaryAddition(lhs, rhs, node.GetLocation());
|
||||
case BinaryOpKind::AddAssign: {
|
||||
auto ret_or_err = EvaluateBinaryAddition(lhs, rhs, node.GetLocation());
|
||||
if (!ret_or_err)
|
||||
return ret_or_err;
|
||||
return EvaluateAssignment(lhs, *ret_or_err, node.GetLocation());
|
||||
}
|
||||
case BinaryOpKind::Assign:
|
||||
return EvaluateAssignment(lhs, rhs, node.GetLocation());
|
||||
case BinaryOpKind::Sub:
|
||||
return EvaluateBinarySubtraction(lhs, rhs, node.GetLocation());
|
||||
case BinaryOpKind::SubAssign: {
|
||||
auto ret_or_err = EvaluateBinarySubtraction(lhs, rhs, node.GetLocation());
|
||||
if (!ret_or_err)
|
||||
return ret_or_err;
|
||||
return EvaluateAssignment(lhs, *ret_or_err, node.GetLocation());
|
||||
}
|
||||
case BinaryOpKind::Mul:
|
||||
return EvaluateBinaryMultiplication(lhs, rhs, node.GetLocation());
|
||||
case BinaryOpKind::Div:
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
|
|||
return "colon";
|
||||
case Kind::coloncolon:
|
||||
return "coloncolon";
|
||||
case Kind::equal:
|
||||
return "equal";
|
||||
case Kind::eof:
|
||||
return "eof";
|
||||
case Kind::float_constant:
|
||||
|
|
@ -50,12 +52,16 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
|
|||
return "lessless";
|
||||
case Kind::minus:
|
||||
return "minus";
|
||||
case Kind::minusequal:
|
||||
return "minusequal";
|
||||
case Token::percent:
|
||||
return "percent";
|
||||
case Kind::period:
|
||||
return "period";
|
||||
case Kind::plus:
|
||||
return "plus";
|
||||
case Kind::plusequal:
|
||||
return "plusequal";
|
||||
case Kind::r_paren:
|
||||
return "r_paren";
|
||||
case Kind::r_square:
|
||||
|
|
@ -188,13 +194,19 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr,
|
|||
return Token(kind, word.str(), position);
|
||||
}
|
||||
|
||||
// IMPORTANT: If two or more tokens share the same prefix, the tokens need to
|
||||
// be ordered longest-to-shortest in the list below. E.g. '::' must come
|
||||
// before ':', and '+=' must come before '+'.
|
||||
constexpr std::pair<Token::Kind, const char *> operators[] = {
|
||||
{Token::arrow, "->"},
|
||||
{Token::coloncolon, "::"},
|
||||
{Token::greatergreater, ">>"},
|
||||
{Token::lessless, "<<"},
|
||||
{Token::minusequal, "-="},
|
||||
{Token::plusequal, "+="},
|
||||
{Token::amp, "&"},
|
||||
{Token::colon, ":"},
|
||||
{Token::equal, "="},
|
||||
{Token::l_paren, "("},
|
||||
{Token::l_square, "["},
|
||||
{Token::minus, "-"},
|
||||
|
|
|
|||
|
|
@ -126,9 +126,38 @@ ASTNodeUP DILParser::Run() {
|
|||
// Parse an expression.
|
||||
//
|
||||
// expression:
|
||||
// shift_expression
|
||||
// assignment_expression
|
||||
//
|
||||
ASTNodeUP DILParser::ParseExpression() { return ParseShiftExpression(); }
|
||||
ASTNodeUP DILParser::ParseExpression() { return ParseAssignmentExpression(); }
|
||||
|
||||
// Parse an assignment_expression
|
||||
//
|
||||
// assignment_expression
|
||||
// shift_expression
|
||||
// shift_expression assignment_operator assignment_expression
|
||||
//
|
||||
// assignment_operator:
|
||||
// "="
|
||||
// "+="
|
||||
// "-="
|
||||
//
|
||||
ASTNodeUP DILParser::ParseAssignmentExpression() {
|
||||
auto lhs = ParseShiftExpression();
|
||||
assert(lhs && "ASTNodeUP must not contain a nullptr");
|
||||
|
||||
// Check if it's an assignment expression.
|
||||
if (CurToken().IsOneOf({Token::equal, Token::plusequal, Token::minusequal})) {
|
||||
// That's an assignment!
|
||||
Token token = CurToken();
|
||||
m_dil_lexer.Advance();
|
||||
auto rhs = ParseAssignmentExpression();
|
||||
assert(rhs && "ASTNodeUP must not contain a nullptr");
|
||||
lhs = std::make_unique<BinaryOpNode>(
|
||||
token.GetLocation(), GetBinaryOpKindFromToken(token.GetKind()),
|
||||
std::move(lhs), std::move(rhs));
|
||||
}
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// Parse a shift_expression.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -1228,87 +1228,82 @@ llvm::Expected<bool> ValueObject::GetValueAsBool() {
|
|||
return llvm::createStringError("type cannot be converted to bool");
|
||||
}
|
||||
|
||||
void ValueObject::SetValueFromInteger(const llvm::APInt &value, Status &error,
|
||||
bool can_update_var) {
|
||||
llvm::Error ValueObject::SetValueFromInteger(const llvm::APInt &value,
|
||||
bool can_update_var) {
|
||||
// Verify the current object is an integer object
|
||||
CompilerType val_type = GetCompilerType();
|
||||
if (!val_type.IsInteger() && !val_type.IsUnscopedEnumerationType() &&
|
||||
!HasFloatingRepresentation(val_type) && !val_type.IsPointerType() &&
|
||||
!val_type.IsScalarType()) {
|
||||
error =
|
||||
Status::FromErrorString("current value object is not an scalar object");
|
||||
return;
|
||||
}
|
||||
!val_type.IsScalarType())
|
||||
return llvm::createStringError(
|
||||
"Not allowed to change the value of a non-scalar object");
|
||||
|
||||
// Verify, if current object is associated with a program variable, that
|
||||
// we are allowing updating program variables in this case.
|
||||
if (GetVariable() && !can_update_var) {
|
||||
error = Status::FromErrorString(
|
||||
"Not allowed to update program variables in this case.");
|
||||
return;
|
||||
}
|
||||
if (GetVariable() && !can_update_var)
|
||||
return llvm::createStringError(
|
||||
"Not allowed to update program variables in this case");
|
||||
|
||||
// Make sure we're not trying to assign to a constant.
|
||||
if (GetIsConstant())
|
||||
return llvm::createStringError(
|
||||
"Not allowed to change the value of a constant");
|
||||
|
||||
// Verify the proposed new value is the right size.
|
||||
lldb::TargetSP target = GetTargetSP();
|
||||
uint64_t byte_size = 0;
|
||||
if (auto temp =
|
||||
llvm::expectedToOptional(GetCompilerType().GetByteSize(target.get())))
|
||||
byte_size = temp.value();
|
||||
if (value.getBitWidth() != byte_size * CHAR_BIT) {
|
||||
error = Status::FromErrorString(
|
||||
"illegal argument: new value should be of the same size");
|
||||
return;
|
||||
// Exclude size check when assigning an integer 1 or 0 to a boolean.
|
||||
if (!val_type.IsBoolean() || (!value.isOne() && !value.isZero())) {
|
||||
byte_size = llvm::expectedToOptional(GetByteSize()).value_or(0);
|
||||
if (value.getBitWidth() > byte_size * CHAR_BIT) {
|
||||
// The type is too big, but maybe the value itself is small enough?
|
||||
uint64_t u_max = (1 << (byte_size * CHAR_BIT)) - 1;
|
||||
if (*(value.getRawData()) > u_max)
|
||||
return llvm::createStringError(
|
||||
"Illegal argument: new value is too big");
|
||||
}
|
||||
}
|
||||
|
||||
Status error;
|
||||
lldb::DataExtractorSP data_sp = std::make_shared<DataExtractor>(
|
||||
reinterpret_cast<const void *>(value.getRawData()), byte_size,
|
||||
target->GetArchitecture().GetByteOrder(),
|
||||
static_cast<uint8_t>(target->GetArchitecture().GetAddressByteSize()));
|
||||
SetData(*data_sp, error);
|
||||
return error.takeError();
|
||||
}
|
||||
|
||||
void ValueObject::SetValueFromInteger(lldb::ValueObjectSP new_val_sp,
|
||||
Status &error, bool can_update_var) {
|
||||
llvm::Error ValueObject::SetValueFromInteger(lldb::ValueObjectSP new_val_sp,
|
||||
bool can_update_var) {
|
||||
// Verify the current object is an integer object
|
||||
CompilerType val_type = GetCompilerType();
|
||||
if (!val_type.IsInteger() && !val_type.IsUnscopedEnumerationType() &&
|
||||
!HasFloatingRepresentation(val_type) && !val_type.IsPointerType() &&
|
||||
!val_type.IsScalarType()) {
|
||||
error =
|
||||
Status::FromErrorString("current value object is not an scalar object");
|
||||
return;
|
||||
}
|
||||
!val_type.IsScalarType())
|
||||
return llvm::createStringError("Not allowed to update a non-scalar object");
|
||||
|
||||
// Verify, if current object is associated with a program variable, that
|
||||
// we are allowing updating program variables in this case.
|
||||
if (GetVariable() && !can_update_var) {
|
||||
error = Status::FromErrorString(
|
||||
"Not allowed to update program variables in this case.");
|
||||
return;
|
||||
}
|
||||
if (GetVariable() && !can_update_var)
|
||||
return llvm::createStringError(
|
||||
"Not allowed to update program variables in this case");
|
||||
|
||||
// Verify the proposed new value is the right type.
|
||||
CompilerType new_val_type = new_val_sp->GetCompilerType();
|
||||
if (!new_val_type.IsInteger() && !HasFloatingRepresentation(new_val_type) &&
|
||||
!new_val_type.IsPointerType()) {
|
||||
error = Status::FromErrorString(
|
||||
"illegal argument: new value should be of the same size");
|
||||
return;
|
||||
}
|
||||
if (!new_val_type.IsInteger() && !new_val_type.IsUnscopedEnumerationType() &&
|
||||
!HasFloatingRepresentation(new_val_type) && !new_val_type.IsPointerType())
|
||||
return llvm::createStringError(
|
||||
"Illegal argument: new value is not a scalar object");
|
||||
|
||||
if (new_val_type.IsInteger()) {
|
||||
if (new_val_type.IsInteger() || new_val_type.IsUnscopedEnumerationType()) {
|
||||
auto value_or_err = new_val_sp->GetValueAsAPSInt();
|
||||
if (value_or_err)
|
||||
SetValueFromInteger(*value_or_err, error, can_update_var);
|
||||
else
|
||||
error = Status::FromError(value_or_err.takeError());
|
||||
return SetValueFromInteger(*value_or_err, can_update_var);
|
||||
} else if (HasFloatingRepresentation(new_val_type)) {
|
||||
auto value_or_err = new_val_sp->GetValueAsAPFloat();
|
||||
if (value_or_err)
|
||||
SetValueFromInteger(value_or_err->bitcastToAPInt(), error,
|
||||
can_update_var);
|
||||
else
|
||||
error = Status::FromError(value_or_err.takeError());
|
||||
return SetValueFromInteger(value_or_err->bitcastToAPInt(),
|
||||
can_update_var);
|
||||
} else if (new_val_type.IsPointerType()) {
|
||||
bool success = true;
|
||||
uint64_t int_val = new_val_sp->GetValueAsUnsigned(0, &success);
|
||||
|
|
@ -1318,11 +1313,12 @@ void ValueObject::SetValueFromInteger(lldb::ValueObjectSP new_val_sp,
|
|||
if (auto temp = llvm::expectedToOptional(
|
||||
new_val_sp->GetCompilerType().GetBitSize(target.get())))
|
||||
num_bits = temp.value();
|
||||
SetValueFromInteger(llvm::APInt(num_bits, int_val), error,
|
||||
can_update_var);
|
||||
return SetValueFromInteger(llvm::APInt(num_bits, int_val),
|
||||
can_update_var);
|
||||
} else
|
||||
error = Status::FromErrorString("error converting new_val_sp to integer");
|
||||
return llvm::createStringError("Error converting new_val_sp to integer");
|
||||
}
|
||||
llvm_unreachable("Unrecognized type for RHS of assignment");
|
||||
}
|
||||
|
||||
// if any more "special cases" are added to
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
CXX_SOURCES := main.cpp
|
||||
|
||||
include Makefile.rules
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
"""
|
||||
Test DIL basic assignment.
|
||||
"""
|
||||
|
||||
import lldb
|
||||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test import *
|
||||
|
||||
|
||||
class TestFrameVarDILAssignment(TestBase):
|
||||
NO_DEBUG_INFO_TESTCASE = True
|
||||
|
||||
def test_assignment(self):
|
||||
self.build()
|
||||
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
|
||||
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
|
||||
)
|
||||
|
||||
self.runCmd("settings set target.experimental.use-DIL true")
|
||||
|
||||
Is32Bit = False
|
||||
if self.target().GetAddressByteSize() == 4:
|
||||
Is32Bit = True
|
||||
|
||||
self.expect(
|
||||
"frame variable '1 = 1'",
|
||||
error=True,
|
||||
substrs=["Not allowed to change the value of a constant"],
|
||||
)
|
||||
|
||||
# Assigning to an int var
|
||||
self.expect_var_path("i", value="1")
|
||||
self.expect("frame variable 'i = 5'", substrs=["i = 5"])
|
||||
self.expect_var_path("i", value="5")
|
||||
self.expect_var_path("j", value="-4")
|
||||
self.expect("frame variable 'i = j'", substrs=["i = -4"])
|
||||
self.expect_var_path("i", value="-4")
|
||||
self.expect("frame variable 'i = 2'", substrs=["i = 2"])
|
||||
self.expect_var_path("i", value="2")
|
||||
self.expect("frame variable 'i = -2'", substrs=["i = -2"])
|
||||
self.expect_var_path("i", value="-2")
|
||||
self.expect("frame variable 'i = (int)eOne'", substrs=["i = 0"])
|
||||
self.expect_var_path("i", value="0")
|
||||
self.expect("frame variable 'i = (int)eTwo'", substrs=["i = 1"])
|
||||
self.expect_var_path("i", value="1")
|
||||
|
||||
# Assigning to a float var
|
||||
self.expect_var_path("f", value="1.5")
|
||||
self.expect("frame variable 'f = 17.823f'", substrs=["f = 17.823"])
|
||||
self.expect_var_path("f", value="17.823")
|
||||
self.expect_var_path("pi", value="3.14159012")
|
||||
self.expect("frame variable 'f = pi'", substrs=["f = 3.14159012"])
|
||||
self.expect_var_path("f", value="3.14159012")
|
||||
self.expect("frame variable 'f = 2.5f'", substrs=["f = 2.5"])
|
||||
self.expect_var_path("f", value="2.5")
|
||||
self.expect("frame variable 'f = 3.5f'", substrs=["f = 3.5"])
|
||||
self.expect_var_path("f", value="3.5")
|
||||
|
||||
# Assigning to an enum
|
||||
self.expect("frame variable 'i = eOne'", substrs=["0"])
|
||||
self.expect("frame variable 'eOne = 1'", substrs=["eOne = TWO"])
|
||||
self.expect("frame variable 'eOne = 0'", substrs=["eOne = ONE"])
|
||||
|
||||
# Assigning to a pointer
|
||||
self.expect(
|
||||
"frame variable 'p = 1'",
|
||||
error=True,
|
||||
substrs=["Invalid assignment: Can only assign pointers to pointers"],
|
||||
)
|
||||
|
||||
self.expect(
|
||||
"frame variable 'p = i + s'",
|
||||
error=True,
|
||||
substrs=["Invalid assignment: Can only assign pointers to pointers"],
|
||||
)
|
||||
|
||||
self.expect(
|
||||
"frame variable 'i = p'",
|
||||
error=True,
|
||||
substrs=["Invalid assignment: Can only assign pointers to pointers"],
|
||||
)
|
||||
|
||||
if Is32Bit:
|
||||
self.expect("frame variable 'p = (int*)12'", substrs=["p = 0x0000000c"])
|
||||
self.expect_var_path("p", value="0x0000000c")
|
||||
self.expect("frame variable 'p = p - s'", substrs=["p = 0x0000000b"])
|
||||
self.expect("frame variable 'p = (int *)0'", substrs=["p = 0x00000000"])
|
||||
else:
|
||||
self.expect(
|
||||
"frame variable 'p = (int*)12'", substrs=["p = 0x000000000000000c"]
|
||||
)
|
||||
self.expect_var_path("p", value="0x000000000000000c")
|
||||
self.expect(
|
||||
"frame variable 'p = (int *)0'", substrs=["p = 0x0000000000000000"]
|
||||
)
|
||||
|
||||
# Assigning to a bool
|
||||
self.expect_var_path("b", value="false")
|
||||
self.expect("frame variable 'b = true'", substrs=["b = true"])
|
||||
self.expect_var_path("b", value="true")
|
||||
self.expect_var_path("(int)b", value="")
|
||||
self.expect("frame variable 'b = (bool)0'", substrs=["b = false"])
|
||||
self.expect_var_path("b", value="false")
|
||||
|
||||
# Assigning to an array
|
||||
self.expect("frame variable 'farr'", substrs=["([0] = 1, [1] = 2)"])
|
||||
self.expect("frame variable 'farr[1] = f'", substrs=["farr[1] = f = 3.5"])
|
||||
self.expect_var_path("farr[1]", value="3.5")
|
||||
self.expect("frame variable 'farr'", substrs=["([0] = 1, [1] = 3.5)"])
|
||||
self.expect("frame variable 'arr'", substrs=["([0] = 1, [1] = 2)"])
|
||||
self.expect("frame variable 'arr[0] = 37'", substrs=["arr[0] = 37"])
|
||||
self.expect("frame variable 'arr[1] = j'", substrs=["arr[1] = j = -4"])
|
||||
self.expect("frame variable 'arr'", substrs=["([0] = 37, [1] = -4)"])
|
||||
|
||||
# Test basic assignment conversions.
|
||||
# int = enum
|
||||
self.expect("frame variable 'i = eOne'", substrs=["i = 0"])
|
||||
# int = bool
|
||||
self.expect("frame variable 'i = true'", substrs=["i = 1"])
|
||||
# int = double
|
||||
self.expect(
|
||||
"frame variable 'i = d2'",
|
||||
error=True,
|
||||
substrs=[
|
||||
"Incompatible types for assignment: Cannot assign 'double' to 'int'"
|
||||
],
|
||||
)
|
||||
# int = float
|
||||
self.expect(
|
||||
"frame variable 'i = pi'",
|
||||
error=True,
|
||||
substrs=[
|
||||
"Incompatible types for assignment: Cannot assign 'float' to 'int'"
|
||||
],
|
||||
)
|
||||
# float = int
|
||||
self.expect(
|
||||
"frame variable 'f = 8'",
|
||||
error=True,
|
||||
substrs=[
|
||||
"Incompatible types for assignment: Cannot assign 'int' to 'float'"
|
||||
],
|
||||
)
|
||||
# float = double
|
||||
self.expect(
|
||||
"frame variable 'f = d2'",
|
||||
error=True,
|
||||
substrs=[
|
||||
"Incompatible types for assignment: Cannot assign 'double' to 'float'"
|
||||
],
|
||||
)
|
||||
# double = double
|
||||
self.expect("frame variable 'd = d2'", substrs=["d = 15.779999999999999"])
|
||||
self.expect("frame variable 'd = 1.25'", substrs=["d = 1.25"])
|
||||
# double = float
|
||||
self.expect(
|
||||
"frame variable 'd = pi'",
|
||||
error=True,
|
||||
substrs=[
|
||||
"Incompatible types for assignment: Cannot assign 'float' to 'double'"
|
||||
],
|
||||
)
|
||||
# double = int
|
||||
self.expect(
|
||||
"frame variable 'd = 17'",
|
||||
error=True,
|
||||
substrs=[
|
||||
"Incompatible types for assignment: Cannot assign 'int' to 'double'"
|
||||
],
|
||||
)
|
||||
|
||||
# bool = int
|
||||
self.expect("frame variable 'b = 0'", substrs=["b = false"])
|
||||
self.expect("frame variable 'b = 1'", substrs=["b = true"])
|
||||
self.expect("frame variable 'b = 32'", substrs=["b = true"])
|
||||
|
||||
# Assigning "int" with a small value to "short" should work.
|
||||
self.expect("frame variable 's = i'", substrs=["s = 1"])
|
||||
# Assigning "int" with a big value to "short" should fail.
|
||||
self.expect(
|
||||
"frame variable 's = 78246'",
|
||||
error=True,
|
||||
substrs=["new value is too big"],
|
||||
)
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
"""
|
||||
Test DIL basic assignment.
|
||||
"""
|
||||
|
||||
import lldb
|
||||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test import lldbutil
|
||||
|
||||
|
||||
class TestFrameVarDILAssignment(TestBase):
|
||||
NO_DEBUG_INFO_TESTCASE = True
|
||||
|
||||
def test_assignment(self):
|
||||
self.build()
|
||||
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
|
||||
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
|
||||
)
|
||||
|
||||
self.runCmd("settings set target.experimental.use-DIL true")
|
||||
|
||||
self.expect("frame variable 'i += 1'", substrs=["2"])
|
||||
self.expect("frame variable 's += i'", substrs=["9"])
|
||||
self.expect("frame variable 'i += 2'", substrs=["4"])
|
||||
self.expect("frame variable 'i += -4'", substrs=["0"])
|
||||
self.expect("frame variable 'i += eOne'", substrs=["0"])
|
||||
self.expect("frame variable 'i += eTwo'", substrs=["1"])
|
||||
|
||||
self.expect("frame variable 'f += 1'", substrs=["2.5"])
|
||||
self.expect("frame variable 'f += -2.0f'", substrs=["0.5"])
|
||||
self.expect("frame variable 'f += 2.5f'", substrs=["3"])
|
||||
self.expect("frame variable 'f += eTwo'", substrs=["4"])
|
||||
|
||||
Is32Bit = False
|
||||
if self.target().GetAddressByteSize() == 4:
|
||||
Is32Bit = True
|
||||
|
||||
self.expect(
|
||||
"frame variable 'i += p'", # Try assigning pointer to int.
|
||||
error=True,
|
||||
substrs=["Invalid assignment: Can only assign pointers to pointers"],
|
||||
)
|
||||
|
||||
if Is32Bit:
|
||||
self.expect("frame variable 'p'", substrs=["p = 0x0000000a"])
|
||||
self.expect("frame variable 'p += 2'", substrs=["p = 0x00000012"])
|
||||
self.expect("frame variable 'p += i'", substrs=["p = 0x00000016"])
|
||||
else:
|
||||
self.expect("frame variable 'p'", substrs=["p = 0x000000000000000a"])
|
||||
self.expect("frame variable 'p += 2'", substrs=["p = 0x0000000000000012"])
|
||||
self.expect("frame variable 'p += i'", substrs=["p = 0x0000000000000016"])
|
||||
|
||||
self.expect("frame variable 'i = 1'", substrs=["1"])
|
||||
self.expect("frame variable 'i -= 1'", substrs=["0"])
|
||||
self.expect("frame variable 'i -= 2'", substrs=["-2"])
|
||||
self.expect("frame variable 'i -= -4'", substrs=["2"])
|
||||
self.expect("frame variable 'i -= eOne'", substrs=["2"])
|
||||
self.expect("frame variable 'i -= eTwo'", substrs=["1"])
|
||||
|
||||
self.expect("frame variable 'f = 1.5f'", substrs=["1.5"])
|
||||
self.expect("frame variable 'f -= 1'", substrs=["0.5"])
|
||||
self.expect("frame variable 'f -= -2.0f'", substrs=["2.5"])
|
||||
self.expect("frame variable 'f -= -2.5f'", substrs=["5"])
|
||||
self.expect("frame variable 'f -= eTwo'", substrs=["4"])
|
||||
|
||||
Is32Bit = False
|
||||
if self.target().GetAddressByteSize() == 4:
|
||||
Is32Bit = True
|
||||
|
||||
self.expect(
|
||||
"frame variable 'i -= p'", # Try assigning pointer to int.
|
||||
error=True,
|
||||
substrs=["invalid operands to binary expression"],
|
||||
)
|
||||
|
||||
if Is32Bit:
|
||||
self.expect("frame variable 'p = (int *)10'", substrs=["p = 0x0000000a"])
|
||||
self.expect("frame variable 'p -= 2'", substrs=["p = 0x00000002"])
|
||||
else:
|
||||
self.expect(
|
||||
"frame variable 'p = (int *)10'", substrs=["p = 0x000000000000000a"]
|
||||
)
|
||||
self.expect("frame variable 'p -= 2'", substrs=["p = 0x0000000000000002"])
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
int main(int argc, char **argv) {
|
||||
int i = 1;
|
||||
int j = -4;
|
||||
float f = 1.5f;
|
||||
float pi = 3.14159f;
|
||||
double d = 1.25;
|
||||
double d2 = 15.78;
|
||||
int *p = (int *)10;
|
||||
bool b = false;
|
||||
short s = 7;
|
||||
float farr[2] = {1.0f, 2.0f};
|
||||
int arr[2] = {1, 2};
|
||||
|
||||
enum Enum { ONE, TWO };
|
||||
Enum eOne = ONE;
|
||||
Enum eTwo = TWO;
|
||||
|
||||
return 0; // Set a breakpoint here
|
||||
}
|
||||
Loading…
Reference in a new issue