Skip to content

Commit 75ca15f

Browse files
committed
Fix backtick handling in parsed commands.
https://reviews.llvm.org/D146779
1 parent 6a423ee commit 75ca15f

File tree

4 files changed

+170
-104
lines changed

4 files changed

+170
-104
lines changed

lldb/include/lldb/Interpreter/CommandInterpreter.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,9 @@ class CommandInterpreter : public Broadcaster,
637637

638638
bool IOHandlerInterrupt(IOHandler &io_handler) override;
639639

640+
Status PreprocessCommand(std::string &command);
641+
Status PreprocessToken(std::string &token);
642+
640643
protected:
641644
friend class Debugger;
642645

@@ -671,8 +674,6 @@ class CommandInterpreter : public Broadcaster,
671674

672675
void RestoreExecutionContext();
673676

674-
Status PreprocessCommand(std::string &command);
675-
676677
void SourceInitFile(FileSpec file, CommandReturnObject &result);
677678

678679
// Completely resolves aliases and abbreviations, returning a pointer to the

lldb/source/Interpreter/CommandInterpreter.cpp

Lines changed: 110 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1750,112 +1750,124 @@ Status CommandInterpreter::PreprocessCommand(std::string &command) {
17501750

17511751
std::string expr_str(command, expr_content_start,
17521752
end_backtick - expr_content_start);
1753+
error = PreprocessToken(expr_str);
1754+
// We always stop at the first error:
1755+
if (error.Fail())
1756+
break;
17531757

1754-
ExecutionContext exe_ctx(GetExecutionContext());
1758+
command.erase(start_backtick, end_backtick - start_backtick + 1);
1759+
command.insert(start_backtick, std::string(expr_str));
1760+
pos = start_backtick + expr_str.size();
1761+
}
1762+
return error;
1763+
}
17551764

