1 | // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "platform/assert.h" |
6 | |
7 | #include "platform/unicode.h" |
8 | #include "vm/double_conversion.h" |
9 | #include "vm/json_writer.h" |
10 | #include "vm/object.h" |
11 | |
12 | namespace dart { |
13 | |
14 | class MaybeOnStackBuffer { |
15 | public: |
16 | explicit MaybeOnStackBuffer(intptr_t size) { |
17 | if (size > kOnStackBufferCapacity) { |
18 | p_ = reinterpret_cast<char*>(malloc(size)); |
19 | } else { |
20 | p_ = &buffer_[0]; |
21 | } |
22 | } |
23 | ~MaybeOnStackBuffer() { |
24 | if (p_ != &buffer_[0]) free(p_); |
25 | } |
26 | |
27 | char* p() { return p_; } |
28 | |
29 | private: |
30 | static const intptr_t kOnStackBufferCapacity = 4096; |
31 | char* p_; |
32 | char buffer_[kOnStackBufferCapacity]; |
33 | }; |
34 | |
35 | JSONWriter::JSONWriter(intptr_t buf_size) |
36 | : open_objects_(0), buffer_(buf_size) {} |
37 | |
38 | void JSONWriter::AppendSerializedObject(const char* serialized_object) { |
39 | PrintCommaIfNeeded(); |
40 | buffer_.AddString(serialized_object); |
41 | } |
42 | |
43 | void JSONWriter::AppendSerializedObject(const uint8_t* buffer, |
44 | intptr_t buffer_length) { |
45 | buffer_.AddRaw(buffer, buffer_length); |
46 | } |
47 | |
48 | void JSONWriter::AppendSerializedObject(const char* property_name, |
49 | const char* serialized_object) { |
50 | PrintCommaIfNeeded(); |
51 | PrintPropertyName(property_name); |
52 | buffer_.AddString(serialized_object); |
53 | } |
54 | |
55 | void JSONWriter::Clear() { |
56 | buffer_.Clear(); |
57 | open_objects_ = 0; |
58 | } |
59 | |
60 | void JSONWriter::OpenObject(const char* property_name) { |
61 | PrintCommaIfNeeded(); |
62 | open_objects_++; |
63 | if (property_name != NULL) { |
64 | PrintPropertyName(property_name); |
65 | } |
66 | buffer_.AddChar('{'); |
67 | } |
68 | |
69 | void JSONWriter::UncloseObject() { |
70 | intptr_t len = buffer_.length(); |
71 | ASSERT(len > 0); |
72 | ASSERT(buffer_.buffer()[len - 1] == '}'); |
73 | open_objects_++; |
74 | buffer_.set_length(len - 1); |
75 | } |
76 | |
77 | void JSONWriter::CloseObject() { |
78 | ASSERT(open_objects_ > 0); |
79 | open_objects_--; |
80 | buffer_.AddChar('}'); |
81 | } |
82 | |
83 | void JSONWriter::OpenArray(const char* property_name) { |
84 | PrintCommaIfNeeded(); |
85 | if (property_name != NULL) { |
86 | PrintPropertyName(property_name); |
87 | } |
88 | open_objects_++; |
89 | buffer_.AddChar('['); |
90 | } |
91 | |
92 | void JSONWriter::CloseArray() { |
93 | ASSERT(open_objects_ > 0); |
94 | open_objects_--; |
95 | buffer_.AddChar(']'); |
96 | } |
97 | |
98 | void JSONWriter::PrintValueNull() { |
99 | PrintCommaIfNeeded(); |
100 | buffer_.Printf("null" ); |
101 | } |
102 | |
103 | void JSONWriter::PrintValueBool(bool b) { |
104 | PrintCommaIfNeeded(); |
105 | buffer_.Printf("%s" , b ? "true" : "false" ); |
106 | } |
107 | |
108 | void JSONWriter::PrintValue(intptr_t i) { |
109 | EnsureIntegerIsRepresentableInJavaScript(static_cast<int64_t>(i)); |
110 | PrintCommaIfNeeded(); |
111 | buffer_.Printf("%" Pd "" , i); |
112 | } |
113 | |
114 | void JSONWriter::PrintValue64(int64_t i) { |
115 | EnsureIntegerIsRepresentableInJavaScript(i); |
116 | PrintCommaIfNeeded(); |
117 | buffer_.Printf("%" Pd64 "" , i); |
118 | } |
119 | |
120 | void JSONWriter::PrintValue(double d) { |
121 | // Max length of a double in characters (including \0). |
122 | // See double_conversion.cc. |
123 | const size_t kBufferLen = 25; |
124 | char buffer[kBufferLen]; |
125 | DoubleToCString(d, buffer, kBufferLen); |
126 | PrintCommaIfNeeded(); |
127 | buffer_.Printf("%s" , buffer); |
128 | } |
129 | |
130 | static const char base64_digits[65] = |
131 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ; |
132 | static const char base64_pad = '='; |
133 | |
134 | void JSONWriter::PrintValueBase64(const uint8_t* bytes, intptr_t length) { |
135 | PrintCommaIfNeeded(); |
136 | buffer_.AddChar('"'); |
137 | |
138 | intptr_t odd_bits = length % 3; |
139 | intptr_t even_bits = length - odd_bits; |
140 | for (intptr_t i = 0; i < even_bits; i += 3) { |
141 | intptr_t triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; |
142 | buffer_.AddChar(base64_digits[triplet >> 18]); |
143 | buffer_.AddChar(base64_digits[(triplet >> 12) & 63]); |
144 | buffer_.AddChar(base64_digits[(triplet >> 6) & 63]); |
145 | buffer_.AddChar(base64_digits[triplet & 63]); |
146 | } |
147 | if (odd_bits == 1) { |
148 | intptr_t triplet = bytes[even_bits] << 16; |
149 | buffer_.AddChar(base64_digits[triplet >> 18]); |
150 | buffer_.AddChar(base64_digits[(triplet >> 12) & 63]); |
151 | buffer_.AddChar(base64_pad); |
152 | buffer_.AddChar(base64_pad); |
153 | } else if (odd_bits == 2) { |
154 | intptr_t triplet = (bytes[even_bits] << 16) | (bytes[even_bits + 1] << 8); |
155 | buffer_.AddChar(base64_digits[triplet >> 18]); |
156 | buffer_.AddChar(base64_digits[(triplet >> 12) & 63]); |
157 | buffer_.AddChar(base64_digits[(triplet >> 6) & 63]); |
158 | buffer_.AddChar(base64_pad); |
159 | } |
160 | |
161 | buffer_.AddChar('"'); |
162 | } |
163 | |
164 | void JSONWriter::PrintValue(const char* s) { |
165 | PrintCommaIfNeeded(); |
166 | buffer_.AddChar('"'); |
167 | AddEscapedUTF8String(s); |
168 | buffer_.AddChar('"'); |
169 | } |
170 | |
171 | bool JSONWriter::PrintValueStr(const String& s, |
172 | intptr_t offset, |
173 | intptr_t count) { |
174 | PrintCommaIfNeeded(); |
175 | buffer_.AddChar('"'); |
176 | bool did_truncate = AddDartString(s, offset, count); |
177 | buffer_.AddChar('"'); |
178 | return did_truncate; |
179 | } |
180 | |
181 | void JSONWriter::PrintValueNoEscape(const char* s) { |
182 | PrintCommaIfNeeded(); |
183 | buffer_.Printf("%s" , s); |
184 | } |
185 | |
186 | void JSONWriter::PrintfValue(const char* format, ...) { |
187 | va_list args; |
188 | va_start(args, format); |
189 | VPrintfValue(format, args); |
190 | va_end(args); |
191 | } |
192 | |
193 | void JSONWriter::VPrintfValue(const char* format, va_list args) { |
194 | PrintCommaIfNeeded(); |
195 | |
196 | va_list measure_args; |
197 | va_copy(measure_args, args); |
198 | intptr_t len = Utils::VSNPrint(NULL, 0, format, measure_args); |
199 | va_end(measure_args); |
200 | |
201 | MaybeOnStackBuffer mosb(len + 1); |
202 | char* p = mosb.p(); |
203 | |
204 | va_list print_args; |
205 | va_copy(print_args, args); |
206 | intptr_t len2 = Utils::VSNPrint(p, len + 1, format, print_args); |
207 | va_end(print_args); |
208 | ASSERT(len == len2); |
209 | |
210 | buffer_.AddChar('"'); |
211 | AddEscapedUTF8String(p, len); |
212 | buffer_.AddChar('"'); |
213 | } |
214 | |
215 | void JSONWriter::PrintPropertyBool(const char* name, bool b) { |
216 | PrintPropertyName(name); |
217 | PrintValueBool(b); |
218 | } |
219 | |
220 | void JSONWriter::PrintProperty(const char* name, intptr_t i) { |
221 | PrintPropertyName(name); |
222 | PrintValue(i); |
223 | } |
224 | |
225 | void JSONWriter::PrintProperty64(const char* name, int64_t i) { |
226 | PrintPropertyName(name); |
227 | PrintValue64(i); |
228 | } |
229 | |
230 | void JSONWriter::PrintProperty(const char* name, double d) { |
231 | PrintPropertyName(name); |
232 | PrintValue(d); |
233 | } |
234 | |
235 | void JSONWriter::PrintProperty(const char* name, const char* s) { |
236 | PrintPropertyName(name); |
237 | PrintValue(s); |
238 | } |
239 | |
240 | void JSONWriter::PrintPropertyBase64(const char* name, |
241 | const uint8_t* b, |
242 | intptr_t len) { |
243 | PrintPropertyName(name); |
244 | PrintValueBase64(b, len); |
245 | } |
246 | |
247 | bool JSONWriter::PrintPropertyStr(const char* name, |
248 | const String& s, |
249 | intptr_t offset, |
250 | intptr_t count) { |
251 | PrintPropertyName(name); |
252 | return PrintValueStr(s, offset, count); |
253 | } |
254 | |
255 | void JSONWriter::PrintPropertyNoEscape(const char* name, const char* s) { |
256 | PrintPropertyName(name); |
257 | PrintValueNoEscape(s); |
258 | } |
259 | |
260 | void JSONWriter::PrintfProperty(const char* name, const char* format, ...) { |
261 | va_list args; |
262 | va_start(args, format); |
263 | VPrintfProperty(name, format, args); |
264 | va_end(args); |
265 | } |
266 | |
267 | void JSONWriter::VPrintfProperty(const char* name, |
268 | const char* format, |
269 | va_list args) { |
270 | PrintPropertyName(name); |
271 | |
272 | va_list measure_args; |
273 | va_copy(measure_args, args); |
274 | intptr_t len = Utils::VSNPrint(NULL, 0, format, measure_args); |
275 | va_end(measure_args); |
276 | |
277 | MaybeOnStackBuffer mosb(len + 1); |
278 | char* p = mosb.p(); |
279 | |
280 | va_list print_args; |
281 | va_copy(print_args, args); |
282 | intptr_t len2 = Utils::VSNPrint(p, len + 1, format, print_args); |
283 | va_end(print_args); |
284 | ASSERT(len == len2); |
285 | |
286 | buffer_.AddChar('"'); |
287 | AddEscapedUTF8String(p, len); |
288 | buffer_.AddChar('"'); |
289 | } |
290 | |
291 | void JSONWriter::Steal(char** buffer, intptr_t* buffer_length) { |
292 | ASSERT(buffer != NULL); |
293 | ASSERT(buffer_length != NULL); |
294 | *buffer_length = buffer_.length(); |
295 | *buffer = buffer_.Steal(); |
296 | } |
297 | |
298 | void JSONWriter::PrintPropertyName(const char* name) { |
299 | ASSERT(name != NULL); |
300 | PrintCommaIfNeeded(); |
301 | buffer_.AddChar('"'); |
302 | AddEscapedUTF8String(name); |
303 | buffer_.AddChar('"'); |
304 | buffer_.AddChar(':'); |
305 | } |
306 | |
307 | void JSONWriter::PrintNewline() { |
308 | buffer_.AddChar('\n'); |
309 | } |
310 | |
311 | void JSONWriter::PrintCommaIfNeeded() { |
312 | if (NeedComma()) { |
313 | buffer_.AddChar(','); |
314 | } |
315 | } |
316 | |
317 | bool JSONWriter::NeedComma() { |
318 | const char* buffer = buffer_.buffer(); |
319 | intptr_t length = buffer_.length(); |
320 | if (length == 0) { |
321 | return false; |
322 | } |
323 | char ch = buffer[length - 1]; |
324 | return (ch != '[') && (ch != '{') && (ch != ':') && (ch != ','); |
325 | } |
326 | |
327 | void JSONWriter::EnsureIntegerIsRepresentableInJavaScript(int64_t i) { |
328 | #ifdef DEBUG |
329 | if (!Utils::IsJavascriptInt(i)) { |
330 | OS::PrintErr( |
331 | "JSONWriter::EnsureIntegerIsRepresentableInJavaScript failed on " |
332 | "%" Pd64 "\n" , |
333 | i); |
334 | UNREACHABLE(); |
335 | } |
336 | #endif |
337 | } |
338 | |
339 | void JSONWriter::AddEscapedUTF8String(const char* s) { |
340 | if (s == NULL) { |
341 | return; |
342 | } |
343 | intptr_t len = strlen(s); |
344 | AddEscapedUTF8String(s, len); |
345 | } |
346 | |
347 | void JSONWriter::AddEscapedUTF8String(const char* s, intptr_t len) { |
348 | if (s == NULL) { |
349 | return; |
350 | } |
351 | const uint8_t* s8 = reinterpret_cast<const uint8_t*>(s); |
352 | intptr_t i = 0; |
353 | for (; i < len;) { |
354 | // Extract next UTF8 character. |
355 | int32_t ch = 0; |
356 | int32_t ch_len = Utf8::Decode(&s8[i], len - i, &ch); |
357 | ASSERT(ch_len != 0); |
358 | buffer_.EscapeAndAddCodeUnit(ch); |
359 | // Move i forward. |
360 | i += ch_len; |
361 | } |
362 | ASSERT(i == len); |
363 | } |
364 | |
365 | bool JSONWriter::AddDartString(const String& s, |
366 | intptr_t offset, |
367 | intptr_t count) { |
368 | intptr_t length = s.Length(); |
369 | ASSERT(offset >= 0); |
370 | if (offset > length) { |
371 | offset = length; |
372 | } |
373 | if (!Utils::RangeCheck(offset, count, length)) { |
374 | count = length - offset; |
375 | } |
376 | intptr_t limit = offset + count; |
377 | for (intptr_t i = offset; i < limit; i++) { |
378 | uint16_t code_unit = s.CharAt(i); |
379 | if (Utf16::IsTrailSurrogate(code_unit)) { |
380 | buffer_.EscapeAndAddUTF16CodeUnit(code_unit); |
381 | } else if (Utf16::IsLeadSurrogate(code_unit)) { |
382 | if (i + 1 == limit) { |
383 | buffer_.EscapeAndAddUTF16CodeUnit(code_unit); |
384 | } else { |
385 | uint16_t next_code_unit = s.CharAt(i + 1); |
386 | if (Utf16::IsTrailSurrogate(next_code_unit)) { |
387 | uint32_t decoded = Utf16::Decode(code_unit, next_code_unit); |
388 | buffer_.EscapeAndAddCodeUnit(decoded); |
389 | i++; |
390 | } else { |
391 | buffer_.EscapeAndAddUTF16CodeUnit(code_unit); |
392 | } |
393 | } |
394 | } else { |
395 | buffer_.EscapeAndAddCodeUnit(code_unit); |
396 | } |
397 | } |
398 | // Return value indicates whether the string is truncated. |
399 | return (offset > 0) || (limit < length); |
400 | } |
401 | |
402 | } // namespace dart |
403 | |