Skip to content

Commit b5f0971

Browse files
authored
Merge pull request #6535 from jimingham/backtick-in-parsed
Fix backtick handling in parsed commands.
2 parents 33e9bd1 + 8800893 commit b5f0971

File tree

4 files changed

+171
-104
lines changed

4 files changed

+171
-104
lines changed

lldb/include/lldb/Interpreter/CommandInterpreter.h

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

637637
bool IOHandlerInterrupt(IOHandler &io_handler) override;
638638

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

@@ -670,8 +673,6 @@ class CommandInterpreter : public Broadcaster,
670673

671674
void RestoreExecutionContext();
672675

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

677678
// 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
@@ -1761,112 +1761,124 @@ Status CommandInterpreter::PreprocessCommand(std::string &command) {
17611761

17621762
std::string expr_str(command, expr_content_start,
17631763
end_backtick - expr_content_start);
1764+
error = PreprocessToken(expr_str);
1765+
// We always stop at the first error:
1766+
if (error.Fail())
1767+
break;
17641768

1765-
ExecutionContext exe_ctx(GetExecutionContext());
1769+
command.erase(start_backtick, end_backtick - start_backtick + 1);
1770+
command.insert(start_backtick, std::string(expr_str));
1771+
pos = start_backtick + expr_str.size();
1772+
}
1773+
return error;
1774+
}
17661775

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

1818-
continue;
1819-
}
1781+
// Get a dummy target to allow for calculator mode while processing
1782+
// backticks. This also helps break the infinite loop caused when target is
1783+
// null.
1784+
Target *exe_target = exe_ctx.GetTargetPtr();
1785+
Target &target = exe_target ? *exe_target : m_debugger.GetDummyTarget();
18201786

1821-
if (expr_result_valobj_sp)
1822-
error = expr_result_valobj_sp->GetError();
1787+
ValueObjectSP expr_result_valobj_sp;
18231788

