Coverage Report

Created: 2023-09-30 09:22

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Host/common/XML.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- XML.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/Host/Config.h"
10
#include "lldb/Host/XML.h"
11
12
#include "llvm/ADT/StringExtras.h"
13
14
using namespace lldb;
15
using namespace lldb_private;
16
17
#pragma mark-- XMLDocument
18
19
2.19k
XMLDocument::XMLDocument() = default;
20
21
2.19k
XMLDocument::~XMLDocument() { Clear(); }
22
23
4.39k
void XMLDocument::Clear() {
24
4.39k
#if LLDB_ENABLE_LIBXML2
25
4.39k
  if (m_document) {
26
2.19k
    xmlDocPtr doc = m_document;
27
2.19k
    m_document = nullptr;
28
2.19k
    xmlFreeDoc(doc);
29
2.19k
  }
30
4.39k
#endif
31
4.39k
}
32
33
4.40k
bool XMLDocument::IsValid() const { return m_document != nullptr; }
34
35
54
void XMLDocument::ErrorCallback(void *ctx, const char *format, ...) {
36
54
  XMLDocument *document = (XMLDocument *)ctx;
37
54
  va_list args;
38
54
  va_start(args, format);
39
54
  document->m_errors.PrintfVarArg(format, args);
40
54
  document->m_errors.EOL();
41
54
  va_end(args);
42
54
}
43
44
1
bool XMLDocument::ParseFile(const char *path) {
45
1
#if LLDB_ENABLE_LIBXML2
46
1
  Clear();
47
1
  xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);
48
1
  m_document = xmlParseFile(path);
49
1
  xmlSetGenericErrorFunc(nullptr, nullptr);
50
1
#endif
51
1
  return IsValid();
52
1
}
53
54
bool XMLDocument::ParseMemory(const char *xml, size_t xml_length,
55
2.19k
                              const char *url) {
56
2.19k
#if LLDB_ENABLE_LIBXML2
57
2.19k
  Clear();
58
2.19k
  xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);
59
2.19k
  m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0);
60
2.19k
  xmlSetGenericErrorFunc(nullptr, nullptr);
61
2.19k
#endif
62
2.19k
  return IsValid();
