blob: 5ce133c33b7e1b3002f23ab6e1b670005c7349d3 [file] [log] [blame]
//===-- EvaluateRequestHandler.cpp ----------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "DAP.h"
#include "EventHelper.h"
#include "JSONUtils.h"
#include "LLDBUtils.h"
#include "RequestHandler.h"
namespace lldb_dap {
// "EvaluateRequest": {
// "allOf": [ { "$ref": "#/definitions/Request" }, {
// "type": "object",
// "description": "Evaluate request; value of command field is 'evaluate'.
// Evaluates the given expression in the context of the
// top most stack frame. The expression has access to any
// variables and arguments that are in scope.",
// "properties": {
// "command": {
// "type": "string",
// "enum": [ "evaluate" ]
// },
// "arguments": {
// "$ref": "#/definitions/EvaluateArguments"
// }
// },
// "required": [ "command", "arguments" ]
// }]
// },
// "EvaluateArguments": {
// "type": "object",
// "description": "Arguments for 'evaluate' request.",
// "properties": {
// "expression": {
// "type": "string",
// "description": "The expression to evaluate."
// },
// "frameId": {
// "type": "integer",
// "description": "Evaluate the expression in the scope of this stack
// frame. If not specified, the expression is evaluated
// in the global scope."
// },
// "context": {
// "type": "string",
// "_enum": [ "watch", "repl", "hover" ],
// "enumDescriptions": [
// "evaluate is run in a watch.",
// "evaluate is run from REPL console.",
// "evaluate is run from a data hover."
// ],
// "description": "The context in which the evaluate request is run."
// },
// "format": {
// "$ref": "#/definitions/ValueFormat",
// "description": "Specifies details on how to format the Evaluate
// result."
// }
// },
// "required": [ "expression" ]
// },
// "EvaluateResponse": {
// "allOf": [ { "$ref": "#/definitions/Response" }, {
// "type": "object",
// "description": "Response to 'evaluate' request.",
// "properties": {
// "body": {
// "type": "object",
// "properties": {
// "result": {
// "type": "string",
// "description": "The result of the evaluate request."
// },
// "type": {
// "type": "string",
// "description": "The optional type of the evaluate result."
// },
// "presentationHint": {
// "$ref": "#/definitions/VariablePresentationHint",
// "description": "Properties of a evaluate result that can be
// used to determine how to render the result in
// the UI."
// },
// "variablesReference": {
// "type": "number",
// "description": "If variablesReference is > 0, the evaluate
// result is structured and its children can be
// retrieved by passing variablesReference to the
// VariablesRequest."
// },
// "namedVariables": {
// "type": "number",
// "description": "The number of named child variables. The
// client can use this optional information to
// present the variables in a paged UI and fetch
// them in chunks."
// },
// "indexedVariables": {
// "type": "number",
// "description": "The number of indexed child variables. The
// client can use this optional information to
// present the variables in a paged UI and fetch
// them in chunks."
// },
// "valueLocationReference": {
// "type": "integer",
// "description": "A reference that allows the client to request
// the location where the returned value is
// declared. For example, if a function pointer is
// returned, the adapter may be able to look up the
// function's location. This should be present only
// if the adapter is likely to be able to resolve
// the location.\n\nThis reference shares the same
// lifetime as the `variablesReference`. See
// 'Lifetime of Object References' in the
// Overview section for details."
// }
// "memoryReference": {
// "type": "string",
// "description": "A memory reference to a location appropriate
// for this result. For pointer type eval
// results, this is generally a reference to the
// memory address contained in the pointer. This
// attribute may be returned by a debug adapter
// if corresponding capability
// `supportsMemoryReferences` is true."
// },
// },
// "required": [ "result", "variablesReference" ]
// }
// },
// "required": [ "body" ]
// }]
// }
void EvaluateRequestHandler::operator()(
const llvm::json::Object &request) const {
llvm::json::Object response;
FillResponse(request, response);
llvm::json::Object body;
const auto *arguments = request.getObject("arguments");
lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
std::string expression =
GetString(arguments, "expression").value_or("").str();
const llvm::StringRef context = GetString(arguments, "context").value_or("");
bool repeat_last_command =
expression.empty() && dap.last_nonempty_var_expression.empty();
if (context == "repl" &&
(repeat_last_command ||
(!expression.empty() &&
dap.DetectReplMode(frame, expression, false) == ReplMode::Command))) {
// Since the current expression is not for a variable, clear the
// last_nonempty_var_expression field.
dap.last_nonempty_var_expression.clear();
// If we're evaluating a command relative to the current frame, set the
// focus_tid to the current frame for any thread related events.
if (frame.IsValid()) {
dap.focus_tid = frame.GetThread().GetThreadID();
}
bool required_command_failed = false;
std::string result = RunLLDBCommands(
dap.debugger, llvm::StringRef(), {expression}, required_command_failed,
/*parse_command_directives=*/false, /*echo_commands=*/false);
EmplaceSafeString(body, "result", result);
body.try_emplace("variablesReference", (int64_t)0);
} else {
if (context == "repl") {
// If the expression is empty and the last expression was for a
// variable, set the expression to the previous expression (repeat the
// evaluation); otherwise save the current non-empty expression for the
// next (possibly empty) variable expression.
if (expression.empty())
expression = dap.last_nonempty_var_expression;
else
dap.last_nonempty_var_expression = expression;
}
// Always try to get the answer from the local variables if possible. If
// 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);
// Freeze dry the value in case users expand it later in the debug console
if (value.GetError().Success() && context == "repl")
value = value.Persist();
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
// by GetCString().
lldb::SBError error = value.GetError();
const char *error_cstr = error.GetCString();
if (error_cstr && error_cstr[0])
EmplaceSafeString(response, "message", std::string(error_cstr));
else
EmplaceSafeString(response, "message", "evaluate failed");
} else {
VariableDescription desc(value,
dap.configuration.enableAutoVariableSummaries);
EmplaceSafeString(body, "result", desc.GetResult(context));
EmplaceSafeString(body, "type", desc.display_type_name);
int64_t var_ref = 0;
if (value.MightHaveChildren() || ValuePointsToCode(value))
var_ref = dap.variables.InsertVariable(
value, /*is_permanent=*/context == "repl");
if (value.MightHaveChildren())
body.try_emplace("variablesReference", var_ref);
else
body.try_emplace("variablesReference", (int64_t)0);
if (lldb::addr_t addr = value.GetLoadAddress();
addr != LLDB_INVALID_ADDRESS)
body.try_emplace("memoryReference", EncodeMemoryReference(addr));
if (ValuePointsToCode(value))
body.try_emplace("valueLocationReference", var_ref);
}
}
response.try_emplace("body", std::move(body));
dap.SendJSON(llvm::json::Value(std::move(response)));
}
} // namespace lldb_dap