[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:
cmtice 2026-06-03 11:38:43 -07:00 committed by GitHub
parent 4169b17acf
commit 0b9dc1bdd3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 498 additions and 73 deletions

View file

@ -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 ;

View file

@ -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.

View file

@ -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;
};

View file

@ -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,

View file

@ -84,6 +84,8 @@ private:
ASTNodeUP Run();
ASTNodeUP ParseExpression();
ASTNodeUP ParseAssignmentExpression();
ASTNodeUP ParseShiftExpression();
ASTNodeUP ParseAdditiveExpression();
ASTNodeUP ParseMultiplicativeExpression();

View file

@ -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);

View file

@ -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:

View file

@ -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:

View file

@ -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, "-"},

View file

@ -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.
//

View file

@ -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

View file

@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp
include Makefile.rules

View file

@ -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"],
)

View file

@ -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"])

View file

@ -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
}