Skip to content

Commit baba5cd

Browse files
da-viperadrian-prantl
authored andcommitted
Make env and source map dictionaries llvm#95137 (llvm#106919)
Fixes llvm#95137 (cherry picked from commit d4c1789) Conflicts: lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py
1 parent 537251e commit baba5cd

File tree

10 files changed

+267
-33
lines changed

10 files changed

+267
-33
lines changed

lldb/test/API/tools/lldb-dap/coreFile/TestDAP_coreFile.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def test_core_file(self):
6060

6161
@skipIfWindows
6262
@skipIfLLVMTargetMissing("X86")
63-
def test_core_file_source_mapping(self):
63+
def test_core_file_source_mapping_array(self):
6464
"""Test that sourceMap property is correctly applied when loading a core"""
6565
current_dir = os.path.dirname(__file__)
6666
exe_file = os.path.join(current_dir, "linux-x86_64.out")
@@ -72,3 +72,17 @@ def test_core_file_source_mapping(self):
7272
self.attach(exe_file, coreFile=core_file, sourceMap=source_map)
7373

7474
self.assertIn(current_dir, self.get_stackFrames()[0]["source"]["path"])
75+
76+
@skipIfLLVMTargetMissing("X86")
77+
def test_core_file_source_mapping_object(self):
78+
"""Test that sourceMap property is correctly applied when loading a core"""
79+
current_dir = os.path.dirname(__file__)
80+
exe_file = os.path.join(current_dir, "linux-x86_64.out")
81+
core_file = os.path.join(current_dir, "linux-x86_64.core")
82+
83+
self.create_debug_adaptor()
84+
85+
source_map = {"/home/labath/test": current_dir}
86+
self.attach(exe_file, coreFile=core_file, sourceMap=source_map)
87+
88+
self.assertIn(current_dir, self.get_stackFrames()[0]["source"]["path"])

lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,12 +224,47 @@ def test_args(self):
224224
'arg[%i] "%s" not in "%s"' % (i + 1, quoted_arg, lines[i]),
225225
)
226226

227-
def test_environment(self):
227+
def test_environment_with_object(self):
228+
"""
229+
Tests launch of a simple program with environment variables
230+
"""
231+
program = self.getBuildArtifact("a.out")
232+
env = {
233+
"NO_VALUE": "",
234+
"WITH_VALUE": "BAR",
235+
"EMPTY_VALUE": "",
236+
"SPACE": "Hello World",
237+
}
238+
239+
self.build_and_launch(program, env=env)
240+
self.continue_to_exit()
241+
242+
# Now get the STDOUT and verify our arguments got passed correctly
243+
output = self.get_stdout()
244+
self.assertTrue(output and len(output) > 0, "expect program output")
245+
lines = output.splitlines()
246+
# Skip the all arguments so we have only environment vars left
247+
while len(lines) and lines[0].startswith("arg["):
248+
lines.pop(0)
249+
# Make sure each environment variable in "env" is actually set in the
250+
# program environment that was printed to STDOUT
251+
for var in env:
252+
found = False
253+
for program_var in lines:
254+
if var in program_var:
255+
found = True
256+
break
257+
self.assertTrue(
258+
found, '"%s" must exist in program environment (%s)' % (var, lines)
259+
)
260+
261+
def test_environment_with_array(self):
228262
"""
229263
Tests launch of a simple program with environment variables
230264
"""
231265
program = self.getBuildArtifact("a.out")
232266
env = ["NO_VALUE", "WITH_VALUE=BAR", "EMPTY_VALUE=", "SPACE=Hello World"]
267+
233268
self.build_and_launch(program, env=env)
234269
self.continue_to_exit()
235270

lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,29 @@ def test_runInTerminal(self):
9191
env = self.dap_server.request_evaluate("foo")["body"]["result"]
9292
self.assertIn("bar", env)
9393

94+
95+
def test_runInTerminalWithObjectEnv(self):
96+
if not self.isTestSupported():
97+
return
98+
"""
99+
Tests the "runInTerminal" reverse request. It makes sure that the IDE can
100+
launch the inferior with the correct environment variables using an object.
101+
"""
102+
program = self.getBuildArtifact("a.out")
103+
self.build_and_launch(program, runInTerminal=True, env={"FOO": "BAR"})
104+
105+
self.assertEqual(
106+
len(self.dap_server.reverse_requests),
107+
1,
108+
"make sure we got a reverse request",
109+
)
110+
111+
request = self.dap_server.reverse_requests[0]
112+
request_envs = request["arguments"]["env"]
113+
114+
self.assertIn("FOO", request_envs)
115+
self.assertEqual("BAR", request_envs["FOO"])
116+
94117
@skipIfLinux # FIXME: doesn't seem to work on Ubuntu 16.04.
95118
@skipIfWindows
96119
@skipIf(archs=no_match(["x86_64"]))

lldb/tools/lldb-dap/JSONUtils.cpp

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,31 @@ std::vector<std::string> GetStrings(const llvm::json::Object *obj,
152152
return strs;
153153
}
154154

155+
std::unordered_map<std::string, std::string>
156+
GetStringMap(const llvm::json::Object &obj, llvm::StringRef key) {
157+
std::unordered_map<std::string, std::string> strs;
158+
const auto *const json_object = obj.getObject(key);
159+
if (!json_object)
160+
return strs;
161+
162+
for (const auto &[key, value] : *json_object) {
163+
switch (value.kind()) {
164+
case llvm::json::Value::String:
165+
strs.emplace(key.str(), value.getAsString()->str());
166+
break;
167+
case llvm::json::Value::Number:
168+
case llvm::json::Value::Boolean:
169+
strs.emplace(key.str(), llvm::to_string(value));
170+
break;
171+
case llvm::json::Value::Null:
172+
case llvm::json::Value::Object:
173+
case llvm::json::Value::Array:
174+
break;
175+
}
176+
}
177+
return strs;
178+
}
179+
155180
static bool IsClassStructOrUnionType(lldb::SBType t) {
156181
return (t.GetTypeClass() & (lldb::eTypeClassUnion | lldb::eTypeClassStruct |
157182
lldb::eTypeClassArray)) != 0;
@@ -1471,16 +1496,22 @@ CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
14711496
if (!cwd.empty())
14721497
run_in_terminal_args.try_emplace("cwd", cwd);
14731498

1474-
// We need to convert the input list of environments variables into a
1475-
// dictionary
1476-
std::vector<std::string> envs = GetStrings(launch_request_arguments, "env");
1477-
llvm::json::Object environment;
1478-
for (const std::string &env : envs) {
1479-
size_t index = env.find('=');
1480-
environment.try_emplace(env.substr(0, index), env.substr(index + 1));
1499+
auto envs = GetEnvironmentFromArguments(*launch_request_arguments);
1500+
llvm::json::Object env_json;
1501+
for (size_t index = 0, env_count = envs.GetNumValues(); index < env_count;
1502+
index++) {
1503+
llvm::StringRef key = envs.GetNameAtIndex(index);
1504+
llvm::StringRef value = envs.GetValueAtIndex(index);
1505+
1506+
if (key.empty())
1507+
g_dap.SendOutput(OutputType::Stderr,
1508+
"empty environment variable for value: \"" +
1509+
value.str() + '\"');
1510+
else
1511+
env_json.try_emplace(key, value);
14811512
}
14821513
run_in_terminal_args.try_emplace("env",
1483-
llvm::json::Value(std::move(environment)));
1514+
llvm::json::Value(std::move(env_json)));
14841515

14851516
return run_in_terminal_args;
14861517
}

lldb/tools/lldb-dap/JSONUtils.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "llvm/Support/JSON.h"
1717
#include <cstdint>
1818
#include <optional>
19+
#include <unordered_map>
1920

2021
namespace lldb_dap {
2122

@@ -159,6 +160,27 @@ DecodeMemoryReference(llvm::StringRef memoryReference);
159160
std::vector<std::string> GetStrings(const llvm::json::Object *obj,
160161
llvm::StringRef key);
161162

163+
/// Extract an object of key value strings for the specified key from an object.
164+
///
165+
/// String values in the object will be extracted without any quotes
166+
/// around them. Numbers and Booleans will be converted into
167+
/// strings. Any NULL, array or objects values in the array will be
168+
/// ignored.
169+
///
170+
/// \param[in] obj
171+
/// A JSON object that we will attempt to extract the array from
172+
///
173+
/// \param[in] key
174+
/// The key to use when extracting the value
175+
///
176+
/// \return
177+
/// An object of key value strings for the specified \a key, or
178+
/// \a fail_value if there is no key that matches or if the
179+
/// value is not an object or key and values in the object are not
180+
/// strings, numbers or booleans.
181+
std::unordered_map<std::string, std::string>
182+
GetStringMap(const llvm::json::Object &obj, llvm::StringRef key);
183+
162184
/// Fill a response object given the request object.
163185
///
164186
/// The \a response object will get its "type" set to "response",

lldb/tools/lldb-dap/LLDBUtils.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,29 @@ int64_t MakeDAPFrameID(lldb::SBFrame &frame) {
135135
frame.GetFrameID();
136136
}
137137

138+
lldb::SBEnvironment
139+
GetEnvironmentFromArguments(const llvm::json::Object &arguments) {
140+
lldb::SBEnvironment envs{};
141+
constexpr llvm::StringRef env_key = "env";
142+
const llvm::json::Value *raw_json_env = arguments.get(env_key);
143+
144+
if (!raw_json_env)
145+
return envs;
146+
147+
if (raw_json_env->kind() == llvm::json::Value::Object) {
148+
auto env_map = GetStringMap(arguments, env_key);
149+
for (const auto &[key, value] : env_map)
150+
envs.Set(key.c_str(), value.c_str(), true);
151+
152+
} else if (raw_json_env->kind() == llvm::json::Value::Array) {
153+
const auto envs_strings = GetStrings(&arguments, env_key);
154+
lldb::SBStringList entries{};
155+
for (const auto &env : envs_strings)
156+
entries.AppendString(env.c_str());
157+
158+
envs.SetEntries(entries, true);
159+
}
160+
return envs;
161+
}
162+
138163
} // namespace lldb_dap

