1//===-- EvaluateRequestHandler.cpp ----------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "DAP.h"
10#include "EventHelper.h"
11#include "JSONUtils.h"
12#include "LLDBUtils.h"
13#include "RequestHandler.h"
14
15namespace lldb_dap {
16
17// "EvaluateRequest": {
18// "allOf": [ { "$ref": "#/definitions/Request" }, {
19// "type": "object",
20// "description": "Evaluate request; value of command field is 'evaluate'.
21// Evaluates the given expression in the context of the
22// top most stack frame. The expression has access to any
23// variables and arguments that are in scope.",
24// "properties": {
25// "command": {
26// "type": "string",
27// "enum": [ "evaluate" ]
28// },
29// "arguments": {
30// "$ref": "#/definitions/EvaluateArguments"
31// }
32// },
33// "required": [ "command", "arguments" ]
34// }]
35// },
36// "EvaluateArguments": {
37// "type": "object",
38// "description": "Arguments for 'evaluate' request.",
39// "properties": {
40// "expression": {
41// "type": "string",
42// "description": "The expression to evaluate."
43// },
44// "frameId": {
45// "type": "integer",
46// "description": "Evaluate the expression in the scope of this stack
47// frame. If not specified, the expression is evaluated
48// in the global scope."
49// },
50// "context": {
51// "type": "string",
52// "_enum": [ "watch", "repl", "hover" ],
53// "enumDescriptions": [
54// "evaluate is run in a watch.",
55// "evaluate is run from REPL console.",
56// "evaluate is run from a data hover."
57// ],
58// "description": "The context in which the evaluate request is run."
59// },
60// "format": {
61// "$ref": "#/definitions/ValueFormat",
62// "description": "Specifies details on how to format the Evaluate
63// result."
64// }
65// },
66// "required": [ "expression" ]
67// },
68// "EvaluateResponse": {
69// "allOf": [ { "$ref": "#/definitions/Response" }, {
70// "type": "object",
71// "description": "Response to 'evaluate' request.",
72// "properties": {
73// "body": {
74// "type": "object",
75// "properties": {
76// "result": {
77// "type": "string",
78// "description": "The result of the evaluate request."
79// },
80// "type": {
81// "type": "string",
82// "description": "The optional type of the evaluate result."
83// },
84// "presentationHint": {
85// "$ref": "#/definitions/VariablePresentationHint",
86// "description": "Properties of a evaluate result that can be
87// used to determine how to render the result in
88// the UI."
89// },
90// "variablesReference": {
91// "type": "number",
92// "description": "If variablesReference is > 0, the evaluate
93// result is structured and its children can be
94// retrieved by passing variablesReference to the
95// VariablesRequest."
96// },
97// "namedVariables": {
98// "type": "number",
99// "description": "The number of named child variables. The
100// client can use this optional information to
101// present the variables in a paged UI and fetch
102// them in chunks."
103// },
104// "indexedVariables": {
105// "type": "number",
106// "description": "The number of indexed child variables. The
107// client can use this optional information to
108// present the variables in a paged UI and fetch
109// them in chunks."
110// },
111// "valueLocationReference": {
112// "type": "integer",
113// "description": "A reference that allows the client to request
114// the location where the returned value is
115// declared. For example, if a function pointer is
116// returned, the adapter may be able to look up the
117// function's location. This should be present only
118// if the adapter is likely to be able to resolve
119// the location.\n\nThis reference shares the same
120// lifetime as the `variablesReference`. See
121// 'Lifetime of Object References' in the
122// Overview section for details."
123// }
124// "memoryReference": {
125// "type": "string",
126// "description": "A memory reference to a location appropriate
127// for this result. For pointer type eval
128// results, this is generally a reference to the
129// memory address contained in the pointer. This
130// attribute may be returned by a debug adapter
131// if corresponding capability
132// `supportsMemoryReferences` is true."
133// },
134// },
135// "required": [ "result", "variablesReference" ]
136// }
137// },
138// "required": [ "body" ]
139// }]
140// }
141void EvaluateRequestHandler::operator()(
142 const llvm::json::Object &request) const {
143 llvm::json::Object response;
144 FillResponse(request, response);
145 llvm::json::Object body;
146 const auto *arguments = request.getObject(K: "arguments");
147 lldb::SBFrame frame = dap.GetLLDBFrame(arguments: *arguments);
148 std::string expression =
149 GetString(obj: arguments, key: "expression").value_or(u: "").str();
150 const llvm::StringRef context = GetString(obj: arguments, key: "context").value_or(u: "");
151 bool repeat_last_command =
152 expression.empty() && dap.last_nonempty_var_expression.empty();
153
154 if (context == "repl" &&
155 (repeat_last_command ||
156 (!expression.empty() &&
157 dap.DetectReplMode(frame, expression, partial_expression: false) == ReplMode::Command))) {
158 // Since the current expression is not for a variable, clear the
159 // last_nonempty_var_expression field.
160 dap.last_nonempty_var_expression.clear();
161 // If we're evaluating a command relative to the current frame, set the
162 // focus_tid to the current frame for any thread related events.
163 if (frame.IsValid()) {
164 dap.focus_tid = frame.GetThread().GetThreadID();
165 }
166
167 bool required_command_failed = false;
168 std::string result = RunLLDBCommands(
169 debugger&: dap.debugger, prefix: llvm::StringRef(), commands: {expression}, required_command_failed,
170 /*parse_command_directives=*/false, /*echo_commands=*/false);
171
172 EmplaceSafeString(obj&: body, key: "result", str: result);
173 body.try_emplace(K: "variablesReference", Args: (int64_t)0);
174 } else {
175 if (context == "repl") {
176 // If the expression is empty and the last expression was for a
177 // variable, set the expression to the previous expression (repeat the
178 // evaluation); otherwise save the current non-empty expression for the
179 // next (possibly empty) variable expression.
180 if (expression.empty())
181 expression = dap.last_nonempty_var_expression;
182 else
183 dap.last_nonempty_var_expression = expression;
184 }
185 // Always try to get the answer from the local variables if possible. If
186 // this fails, then if the context is not "hover", actually evaluate an
187 // expression using the expression parser.
188 //
189 // "frame variable" is more reliable than the expression parser in
190 // many cases and it is faster.
191 lldb::SBValue value = frame.GetValueForVariablePath(
192 var_expr_cstr: expression.data(), use_dynamic: lldb::eDynamicDontRunTarget);
193
194 // Freeze dry the value in case users expand it later in the debug console
195 if (value.GetError().Success() && context == "repl")
196 value = value.Persist();
197
198 if (value.GetError().Fail() && context != "hover")
199 value = frame.EvaluateExpression(expr: expression.data());
200
201 if (value.GetError().Fail()) {
202 response["success"] = llvm::json::Value(false);
203 // This error object must live until we're done with the pointer returned
204 // by GetCString().
205 lldb::SBError error = value.GetError();
206 const char *error_cstr = error.GetCString();
207 if (error_cstr && error_cstr[0])
208 EmplaceSafeString(obj&: response, key: "message", str: error_cstr);
209 else
210 EmplaceSafeString(obj&: response, key: "message", str: "evaluate failed");
211 } else {
212 VariableDescription desc(value,
213 dap.configuration.enableAutoVariableSummaries);
214 EmplaceSafeString(obj&: body, key: "result", str: desc.GetResult(context));
215 EmplaceSafeString(obj&: body, key: "type", str: desc.display_type_name);
216 int64_t var_ref = 0;
217 if (value.MightHaveChildren() || ValuePointsToCode(v: value))
218 var_ref = dap.variables.InsertVariable(
219 variable: value, /*is_permanent=*/context == "repl");
220 if (value.MightHaveChildren())
221 body.try_emplace(K: "variablesReference", Args&: var_ref);
222 else
223 body.try_emplace(K: "variablesReference", Args: (int64_t)0);
224 if (lldb::addr_t addr = value.GetLoadAddress();
225 addr != LLDB_INVALID_ADDRESS)
226 body.try_emplace(K: "memoryReference", Args: EncodeMemoryReference(addr));
227 if (ValuePointsToCode(v: value))
228 body.try_emplace(K: "valueLocationReference", Args&: var_ref);
229 }
230 }
231 response.try_emplace(K: "body", Args: std::move(body));
232 dap.SendJSON(json: llvm::json::Value(std::move(response)));
233}
234} // namespace lldb_dap
235

source code of lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp