OLD | NEW |
| (Empty) |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "platform/inspector_protocol/Parser.h" | |
6 | |
7 #include "platform/inspector_protocol/String16.h" | |
8 #include "platform/inspector_protocol/Values.h" | |
9 | |
10 namespace blink { | |
11 namespace protocol { | |
12 | |
13 namespace { | |
14 | |
15 const int stackLimit = 1000; | |
16 | |
17 enum Token { | |
18 ObjectBegin, | |
19 ObjectEnd, | |
20 ArrayBegin, | |
21 ArrayEnd, | |
22 StringLiteral, | |
23 Number, | |
24 BoolTrue, | |
25 BoolFalse, | |
26 NullToken, | |
27 ListSeparator, | |
28 ObjectPairSeparator, | |
29 InvalidToken, | |
30 }; | |
31 | |
32 const char* const nullString = "null"; | |
33 const char* const trueString = "true"; | |
34 const char* const falseString = "false"; | |
35 | |
36 bool parseConstToken(const UChar* start, const UChar* end, const UChar** tokenEn
d, const char* token) | |
37 { | |
38 while (start < end && *token != '\0' && *start++ == *token++) { } | |
39 if (*token != '\0') | |
40 return false; | |
41 *tokenEnd = start; | |
42 return true; | |
43 } | |
44 | |
45 bool readInt(const UChar* start, const UChar* end, const UChar** tokenEnd, bool
canHaveLeadingZeros) | |
46 { | |
47 if (start == end) | |
48 return false; | |
49 bool haveLeadingZero = '0' == *start; | |
50 int length = 0; | |
51 while (start < end && '0' <= *start && *start <= '9') { | |
52 ++start; | |
53 ++length; | |
54 } | |
55 if (!length) | |
56 return false; | |
57 if (!canHaveLeadingZeros && length > 1 && haveLeadingZero) | |
58 return false; | |
59 *tokenEnd = start; | |
60 return true; | |
61 } | |
62 | |
63 bool parseNumberToken(const UChar* start, const UChar* end, const UChar** tokenE
nd) | |
64 { | |
65 // We just grab the number here. We validate the size in DecodeNumber. | |
66 // According to RFC4627, a valid number is: [minus] int [frac] [exp] | |
67 if (start == end) | |
68 return false; | |
69 UChar c = *start; | |
70 if ('-' == c) | |
71 ++start; | |
72 | |
73 if (!readInt(start, end, &start, false)) | |
74 return false; | |
75 if (start == end) { | |
76 *tokenEnd = start; | |
77 return true; | |
78 } | |
79 | |
80 // Optional fraction part | |
81 c = *start; | |
82 if ('.' == c) { | |
83 ++start; | |
84 if (!readInt(start, end, &start, true)) | |
85 return false; | |
86 if (start == end) { | |
87 *tokenEnd = start; | |
88 return true; | |
89 } | |
90 c = *start; | |
91 } | |
92 | |
93 // Optional exponent part | |
94 if ('e' == c || 'E' == c) { | |
95 ++start; | |
96 if (start == end) | |
97 return false; | |
98 c = *start; | |
99 if ('-' == c || '+' == c) { | |
100 ++start; | |
101 if (start == end) | |
102 return false; | |
103 } | |
104 if (!readInt(start, end, &start, true)) | |
105 return false; | |
106 } | |
107 | |
108 *tokenEnd = start; | |
109 return true; | |
110 } | |
111 | |
112 bool readHexDigits(const UChar* start, const UChar* end, const UChar** tokenEnd,
int digits) | |
113 { | |
114 if (end - start < digits) | |
115 return false; | |
116 for (int i = 0; i < digits; ++i) { | |
117 UChar c = *start++; | |
118 if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c
<= 'F'))) | |
119 return false; | |
120 } | |
121 *tokenEnd = start; | |
122 return true; | |
123 } | |
124 | |
125 bool parseStringToken(const UChar* start, const UChar* end, const UChar** tokenE
nd) | |
126 { | |
127 while (start < end) { | |
128 UChar c = *start++; | |
129 if ('\\' == c) { | |
130 c = *start++; | |
131 // Make sure the escaped char is valid. | |
132 switch (c) { | |
133 case 'x': | |
134 if (!readHexDigits(start, end, &start, 2)) | |
135 return false; | |
136 break; | |
137 case 'u': | |
138 if (!readHexDigits(start, end, &start, 4)) | |
139 return false; | |
140 break; | |
141 case '\\': | |
142 case '/': | |
143 case 'b': | |
144 case 'f': | |
145 case 'n': | |
146 case 'r': | |
147 case 't': | |
148 case 'v': | |
149 case '"': | |
150 break; | |
151 default: | |
152 return false; | |
153 } | |
154 } else if ('"' == c) { | |
155 *tokenEnd = start; | |
156 return true; | |
157 } | |
158 } | |
159 return false; | |
160 } | |
161 | |
162 bool skipComment(const UChar* start, const UChar* end, const UChar** commentEnd) | |
163 { | |
164 if (start == end) | |
165 return false; | |
166 | |
167 if (*start != '/' || start + 1 >= end) | |
168 return false; | |
169 ++start; | |
170 | |
171 if (*start == '/') { | |
172 // Single line comment, read to newline. | |
173 for (++start; start < end; ++start) { | |
174 if (*start == '\n' || *start == '\r') { | |
175 *commentEnd = start + 1; | |
176 return true; | |
177 } | |
178 } | |
179 *commentEnd = end; | |
180 // Comment reaches end-of-input, which is fine. | |
181 return true; | |
182 } | |
183 | |
184 if (*start == '*') { | |
185 UChar previous = '\0'; | |
186 // Block comment, read until end marker. | |
187 for (++start; start < end; previous = *start++) { | |
188 if (previous == '*' && *start == '/') { | |
189 *commentEnd = start + 1; | |
190 return true; | |
191 } | |
192 } | |
193 // Block comment must close before end-of-input. | |
194 return false; | |
195 } | |
196 | |
197 return false; | |
198 } | |
199 | |
200 void skipWhitespaceAndComments(const UChar* start, const UChar* end, const UChar
** whitespaceEnd) | |
201 { | |
202 while (start < end) { | |
203 if (String16::isSpaceOrNewLine(*start)) { | |
204 ++start; | |
205 } else if (*start == '/') { | |
206 const UChar* commentEnd; | |
207 if (!skipComment(start, end, &commentEnd)) | |
208 break; | |
209 start = commentEnd; | |
210 } else { | |
211 break; | |
212 } | |
213 } | |
214 *whitespaceEnd = start; | |
215 } | |
216 | |
217 Token parseToken(const UChar* start, const UChar* end, const UChar** tokenStart,
const UChar** tokenEnd) | |
218 { | |
219 skipWhitespaceAndComments(start, end, tokenStart); | |
220 start = *tokenStart; | |
221 | |
222 if (start == end) | |
223 return InvalidToken; | |
224 | |
225 switch (*start) { | |
226 case 'n': | |
227 if (parseConstToken(start, end, tokenEnd, nullString)) | |
228 return NullToken; | |
229 break; | |
230 case 't': | |
231 if (parseConstToken(start, end, tokenEnd, trueString)) | |
232 return BoolTrue; | |
233 break; | |
234 case 'f': | |
235 if (parseConstToken(start, end, tokenEnd, falseString)) | |
236 return BoolFalse; | |
237 break; | |
238 case '[': | |
239 *tokenEnd = start + 1; | |
240 return ArrayBegin; | |
241 case ']': | |
242 *tokenEnd = start + 1; | |
243 return ArrayEnd; | |
244 case ',': | |
245 *tokenEnd = start + 1; | |
246 return ListSeparator; | |
247 case '{': | |
248 *tokenEnd = start + 1; | |
249 return ObjectBegin; | |
250 case '}': | |
251 *tokenEnd = start + 1; | |
252 return ObjectEnd; | |
253 case ':': | |
254 *tokenEnd = start + 1; | |
255 return ObjectPairSeparator; | |
256 case '0': | |
257 case '1': | |
258 case '2': | |
259 case '3': | |
260 case '4': | |
261 case '5': | |
262 case '6': | |
263 case '7': | |
264 case '8': | |
265 case '9': | |
266 case '-': | |
267 if (parseNumberToken(start, end, tokenEnd)) | |
268 return Number; | |
269 break; | |
270 case '"': | |
271 if (parseStringToken(start + 1, end, tokenEnd)) | |
272 return StringLiteral; | |
273 break; | |
274 } | |
275 return InvalidToken; | |
276 } | |
277 | |
278 inline int hexToInt(UChar c) | |
279 { | |
280 if ('0' <= c && c <= '9') | |
281 return c - '0'; | |
282 if ('A' <= c && c <= 'F') | |
283 return c - 'A' + 10; | |
284 if ('a' <= c && c <= 'f') | |
285 return c - 'a' + 10; | |
286 NOTREACHED(); | |
287 return 0; | |
288 } | |
289 | |
290 bool decodeString(const UChar* start, const UChar* end, String16Builder* output) | |
291 { | |
292 while (start < end) { | |
293 UChar c = *start++; | |
294 if ('\\' != c) { | |
295 output->append(c); | |
296 continue; | |
297 } | |
298 c = *start++; | |
299 | |
300 if (c == 'x') { | |
301 // \x is not supported. | |
302 return false; | |
303 } | |
304 | |
305 switch (c) { | |
306 case '"': | |
307 case '/': | |
308 case '\\': | |
309 break; | |
310 case 'b': | |
311 c = '\b'; | |
312 break; | |
313 case 'f': | |
314 c = '\f'; | |
315 break; | |
316 case 'n': | |
317 c = '\n'; | |
318 break; | |
319 case 'r': | |
320 c = '\r'; | |
321 break; | |
322 case 't': | |
323 c = '\t'; | |
324 break; | |
325 case 'v': | |
326 c = '\v'; | |
327 break; | |
328 case 'u': | |
329 c = (hexToInt(*start) << 12) + | |
330 (hexToInt(*(start + 1)) << 8) + | |
331 (hexToInt(*(start + 2)) << 4) + | |
332 hexToInt(*(start + 3)); | |
333 start += 4; | |
334 break; | |
335 default: | |
336 return false; | |
337 } | |
338 output->append(c); | |
339 } | |
340 return true; | |
341 } | |
342 | |
343 bool decodeString(const UChar* start, const UChar* end, String16* output) | |
344 { | |
345 if (start == end) { | |
346 *output = ""; | |
347 return true; | |
348 } | |
349 if (start > end) | |
350 return false; | |
351 String16Builder buffer; | |
352 buffer.reserveCapacity(end - start); | |
353 if (!decodeString(start, end, &buffer)) | |
354 return false; | |
355 *output = buffer.toString(); | |
356 return true; | |
357 } | |
358 | |
359 std::unique_ptr<Value> buildValue(const UChar* start, const UChar* end, const UC
har** valueTokenEnd, int depth) | |
360 { | |
361 if (depth > stackLimit) | |
362 return nullptr; | |
363 | |
364 std::unique_ptr<Value> result; | |
365 const UChar* tokenStart; | |
366 const UChar* tokenEnd; | |
367 Token token = parseToken(start, end, &tokenStart, &tokenEnd); | |
368 switch (token) { | |
369 case InvalidToken: | |
370 return nullptr; | |
371 case NullToken: | |
372 result = Value::null(); | |
373 break; | |
374 case BoolTrue: | |
375 result = FundamentalValue::create(true); | |
376 break; | |
377 case BoolFalse: | |
378 result = FundamentalValue::create(false); | |
379 break; | |
380 case Number: { | |
381 bool ok; | |
382 double value = String16::charactersToDouble(tokenStart, tokenEnd - token
Start, &ok); | |
383 if (!ok) | |
384 return nullptr; | |
385 int number = static_cast<int>(value); | |
386 if (number == value) | |
387 result = FundamentalValue::create(number); | |
388 else | |
389 result = FundamentalValue::create(value); | |
390 break; | |
391 } | |
392 case StringLiteral: { | |
393 String16 value; | |
394 bool ok = decodeString(tokenStart + 1, tokenEnd - 1, &value); | |
395 if (!ok) | |
396 return nullptr; | |
397 result = StringValue::create(value); | |
398 break; | |
399 } | |
400 case ArrayBegin: { | |
401 std::unique_ptr<ListValue> array = ListValue::create(); | |
402 start = tokenEnd; | |
403 token = parseToken(start, end, &tokenStart, &tokenEnd); | |
404 while (token != ArrayEnd) { | |
405 std::unique_ptr<Value> arrayNode = buildValue(start, end, &tokenEnd,
depth + 1); | |
406 if (!arrayNode) | |
407 return nullptr; | |
408 array->pushValue(std::move(arrayNode)); | |
409 | |
410 // After a list value, we expect a comma or the end of the list. | |
411 start = tokenEnd; | |
412 token = parseToken(start, end, &tokenStart, &tokenEnd); | |
413 if (token == ListSeparator) { | |
414 start = tokenEnd; | |
415 token = parseToken(start, end, &tokenStart, &tokenEnd); | |
416 if (token == ArrayEnd) | |
417 return nullptr; | |
418 } else if (token != ArrayEnd) { | |
419 // Unexpected value after list value. Bail out. | |
420 return nullptr; | |
421 } | |
422 } | |
423 if (token != ArrayEnd) | |
424 return nullptr; | |
425 result = std::move(array); | |
426 break; | |
427 } | |
428 case ObjectBegin: { | |
429 std::unique_ptr<DictionaryValue> object = DictionaryValue::create(); | |
430 start = tokenEnd; | |
431 token = parseToken(start, end, &tokenStart, &tokenEnd); | |
432 while (token != ObjectEnd) { | |
433 if (token != StringLiteral) | |
434 return nullptr; | |
435 String16 key; | |
436 if (!decodeString(tokenStart + 1, tokenEnd - 1, &key)) | |
437 return nullptr; | |
438 start = tokenEnd; | |
439 | |
440 token = parseToken(start, end, &tokenStart, &tokenEnd); | |
441 if (token != ObjectPairSeparator) | |
442 return nullptr; | |
443 start = tokenEnd; | |
444 | |
445 std::unique_ptr<Value> value = buildValue(start, end, &tokenEnd, dep
th + 1); | |
446 if (!value) | |
447 return nullptr; | |
448 object->setValue(key, std::move(value)); | |
449 start = tokenEnd; | |
450 | |
451 // After a key/value pair, we expect a comma or the end of the | |
452 // object. | |
453 token = parseToken(start, end, &tokenStart, &tokenEnd); | |
454 if (token == ListSeparator) { | |
455 start = tokenEnd; | |
456 token = parseToken(start, end, &tokenStart, &tokenEnd); | |
457 if (token == ObjectEnd) | |
458 return nullptr; | |
459 } else if (token != ObjectEnd) { | |
460 // Unexpected value after last object value. Bail out. | |
461 return nullptr; | |
462 } | |
463 } | |
464 if (token != ObjectEnd) | |
465 return nullptr; | |
466 result = std::move(object); | |
467 break; | |
468 } | |
469 | |
470 default: | |
471 // We got a token that's not a value. | |
472 return nullptr; | |
473 } | |
474 | |
475 skipWhitespaceAndComments(tokenEnd, end, valueTokenEnd); | |
476 return result; | |
477 } | |
478 | |
479 std::unique_ptr<Value> parseJSONInternal(const UChar* start, unsigned length) | |
480 { | |
481 const UChar* end = start + length; | |
482 const UChar *tokenEnd; | |
483 std::unique_ptr<Value> value = buildValue(start, end, &tokenEnd, 0); | |
484 if (!value || tokenEnd != end) | |
485 return nullptr; | |
486 return value; | |
487 } | |
488 | |
489 } // anonymous namespace | |
490 | |
491 std::unique_ptr<Value> parseJSON(const String16& json) | |
492 { | |
493 if (json.isEmpty()) | |
494 return nullptr; | |
495 return parseJSONInternal(json.characters16(), json.length()); | |
496 } | |
497 | |
498 } // namespace protocol | |
499 } // namespace blink | |
OLD | NEW |