Index: third_party/inspector_protocol/lib/Parser_cpp.template |
diff --git a/third_party/inspector_protocol/lib/Parser_cpp.template b/third_party/inspector_protocol/lib/Parser_cpp.template |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a103b8228e066e0110bceae8267e1dd070eeb202 |
--- /dev/null |
+++ b/third_party/inspector_protocol/lib/Parser_cpp.template |
@@ -0,0 +1,553 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+{% for namespace in config.protocol.namespace %} |
+namespace {{namespace}} { |
+{% endfor %} |
+ |
+namespace { |
+ |
+const int stackLimit = 1000; |
+ |
+enum Token { |
+ ObjectBegin, |
+ ObjectEnd, |
+ ArrayBegin, |
+ ArrayEnd, |
+ StringLiteral, |
+ Number, |
+ BoolTrue, |
+ BoolFalse, |
+ NullToken, |
+ ListSeparator, |
+ ObjectPairSeparator, |
+ InvalidToken, |
+}; |
+ |
+const char* const nullString = "null"; |
+const char* const trueString = "true"; |
+const char* const falseString = "false"; |
+ |
+bool isASCII(uint16_t c) |
+{ |
+ return !(c & ~0x7F); |
+} |
+ |
+bool isSpaceOrNewLine(uint16_t c) |
+{ |
+ return isASCII(c) && c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); |
+} |
+ |
+double charactersToDouble(const uint16_t* characters, size_t length, bool* ok) |
+{ |
+ std::vector<char> buffer; |
+ buffer.reserve(length + 1); |
+ for (size_t i = 0; i < length; ++i) { |
+ if (!isASCII(characters[i])) { |
+ *ok = false; |
+ return 0; |
+ } |
+ buffer.push_back(static_cast<char>(characters[i])); |
+ } |
+ buffer.push_back('\0'); |
+ char* endptr; |
+ double result = std::strtod(buffer.data(), &endptr); |
+ *ok = !(*endptr); |
+ return result; |
+} |
+ |
+double charactersToDouble(const uint8_t* characters, size_t length, bool* ok) |
+{ |
+ std::string buffer(reinterpret_cast<const char*>(characters), length); |
+ char* endptr; |
+ double result = std::strtod(buffer.data(), &endptr); |
+ *ok = !(*endptr); |
+ return result; |
+} |
+ |
+template<typename Char> |
+bool parseConstToken(const Char* start, const Char* end, const Char** tokenEnd, const char* token) |
+{ |
+ while (start < end && *token != '\0' && *start++ == *token++) { } |
+ if (*token != '\0') |
+ return false; |
+ *tokenEnd = start; |
+ return true; |
+} |
+ |
+template<typename Char> |
+bool readInt(const Char* start, const Char* end, const Char** tokenEnd, bool canHaveLeadingZeros) |
+{ |
+ if (start == end) |
+ return false; |
+ bool haveLeadingZero = '0' == *start; |
+ int length = 0; |
+ while (start < end && '0' <= *start && *start <= '9') { |
+ ++start; |
+ ++length; |
+ } |
+ if (!length) |
+ return false; |
+ if (!canHaveLeadingZeros && length > 1 && haveLeadingZero) |
+ return false; |
+ *tokenEnd = start; |
+ return true; |
+} |
+ |
+template<typename Char> |
+bool parseNumberToken(const Char* start, const Char* end, const Char** tokenEnd) |
+{ |
+ // We just grab the number here. We validate the size in DecodeNumber. |
+ // According to RFC4627, a valid number is: [minus] int [frac] [exp] |
+ if (start == end) |
+ return false; |
+ Char c = *start; |
+ if ('-' == c) |
+ ++start; |
+ |
+ if (!readInt(start, end, &start, false)) |
+ return false; |
+ if (start == end) { |
+ *tokenEnd = start; |
+ return true; |
+ } |
+ |
+ // Optional fraction part |
+ c = *start; |
+ if ('.' == c) { |
+ ++start; |
+ if (!readInt(start, end, &start, true)) |
+ return false; |
+ if (start == end) { |
+ *tokenEnd = start; |
+ return true; |
+ } |
+ c = *start; |
+ } |
+ |
+ // Optional exponent part |
+ if ('e' == c || 'E' == c) { |
+ ++start; |
+ if (start == end) |
+ return false; |
+ c = *start; |
+ if ('-' == c || '+' == c) { |
+ ++start; |
+ if (start == end) |
+ return false; |
+ } |
+ if (!readInt(start, end, &start, true)) |
+ return false; |
+ } |
+ |
+ *tokenEnd = start; |
+ return true; |
+} |
+ |
+template<typename Char> |
+bool readHexDigits(const Char* start, const Char* end, const Char** tokenEnd, int digits) |
+{ |
+ if (end - start < digits) |
+ return false; |
+ for (int i = 0; i < digits; ++i) { |
+ Char c = *start++; |
+ if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'))) |
+ return false; |
+ } |
+ *tokenEnd = start; |
+ return true; |
+} |
+ |
+template<typename Char> |
+bool parseStringToken(const Char* start, const Char* end, const Char** tokenEnd) |
+{ |
+ while (start < end) { |
+ Char c = *start++; |
+ if ('\\' == c) { |
+ if (start == end) |
+ return false; |
+ c = *start++; |
+ // Make sure the escaped char is valid. |
+ switch (c) { |
+ case 'x': |
+ if (!readHexDigits(start, end, &start, 2)) |
+ return false; |
+ break; |
+ case 'u': |
+ if (!readHexDigits(start, end, &start, 4)) |
+ return false; |
+ break; |
+ case '\\': |
+ case '/': |
+ case 'b': |
+ case 'f': |
+ case 'n': |
+ case 'r': |
+ case 't': |
+ case 'v': |
+ case '"': |
+ break; |
+ default: |
+ return false; |
+ } |
+ } else if ('"' == c) { |
+ *tokenEnd = start; |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+template<typename Char> |
+bool skipComment(const Char* start, const Char* end, const Char** commentEnd) |
+{ |
+ if (start == end) |
+ return false; |
+ |
+ if (*start != '/' || start + 1 >= end) |
+ return false; |
+ ++start; |
+ |
+ if (*start == '/') { |
+ // Single line comment, read to newline. |
+ for (++start; start < end; ++start) { |
+ if (*start == '\n' || *start == '\r') { |
+ *commentEnd = start + 1; |
+ return true; |
+ } |
+ } |
+ *commentEnd = end; |
+ // Comment reaches end-of-input, which is fine. |
+ return true; |
+ } |
+ |
+ if (*start == '*') { |
+ Char previous = '\0'; |
+ // Block comment, read until end marker. |
+ for (++start; start < end; previous = *start++) { |
+ if (previous == '*' && *start == '/') { |
+ *commentEnd = start + 1; |
+ return true; |
+ } |
+ } |
+ // Block comment must close before end-of-input. |
+ return false; |
+ } |
+ |
+ return false; |
+} |
+ |
+template<typename Char> |
+void skipWhitespaceAndComments(const Char* start, const Char* end, const Char** whitespaceEnd) |
+{ |
+ while (start < end) { |
+ if (isSpaceOrNewLine(*start)) { |
+ ++start; |
+ } else if (*start == '/') { |
+ const Char* commentEnd; |
+ if (!skipComment(start, end, &commentEnd)) |
+ break; |
+ start = commentEnd; |
+ } else { |
+ break; |
+ } |
+ } |
+ *whitespaceEnd = start; |
+} |
+ |
+template<typename Char> |
+Token parseToken(const Char* start, const Char* end, const Char** tokenStart, const Char** tokenEnd) |
+{ |
+ skipWhitespaceAndComments(start, end, tokenStart); |
+ start = *tokenStart; |
+ |
+ if (start == end) |
+ return InvalidToken; |
+ |
+ switch (*start) { |
+ case 'n': |
+ if (parseConstToken(start, end, tokenEnd, nullString)) |
+ return NullToken; |
+ break; |
+ case 't': |
+ if (parseConstToken(start, end, tokenEnd, trueString)) |
+ return BoolTrue; |
+ break; |
+ case 'f': |
+ if (parseConstToken(start, end, tokenEnd, falseString)) |
+ return BoolFalse; |
+ break; |
+ case '[': |
+ *tokenEnd = start + 1; |
+ return ArrayBegin; |
+ case ']': |
+ *tokenEnd = start + 1; |
+ return ArrayEnd; |
+ case ',': |
+ *tokenEnd = start + 1; |
+ return ListSeparator; |
+ case '{': |
+ *tokenEnd = start + 1; |
+ return ObjectBegin; |
+ case '}': |
+ *tokenEnd = start + 1; |
+ return ObjectEnd; |
+ case ':': |
+ *tokenEnd = start + 1; |
+ return ObjectPairSeparator; |
+ case '0': |
+ case '1': |
+ case '2': |
+ case '3': |
+ case '4': |
+ case '5': |
+ case '6': |
+ case '7': |
+ case '8': |
+ case '9': |
+ case '-': |
+ if (parseNumberToken(start, end, tokenEnd)) |
+ return Number; |
+ break; |
+ case '"': |
+ if (parseStringToken(start + 1, end, tokenEnd)) |
+ return StringLiteral; |
+ break; |
+ } |
+ return InvalidToken; |
+} |
+ |
+template<typename Char> |
+int hexToInt(Char c) |
+{ |
+ if ('0' <= c && c <= '9') |
+ return c - '0'; |
+ if ('A' <= c && c <= 'F') |
+ return c - 'A' + 10; |
+ if ('a' <= c && c <= 'f') |
+ return c - 'a' + 10; |
+ DCHECK(false); |
+ return 0; |
+} |
+ |
+template<typename Char> |
+bool decodeString(const Char* start, const Char* end, StringBuilder* output) |
+{ |
+ while (start < end) { |
+ uint16_t c = *start++; |
+ if ('\\' != c) { |
+ output->append(c); |
+ continue; |
+ } |
+ if (start == end) |
+ return false; |
+ c = *start++; |
+ |
+ if (c == 'x') { |
+ // \x is not supported. |
+ return false; |
+ } |
+ |
+ switch (c) { |
+ case '"': |
+ case '/': |
+ case '\\': |
+ break; |
+ case 'b': |
+ c = '\b'; |
+ break; |
+ case 'f': |
+ c = '\f'; |
+ break; |
+ case 'n': |
+ c = '\n'; |
+ break; |
+ case 'r': |
+ c = '\r'; |
+ break; |
+ case 't': |
+ c = '\t'; |
+ break; |
+ case 'v': |
+ c = '\v'; |
+ break; |
+ case 'u': |
+ c = (hexToInt(*start) << 12) + |
+ (hexToInt(*(start + 1)) << 8) + |
+ (hexToInt(*(start + 2)) << 4) + |
+ hexToInt(*(start + 3)); |
+ start += 4; |
+ break; |
+ default: |
+ return false; |
+ } |
+ output->append(c); |
+ } |
+ return true; |
+} |
+ |
+template<typename Char> |
+bool decodeString(const Char* start, const Char* end, String* output) |
+{ |
+ if (start == end) { |
+ *output = ""; |
+ return true; |
+ } |
+ if (start > end) |
+ return false; |
+ StringBuilder buffer; |
+ StringUtil::builderReserve(buffer, end - start); |
+ if (!decodeString(start, end, &buffer)) |
+ return false; |
+ *output = buffer.toString(); |
+ return true; |
+} |
+ |
+template<typename Char> |
+std::unique_ptr<Value> buildValue(const Char* start, const Char* end, const Char** valueTokenEnd, int depth) |
+{ |
+ if (depth > stackLimit) |
+ return nullptr; |
+ |
+ std::unique_ptr<Value> result; |
+ const Char* tokenStart; |
+ const Char* tokenEnd; |
+ Token token = parseToken(start, end, &tokenStart, &tokenEnd); |
+ switch (token) { |
+ case InvalidToken: |
+ return nullptr; |
+ case NullToken: |
+ result = Value::null(); |
+ break; |
+ case BoolTrue: |
+ result = FundamentalValue::create(true); |
+ break; |
+ case BoolFalse: |
+ result = FundamentalValue::create(false); |
+ break; |
+ case Number: { |
+ bool ok; |
+ double value = charactersToDouble(tokenStart, tokenEnd - tokenStart, &ok); |
+ if (!ok) |
+ return nullptr; |
+ int number = static_cast<int>(value); |
+ if (number == value) |
+ result = FundamentalValue::create(number); |
+ else |
+ result = FundamentalValue::create(value); |
+ break; |
+ } |
+ case StringLiteral: { |
+ String value; |
+ bool ok = decodeString(tokenStart + 1, tokenEnd - 1, &value); |
+ if (!ok) |
+ return nullptr; |
+ result = StringValue::create(value); |
+ break; |
+ } |
+ case ArrayBegin: { |
+ std::unique_ptr<ListValue> array = ListValue::create(); |
+ start = tokenEnd; |
+ token = parseToken(start, end, &tokenStart, &tokenEnd); |
+ while (token != ArrayEnd) { |
+ std::unique_ptr<Value> arrayNode = buildValue(start, end, &tokenEnd, depth + 1); |
+ if (!arrayNode) |
+ return nullptr; |
+ array->pushValue(std::move(arrayNode)); |
+ |
+ // After a list value, we expect a comma or the end of the list. |
+ start = tokenEnd; |
+ token = parseToken(start, end, &tokenStart, &tokenEnd); |
+ if (token == ListSeparator) { |
+ start = tokenEnd; |
+ token = parseToken(start, end, &tokenStart, &tokenEnd); |
+ if (token == ArrayEnd) |
+ return nullptr; |
+ } else if (token != ArrayEnd) { |
+ // Unexpected value after list value. Bail out. |
+ return nullptr; |
+ } |
+ } |
+ if (token != ArrayEnd) |
+ return nullptr; |
+ result = std::move(array); |
+ break; |
+ } |
+ case ObjectBegin: { |
+ std::unique_ptr<DictionaryValue> object = DictionaryValue::create(); |
+ start = tokenEnd; |
+ token = parseToken(start, end, &tokenStart, &tokenEnd); |
+ while (token != ObjectEnd) { |
+ if (token != StringLiteral) |
+ return nullptr; |
+ String key; |
+ if (!decodeString(tokenStart + 1, tokenEnd - 1, &key)) |
+ return nullptr; |
+ start = tokenEnd; |
+ |
+ token = parseToken(start, end, &tokenStart, &tokenEnd); |
+ if (token != ObjectPairSeparator) |
+ return nullptr; |
+ start = tokenEnd; |
+ |
+ std::unique_ptr<Value> value = buildValue(start, end, &tokenEnd, depth + 1); |
+ if (!value) |
+ return nullptr; |
+ object->setValue(key, std::move(value)); |
+ start = tokenEnd; |
+ |
+ // After a key/value pair, we expect a comma or the end of the |
+ // object. |
+ token = parseToken(start, end, &tokenStart, &tokenEnd); |
+ if (token == ListSeparator) { |
+ start = tokenEnd; |
+ token = parseToken(start, end, &tokenStart, &tokenEnd); |
+ if (token == ObjectEnd) |
+ return nullptr; |
+ } else if (token != ObjectEnd) { |
+ // Unexpected value after last object value. Bail out. |
+ return nullptr; |
+ } |
+ } |
+ if (token != ObjectEnd) |
+ return nullptr; |
+ result = std::move(object); |
+ break; |
+ } |
+ |
+ default: |
+ // We got a token that's not a value. |
+ return nullptr; |
+ } |
+ |
+ skipWhitespaceAndComments(tokenEnd, end, valueTokenEnd); |
+ return result; |
+} |
+ |
+template<typename Char> |
+std::unique_ptr<Value> parseJSONInternal(const Char* start, unsigned length) |
+{ |
+ const Char* end = start + length; |
+ const Char *tokenEnd; |
+ std::unique_ptr<Value> value = buildValue(start, end, &tokenEnd, 0); |
+ if (!value || tokenEnd != end) |
+ return nullptr; |
+ return value; |
+} |
+ |
+} // anonymous namespace |
+ |
+std::unique_ptr<Value> parseJSON(const uint16_t* characters, unsigned length) |
+{ |
+ return parseJSONInternal<uint16_t>(characters, length); |
+} |
+ |
+std::unique_ptr<Value> parseJSON(const uint8_t* characters, unsigned length) |
+{ |
+ return parseJSONInternal<uint8_t>(characters, length); |
+} |
+ |
+{% for namespace in config.protocol.namespace %} |
+} // namespace {{namespace}} |
+{% endfor %} |