Skip to content

Commit d4c1789

Browse files
authored
Make env and source map dictionaries llvm#95137 (llvm#106919)
Fixes llvm#95137
1 parent f07e1c8 commit d4c1789

File tree

10 files changed

+266
-33
lines changed

10 files changed

+266
-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
@@ -58,7 +58,7 @@ def test_core_file(self):
5858
self.assertEqual(self.get_stackFrames(), expected_frames)
5959

6060
@skipIfLLVMTargetMissing("X86")
61-
def test_core_file_source_mapping(self):
61+
def test_core_file_source_mapping_array(self):
6262
"""Test that sourceMap property is correctly applied when loading a core"""
6363
current_dir = os.path.dirname(__file__)
6464
exe_file = os.path.join(current_dir, "linux-x86_64.out")
@@ -70,3 +70,17 @@ def test_core_file_source_mapping(self):
7070
self.attach(exe_file, coreFile=core_file, sourceMap=source_map)
7171

7272
self.assertIn(current_dir, self.get_stackFrames()[0]["source"]["path"])
73+
74+
@skipIfLLVMTargetMissing("X86")
75+
def test_core_file_source_mapping_object(self):
76+
"""Test that sourceMap property is correctly applied when loading a core"""
77+
current_dir = os.path.dirname(__file__)
78+
exe_file = os.path.join(current_dir, "linux-x86_64.out")
79+
core_file = os.path.join(current_dir, "linux-x86_64.core")
80+
81+
self.create_debug_adaptor()
82+
83+
source_map = {"/home/labath/test": current_dir}
84+
self.attach(exe_file, coreFile=core_file, sourceMap=source_map)
85+
86+
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: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,28 @@ def test_runInTerminal(self):
9090
env = self.dap_server.request_evaluate("foo")["body"]["result"]
9191
self.assertIn("bar", env)
9292

93+
def test_runInTerminalWithObjectEnv(self):
94+
if not self.isTestSupported():
95+
return
96+
"""
97+
Tests the "runInTerminal" reverse request. It makes sure that the IDE can
98+
launch the inferior with the correct environment variables using an object.
99+
"""
100+
program = self.getBuildArtifact("a.out")
101+
self.build_and_launch(program, runInTerminal=True, env={"FOO": "BAR"})
102+
103+
self.assertEqual(
104+
len(self.dap_server.reverse_requests),
105+
1,
106+
"make sure we got a reverse request",
107+
)
108+
109+
request = self.dap_server.reverse_requests[0]
110+
request_envs = request["arguments"]["env"]
111+
112+
self.assertIn("FOO", request_envs)
113+
self.assertEqual("BAR", request_envs["FOO"])
114+
93115
@skipIfWindows
94116
@skipIf(archs=no_match(["x86_64"]))
95117
def test_runInTerminalInvalidTarget(self):

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;
@@ -1439,16 +1464,22 @@ CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
14391464
if (!cwd.empty())
14401465
run_in_terminal_args.try_emplace("cwd", cwd);
14411466

1442-
// We need to convert the input list of environments variables into a
1443-
// dictionary
1444-
std::vector<std::string> envs = GetStrings(launch_request_arguments, "env");
1445-
llvm::json::Object environment;
1446-
for (const std::string &env : envs) {
1447-
size_t index = env.find('=');
1448-
environment.try_emplace(env.substr(0, index), env.substr(index + 1));
1467+
auto envs = GetEnvironmentFromArguments(*launch_request_arguments);
1468+
llvm::json::Object env_json;
1469+
for (size_t index = 0, env_count = envs.GetNumValues(); index < env_count;
1470+
index++) {
1471+
llvm::StringRef key = envs.GetNameAtIndex(index);
1472+
llvm::StringRef value = envs.GetValueAtIndex(index);
1473+
1474+
if (key.empty())
1475+
g_dap.SendOutput(OutputType::Stderr,
1476+
"empty environment variable for value: \"" +
1477+
value.str() + '\"');
1478+
else
1479+
env_json.try_emplace(key, value);
14491480
}
14501481
run_in_terminal_args.try_emplace("env",
1451-
llvm::json::Value(std::move(environment)));
1482+
llvm::json::Value(std::move(env_json)));
14521483

14531484
return run_in_terminal_args;
14541485
}

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
}
@@ -2069,9 +2077,8 @@ lldb::SBError LaunchProcess(const llvm::json::Object &request) {
20692077
launch_info.SetArguments(MakeArgv(args).data(), true);
20702078

20712079
// Pass any environment variables along that the user specified.
2072-
auto envs = GetStrings(arguments, "env");
2073-
if (!envs.empty())
2074-
launch_info.SetEnvironmentEntries(MakeArgv(envs).data(), true);
2080+
const auto envs = GetEnvironmentFromArguments(*arguments);
2081+
launch_info.SetEnvironment(envs, true);
20752082

20762083
auto flags = launch_info.GetLaunchFlags();
20772084

0 commit comments

Comments
 (0)