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 | |
15 | namespace 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 | // } |
141 | void 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 | |