Coverage Report

Created: 2025-12-12 07:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hermes/include/hermes/Support/JSONEmitter.h
Line
Count
Source
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
#ifndef HERMES_SUPPORT_JSONEMITTER_H
9
#define HERMES_SUPPORT_JSONEMITTER_H
10
11
#include <cstdint>
12
#include "llvh/ADT/ArrayRef.h"
13
#include "llvh/ADT/SmallVector.h"
14
#include "llvh/ADT/StringRef.h"
15
#include "llvh/Support/Format.h"
16
#include "llvh/Support/raw_ostream.h"
17
18
namespace hermes {
19
20
/// JSONEmitter is a simple, stateful class for emitting JSON to an
21
/// llvh::raw_ostream.
22
/// To emit an array, use openArray(). Then call a sequence of emitValue() or
23
/// emitValues(), or use openArray()/openDict() to nest a collection. Finish
24
/// with closeArray(). To emit a dictionary, use openDict(), a sequence of
25
/// emitKeyValue() or emitKey() followed by openArray()/openDict() to nest a
26
/// collection. Finish with closeDict(). Unbalanced dictionaries/arrays are
27
/// caught via assert().
28
///
29
/// JSONEmitter accepts 8-bit strings which it expects to be in UTF-8 format.
30
/// Invalid UTF-8 strings are fatal errors. It is the caller's responsibility
31
/// to ensure only valid UTF-8 is passed.
32
///
33
/// Example usage:
34
///  JSONEmitter json(llvh::outs());
35
///  json.openDict();
36
///  json.emitKeyValue("name", "hermes");
37
///  json.emitKeyValue("age", 2);
38
///  json.emitKey("platforms");
39
///  json.openArray();
40
///  json.emitValues({"linux", "mac", "android"});
41
///  json.closeArray();
42
///  json.closeDict();
43
///
44
class JSONEmitter {
45
 public:
46
  /// Construct a JSONEmitter to output to a stream \p OS. The emitter is
47
  /// initially empty. Usually you will want to invoke openDict() immediately on
48
  /// it.
49
  /// \p pretty is the option to pretty print the JSON object, with new lines
50
  /// and indentation. Note that since the JSONEmitter does not know when
51
  /// the printing will stop, the user needs to print a new line themselves
52
  /// when they finish.
53
  JSONEmitter(llvh::raw_ostream &OS, bool pretty = false)
54
0
      : OS(OS), pretty_(pretty) {}
55
56
  /// Emit a boolean value \p val.
57
  void emitValue(bool val);
58
59
  /// Emit an integer value \p val.
60
  void emitValue(short val);
61
  void emitValue(int val);
62
  void emitValue(long val);
63
  void emitValue(long long val);
64
65
  /// Emit an unsigned integer value \p val.
66
  void emitValue(unsigned short val);
67
  void emitValue(unsigned int val);
68
  void emitValue(unsigned long val);
69
  void emitValue(unsigned long long val);
70
71
  /// Emit a double value \p val.
72
  /// Note that only finite values may be so emitted. Infinities and NaNs are
73
  /// an error.
74
  void emitValue(double val);
75
76
  /// Emit a string \p val. This is not used to emit dictionary keys: use
77
  /// emitKey() instead.
78
  void emitValue(llvh::StringRef val);
79
0
  void emitValue(const char *val) {
80
0
    emitValue(llvh::StringRef(val));
81
0
  }
82
  /// Emit a string \p val. Any ascii characters will be emitted as is. Any
83
  /// non-ascii characters will be emitted as the escaped 16-bit code unit.
84
  void emitValue(llvh::ArrayRef<char16_t> val);
85
86
  /// Emit a null as value.
87
  void emitNullValue();
88
89
  /// Emit a dictionary key \p key. This requires that we are currently emitting
90
  /// a dictionary, and it expects a key (not a value).
91
  void emitKey(llvh::StringRef key);
92
93
  /// Emit a key \p key followed by a value \p val. This requires that we are
94
  /// currently emitting a dictionary and it expects a key.
95
  template <typename T>
96
0
  void emitKeyValue(llvh::StringRef key, const T &val) {
97
0
    emitKey(key);
98
0
    emitValue(val);
99
0
  }
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char [7]>(llvh::StringRef, char const (&) [7])
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<int>(llvh::StringRef, int const&)
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<unsigned int>(llvh::StringRef, unsigned int const&)
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<unsigned long>(llvh::StringRef, unsigned long const&)
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<double>(llvh::StringRef, double const&)
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<long>(llvh::StringRef, long const&)
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(llvh::StringRef, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<long long>(llvh::StringRef, long long const&)
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<llvh::StringRef>(llvh::StringRef, llvh::StringRef const&)
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char [8]>(llvh::StringRef, char const (&) [8])
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<bool>(llvh::StringRef, bool const&)
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char [13]>(llvh::StringRef, char const (&) [13])
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char const*>(llvh::StringRef, char const* const&)
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char [11]>(llvh::StringRef, char const (&) [11])
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char [3]>(llvh::StringRef, char const (&) [3])
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char [12]>(llvh::StringRef, char const (&) [12])
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char [1]>(llvh::StringRef, char const (&) [1])
Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char [5]>(llvh::StringRef, char const (&) [5])
100
101
  /// Emit a sequence of values \p val. This requires that we are currently
102
  /// emitting an array.
103
  template <typename T>
104
0
  void emitValues(llvh::ArrayRef<T> vals) {
105
0
    for (const T &val : vals)
106
0
      emitValue(val);
107
0
  }
Unexecuted instantiation: void hermes::JSONEmitter::emitValues<llvh::StringRef>(llvh::ArrayRef<llvh::StringRef>)
Unexecuted instantiation: void hermes::JSONEmitter::emitValues<unsigned int>(llvh::ArrayRef<unsigned int>)
108
109
  template <typename T>
110
0
  void emitValues(std::initializer_list<T> vals) {
111
0
    for (const T &val : vals)
112
0
      emitValue(val);
113
0
  }
Unexecuted instantiation: void hermes::JSONEmitter::emitValues<unsigned long>(std::initializer_list<unsigned long>)
Unexecuted instantiation: void hermes::JSONEmitter::emitValues<char const*>(std::initializer_list<char const*>)
114
115
  /// Begin emitting a dictionary.
116
  void openDict();
117
118
  /// Close the currently emitting dictionary.
119
  void closeDict();
120
121
  /// Begin emitting an array.
122
  void openArray();
123
124
  /// Close the currently emitting array.
125
  void closeArray();
126
127
  /// Terminate a JSON Lines record.
128
  void endJSONL();
129
130
  /// JSONEmitters hold raw_ostreams by reference.
131
  /// They may be moved but not copied.
132
  JSONEmitter(JSONEmitter &&);
133
134
 private:
135
  JSONEmitter(const JSONEmitter &) = delete;
136
  void operator=(const JSONEmitter &) = delete;
137
  void operator=(JSONEmitter &&) = delete;
138
139
  /// A function to unconditionally emit a string. This is used for both
140
  /// dictionary keys and ordinary string values.
141
  /// Assumes \p str is encoded in utf-8.
142
  /// Escapes certain control characters and non-ascii characters.
143
  void primitiveEmitString(llvh::StringRef str);
144
145
  /// \return whether a dictionary is currently being emitted.
146
0
  bool inDict() const {
147
0
    return !states_.empty() && states_.back().type == State::Dict;
148
0
  }
149
150
  /// \return whether an array is currently being emitted.
151
0
  bool inArray() const {
152
0
    return !states_.empty() && states_.back().type == State::Array;
153
0
  }
154
155
  /// Given that we are about to emit a value of any type (but not a dictionary
156
  /// key), perform housekeeping tasks such as emitting a trailing comma.
157
  void willEmitValue();
158
159
  /// In pretty printing mode, print a new line then indent.
160
  void prettyNewLine();
161
162
  /// In pretty printing mode, indent one level more.
163
  void indentMore();
164
165
  /// In pretty printing mode, indent one level less.
166
  void indentLess();
167
168
  /// A State represents the status of a single object (Dictionary or Array)
169
  /// being emitted.
170
  struct State {
171
    /// Whether the object is a dictionary or array.
172
    enum Type : uint8_t { Dict, Array } type;
173
174
    /// Whether we need a comma before emitting another value.
175
    bool needsComma;
176
177
    /// Whether we are a dictionary and expect a key.
178
    bool needsKey;
179
180
    /// Whether we expect a value (only after emitting a key in a dict).
181
    bool needsValue;
182
183
    /// Whether the dict or object is empty.
184
    bool isEmpty;
185
186
    /// Construct a State given a type \p t.
187
    /* implicit */ State(Type t)
188
0
        : type(t),
189
0
          needsComma(false),
190
0
          needsKey(type == Dict),
191
0
          needsValue(false),
192
0
          isEmpty(true) {}
193
  };
194
195
  /// The stack of states.
196
  llvh::SmallVector<State, 8> states_;
197
198
  /// The stream to output to.
199
  llvh::raw_ostream &OS;
200
201
  /// Pretty print the JSON object.
202
  bool pretty_{false};
203
204
  /// Number of spaces needed to indent.
205
  uint32_t indent_{0};
206
};
207
208
} // namespace hermes
209
210
#endif // HERMES_SUPPORT_JSONEMITTER_H