63
2.19k
}
64
65
2.20k
XMLNode XMLDocument::GetRootElement(const char *required_name) {
66
2.20k
#if LLDB_ENABLE_LIBXML2
67
2.20k
  if (IsValid()) {
68
2.20k
    XMLNode root_node(xmlDocGetRootElement(m_document));
69
2.20k
    if (required_name) {
70
2.20k
      llvm::StringRef actual_name = root_node.GetName();
71
2.20k
      if (actual_name == required_name)
72
2.19k
        return root_node;
73
2.20k
    } else {
74
1
      return root_node;
75
1
    }
76
2.20k
  }
77
9
#endif
78
9
  return XMLNode();
79
2.20k
}
80
81
0
llvm::StringRef XMLDocument::GetErrors() const { return m_errors.GetString(); }
82
83
50.9k
bool XMLDocument::XMLEnabled() {
84
50.9k
#if LLDB_ENABLE_LIBXML2
85
50.9k
  return true;
86
#else
87
  return false;
88
#endif
89
50.9k
}
90
91
#pragma mark-- XMLNode
92
93
38
XMLNode::XMLNode() = default;
94
95
290k
XMLNode::XMLNode(XMLNodeImpl node) : m_node(node) {}
96
97
297k
XMLNode::~XMLNode() = default;
98
99
2
void XMLNode::Clear() { m_node = nullptr; }
100
101
0
XMLNode XMLNode::GetParent() const {
102
0
#if LLDB_ENABLE_LIBXML2
103
0
  if (IsValid())
104
0
    return XMLNode(m_node->parent);
105
0
  else
106
0
    return XMLNode();
107
#else
108
  return XMLNode();
109
#endif
110
0
}
111
112
0
XMLNode XMLNode::GetSibling() const {
113
0
#if LLDB_ENABLE_LIBXML2
114
0
  if (IsValid())
115
0
    return XMLNode(m_node->next);
116
0
  else
117
0
    return XMLNode();
118
#else
119
  return XMLNode();
120
#endif
121
0
}
122
123
8.75k
XMLNode XMLNode::GetChild() const {
124
8.75k
#if LLDB_ENABLE_LIBXML2
125
126
8.75k
  if (IsValid())
127
8.75k
    return XMLNode(m_node->children);
128
0
  else
129
0
    return XMLNode();
130
#else
131
  return XMLNode();
132
#endif
133
8.75k
}
134
135
std::string XMLNode::GetAttributeValue(const char *name,
136
78
                                       const char *fail_value) const {
137
78
  std::string attr_value;
138
78
#if LLDB_ENABLE_LIBXML2
139
78
  if (IsValid()) {
140
78
    xmlChar *value = xmlGetProp(m_node, (const xmlChar *)name);
141
78
    if (value) {
142
73
      attr_value = (const char *)value;
143
73
      xmlFree(value);
144
73
    }
145
78
  } else {
146
0
    if (fail_value)
147
0
      attr_value = fail_value;
148
0
  }
149
#else
150
  if (fail_value)
151
    attr_value = fail_value;
152
#endif
153
78
  return attr_value;
154
78
}
155
156
bool XMLNode::GetAttributeValueAsUnsigned(const char *name, uint64_t &value,
157
15
                                          uint64_t fail_value, int base) const {
158
15
  value = fail_value;
159
15
  return llvm::to_integer(GetAttributeValue(name, ""), value, base);
160
15
}
161
162
0
void XMLNode::ForEachChildNode(NodeCallback const &callback) const {
163
0
#if LLDB_ENABLE_LIBXML2
164
0
  if (IsValid())
165
0
    GetChild().ForEachSiblingNode(callback);
166
0
#endif
167
0
}
168
169
2.18k
void XMLNode::ForEachChildElement(NodeCallback const &callback) const {
170
2.18k
#if LLDB_ENABLE_LIBXML2
171
2.18k
  XMLNode child = GetChild();
172
2.18k
  if (child)
173
2.18k
    child.ForEachSiblingElement(callback);
174
2.18k
#endif
175
2.18k
}
176
177
void XMLNode::ForEachChildElementWithName(const char *name,
178
6.57k
                                          NodeCallback const &callback) const {
179
6.57k
#if LLDB_ENABLE_LIBXML2
180
6.57k
  XMLNode child = GetChild();
181
6.57k
  if (child)
182
6.57k
    child.ForEachSiblingElementWithName(name, callback);
183
6.57k
#endif
184
6.57k
}
185
186
275k
void XMLNode::ForEachAttribute(AttributeCallback const &callback) const {
187
275k
#if LLDB_ENABLE_LIBXML2
188
189
275k
  if (IsValid()) {
190
2.72M
    for (xmlAttrPtr attr = m_node->properties; attr != nullptr;
191
2.45M
         attr = attr->next) {
192
      // check if name matches
193
2.45M
      if (
attr->name2.45M
) {
194
        // check child is a text node
195
2.45M
        xmlNodePtr child = attr->children;
196
2.45M
        if (
child->type == XML_TEXT_NODE2.45M
) {
197
2.45M
          llvm::StringRef attr_value;
198
2.45M
          if (child->content)
199
2.45M
            attr_value = llvm::StringRef((const char *)child->content);
200
2.45M
          if (!callback(llvm::StringRef((const char *)attr->name), attr_value))
201
0
            return;
202
2.45M
        }
203
2.45M
      }
204
2.45M
    }
205
275k
  }
206
275k
#endif
207
275k
}
208
209
0
void XMLNode::ForEachSiblingNode(NodeCallback const &callback) const {
210
0
#if LLDB_ENABLE_LIBXML2
211
212
0
  if (IsValid()) {
213
    // iterate through all siblings
214
0
    for (xmlNodePtr node = m_node; node; node = node->next) {
215
0
      if (!callback(XMLNode(node)))
216
0
        return;
217
0
    }
218
0
  }
219
0
#endif
220
0
}
221
222
2.18k
void XMLNode::ForEachSiblingElement(NodeCallback const &callback) const {
223
2.18k
#if LLDB_ENABLE_LIBXML2
224
225
2.18k
  if (IsValid()) {
226
    // iterate through all siblings
227
13.3k
    for (xmlNodePtr node = m_node; node; 
node = node->next11.1k
) {
228
      // we are looking for element nodes only
229
11.1k
      if (node->type != XML_ELEMENT_NODE)
230
6.67k
        continue;
231
232
4.49k
      if (!callback(XMLNode(node)))
233
0
        return;
234
4.49k
    }
235
2.18k
  }
236
2.18k
#endif
237
2.18k
}
238
239
void XMLNode::ForEachSiblingElementWithName(
240
6.57k
    const char *name, NodeCallback const &callback) const {
241
6.57k
#if LLDB_ENABLE_LIBXML2
242
243
6.57k
  if (IsValid()) {
244
    // iterate through all siblings
245
1.10M
    for (xmlNodePtr node = m_node; node; 
node = node->next1.09M
) {
246
      // we are looking for element nodes only
247
1.09M
      if (node->type != XML_ELEMENT_NODE)
248
550k
        continue;
249
250
      // If name is nullptr, we take all nodes of type "t", else just the ones
251
      // whose name matches
252
543k
      if (name) {
253
543k
        if (strcmp((const char *)node->name, name) != 0)
254
268k
          continue; // Name mismatch, ignore this one
255
543k
      } else {
256
0
        if (node->name)
257
0
          continue; // nullptr name specified and this element has a name,
258
                    // ignore this one
259
0
      }
260
261
275k
      if (!callback(XMLNode(node)))
262
25
        return;
263
275k
    }
264
6.57k
  }
265
6.57k
#endif
266
6.57k
}
267
268
6.69k
llvm::StringRef XMLNode::GetName() const {
269
6.69k
#if LLDB_ENABLE_LIBXML2
270
6.69k
  if (IsValid()) {
271
6.69k
    if (m_node->name)
272
6.69k
      return llvm::StringRef((const char *)m_node->name);
273
6.69k
  }
274
0
#endif
275
0
  return llvm::StringRef();
276
6.69k
}
277
278
77
bool XMLNode::GetElementText(std::string &text) const {
279
77
  text.clear();
280
77
#if LLDB_ENABLE_LIBXML2
281
77
  if (IsValid()) {
282
77
    bool success = false;
283
77
    if (m_node->type == XML_ELEMENT_NODE) {
284
      // check child is a text node
285
152
      for (xmlNodePtr node = m_node->children; node != nullptr;
286
77
           
node = node->next75
) {
287
75
        if (node->type == XML_TEXT_NODE) {
288
75
          text.append((const char *)node->content);
289
75
          success = true;
290
75
        }
291
75
      }
292
77
    }
293
77
    return success;
294
77
  }
295
0
#endif
296
0
  return false;
297
77
}
298
299
bool XMLNode::GetElementTextAsUnsigned(uint64_t &value, uint64_t fail_value,
300
10
                                       int base) const {
301
10
  std::string text;
302
303
10
  value = fail_value;
304
10
  return GetElementText(text) && 
llvm::to_integer(text, value, base)9
;
305
10
}
306
307
9
bool XMLNode::GetElementTextAsFloat(double &value, double fail_value) const {
308
9
  std::string text;
309
310
9
  value = fail_value;
311
9
  return GetElementText(text) && 
llvm::to_float(text, value)8
;
312
9
}
313
314
4
bool XMLNode::NameIs(const char *name) const {
315
4
#if LLDB_ENABLE_LIBXML2
316
317
4
  if (IsValid()) {
318
    // In case we are looking for a nullptr name or an exact pointer match
319
4
    if (m_node->name == (const xmlChar *)name)
320
0
      return true;
321
4
    if (m_node->name)
322
4
      return strcmp((const char *)m_node->name, name) == 0;
323
4
  }
324
0
#endif
325
0
  return false;
326
4
}
327
328
24
XMLNode XMLNode::FindFirstChildElementWithName(const char *name) const {
329
24
  XMLNode result_node;
330
331
24
#if LLDB_ENABLE_LIBXML2
332
24
  ForEachChildElementWithName(
333
24
      name, [&result_node](const XMLNode &node) -> bool {
334
24
        result_node = node;
335
        // Stop iterating, we found the node we wanted
336
24
        return false;
337
24
      });
338
24
#endif
339
340
24
  return result_node;
341
24
}
342
343
312k
bool XMLNode::IsValid() const { return m_node != nullptr; }
344
345
4
bool XMLNode::IsElement() const {
346
4
#if LLDB_ENABLE_LIBXML2
347
4
  if (IsValid())
348
4
    return m_node->type == XML_ELEMENT_NODE;
349
0
#endif
350
0
  return false;
351
4
}
352
353
0
XMLNode XMLNode::GetElementForPath(const NamePath &path) {
354
0
#if LLDB_ENABLE_LIBXML2
355
356
0
  if (IsValid()) {
357
0
    if (path.empty())
358
0
      return *this;
359
0
    else {
360
0
      XMLNode node = FindFirstChildElementWithName(path[0].c_str());
361
0
      const size_t n = path.size();
362
0
      for (size_t i = 1; node && i < n; ++i)
363
0
        node = node.FindFirstChildElementWithName(path[i].c_str());
364
0
      return node;
365
0
    }
366
0
  }
367
0
#endif
368
369
0
  return XMLNode();
370
0
}
371
372
#pragma mark-- ApplePropertyList
373
374
0
ApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {}
375
376
ApplePropertyList::ApplePropertyList(const char *path)
377
1
    : m_xml_doc(), m_dict_node() {
378
1
  ParseFile(path);
379
1
}
380
381
1
ApplePropertyList::~ApplePropertyList() = default;
382
383
0
llvm::StringRef ApplePropertyList::GetErrors() const {
384
0
  return m_xml_doc.GetErrors();
385
0
}
386
387
1
bool ApplePropertyList::ParseFile(const char *path) {
388
1
  if (m_xml_doc.ParseFile(path)) {
389
1
    XMLNode plist = m_xml_doc.GetRootElement("plist");
390
1
    if (plist) {
391
1
      plist.ForEachChildElementWithName("dict",
392
1
                                        [this](const XMLNode &dict) -> bool {
393
1
                                          this->m_dict_node = dict;
394
1
                                          return false; // Stop iterating
395
1
                                        });
396
1
      return (bool)m_dict_node;
397
1
    }
398
1
  }
399
0
  return false;
400
1
}
401
402
4
bool ApplePropertyList::IsValid() const { return (bool)m_dict_node; }
403
404
bool ApplePropertyList::GetValueAsString(const char *key,
405
2
                                         std::string &value) const {
406
2
  XMLNode value_node = GetValueNode(key);
407
2
  if (value_node)
408
0
    return ApplePropertyList::ExtractStringFromValueNode(value_node, value);
409
2
  return false;
410
2
}
411
412
2
XMLNode ApplePropertyList::GetValueNode(const char *key) const {
413
2
  XMLNode value_node;
414
2
#if LLDB_ENABLE_LIBXML2
415
416
2
  if (IsValid()) {
417
2
    m_dict_node.ForEachChildElementWithName(
418
2
        "key", [key, &value_node](const XMLNode &key_node) -> bool {
419
2
          std::string key_name;
420
2
          if (key_node.GetElementText(key_name)) {
421
2
            if (key_name == key) {
422
0
              value_node = key_node.GetSibling();
423
0
              while (value_node && !value_node.IsElement())
424
0
                value_node = value_node.GetSibling();
425
0
              return false; // Stop iterating
426
0
            }
427
2
          }
428
2
          return true; // Keep iterating
429
2
        });
430
2
  }
431
2
#endif
432
2
  return value_node;
433
2
}
434
435
bool ApplePropertyList::ExtractStringFromValueNode(const XMLNode &node,
436
0
                                                   std::string &value) {
437
0
  value.clear();
438
0
#if LLDB_ENABLE_LIBXML2
439
0
  if (node.IsValid()) {
440
0
    llvm::StringRef element_name = node.GetName();
441
0
    if (element_name == "true" || element_name == "false") {
442
      // The text value _is_ the element name itself...
443
0
      value = element_name.str();
444
0
      return true;
445
0
    } else if (element_name == "dict" || element_name == "array")
446
0
      return false; // dictionaries and arrays have no text value, so we fail
447
0
    else
448
0
      return node.GetElementText(value);
449
0
  }
450
0
#endif
451
0
  return false;
452
0
}
453
454
#if LLDB_ENABLE_LIBXML2
455
456
3
static StructuredData::ObjectSP CreatePlistValue(XMLNode node) {
457
3
  llvm::StringRef element_name = node.GetName();
458
3
  if (element_name == "array") {
459
0
    std::shared_ptr<StructuredData::Array> array_sp(
460
0
        new StructuredData::Array());
461
0
    node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool {
462
0
      array_sp->AddItem(CreatePlistValue(node));
463
0
      return true; // Keep iterating through all child elements of the array
464
0
    });
465
0
    return array_sp;
466
3
  } else if (element_name == "dict") {
467
2
    XMLNode key_node;
468
2
    std::shared_ptr<StructuredData::Dictionary> dict_sp(
469
2
        new StructuredData::Dictionary());
470
2
    node.ForEachChildElement(
471
4
        [&key_node, &dict_sp](const XMLNode &node) -> bool {
472
4
          if (node.NameIs("key")) {
473
            // This is a "key" element node
474
2
            key_node = node;
475
2
          } else {
476
            // This is a value node
477
2
            if (key_node) {
478
2
              std::string key_name;
479
2
              key_node.GetElementText(key_name);
480
2
              dict_sp->AddItem(key_name, CreatePlistValue(node));
481
2
              key_node.Clear();
482
2
            }
483
2
          }
484
4
          return true; // Keep iterating through all child elements of the
485
                       // dictionary
486
4
        });
487
2
    return dict_sp;
488
2
  } else 
