1//===-- OptionArgParser.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 "lldb/Interpreter/OptionArgParser.h"
10#include "lldb/DataFormatters/FormatManager.h"
11#include "lldb/Target/ABI.h"
12#include "lldb/Target/Target.h"
13#include "lldb/Utility/Status.h"
14#include "lldb/Utility/StreamString.h"
15
16using namespace lldb_private;
17using namespace lldb;
18
19bool OptionArgParser::ToBoolean(llvm::StringRef ref, bool fail_value,
20                                bool *success_ptr) {
21  if (success_ptr)
22    *success_ptr = true;
23  ref = ref.trim();
24  if (ref.equals_insensitive("false") || ref.equals_insensitive("off") ||
25      ref.equals_insensitive("no") || ref.equals_insensitive("0")) {
26    return false;
27  } else if (ref.equals_insensitive("true") || ref.equals_insensitive("on") ||
28             ref.equals_insensitive("yes") || ref.equals_insensitive("1")) {
29    return true;
30  }
31  if (success_ptr)
32    *success_ptr = false;
33  return fail_value;
34}
35
36char OptionArgParser::ToChar(llvm::StringRef s, char fail_value,
37                             bool *success_ptr) {
38  if (success_ptr)
39    *success_ptr = false;
40  if (s.size() != 1)
41    return fail_value;
42
43  if (success_ptr)
44    *success_ptr = true;
45  return s[0];
46}
47
48int64_t OptionArgParser::ToOptionEnum(llvm::StringRef s,
49                                      const OptionEnumValues &enum_values,
50                                      int32_t fail_value, Status &error) {
51  error.Clear();
52  if (enum_values.empty()) {
53    error.SetErrorString("invalid enumeration argument");
54    return fail_value;
55  }
56
57  if (s.empty()) {
58    error.SetErrorString("empty enumeration string");
59    return fail_value;
60  }
61
62  for (const auto &enum_value : enum_values) {
63    llvm::StringRef this_enum(enum_value.string_value);
64    if (this_enum.starts_with(s))
65      return enum_value.value;
66  }
67
68  StreamString strm;
69  strm.PutCString("invalid enumeration value, valid values are: ");
70  bool is_first = true;
71  for (const auto &enum_value : enum_values) {
72    strm.Printf("%s\"%s\"",
73        is_first ? is_first = false,"" : ", ", enum_value.string_value);
74  }
75  error.SetErrorString(strm.GetString());
76  return fail_value;
77}
78
79Status OptionArgParser::ToFormat(const char *s, lldb::Format &format,
80                                 size_t *byte_size_ptr) {
81  format = eFormatInvalid;
82  Status error;
83
84  if (s && s[0]) {
85    if (byte_size_ptr) {
86      if (isdigit(s[0])) {
87        char *format_char = nullptr;
88        unsigned long byte_size = ::strtoul(s, &format_char, 0);
89        if (byte_size != ULONG_MAX)
90          *byte_size_ptr = byte_size;
91        s = format_char;
92      } else
93        *byte_size_ptr = 0;
94    }
95
96    const bool partial_match_ok = true;
97    if (!FormatManager::GetFormatFromCString(s, partial_match_ok, format)) {
98      StreamString error_strm;
99      error_strm.Printf(
100          "Invalid format character or name '%s'. Valid values are:\n", s);
101      for (Format f = eFormatDefault; f < kNumFormats; f = Format(f + 1)) {
102        char format_char = FormatManager::GetFormatAsFormatChar(f);
103        if (format_char)
104          error_strm.Printf("'%c' or ", format_char);
105
106        error_strm.Printf("\"%s\"", FormatManager::GetFormatAsCString(f));
107        error_strm.EOL();
108      }
109
110      if (byte_size_ptr)
111        error_strm.PutCString(
112            "An optional byte size can precede the format character.\n");
113      error.SetErrorString(error_strm.GetString());
114    }
115
116    if (error.Fail())
117      return error;
118  } else {
119    error.SetErrorStringWithFormat("%s option string", s ? "empty" : "invalid");
120  }
121  return error;
122}
123
124lldb::ScriptLanguage OptionArgParser::ToScriptLanguage(
125    llvm::StringRef s, lldb::ScriptLanguage fail_value, bool *success_ptr) {
126  if (success_ptr)
127    *success_ptr = true;
128
129  if (s.equals_insensitive("python"))
130    return eScriptLanguagePython;
131  if (s.equals_insensitive("lua"))
132    return eScriptLanguageLua;
133  if (s.equals_insensitive("default"))
134    return eScriptLanguageDefault;
135  if (s.equals_insensitive("none"))
136    return eScriptLanguageNone;
137
138  if (success_ptr)
139    *success_ptr = false;
140  return fail_value;
141}
142
143lldb::addr_t OptionArgParser::ToRawAddress(const ExecutionContext *exe_ctx,
144                                           llvm::StringRef s,
145                                           lldb::addr_t fail_value,
146                                           Status *error_ptr) {
147  std::optional<lldb::addr_t> maybe_addr = DoToAddress(exe_ctx, s, error_ptr);
148  return maybe_addr ? *maybe_addr : fail_value;
149}
150
151lldb::addr_t OptionArgParser::ToAddress(const ExecutionContext *exe_ctx,
152                                        llvm::StringRef s,
153                                        lldb::addr_t fail_value,
154                                        Status *error_ptr) {
155  std::optional<lldb::addr_t> maybe_addr = DoToAddress(exe_ctx, s, error_ptr);
156  if (!maybe_addr)
157    return fail_value;
158
159  lldb::addr_t addr = *maybe_addr;
160
161  if (Process *process = exe_ctx->GetProcessPtr())
162    if (ABISP abi_sp = process->GetABI())
163      addr = abi_sp->FixCodeAddress(addr);
164
165  return addr;
166}
167
168std::optional<lldb::addr_t>
169OptionArgParser::DoToAddress(const ExecutionContext *exe_ctx, llvm::StringRef s,
170                             Status *error_ptr) {
171  if (s.empty()) {
172    if (error_ptr)
173      error_ptr->SetErrorStringWithFormat("invalid address expression \"%s\"",
174                                          s.str().c_str());
175    return {};
176  }
177
178  llvm::StringRef sref = s;
179
180  lldb::addr_t addr = LLDB_INVALID_ADDRESS;
181  if (!s.getAsInteger(0, addr)) {
182    if (error_ptr)
183      error_ptr->Clear();
184
185    return addr;
186  }
187
188  // Try base 16 with no prefix...
189  if (!s.getAsInteger(16, addr)) {
190    if (error_ptr)
191      error_ptr->Clear();
192    return addr;
193  }
194
195  Target *target = nullptr;
196  if (!exe_ctx || !(target = exe_ctx->GetTargetPtr())) {
197    if (error_ptr)
198      error_ptr->SetErrorStringWithFormat("invalid address expression \"%s\"",
199                                          s.str().c_str());
200    return {};
201  }
202
203  lldb::ValueObjectSP valobj_sp;
204  EvaluateExpressionOptions options;
205  options.SetCoerceToId(false);
206  options.SetUnwindOnError(true);
207  options.SetKeepInMemory(false);
208  options.SetTryAllThreads(true);
209
210  ExpressionResults expr_result =
211      target->EvaluateExpression(s, exe_ctx->GetFramePtr(), valobj_sp, options);
212
213  bool success = false;
214  if (expr_result == eExpressionCompleted) {
215    if (valobj_sp)
216      valobj_sp = valobj_sp->GetQualifiedRepresentationIfAvailable(
217          valobj_sp->GetDynamicValueType(), true);
218    // Get the address to watch.
219    if (valobj_sp)
220      addr = valobj_sp->GetValueAsUnsigned(0, &success);
221    if (success) {
222      if (error_ptr)
223        error_ptr->Clear();
224      return addr;
225    }
226    if (error_ptr)
227      error_ptr->SetErrorStringWithFormat(
228          "address expression \"%s\" resulted in a value whose type "
229          "can't be converted to an address: %s",
230          s.str().c_str(), valobj_sp->GetTypeName().GetCString());
231    return {};
232  }
233
234  // Since the compiler can't handle things like "main + 12" we should try to
235  // do this for now. The compiler doesn't like adding offsets to function
236  // pointer types.
237  static RegularExpression g_symbol_plus_offset_regex(
238      "^(.*)([-\\+])[[:space:]]*(0x[0-9A-Fa-f]+|[0-9]+)[[:space:]]*$");
239
240  llvm::SmallVector<llvm::StringRef, 4> matches;
241  if (g_symbol_plus_offset_regex.Execute(sref, &matches)) {
242    uint64_t offset = 0;
243    llvm::StringRef name = matches[1];
244    llvm::StringRef sign = matches[2];
245    llvm::StringRef str_offset = matches[3];
246    if (!str_offset.getAsInteger(0, offset)) {
247      Status error;
248      addr = ToAddress(exe_ctx, name, LLDB_INVALID_ADDRESS, &error);
249      if (addr != LLDB_INVALID_ADDRESS) {
250        if (sign[0] == '+')
251          return addr + offset;
252        return addr - offset;
253      }
254    }
255  }
256
257  if (error_ptr)
258    error_ptr->SetErrorStringWithFormat(
259        "address expression \"%s\" evaluation failed", s.str().c_str());
260  return {};
261}
262