1756-
// Get a dummy target to allow for calculator mode while processing
1757-
// backticks. This also helps break the infinite loop caused when target is
1758-
// null.
1759-
Target *exe_target = exe_ctx.GetTargetPtr();
1760-
Target &target = exe_target ? *exe_target : m_debugger.GetDummyTarget();
1761-
1762-
ValueObjectSP expr_result_valobj_sp;
1763-
1764-
EvaluateExpressionOptions options;
1765-
options.SetCoerceToId(false);
1766-
options.SetUnwindOnError(true);
1767-
options.SetIgnoreBreakpoints(true);
1768-
options.SetKeepInMemory(false);
1769-
options.SetTryAllThreads(true);
1770-
options.SetTimeout(std::nullopt);
1771-
1772-
ExpressionResults expr_result =
1773-
target.EvaluateExpression(expr_str.c_str(), exe_ctx.GetFramePtr(),
1774-
expr_result_valobj_sp, options);
1775-
1776-
if (expr_result == eExpressionCompleted) {
1777-
Scalar scalar;
1778-
if (expr_result_valobj_sp)
1779-
expr_result_valobj_sp =
1780-
expr_result_valobj_sp->GetQualifiedRepresentationIfAvailable(
1781-
expr_result_valobj_sp->GetDynamicValueType(), true);
1782-
if (expr_result_valobj_sp->ResolveValue(scalar)) {
1783-
command.erase(start_backtick, end_backtick - start_backtick + 1);
1784-
StreamString value_strm;
1785-
const bool show_type = false;
1786-
scalar.GetValue(&value_strm, show_type);
1787-
size_t value_string_size = value_strm.GetSize();
1788-
if (value_string_size) {
1789-
command.insert(start_backtick, std::string(value_strm.GetString()));
1790-
pos = start_backtick + value_string_size;
1791-
continue;
1792-
} else {
1793-
error.SetErrorStringWithFormat("expression value didn't result "
1794-
"in a scalar value for the "
1795-
"expression '%s'",
1796-
expr_str.c_str());
1797-
break;
1798-
}
1799-
} else {
1800-
error.SetErrorStringWithFormat("expression value didn't result "
1801-
"in a scalar value for the "
1802-
"expression '%s'",
1803-
expr_str.c_str());
1804-
break;
1805-
}
1765+
Status
1766+
CommandInterpreter::PreprocessToken(std::string &expr_str) {
1767+
Status error;
1768+
ExecutionContext exe_ctx(GetExecutionContext());
18061769

1807-
continue;
1808-
}
1770+
// Get a dummy target to allow for calculator mode while processing
1771+
// backticks. This also helps break the infinite loop caused when target is
1772+
// null.
1773+
Target *exe_target = exe_ctx.GetTargetPtr();
1774+
Target &target = exe_target ? *exe_target : m_debugger.GetDummyTarget();
18091775

1810-
if (expr_result_valobj_sp)
1811-
error = expr_result_valobj_sp->GetError();
1776+
ValueObjectSP expr_result_valobj_sp;
18121777

1813-
if (error.Success()) {
1814-
switch (expr_result) {
1815-
case eExpressionSetupError:
1816-
error.SetErrorStringWithFormat(
1817-
"expression setup error for the expression '%s'", expr_str.c_str());
1818-
break;
1819-
case eExpressionParseError:
1820-
error.SetErrorStringWithFormat(
1821-
"expression parse error for the expression '%s'", expr_str.c_str());
1822-
break;
1823-
case eExpressionResultUnavailable:
1824-
error.SetErrorStringWithFormat(
1825-
"expression error fetching result for the expression '%s'",
1826-
expr_str.c_str());
1827-
break;
1828-
case eExpressionCompleted:
1829-
break;
1830-
case eExpressionDiscarded:
1831-
error.SetErrorStringWithFormat(
1832-
"expression discarded for the expression '%s'", expr_str.c_str());
1833-
break;
1834-
case eExpressionInterrupted:
1835-
error.SetErrorStringWithFormat(
1836-
"expression interrupted for the expression '%s'", expr_str.c_str());
1837-
break;
1838-
case eExpressionHitBreakpoint:
1839-
error.SetErrorStringWithFormat(
1840-
"expression hit breakpoint for the expression '%s'",
1841-
expr_str.c_str());
1842-
break;
1843-
case eExpressionTimedOut:
1844-
error.SetErrorStringWithFormat(
1845-
"expression timed out for the expression '%s'", expr_str.c_str());
1846-
break;
1847-
case eExpressionStoppedForDebug:
1848-
error.SetErrorStringWithFormat("expression stop at entry point "
1849-
"for debugging for the "
1778+
EvaluateExpressionOptions options;
1779+
options.SetCoerceToId(false);
1780+
options.SetUnwindOnError(true);
1781+
options.SetIgnoreBreakpoints(true);
1782+
options.SetKeepInMemory(false);
1783+
options.SetTryAllThreads(true);
1784+
options.SetTimeout(std::nullopt);
1785+
1786+
ExpressionResults expr_result =
1787+
target.EvaluateExpression(expr_str.c_str(), exe_ctx.GetFramePtr(),
1788+
expr_result_valobj_sp, options);
1789+
1790+
if (expr_result == eExpressionCompleted) {
1791+
Scalar scalar;
1792+
if (expr_result_valobj_sp)
1793+
expr_result_valobj_sp =
1794+
expr_result_valobj_sp->GetQualifiedRepresentationIfAvailable(
1795+
expr_result_valobj_sp->GetDynamicValueType(), true);
1796+
if (expr_result_valobj_sp->ResolveValue(scalar)) {
1797+
1798+
StreamString value_strm;
1799+
const bool show_type = false;
1800+
scalar.GetValue(&value_strm, show_type);
1801+
size_t value_string_size = value_strm.GetSize();
1802+
if (value_string_size) {
1803+
expr_str = value_strm.GetData();
1804+
} else {
1805+
error.SetErrorStringWithFormat("expression value didn't result "
1806+
"in a scalar value for the "
18501807
"expression '%s'",
18511808
expr_str.c_str());
1852-
break;
1853-
case eExpressionThreadVanished:
1854-
error.SetErrorStringWithFormat(
1855-
"expression thread vanished for the expression '%s'",
1856-
expr_str.c_str());
1857-
break;
18581809
}
1810+
} else {
1811+
error.SetErrorStringWithFormat("expression value didn't result "
1812+
"in a scalar value for the "
1813+
"expression '%s'",
1814+
expr_str.c_str());
1815+
}
1816+
return error;
1817+
}
1818+
1819+
// If we have an error from the expression evaluation it will be in the
1820+
// ValueObject error, which won't be success and we will just report it.
1821+
// But if for some reason we didn't get a value object at all, then we will
1822+
// make up some helpful errors from the expression result.
1823+
if (expr_result_valobj_sp)
1824+
error = expr_result_valobj_sp->GetError();
1825+
1826+
if (error.Success()) {
1827+
switch (expr_result) {
1828+
case eExpressionSetupError:
1829+
error.SetErrorStringWithFormat(
1830+
"expression setup error for the expression '%s'", expr_str.c_str());
1831+
break;
1832+
case eExpressionParseError:
1833+
error.SetErrorStringWithFormat(
1834+
"expression parse error for the expression '%s'", expr_str.c_str());
1835+
break;
1836+
case eExpressionResultUnavailable:
1837+
error.SetErrorStringWithFormat(
1838+
"expression error fetching result for the expression '%s'",
1839+
expr_str.c_str());
1840+
break;
1841+
case eExpressionCompleted:
1842+
break;
1843+
case eExpressionDiscarded:
1844+
error.SetErrorStringWithFormat(
1845+
"expression discarded for the expression '%s'", expr_str.c_str());
1846+
break;
1847+
case eExpressionInterrupted:
1848+
error.SetErrorStringWithFormat(
1849+
"expression interrupted for the expression '%s'", expr_str.c_str());
1850+
break;
1851+
case eExpressionHitBreakpoint:
1852+
error.SetErrorStringWithFormat(
1853+
"expression hit breakpoint for the expression '%s'",
1854+
expr_str.c_str());
1855+
break;
1856+
case eExpressionTimedOut:
1857+
error.SetErrorStringWithFormat(
1858+
"expression timed out for the expression '%s'", expr_str.c_str());
1859+
break;
1860+
case eExpressionStoppedForDebug:
1861+
error.SetErrorStringWithFormat("expression stop at entry point "
1862+
"for debugging for the "
1863+
"expression '%s'",
1864+
expr_str.c_str());
1865+
break;
1866+
case eExpressionThreadVanished:
1867+
error.SetErrorStringWithFormat(
1868+
"expression thread vanished for the expression '%s'",
1869+
expr_str.c_str());
1870+
break;
18591871
}
18601872
}
18611873
return error;

lldb/source/Interpreter/CommandObject.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -727,10 +727,14 @@ bool CommandObjectParsed::Execute(const char *args_string,
727727
}
728728
if (!handled) {
729729
for (auto entry : llvm::enumerate(cmd_args.entries())) {
730-
if (!entry.value().ref().empty() && entry.value().GetQuoteChar() == '`') {
731-
cmd_args.ReplaceArgumentAtIndex(
732-
entry.index(),
733-
m_interpreter.ProcessEmbeddedScriptCommands(entry.value().c_str()));
730+
const Args::ArgEntry &value = entry.value();
731+
if (!value.ref().empty() && value.GetQuoteChar() == '`') {
732+
// We have to put the backtick back in place for PreprocessCommand.
733+
std::string opt_string = value.c_str();
734+
Status error;
735+
error = m_interpreter.PreprocessToken(opt_string);
736+
if (error.Success())
737+
cmd_args.ReplaceArgumentAtIndex(entry.index(), opt_string);
734738
}
735739
}
736740

lldb/test/API/commands/command/backticks/TestBackticksInAlias.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,53 @@ def test_backticks_in_alias(self):
2929
interp.HandleCommand("_test-argv-parray-cmd", result)
3030
self.assertFalse(result.Succeeded(), "CommandAlias::Desugar currently fails if a alias substitutes %N arguments in another alias")
3131

32+
def test_backticks_in_parsed_cmd_argument(self):
33+
""" break list is a parsed command, use a variable for the breakpoint number
34+
and make sure that and the direct use of the ID get the same result. """
35+
self.build()
36+
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
37+
# Make a second breakpoint so that if the backtick part -> nothing we'll print too much:
38+
# It doesn't need to resolve to anything.
39+
dummy_bkpt = target.BreakpointCreateByName("dont_really_care_if_this_exists")
40+
41+
bkpt_id = bkpt.GetID()
42+
self.runCmd(f"expr int $number = {bkpt_id}")
43+
direct_result = lldb.SBCommandReturnObject()
44+
backtick_result = lldb.SBCommandReturnObject()
45+
interp = self.dbg.GetCommandInterpreter()
46+
interp.HandleCommand(f"break list {bkpt_id}", direct_result)
47+
self.assertTrue(direct_result.Succeeded(), "Break list with id works")
48+
interp.HandleCommand("break list `$number`", backtick_result)
49+
self.assertTrue(direct_result.Succeeded(), "Break list with backtick works")
50+
self.assertEqual(direct_result.GetOutput(), backtick_result.GetOutput(), "Output is the same")
51+
52+
def test_backticks_in_parsed_cmd_option(self):
53+
# The script interpreter is a raw command, so try that one:
54+
self.build()
55+
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
56+
57+
self.runCmd(f"expr int $number = 2")
58+
direct_result = lldb.SBCommandReturnObject()
59+
backtick_result = lldb.SBCommandReturnObject()
60+
interp = self.dbg.GetCommandInterpreter()
61+
interp.HandleCommand(f"memory read --count 2 argv", direct_result)
62+
self.assertTrue(direct_result.Succeeded(), "memory read with direct count works")
63+
interp.HandleCommand("memory read --count `$number` argv", backtick_result)
64+
self.assertTrue(direct_result.Succeeded(), "memory read with backtick works")
65+
self.assertEqual(direct_result.GetOutput(), backtick_result.GetOutput(), "Output is the same")
66+
67+
def test_backticks_in_raw_cmd(self):
68+
# The script interpreter is a raw command, so try that one:
69+
self.build()
70+
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
71+
argc_valobj = thread.frames[0].FindVariable("argc")
72+
self.assertTrue(argc_valobj.GetError().Success(), "Made argc valobj")
73+
argc_value = argc_valobj.GetValueAsUnsigned(0)
74+
self.assertNotEqual(argc_value, 0, "Got a value for argc")
75+
result = lldb.SBCommandReturnObject()
76+
interp = self.dbg.GetCommandInterpreter()
77+
interp.HandleCommand(f"script {argc_value} - `argc`", result)
78+
self.assertTrue(result.Succeeded(), "Command succeeded")
79+
self.assertEqual("0\n", result.GetOutput(), "Substitution worked")
80+
3281

0 commit comments

Comments
 (0)