if (1
element_name == "real"1
) {
489
0
    double value = 0.0;
490
0
    node.GetElementTextAsFloat(value);
491
0
    return StructuredData::ObjectSP(new StructuredData::Float(value));
492
1
  } else if (element_name == "integer") {
493
0
    uint64_t value = 0;
494
0
    node.GetElementTextAsUnsigned(value, 0, 0);
495
0
    return StructuredData::ObjectSP(new StructuredData::UnsignedInteger(value));
496
1
  } else if ((element_name == "string") || 
(element_name == "data")0
||
497
1
             
(element_name == "date")0
) {
498
1
    std::string text;
499
1
    node.GetElementText(text);
500
1
    return StructuredData::ObjectSP(
501
1
        new StructuredData::String(std::move(text)));
502
1
  } else 
if (0
element_name == "true"0
) {
503
0
    return StructuredData::ObjectSP(new StructuredData::Boolean(true));
504
0
  } else if (element_name == "false") {
505
0
    return StructuredData::ObjectSP(new StructuredData::Boolean(false));
506
0
  }
507
0
  return StructuredData::ObjectSP(new StructuredData::Null());
508
3
}
509
#endif
510
511
1
StructuredData::ObjectSP ApplePropertyList::GetStructuredData() {
512
1
  StructuredData::ObjectSP root_sp;
513
1
#if LLDB_ENABLE_LIBXML2
514
1
  if (IsValid()) {
515
1
    return CreatePlistValue(m_dict_node);
516
1
  }
517
0
#endif
518
0
  return root_sp;
519
1
}