| 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 | |