1824-
if (error.Success()) {
1825-
switch (expr_result) {
1826-
case eExpressionSetupError:
1827-
error.SetErrorStringWithFormat(
1828-
"expression setup error for the expression '%s'", expr_str.c_str());
1829-
break;
1830-
case eExpressionParseError:
1831-
error.SetErrorStringWithFormat(
1832-
"expression parse error for the expression '%s'", expr_str.c_str());
1833-
break;
1834-
case eExpressionResultUnavailable:
1835-
error.SetErrorStringWithFormat(
1836-
"expression error fetching result for the expression '%s'",
1837-
expr_str.c_str());
1838-
break;
1839-
case eExpressionCompleted:
1840-
break;
1841-
case eExpressionDiscarded:
1842-
error.SetErrorStringWithFormat(
1843-
"expression discarded for the expression '%s'", expr_str.c_str());
1844-
break;
1845-
case eExpressionInterrupted:
1846-
error.SetErrorStringWithFormat(
1847-
"expression interrupted for the expression '%s'", expr_str.c_str());
1848-
break;
1849-
case eExpressionHitBreakpoint:
1850-
error.SetErrorStringWithFormat(
1851-
"expression hit breakpoint for the expression '%s'",
1852-
expr_str.c_str());
1853-
break;
1854-
case eExpressionTimedOut:
1855-
error.SetErrorStringWithFormat(
1856-
"expression timed out for the expression '%s'", expr_str.c_str());
1857-
break;
1858-
case eExpressionStoppedForDebug:
1859-
error.SetErrorStringWithFormat("expression stop at entry point "
1860-
"for debugging for the "
1789+
EvaluateExpressionOptions options;
1790+
options.SetCoerceToId(false);
1791+
options.SetUnwindOnError(true);
1792+
options.SetIgnoreBreakpoints(true);
1793+
options.SetKeepInMemory(false);
1794+
options.SetTryAllThreads(true);
1795+
options.SetTimeout(std::nullopt);
1796+
1797+
ExpressionResults expr_result =
1798+
target.EvaluateExpression(expr_str.c_str(), exe_ctx.GetFramePtr(),
1799+
expr_result_valobj_sp, options);
1800+
1801+
if (expr_result == eExpressionCompleted) {
1802+
Scalar scalar;
1803+
if (expr_result_valobj_sp)
1804+
expr_result_valobj_sp =
1805+
expr_result_valobj_sp->GetQualifiedRepresentationIfAvailable(
1806+
expr_result_valobj_sp->GetDynamicValueType(), true);
1807+
if (expr_result_valobj_sp->ResolveValue(scalar)) {
1808+
1809+
StreamString value_strm;
1810+
const bool show_type = false;
1811+
scalar.GetValue(&value_strm, show_type);
1812+
size_t value_string_size = value_strm.GetSize();
1813+
if (value_string_size) {
1814+
expr_str = value_strm.GetData();
1815+
} else {
1816+
error.SetErrorStringWithFormat("expression value didn't result "
1817+
"in a scalar value for the "
18611818
"expression '%s'",
18621819
expr_str.c_str());
1863-
break;
1864-
case eExpressionThreadVanished:
1865-
error.SetErrorStringWithFormat(
1866-
"expression thread vanished for the expression '%s'",
1867-
expr_str.c_str());
1868-
break;
18691820
}
1821+
} else {
1822+
error.SetErrorStringWithFormat("expression value didn't result "
1823+
"in a scalar value for the "
1824+
"expression '%s'",
1825+
expr_str.c_str());
1826+
}
1827+
return error;
1828+
}
1829+
1830+
// If we have an error from the expression evaluation it will be in the
1831+
// ValueObject error, which won't be success and we will just report it.
1832+
// But if for some reason we didn't get a value object at all, then we will
1833+
// make up some helpful errors from the expression result.
1834+
if (expr_result_valobj_sp)
1835+
error = expr_result_valobj_sp->GetError();
1836+
1837+
if (error.Success()) {
1838+
switch (expr_result) {
1839+
case eExpressionSetupError:
1840+
error.SetErrorStringWithFormat(
1841+
"expression setup error for the expression '%s'", expr_str.c_str());
1842+
break;
1843+
case eExpressionParseError:
1844+
error.SetErrorStringWithFormat(
1845+
"expression parse error for the expression '%s'", expr_str.c_str());
1846+
break;
1847+
case eExpressionResultUnavailable:
1848+
error.SetErrorStringWithFormat(
1849+
"expression error fetching result for the expression '%s'",
1850+
expr_str.c_str());
1851+
break;
1852+
case eExpressionCompleted:
1853+
break;
1854+
case eExpressionDiscarded:
1855+
error.SetErrorStringWithFormat(
1856+
"expression discarded for the expression '%s'", expr_str.c_str());
1857+
break;
1858+
case eExpressionInterrupted:
1859+
error.SetErrorStringWithFormat(
1860+
"expression interrupted for the expression '%s'", expr_str.c_str());
1861+
break;
1862+
case eExpressionHitBreakpoint:
1863+
error.SetErrorStringWithFormat(
1864+
"expression hit breakpoint for the expression '%s'",
1865+
expr_str.c_str());
1866+
break;
1867+
case eExpressionTimedOut:
1868+
error.SetErrorStringWithFormat(
1869+
"expression timed out for the expression '%s'", expr_str.c_str());
1870+
break;
1871+
case eExpressionStoppedForDebug:
1872+
error.SetErrorStringWithFormat("expression stop at entry point "
1873+
"for debugging for the "
1874+
"expression '%s'",
1875+
expr_str.c_str());
1876+
break;
1877+
case eExpressionThreadVanished:
1878+
error.SetErrorStringWithFormat(
1879+
"expression thread vanished for the expression '%s'",
1880+
expr_str.c_str());
1881+
break;
18701882
}
18711883
}
18721884
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: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,54 @@ 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+
fixed_output = result.GetOutput().rstrip()
80+
self.assertEqual("0", fixed_output, "Substitution worked")
81+
3282

0 commit comments

Comments
 (0)