lldb/tools/lldb-dap/LLDBUtils.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010
#define LLDB_TOOLS_LLDB_DAP_LLDBUTILS_H
1111

1212
#include "DAPForward.h"
13+
#include "lldb/API/SBEnvironment.h"
1314
#include "llvm/ADT/ArrayRef.h"
1415
#include "llvm/ADT/StringRef.h"
16+
#include "llvm/Support/JSON.h"
1517
#include "llvm/Support/raw_ostream.h"
1618
#include <string>
17-
#include <vector>
1819

1920
namespace lldb_dap {
2021

@@ -135,6 +136,17 @@ uint32_t GetLLDBThreadIndexID(uint64_t dap_frame_id);
135136
/// The LLDB frame index ID.
136137
uint32_t GetLLDBFrameID(uint64_t dap_frame_id);
137138

139+
/// Gets all the environment variables from the json object depending on if the
140+
/// kind is an object or an array.
141+
///
142+
/// \param[in] arguments
143+
/// The json object with the launch options
144+
///
145+
/// \return
146+
/// The environment variables stored in the env key
147+
lldb::SBEnvironment
148+
GetEnvironmentFromArguments(const llvm::json::Object &arguments);
149+
138150
} // namespace lldb_dap
139151

140152
#endif

lldb/tools/lldb-dap/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ adds `FOO=1` and `bar` to the environment:
3636
"name": "Debug",
3737
"program": "/tmp/a.out",
3838
"args": [ "one", "two", "three" ],
39-
"env": [ "FOO=1", "BAR" ],
39+
"env": {
40+
"FOO": "1"
41+
"BAR": ""
42+
}
4043
}
4144
```
4245

lldb/tools/lldb-dap/lldb-dap.cpp

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#include <thread>
5454
#include <vector>
5555

56+
#include "lldb/API/SBEnvironment.h"
5657
#include "lldb/API/SBStream.h"
5758
#include "lldb/Host/Config.h"
5859
#include "llvm/ADT/ArrayRef.h"
@@ -610,25 +611,32 @@ void SetSourceMapFromArguments(const llvm::json::Object &arguments) {
610611
std::string sourceMapCommand;
611612
llvm::raw_string_ostream strm(sourceMapCommand);
612613
strm << "settings set target.source-map ";
613-
auto sourcePath = GetString(arguments, "sourcePath");
614+
const auto sourcePath = GetString(arguments, "sourcePath");
614615

615616
// sourceMap is the new, more general form of sourcePath and overrides it.
616-
auto sourceMap = arguments.getArray("sourceMap");
617-
if (sourceMap) {
618-
for (const auto &value : *sourceMap) {
619-
auto mapping = value.getAsArray();
617+
constexpr llvm::StringRef sourceMapKey = "sourceMap";
618+
619+
if (const auto *sourceMapArray = arguments.getArray(sourceMapKey)) {
620+
for (const auto &value : *sourceMapArray) {
621+
const auto *mapping = value.getAsArray();
620622
if (mapping == nullptr || mapping->size() != 2 ||
621623
(*mapping)[0].kind() != llvm::json::Value::String ||
622624
(*mapping)[1].kind() != llvm::json::Value::String) {
623625
g_dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
624626
return;
625627
}
626-
auto mapFrom = GetAsString((*mapping)[0]);
627-
auto mapTo = GetAsString((*mapping)[1]);
628+
const auto mapFrom = GetAsString((*mapping)[0]);
629+
const auto mapTo = GetAsString((*mapping)[1]);
628630
strm << "\"" << mapFrom << "\" \"" << mapTo << "\" ";
629631
}
632+
} else if (const auto *sourceMapObj = arguments.getObject(sourceMapKey)) {
633+
for (const auto &[key, value] : *sourceMapObj) {
634+
if (value.kind() == llvm::json::Value::String) {
635+
strm << "\"" << key.str() << "\" \"" << GetAsString(value) << "\" ";
636+
}
637+
}
630638
} else {
631-
if (ObjectContainsKey(arguments, "sourceMap")) {
639+
if (ObjectContainsKey(arguments, sourceMapKey)) {
632640
g_dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
633641
return;
634642
}
@@ -2086,9 +2094,8 @@ lldb::SBError LaunchProcess(const llvm::json::Object &request) {
20862094
launch_info.SetArguments(MakeArgv(args).data(), true);
20872095

20882096
// Pass any environment variables along that the user specified.
2089-
auto envs = GetStrings(arguments, "env");
2090-
if (!envs.empty())
2091-
launch_info.SetEnvironmentEntries(MakeArgv(envs).data(), true);
2097+
const auto envs = GetEnvironmentFromArguments(*arguments);
2098+
launch_info.SetEnvironment(envs, true);
20922099

20932100
auto flags = launch_info.GetLaunchFlags();
20942101

0 commit comments

Comments
 (0)