OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "platform/inspector_protocol/Parser.h" | 5 #include "platform/inspector_protocol/Parser.h" |
6 | 6 |
7 #include "platform/inspector_protocol/String16.h" | 7 #include "platform/inspector_protocol/String16.h" |
8 #include "platform/inspector_protocol/Values.h" | 8 #include "platform/inspector_protocol/Values.h" |
9 | 9 |
10 namespace blink { | 10 namespace blink { |
(...skipping 15 matching lines...) Expand all Loading... |
26 NullToken, | 26 NullToken, |
27 ListSeparator, | 27 ListSeparator, |
28 ObjectPairSeparator, | 28 ObjectPairSeparator, |
29 InvalidToken, | 29 InvalidToken, |
30 }; | 30 }; |
31 | 31 |
32 const char* const nullString = "null"; | 32 const char* const nullString = "null"; |
33 const char* const trueString = "true"; | 33 const char* const trueString = "true"; |
34 const char* const falseString = "false"; | 34 const char* const falseString = "false"; |
35 | 35 |
36 template<typename CharType> | 36 bool parseConstToken(const UChar* start, const UChar* end, const UChar** tokenEn
d, const char* token) |
37 bool parseConstToken(const CharType* start, const CharType* end, const CharType*
* tokenEnd, const char* token) | |
38 { | 37 { |
39 while (start < end && *token != '\0' && *start++ == *token++) { } | 38 while (start < end && *token != '\0' && *start++ == *token++) { } |
40 if (*token != '\0') | 39 if (*token != '\0') |
41 return false; | 40 return false; |
42 *tokenEnd = start; | 41 *tokenEnd = start; |
43 return true; | 42 return true; |
44 } | 43 } |
45 | 44 |
46 template<typename CharType> | 45 bool readInt(const UChar* start, const UChar* end, const UChar** tokenEnd, bool
canHaveLeadingZeros) |
47 bool readInt(const CharType* start, const CharType* end, const CharType** tokenE
nd, bool canHaveLeadingZeros) | |
48 { | 46 { |
49 if (start == end) | 47 if (start == end) |
50 return false; | 48 return false; |
51 bool haveLeadingZero = '0' == *start; | 49 bool haveLeadingZero = '0' == *start; |
52 int length = 0; | 50 int length = 0; |
53 while (start < end && '0' <= *start && *start <= '9') { | 51 while (start < end && '0' <= *start && *start <= '9') { |
54 ++start; | 52 ++start; |
55 ++length; | 53 ++length; |
56 } | 54 } |
57 if (!length) | 55 if (!length) |
58 return false; | 56 return false; |
59 if (!canHaveLeadingZeros && length > 1 && haveLeadingZero) | 57 if (!canHaveLeadingZeros && length > 1 && haveLeadingZero) |
60 return false; | 58 return false; |
61 *tokenEnd = start; | 59 *tokenEnd = start; |
62 return true; | 60 return true; |
63 } | 61 } |
64 | 62 |
65 template<typename CharType> | 63 bool parseNumberToken(const UChar* start, const UChar* end, const UChar** tokenE
nd) |
66 bool parseNumberToken(const CharType* start, const CharType* end, const CharType
** tokenEnd) | |
67 { | 64 { |
68 // We just grab the number here. We validate the size in DecodeNumber. | 65 // We just grab the number here. We validate the size in DecodeNumber. |
69 // According to RFC4627, a valid number is: [minus] int [frac] [exp] | 66 // According to RFC4627, a valid number is: [minus] int [frac] [exp] |
70 if (start == end) | 67 if (start == end) |
71 return false; | 68 return false; |
72 CharType c = *start; | 69 UChar c = *start; |
73 if ('-' == c) | 70 if ('-' == c) |
74 ++start; | 71 ++start; |
75 | 72 |
76 if (!readInt(start, end, &start, false)) | 73 if (!readInt(start, end, &start, false)) |
77 return false; | 74 return false; |
78 if (start == end) { | 75 if (start == end) { |
79 *tokenEnd = start; | 76 *tokenEnd = start; |
80 return true; | 77 return true; |
81 } | 78 } |
82 | 79 |
(...skipping 22 matching lines...) Expand all Loading... |
105 return false; | 102 return false; |
106 } | 103 } |
107 if (!readInt(start, end, &start, true)) | 104 if (!readInt(start, end, &start, true)) |
108 return false; | 105 return false; |
109 } | 106 } |
110 | 107 |
111 *tokenEnd = start; | 108 *tokenEnd = start; |
112 return true; | 109 return true; |
113 } | 110 } |
114 | 111 |
115 template<typename CharType> | 112 bool readHexDigits(const UChar* start, const UChar* end, const UChar** tokenEnd,
int digits) |
116 bool readHexDigits(const CharType* start, const CharType* end, const CharType**
tokenEnd, int digits) | |
117 { | 113 { |
118 if (end - start < digits) | 114 if (end - start < digits) |
119 return false; | 115 return false; |
120 for (int i = 0; i < digits; ++i) { | 116 for (int i = 0; i < digits; ++i) { |
121 CharType c = *start++; | 117 UChar c = *start++; |
122 if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c
<= 'F'))) | 118 if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c
<= 'F'))) |
123 return false; | 119 return false; |
124 } | 120 } |
125 *tokenEnd = start; | 121 *tokenEnd = start; |
126 return true; | 122 return true; |
127 } | 123 } |
128 | 124 |
129 template<typename CharType> | 125 bool parseStringToken(const UChar* start, const UChar* end, const UChar** tokenE
nd) |
130 bool parseStringToken(const CharType* start, const CharType* end, const CharType
** tokenEnd) | |
131 { | 126 { |
132 while (start < end) { | 127 while (start < end) { |
133 CharType c = *start++; | 128 UChar c = *start++; |
134 if ('\\' == c) { | 129 if ('\\' == c) { |
135 c = *start++; | 130 c = *start++; |
136 // Make sure the escaped char is valid. | 131 // Make sure the escaped char is valid. |
137 switch (c) { | 132 switch (c) { |
138 case 'x': | 133 case 'x': |
139 if (!readHexDigits(start, end, &start, 2)) | 134 if (!readHexDigits(start, end, &start, 2)) |
140 return false; | 135 return false; |
141 break; | 136 break; |
142 case 'u': | 137 case 'u': |
143 if (!readHexDigits(start, end, &start, 4)) | 138 if (!readHexDigits(start, end, &start, 4)) |
(...skipping 13 matching lines...) Expand all Loading... |
157 return false; | 152 return false; |
158 } | 153 } |
159 } else if ('"' == c) { | 154 } else if ('"' == c) { |
160 *tokenEnd = start; | 155 *tokenEnd = start; |
161 return true; | 156 return true; |
162 } | 157 } |
163 } | 158 } |
164 return false; | 159 return false; |
165 } | 160 } |
166 | 161 |
167 template<typename CharType> | 162 bool skipComment(const UChar* start, const UChar* end, const UChar** commentEnd) |
168 bool skipComment(const CharType* start, const CharType* end, const CharType** co
mmentEnd) | |
169 { | 163 { |
170 if (start == end) | 164 if (start == end) |
171 return false; | 165 return false; |
172 | 166 |
173 if (*start != '/' || start + 1 >= end) | 167 if (*start != '/' || start + 1 >= end) |
174 return false; | 168 return false; |
175 ++start; | 169 ++start; |
176 | 170 |
177 if (*start == '/') { | 171 if (*start == '/') { |
178 // Single line comment, read to newline. | 172 // Single line comment, read to newline. |
179 for (++start; start < end; ++start) { | 173 for (++start; start < end; ++start) { |
180 if (*start == '\n' || *start == '\r') { | 174 if (*start == '\n' || *start == '\r') { |
181 *commentEnd = start + 1; | 175 *commentEnd = start + 1; |
182 return true; | 176 return true; |
183 } | 177 } |
184 } | 178 } |
185 *commentEnd = end; | 179 *commentEnd = end; |
186 // Comment reaches end-of-input, which is fine. | 180 // Comment reaches end-of-input, which is fine. |
187 return true; | 181 return true; |
188 } | 182 } |
189 | 183 |
190 if (*start == '*') { | 184 if (*start == '*') { |
191 CharType previous = '\0'; | 185 UChar previous = '\0'; |
192 // Block comment, read until end marker. | 186 // Block comment, read until end marker. |
193 for (++start; start < end; previous = *start++) { | 187 for (++start; start < end; previous = *start++) { |
194 if (previous == '*' && *start == '/') { | 188 if (previous == '*' && *start == '/') { |
195 *commentEnd = start + 1; | 189 *commentEnd = start + 1; |
196 return true; | 190 return true; |
197 } | 191 } |
198 } | 192 } |
199 // Block comment must close before end-of-input. | 193 // Block comment must close before end-of-input. |
200 return false; | 194 return false; |
201 } | 195 } |
202 | 196 |
203 return false; | 197 return false; |
204 } | 198 } |
205 | 199 |
206 template<typename CharType> | 200 void skipWhitespaceAndComments(const UChar* start, const UChar* end, const UChar
** whitespaceEnd) |
207 void skipWhitespaceAndComments(const CharType* start, const CharType* end, const
CharType** whitespaceEnd) | |
208 { | 201 { |
209 while (start < end) { | 202 while (start < end) { |
210 if (isSpaceOrNewline(*start)) { | 203 if (isSpaceOrNewline(*start)) { |
211 ++start; | 204 ++start; |
212 } else if (*start == '/') { | 205 } else if (*start == '/') { |
213 const CharType* commentEnd; | 206 const UChar* commentEnd; |
214 if (!skipComment(start, end, &commentEnd)) | 207 if (!skipComment(start, end, &commentEnd)) |
215 break; | 208 break; |
216 start = commentEnd; | 209 start = commentEnd; |
217 } else { | 210 } else { |
218 break; | 211 break; |
219 } | 212 } |
220 } | 213 } |
221 *whitespaceEnd = start; | 214 *whitespaceEnd = start; |
222 } | 215 } |
223 | 216 |
224 template<typename CharType> | 217 Token parseToken(const UChar* start, const UChar* end, const UChar** tokenStart,
const UChar** tokenEnd) |
225 Token parseToken(const CharType* start, const CharType* end, const CharType** to
kenStart, const CharType** tokenEnd) | |
226 { | 218 { |
227 skipWhitespaceAndComments(start, end, tokenStart); | 219 skipWhitespaceAndComments(start, end, tokenStart); |
228 start = *tokenStart; | 220 start = *tokenStart; |
229 | 221 |
230 if (start == end) | 222 if (start == end) |
231 return InvalidToken; | 223 return InvalidToken; |
232 | 224 |
233 switch (*start) { | 225 switch (*start) { |
234 case 'n': | 226 case 'n': |
235 if (parseConstToken(start, end, tokenEnd, nullString)) | 227 if (parseConstToken(start, end, tokenEnd, nullString)) |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
276 return Number; | 268 return Number; |
277 break; | 269 break; |
278 case '"': | 270 case '"': |
279 if (parseStringToken(start + 1, end, tokenEnd)) | 271 if (parseStringToken(start + 1, end, tokenEnd)) |
280 return StringLiteral; | 272 return StringLiteral; |
281 break; | 273 break; |
282 } | 274 } |
283 return InvalidToken; | 275 return InvalidToken; |
284 } | 276 } |
285 | 277 |
286 template<typename CharType> | 278 inline int hexToInt(UChar c) |
287 inline int hexToInt(CharType c) | |
288 { | 279 { |
289 if ('0' <= c && c <= '9') | 280 if ('0' <= c && c <= '9') |
290 return c - '0'; | 281 return c - '0'; |
291 if ('A' <= c && c <= 'F') | 282 if ('A' <= c && c <= 'F') |
292 return c - 'A' + 10; | 283 return c - 'A' + 10; |
293 if ('a' <= c && c <= 'f') | 284 if ('a' <= c && c <= 'f') |
294 return c - 'a' + 10; | 285 return c - 'a' + 10; |
295 ASSERT_NOT_REACHED(); | 286 ASSERT_NOT_REACHED(); |
296 return 0; | 287 return 0; |
297 } | 288 } |
298 | 289 |
299 template<typename CharType> | 290 bool decodeString(const UChar* start, const UChar* end, String16Builder* output) |
300 bool decodeUTF8(const CharType* start, const CharType* end, const CharType** utf
8charEnd, String16Builder* output) | |
301 { | |
302 UChar utf16[4] = {0}; | |
303 char utf8[6] = {0}; | |
304 size_t utf8count = 0; | |
305 | |
306 while (start < end) { | |
307 if (start + 1 >= end || *start != '\\' || *(start + 1) != 'x') | |
308 return false; | |
309 start += 2; | |
310 | |
311 // Accumulate one more utf8 character and try converting to utf16. | |
312 if (start + 1 >= end) | |
313 return false; | |
314 utf8[utf8count++] = (hexToInt(*start) << 4) + hexToInt(*(start + 1)); | |
315 start += 2; | |
316 | |
317 const char* utf8start = utf8; | |
318 UChar* utf16start = utf16; | |
319 String16::ConversionResult conversionResult = String16::convertUTF8ToUTF
16(&utf8start, utf8start + utf8count, &utf16start, utf16start + 4, nullptr, true
); | |
320 | |
321 if (conversionResult == String16::sourceIllegal) | |
322 return false; | |
323 | |
324 if (conversionResult == String16::conversionOK) { | |
325 // Not all utf8 characters were consumed - failed parsing. | |
326 if (utf8start != utf8 + utf8count) | |
327 return false; | |
328 | |
329 size_t utf16length = utf16start - utf16; | |
330 output->append(utf16, utf16length); | |
331 *utf8charEnd = start; | |
332 return true; | |
333 } | |
334 | |
335 // Keep accumulating utf8 characters up to buffer length (6 should be en
ough). | |
336 if (utf8count >= 6) | |
337 return false; | |
338 } | |
339 | |
340 return false; | |
341 } | |
342 | |
343 template<typename CharType> | |
344 bool decodeString(const CharType* start, const CharType* end, String16Builder* o
utput) | |
345 { | 291 { |
346 while (start < end) { | 292 while (start < end) { |
347 UChar c = *start++; | 293 UChar c = *start++; |
348 if ('\\' != c) { | 294 if ('\\' != c) { |
349 output->append(c); | 295 output->append(c); |
350 continue; | 296 continue; |
351 } | 297 } |
352 c = *start++; | 298 c = *start++; |
353 | 299 |
354 if (c == 'x') { | 300 if (c == 'x') { |
355 // Rewind "\x". | 301 // \x is not supported. |
356 if (!decodeUTF8(start - 2, end, &start, output)) | 302 return false; |
357 return false; | |
358 continue; | |
359 } | 303 } |
360 | 304 |
361 switch (c) { | 305 switch (c) { |
362 case '"': | 306 case '"': |
363 case '/': | 307 case '/': |
364 case '\\': | 308 case '\\': |
365 break; | 309 break; |
366 case 'b': | 310 case 'b': |
367 c = '\b'; | 311 c = '\b'; |
368 break; | 312 break; |
(...skipping 20 matching lines...) Expand all Loading... |
389 start += 4; | 333 start += 4; |
390 break; | 334 break; |
391 default: | 335 default: |
392 return false; | 336 return false; |
393 } | 337 } |
394 output->append(c); | 338 output->append(c); |
395 } | 339 } |
396 return true; | 340 return true; |
397 } | 341 } |
398 | 342 |
399 template<typename CharType> | 343 bool decodeString(const UChar* start, const UChar* end, String16* output) |
400 bool decodeString(const CharType* start, const CharType* end, String16* output) | |
401 { | 344 { |
402 if (start == end) { | 345 if (start == end) { |
403 *output = ""; | 346 *output = ""; |
404 return true; | 347 return true; |
405 } | 348 } |
406 if (start > end) | 349 if (start > end) |
407 return false; | 350 return false; |
408 String16Builder buffer; | 351 String16Builder buffer; |
409 buffer.reserveCapacity(end - start); | 352 buffer.reserveCapacity(end - start); |
410 if (!decodeString(start, end, &buffer)) | 353 if (!decodeString(start, end, &buffer)) |
411 return false; | 354 return false; |
412 *output = buffer.toString(); | 355 *output = buffer.toString(); |
413 // Validate constructed utf16 string. | |
414 if (!output->validateUTF8()) | |
415 return false; | |
416 return true; | 356 return true; |
417 } | 357 } |
418 | 358 |
419 template<typename CharType> | 359 PassOwnPtr<Value> buildValue(const UChar* start, const UChar* end, const UChar**
valueTokenEnd, int depth) |
420 PassOwnPtr<Value> buildValue(const CharType* start, const CharType* end, const C
harType** valueTokenEnd, int depth) | |
421 { | 360 { |
422 if (depth > stackLimit) | 361 if (depth > stackLimit) |
423 return nullptr; | 362 return nullptr; |
424 | 363 |
425 OwnPtr<Value> result; | 364 OwnPtr<Value> result; |
426 const CharType* tokenStart; | 365 const UChar* tokenStart; |
427 const CharType* tokenEnd; | 366 const UChar* tokenEnd; |
428 Token token = parseToken(start, end, &tokenStart, &tokenEnd); | 367 Token token = parseToken(start, end, &tokenStart, &tokenEnd); |
429 switch (token) { | 368 switch (token) { |
430 case InvalidToken: | 369 case InvalidToken: |
431 return nullptr; | 370 return nullptr; |
432 case NullToken: | 371 case NullToken: |
433 result = Value::null(); | 372 result = Value::null(); |
434 break; | 373 break; |
435 case BoolTrue: | 374 case BoolTrue: |
436 result = FundamentalValue::create(true); | 375 result = FundamentalValue::create(true); |
437 break; | 376 break; |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
526 | 465 |
527 default: | 466 default: |
528 // We got a token that's not a value. | 467 // We got a token that's not a value. |
529 return nullptr; | 468 return nullptr; |
530 } | 469 } |
531 | 470 |
532 skipWhitespaceAndComments(tokenEnd, end, valueTokenEnd); | 471 skipWhitespaceAndComments(tokenEnd, end, valueTokenEnd); |
533 return result.release(); | 472 return result.release(); |
534 } | 473 } |
535 | 474 |
536 template<typename CharType> | 475 PassOwnPtr<Value> parseJSONInternal(const UChar* start, unsigned length) |
537 PassOwnPtr<Value> parseJSONInternal(const CharType* start, unsigned length) | |
538 { | 476 { |
539 const CharType* end = start + length; | 477 const UChar* end = start + length; |
540 const CharType *tokenEnd; | 478 const UChar *tokenEnd; |
541 OwnPtr<Value> value = buildValue(start, end, &tokenEnd, 0); | 479 OwnPtr<Value> value = buildValue(start, end, &tokenEnd, 0); |
542 if (!value || tokenEnd != end) | 480 if (!value || tokenEnd != end) |
543 return nullptr; | 481 return nullptr; |
544 return value.release(); | 482 return value.release(); |
545 } | 483 } |
546 | 484 |
547 } // anonymous namespace | 485 } // anonymous namespace |
548 | 486 |
549 PassOwnPtr<Value> parseJSON(const String16& json) | 487 PassOwnPtr<Value> parseJSON(const String16& json) |
550 { | 488 { |
551 if (json.isEmpty()) | 489 if (json.isEmpty()) |
552 return nullptr; | 490 return nullptr; |
553 if (json.is8Bit()) | |
554 return parseJSONInternal(json.characters8(), json.length()); | |
555 return parseJSONInternal(json.characters16(), json.length()); | 491 return parseJSONInternal(json.characters16(), json.length()); |
556 } | 492 } |
557 | 493 |
558 } // namespace protocol | 494 } // namespace protocol |
559 } // namespace blink | 495 } // namespace blink |
OLD | NEW |