diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py @@ -139,7 +139,7 @@ for module in module_list: modules[module['name']] = module return modules - + def get_output(self, category, timeout=0.0, clear=True): self.output_condition.acquire() output = None @@ -304,7 +304,7 @@ return response_or_request else: if response_or_request['command'] == 'runInTerminal': - subprocess.Popen(response_or_request['arguments']['args'], + subprocess.Popen(response_or_request['arguments']['args'], env=response_or_request['arguments']['env']) self.send_packet({ "type": "response", @@ -317,7 +317,7 @@ else: desc = 'unkonwn reverse request "%s"' % (response_or_request['command']) raise ValueError(desc) - + return None def wait_for_event(self, filter=None, timeout=None): @@ -567,13 +567,14 @@ } return self.send_recv(command_dict) - def request_evaluate(self, expression, frameIndex=0, threadId=None): + def request_evaluate(self, expression, frameIndex=0, threadId=None, context=None): stackFrame = self.get_stackFrame(frameIndex=frameIndex, threadId=threadId) if stackFrame is None: return [] args_dict = { 'expression': expression, + 'context': context, 'frameId': stackFrame['id'], } command_dict = { @@ -787,7 +788,7 @@ } response = self.send_recv(command_dict) return response - + def request_completions(self, text): args_dict = { 'text': text, diff --git a/lldb/test/API/tools/lldb-vscode/evaluate/Makefile b/lldb/test/API/tools/lldb-vscode/evaluate/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/evaluate/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp foo.cpp + +include Makefile.rules diff --git a/lldb/test/API/tools/lldb-vscode/evaluate/TestVSCode_evaluate.py b/lldb/test/API/tools/lldb-vscode/evaluate/TestVSCode_evaluate.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/evaluate/TestVSCode_evaluate.py @@ -0,0 +1,157 @@ +""" +Test lldb-vscode completions request +""" + + +import lldbvscode_testcase +import unittest2 +import vscode +from lldbsuite.test import lldbutil +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * + +class TestVSCode_variables(lldbvscode_testcase.VSCodeTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + def assertEvaluate(self, expression, regex): + self.assertRegexpMatches( + self.vscode.request_evaluate(expression, context=self.context)['body']['result'], + regex) + + def assertEvaluateFailure(self, expression): + self.assertNotIn('result', + self.vscode.request_evaluate(expression, context=self.context)['body']) + + def isExpressionParsedExpected(self): + return self.context != "hover" + + def run_test_evaluate_expressions(self, context=None): + """ + Tests the evaluate expression request at different breakpoints + """ + self.context = context + program = self.getBuildArtifact("a.out") + self.build_and_launch(program) + source = "main.cpp" + self.set_source_breakpoints( + source, + [ + line_number(source, "// breakpoint 1"), + line_number(source, "// breakpoint 2"), + line_number(source, "// breakpoint 3") + ] + ) + self.continue_to_next_stop() + + # Expressions at breakpoint 1, which is in main + self.assertEvaluate("var1", "20") + self.assertEvaluate("var2", "21") + self.assertEvaluate("static_int", "42") + self.assertEvaluate("non_static_int", "43") + self.assertEvaluate("struct1", "my_struct @ 0x.*") + self.assertEvaluate("struct1.foo", "15") + self.assertEvaluate("struct2->foo", "16") + + self.assertEvaluateFailure("var") # local variable of a_function + self.assertEvaluateFailure("my_struct") # type name + self.assertEvaluateFailure("int") # type name + self.assertEvaluateFailure("foo") # member of my_struct + + if self.isExpressionParsedExpected(): + self.assertEvaluate( + "a_function", + "0x.* \(a.out`a_function\(int\) at main.cpp:7\)") + self.assertEvaluate("a_function(1)", "1") + self.assertEvaluate("var2 + struct1.foo", "36") + self.assertEvaluate( + "foo_func", + "0x.* \(a.out`foo_func\(\) at foo.cpp:3\)") + self.assertEvaluate("foo_var", "44") + else: + self.assertEvaluateFailure("a_function") + self.assertEvaluateFailure("a_function(1)") + self.assertEvaluateFailure("var2 + struct1.foo") + self.assertEvaluateFailure("foo_func") + self.assertEvaluateFailure("foo_var") + + # Expressions at breakpoint 2, which is an anonymous block + self.continue_to_next_stop() + self.assertEvaluate("var1", "20") + self.assertEvaluate("var2", "2") # different variable with the same name + self.assertEvaluate("static_int", "42") + self.assertEvaluate("non_static_int", "10") # different variable with the same name + self.assertEvaluate("struct1", "my_struct @ 0x.*") + self.assertEvaluate("struct1.foo", "15") + self.assertEvaluate("struct2->foo", "16") + + if self.isExpressionParsedExpected(): + self.assertEvaluate( + "a_function", + "0x.* \(a.out`a_function\(int\) at main.cpp:7\)") + self.assertEvaluate("a_function(1)", "1") + self.assertEvaluate("var2 + struct1.foo", "17") + self.assertEvaluate( + "foo_func", + "0x.* \(a.out`foo_func\(\) at foo.cpp:3\)") + self.assertEvaluate("foo_var", "44") + else: + self.assertEvaluateFailure("a_function") + self.assertEvaluateFailure("a_function(1)") + self.assertEvaluateFailure("var2 + struct1.foo") + self.assertEvaluateFailure("foo_func") + self.assertEvaluateFailure("foo_var") + + # Expressions at breakpoint 3, which is inside a_function + self.continue_to_next_stop() + self.assertEvaluate("var", "42") + self.assertEvaluate("static_int", "42") + self.assertEvaluate("non_static_int", "43") + + self.assertEvaluateFailure("var1") + self.assertEvaluateFailure("var2") + self.assertEvaluateFailure("struct1") + self.assertEvaluateFailure("struct1.foo") + self.assertEvaluateFailure("struct2->foo") + self.assertEvaluateFailure("var2 + struct1.foo") + + if self.isExpressionParsedExpected(): + self.assertEvaluate( + "a_function", + "0x.* \(a.out`a_function\(int\) at main.cpp:7\)") + self.assertEvaluate("a_function(1)", "1") + self.assertEvaluate("var + 1", "43") + self.assertEvaluate( + "foo_func", + "0x.* \(a.out`foo_func\(\) at foo.cpp:3\)") + self.assertEvaluate("foo_var", "44") + else: + self.assertEvaluateFailure("a_function") + self.assertEvaluateFailure("a_function(1)") + self.assertEvaluateFailure("var + 1") + self.assertEvaluateFailure("foo_func") + self.assertEvaluateFailure("foo_var") + + @skipIfWindows + @skipIfRemote + def test_generic_evaluate_expressions(self): + # Tests context-less expression evaluations + self.run_test_evaluate_expressions() + + @skipIfWindows + @skipIfRemote + def test_repl_evaluate_expressions(self): + # Tests expression evaluations that are triggered from the Debug Console + self.run_test_evaluate_expressions("repl") + + @skipIfWindows + @skipIfRemote + def test_watch_evaluate_expressions(self): + # Tests expression evaluations that are triggered from a watch expression + self.run_test_evaluate_expressions("watch") + + @skipIfWindows + @skipIfRemote + def test_hover_evaluate_expressions(self): + # Tests expression evaluations that are triggered when hovering on the editor + self.run_test_evaluate_expressions("hover") diff --git a/lldb/test/API/tools/lldb-vscode/evaluate/foo.h b/lldb/test/API/tools/lldb-vscode/evaluate/foo.h new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/evaluate/foo.h @@ -0,0 +1,3 @@ +int foo_func(); + +extern int foo_var; diff --git a/lldb/test/API/tools/lldb-vscode/evaluate/foo.cpp b/lldb/test/API/tools/lldb-vscode/evaluate/foo.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/evaluate/foo.cpp @@ -0,0 +1,5 @@ +#include "foo.h" + +int foo_func() { return 43; } + +int foo_var = 44; diff --git a/lldb/test/API/tools/lldb-vscode/evaluate/main.cpp b/lldb/test/API/tools/lldb-vscode/evaluate/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/evaluate/main.cpp @@ -0,0 +1,29 @@ +#include "foo.h" + +static int static_int = 42; + +int non_static_int = 43; + +int a_function(int var) { + return var; // breakpoint 3 +} + +struct my_struct { + int foo; +}; + +int main(int argc, char const *argv[]) { + my_struct struct1 = {15}; + my_struct *struct2 = new my_struct{16}; + int var1 = 20; + int var2 = 21; + int var3 = static_int; // breakpoint 1 + { + int non_static_int = 10; + int var2 = 2; + int var3 = non_static_int; // breakpoint 2 + } + a_function(var3); + foo_func(); + return 0; +} diff --git a/lldb/tools/lldb-vscode/lldb-vscode.cpp b/lldb/tools/lldb-vscode/lldb-vscode.cpp --- a/lldb/tools/lldb-vscode/lldb-vscode.cpp +++ b/lldb/tools/lldb-vscode/lldb-vscode.cpp @@ -1125,6 +1125,7 @@ auto arguments = request.getObject("arguments"); lldb::SBFrame frame = g_vsc.GetLLDBFrame(*arguments); const auto expression = GetString(arguments, "expression"); + llvm::StringRef context = GetString(arguments, "context"); if (!expression.empty() && expression[0] == '`') { auto result = @@ -1133,13 +1134,17 @@ body.try_emplace("variablesReference", (int64_t)0); } else { // Always try to get the answer from the local variables if possible. If - // this fails, then actually evaluate an expression using the expression - // parser. "frame variable" is more reliable than the expression parser in + // this fails, then if the context is not "hover", actually evaluate an + // expression using the expression parser. + // + // "frame variable" is more reliable than the expression parser in // many cases and it is faster. lldb::SBValue value = frame.GetValueForVariablePath( expression.data(), lldb::eDynamicDontRunTarget); - if (value.GetError().Fail()) + + if (value.GetError().Fail() && context != "hover") value = frame.EvaluateExpression(expression.data()); + if (value.GetError().Fail()) { response["success"] = llvm::json::Value(false); // This error object must live until we're done with the pointer returned