summaryrefslogtreecommitdiff
path: root/tools/inspector_protocol
diff options
context:
space:
mode:
authorPavel Feldman <pfeldman@chromium.org>2019-02-25 12:43:13 -0800
committerRefael Ackermann <refack@gmail.com>2019-03-04 11:44:49 -0500
commitd775d74698e1b321580b1560fbcee7943750aedc (patch)
treee603e6285066de1e55a25397deef5edd41959887 /tools/inspector_protocol
parentb2abda9ba0b7b8bfbbf14e990ea86434f3f20de3 (diff)
downloadandroid-node-v8-d775d74698e1b321580b1560fbcee7943750aedc.tar.gz
android-node-v8-d775d74698e1b321580b1560fbcee7943750aedc.tar.bz2
android-node-v8-d775d74698e1b321580b1560fbcee7943750aedc.zip
tools: roll inspector_protocol to f67ec5
Fixes: https://github.com/nodejs/node/issues/25808 PR-URL: https://github.com/nodejs/node/pull/26303 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Refael Ackermann <refack@gmail.com>
Diffstat (limited to 'tools/inspector_protocol')
-rw-r--r--tools/inspector_protocol/README.node (renamed from tools/inspector_protocol/README.v8)2
-rwxr-xr-xtools/inspector_protocol/check_protocol_compatibility.py (renamed from tools/inspector_protocol/CheckProtocolCompatibility.py)17
-rwxr-xr-x[-rw-r--r--]tools/inspector_protocol/code_generator.py (renamed from tools/inspector_protocol/CodeGenerator.py)74
-rwxr-xr-xtools/inspector_protocol/concatenate_protocols.py (renamed from tools/inspector_protocol/ConcatenateProtocols.py)4
-rwxr-xr-xtools/inspector_protocol/convert_protocol_to_json.py37
-rw-r--r--tools/inspector_protocol/lib/Allocator_h.template9
-rw-r--r--tools/inspector_protocol/lib/Array_h.template2
-rw-r--r--tools/inspector_protocol/lib/CBOR_cpp.template803
-rw-r--r--tools/inspector_protocol/lib/CBOR_h.template416
-rw-r--r--tools/inspector_protocol/lib/DispatcherBase_cpp.template173
-rw-r--r--tools/inspector_protocol/lib/DispatcherBase_h.template71
-rw-r--r--tools/inspector_protocol/lib/ErrorSupport_cpp.template2
-rw-r--r--tools/inspector_protocol/lib/ErrorSupport_h.template4
-rw-r--r--tools/inspector_protocol/lib/Forward_h.template6
-rw-r--r--tools/inspector_protocol/lib/FrontendChannel_h.template12
-rw-r--r--tools/inspector_protocol/lib/Maybe_h.template71
-rw-r--r--tools/inspector_protocol/lib/Object_cpp.template2
-rw-r--r--tools/inspector_protocol/lib/Object_h.template4
-rw-r--r--tools/inspector_protocol/lib/Parser_cpp.template7
-rw-r--r--tools/inspector_protocol/lib/Parser_h.template2
-rw-r--r--tools/inspector_protocol/lib/Protocol_cpp.template4
-rw-r--r--tools/inspector_protocol/lib/ValueConversions_h.template29
-rw-r--r--tools/inspector_protocol/lib/Values_cpp.template254
-rw-r--r--tools/inspector_protocol/lib/Values_h.template58
-rw-r--r--tools/inspector_protocol/lib/base_string_adapter_cc.template304
-rw-r--r--tools/inspector_protocol/lib/base_string_adapter_h.template150
-rw-r--r--tools/inspector_protocol/pdl.py (renamed from tools/inspector_protocol/ConvertProtocolToJSON.py)48
-rw-r--r--tools/inspector_protocol/templates/Exported_h.template18
-rw-r--r--tools/inspector_protocol/templates/Imported_h.template45
-rw-r--r--tools/inspector_protocol/templates/TypeBuilder_cpp.template92
-rw-r--r--tools/inspector_protocol/templates/TypeBuilder_h.template14
31 files changed, 2470 insertions, 264 deletions
diff --git a/tools/inspector_protocol/README.v8 b/tools/inspector_protocol/README.node
index 8a82f2a9c9..6f22020a4d 100644
--- a/tools/inspector_protocol/README.v8
+++ b/tools/inspector_protocol/README.node
@@ -2,7 +2,7 @@ Name: inspector protocol
Short Name: inspector_protocol
URL: https://chromium.googlesource.com/deps/inspector_protocol/
Version: 0
-Revision: 752d4abd13119010cf30e454e8ef9b5fb7ef43a3
+Revision: f67ec5180f476830e839226b5ca948e43070fdab
License: BSD
License File: LICENSE
Security Critical: no
diff --git a/tools/inspector_protocol/CheckProtocolCompatibility.py b/tools/inspector_protocol/check_protocol_compatibility.py
index c70162a2a4..d2df244fa9 100755
--- a/tools/inspector_protocol/CheckProtocolCompatibility.py
+++ b/tools/inspector_protocol/check_protocol_compatibility.py
@@ -45,11 +45,14 @@
#
# Adding --show_changes to the command line prints out a list of valid public API changes.
+from __future__ import print_function
import copy
import os.path
import optparse
import sys
+import pdl
+
try:
import json
except ImportError:
@@ -166,6 +169,11 @@ def compare_types(context, kind, type_1, type_2, types_map_1, types_map_2, depth
base_type_1 = type_1["type"]
base_type_2 = type_2["type"]
+ # Binary and string have the same wire representation in JSON.
+ if ((base_type_1 == "string" and base_type_2 == "binary") or
+ (base_type_2 == "string" and base_type_1 == "binary")):
+ return
+
if base_type_1 != base_type_2:
errors.append("%s: %s base type mismatch, '%s' vs '%s'" % (context, kind, base_type_1, base_type_2))
elif base_type_1 == "object":
@@ -228,8 +236,8 @@ def load_schema(file_name, domains):
if not os.path.isfile(file_name):
return
input_file = open(file_name, "r")
- json_string = input_file.read()
- parsed_json = json.loads(json_string)
+ parsed_json = pdl.loads(input_file.read(), file_name)
+ input_file.close()
domains += parsed_json["domains"]
return parsed_json["version"]
@@ -422,6 +430,7 @@ def load_domains_and_baselines(file_name, domains, baseline_domains):
version = load_schema(os.path.normpath(file_name), domains)
suffix = "-%s.%s.json" % (version["major"], version["minor"])
baseline_file = file_name.replace(".json", suffix)
+ baseline_file = file_name.replace(".pdl", suffix)
load_schema(os.path.normpath(baseline_file), baseline_domains)
return version
@@ -467,9 +476,9 @@ def main():
if arg_options.show_changes:
changes = compare_schemas(domains, baseline_domains, True)
if len(changes) > 0:
- print " Public changes since %s:" % version
+ print(" Public changes since %s:" % version)
for change in changes:
- print " %s" % change
+ print(" %s" % change)
if arg_options.stamp:
with open(arg_options.stamp, 'a') as _:
diff --git a/tools/inspector_protocol/CodeGenerator.py b/tools/inspector_protocol/code_generator.py
index e630b02985..fb9959d608 100644..100755
--- a/tools/inspector_protocol/CodeGenerator.py
+++ b/tools/inspector_protocol/code_generator.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
# 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.
@@ -14,6 +15,8 @@ try:
except ImportError:
import simplejson as json
+import pdl
+
# Path handling for libraries and templates
# Paths have to be normalized because Jinja uses the exact template path to
# determine the hash used in the cache filename, and we need a pre-caching step
@@ -30,14 +33,14 @@ def read_config():
def json_object_hook(object_dict):
items = [(k, os.path.join(config_base, v) if k == "path" else v) for (k, v) in object_dict.items()]
items = [(k, os.path.join(output_base, v) if k == "output" else v) for (k, v) in items]
- keys, values = zip(*items)
+ keys, values = list(zip(*items))
return collections.namedtuple('X', keys)(*values)
return json.loads(data, object_hook=json_object_hook)
def init_defaults(config_tuple, path, defaults):
keys = list(config_tuple._fields) # pylint: disable=E1101
values = [getattr(config_tuple, k) for k in keys]
- for i in xrange(len(keys)):
+ for i in range(len(keys)):
if hasattr(values[i], "_fields"):
values[i] = init_defaults(values[i], path + "." + keys[i], defaults)
for optional in defaults:
@@ -95,6 +98,7 @@ def read_config():
".protocol.export_macro": "",
".protocol.export_header": False,
".protocol.options": False,
+ ".protocol.file_name_prefix": "",
".exported": False,
".exported.export_macro": "",
".exported.export_header": False,
@@ -130,7 +134,7 @@ def dash_to_camelcase(word):
def to_snake_case(name):
- return re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", name, sys.maxint).lower()
+ return re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", name, sys.maxsize).lower()
def to_method_case(config, name):
@@ -156,6 +160,10 @@ def format_include(config, header, file_name=None):
return header
+def format_domain_include(config, header, file_name):
+ return format_include(config, header, config.protocol.file_name_prefix + file_name)
+
+
def to_file_name(config, file_name):
if config.use_snake_file_names:
return to_snake_case(file_name).replace(".cpp", ".cc")
@@ -258,6 +266,21 @@ def create_string_type_definition():
}
+def create_binary_type_definition():
+ # pylint: disable=W0622
+ return {
+ "return_type": "Binary",
+ "pass_type": "const Binary&",
+ "to_pass_type": "%s",
+ "to_raw_type": "%s",
+ "to_rvalue": "%s",
+ "type": "Binary",
+ "raw_type": "Binary",
+ "raw_pass_type": "const Binary&",
+ "raw_return_type": "Binary",
+ }
+
+
def create_primitive_type_definition(type):
# pylint: disable=W0622
typedefs = {
@@ -330,9 +353,8 @@ class Protocol(object):
def read_protocol_file(self, file_name):
input_file = open(file_name, "r")
- json_string = input_file.read()
+ parsed_json = pdl.loads(input_file.read(), file_name)
input_file.close()
- parsed_json = json.loads(json_string)
version = parsed_json["version"]["major"] + "." + parsed_json["version"]["minor"]
domains = []
for domain in parsed_json["domains"]:
@@ -436,8 +458,10 @@ class Protocol(object):
self.type_definitions["boolean"] = create_primitive_type_definition("boolean")
self.type_definitions["object"] = create_object_type_definition()
self.type_definitions["any"] = create_any_type_definition()
+ self.type_definitions["binary"] = create_binary_type_definition()
for domain in self.json_api["domains"]:
self.type_definitions[domain["domain"] + ".string"] = create_string_type_definition()
+ self.type_definitions[domain["domain"] + ".binary"] = create_binary_type_definition()
if not ("types" in domain):
continue
for type in domain["types"]:
@@ -447,10 +471,11 @@ class Protocol(object):
elif type["type"] == "object":
self.type_definitions[type_name] = create_user_type_definition(domain["domain"], type)
elif type["type"] == "array":
- items_type = type["items"]["type"]
- self.type_definitions[type_name] = wrap_array_definition(self.type_definitions[items_type])
+ self.type_definitions[type_name] = self.resolve_type(type)
elif type["type"] == domain["domain"] + ".string":
self.type_definitions[type_name] = create_string_type_definition()
+ elif type["type"] == domain["domain"] + ".binary":
+ self.type_definitions[type_name] = create_binary_type_definition()
else:
self.type_definitions[type_name] = create_primitive_type_definition(type["type"])
@@ -571,21 +596,23 @@ def main():
for domain in protocol.json_api["domains"]:
class_name = domain["domain"]
+ file_name = config.protocol.file_name_prefix + class_name
template_context = {
"protocol": protocol,
"config": config,
"domain": domain,
"join_arrays": join_arrays,
"format_include": functools.partial(format_include, config),
+ "format_domain_include": functools.partial(format_domain_include, config),
}
if domain["domain"] in protocol.generate_domains:
- outputs[os.path.join(config.protocol.output, to_file_name(config, class_name + ".h"))] = h_template.render(template_context)
- outputs[os.path.join(config.protocol.output, to_file_name(config, class_name + ".cpp"))] = cpp_template.render(template_context)
+ outputs[os.path.join(config.protocol.output, to_file_name(config, file_name + ".h"))] = h_template.render(template_context)
+ outputs[os.path.join(config.protocol.output, to_file_name(config, file_name + ".cpp"))] = cpp_template.render(template_context)
if domain["domain"] in protocol.exported_domains:
- outputs[os.path.join(config.exported.output, to_file_name(config, class_name + ".h"))] = exported_template.render(template_context)
+ outputs[os.path.join(config.exported.output, to_file_name(config, file_name + ".h"))] = exported_template.render(template_context)
if domain["domain"] in protocol.imported_domains:
- outputs[os.path.join(config.protocol.output, to_file_name(config, class_name + ".h"))] = imported_template.render(template_context)
+ outputs[os.path.join(config.protocol.output, to_file_name(config, file_name + ".h"))] = imported_template.render(template_context)
if config.lib:
template_context = {
@@ -596,8 +623,7 @@ def main():
lib_templates_dir = os.path.join(module_path, "lib")
# Note these should be sorted in the right order.
# TODO(dgozman): sort them programmatically based on commented includes.
- lib_h_templates = [
- "Collections_h.template",
+ protocol_h_templates = [
"ErrorSupport_h.template",
"Values_h.template",
"Object_h.template",
@@ -606,15 +632,17 @@ def main():
"Array_h.template",
"DispatcherBase_h.template",
"Parser_h.template",
+ "CBOR_h.template",
]
- lib_cpp_templates = [
+ protocol_cpp_templates = [
"Protocol_cpp.template",
"ErrorSupport_cpp.template",
"Values_cpp.template",
"Object_cpp.template",
"DispatcherBase_cpp.template",
"Parser_cpp.template",
+ "CBOR_cpp.template",
]
forward_h_templates = [
@@ -623,6 +651,14 @@ def main():
"FrontendChannel_h.template",
]
+ base_string_adapter_h_templates = [
+ "base_string_adapter_h.template",
+ ]
+
+ base_string_adapter_cc_templates = [
+ "base_string_adapter_cc.template",
+ ]
+
def generate_lib_file(file_name, template_files):
parts = []
for template_file in template_files:
@@ -632,20 +668,22 @@ def main():
outputs[file_name] = "\n\n".join(parts)
generate_lib_file(os.path.join(config.lib.output, to_file_name(config, "Forward.h")), forward_h_templates)
- generate_lib_file(os.path.join(config.lib.output, to_file_name(config, "Protocol.h")), lib_h_templates)
- generate_lib_file(os.path.join(config.lib.output, to_file_name(config, "Protocol.cpp")), lib_cpp_templates)
+ generate_lib_file(os.path.join(config.lib.output, to_file_name(config, "Protocol.h")), protocol_h_templates)
+ generate_lib_file(os.path.join(config.lib.output, to_file_name(config, "Protocol.cpp")), protocol_cpp_templates)
+ generate_lib_file(os.path.join(config.lib.output, to_file_name(config, "base_string_adapter.h")), base_string_adapter_h_templates)
+ generate_lib_file(os.path.join(config.lib.output, to_file_name(config, "base_string_adapter.cc")), base_string_adapter_cc_templates)
# Make gyp / make generatos happy, otherwise make rebuilds world.
inputs_ts = max(map(os.path.getmtime, inputs))
up_to_date = True
- for output_file in outputs.iterkeys():
+ for output_file in outputs.keys():
if not os.path.exists(output_file) or os.path.getmtime(output_file) < inputs_ts:
up_to_date = False
break
if up_to_date:
sys.exit()
- for file_name, content in outputs.iteritems():
+ for file_name, content in outputs.items():
out_file = open(file_name, "w")
out_file.write(content)
out_file.close()
diff --git a/tools/inspector_protocol/ConcatenateProtocols.py b/tools/inspector_protocol/concatenate_protocols.py
index a7cbc992c7..e9f448efe7 100755
--- a/tools/inspector_protocol/ConcatenateProtocols.py
+++ b/tools/inspector_protocol/concatenate_protocols.py
@@ -11,6 +11,7 @@ try:
except ImportError:
import simplejson as json
+import pdl
def main(argv):
if len(argv) < 1:
@@ -25,8 +26,7 @@ def main(argv):
sys.stderr.write("Cannot find %s\n" % file_name)
return 1
input_file = open(file_name, "r")
- json_string = input_file.read()
- parsed_json = json.loads(json_string)
+ parsed_json = pdl.loads(input_file.read(), file_name)
domains += parsed_json["domains"]
version = parsed_json["version"]
diff --git a/tools/inspector_protocol/convert_protocol_to_json.py b/tools/inspector_protocol/convert_protocol_to_json.py
new file mode 100755
index 0000000000..96048f793d
--- /dev/null
+++ b/tools/inspector_protocol/convert_protocol_to_json.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+# Copyright 2017 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.
+
+import argparse
+import collections
+import json
+import os.path
+import re
+import sys
+
+import pdl
+
+def main(argv):
+ parser = argparse.ArgumentParser(description=(
+ "Converts from .pdl to .json by invoking the pdl Python module."))
+ parser.add_argument('--map_binary_to_string', type=bool,
+ help=('If set, binary in the .pdl is mapped to a '
+ 'string in .json. Client code will have to '
+ 'base64 decode the string to get the payload.'))
+ parser.add_argument("pdl_file", help="The .pdl input file to parse.")
+ parser.add_argument("json_file", help="The .json output file write.")
+ args = parser.parse_args(argv)
+ file_name = os.path.normpath(args.pdl_file)
+ input_file = open(file_name, "r")
+ pdl_string = input_file.read()
+ protocol = pdl.loads(pdl_string, file_name, args.map_binary_to_string)
+ input_file.close()
+
+ output_file = open(os.path.normpath(args.json_file), 'wb')
+ json.dump(protocol, output_file, indent=4, separators=(',', ': '))
+ output_file.close()
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/tools/inspector_protocol/lib/Allocator_h.template b/tools/inspector_protocol/lib/Allocator_h.template
index 8f8109d695..15eaaaff02 100644
--- a/tools/inspector_protocol/lib/Allocator_h.template
+++ b/tools/inspector_protocol/lib/Allocator_h.template
@@ -1,3 +1,5 @@
+// This file is generated by Allocator_h.template.
+
// 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.
@@ -11,13 +13,6 @@ namespace {{namespace}} {
enum NotNullTagEnum { NotNullLiteral };
-#define PROTOCOL_DISALLOW_NEW() \
- private: \
- void* operator new(size_t) = delete; \
- void* operator new(size_t, NotNullTagEnum, void*) = delete; \
- void* operator new(size_t, void*) = delete; \
- public:
-
#define PROTOCOL_DISALLOW_COPY(ClassName) \
private: \
ClassName(const ClassName&) = delete; \
diff --git a/tools/inspector_protocol/lib/Array_h.template b/tools/inspector_protocol/lib/Array_h.template
index 3854f6e5cd..c420a0f7e9 100644
--- a/tools/inspector_protocol/lib/Array_h.template
+++ b/tools/inspector_protocol/lib/Array_h.template
@@ -1,3 +1,5 @@
+// This file is generated by Array_h.template.
+
// 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.
diff --git a/tools/inspector_protocol/lib/CBOR_cpp.template b/tools/inspector_protocol/lib/CBOR_cpp.template
new file mode 100644
index 0000000000..36750b19a3
--- /dev/null
+++ b/tools/inspector_protocol/lib/CBOR_cpp.template
@@ -0,0 +1,803 @@
+{# This template is generated by gen_cbor_templates.py. #}
+// Generated by lib/CBOR_cpp.template.
+
+// Copyright 2019 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.
+
+
+#include <cassert>
+#include <limits>
+
+{% for namespace in config.protocol.namespace %}
+namespace {{namespace}} {
+{% endfor %}
+
+// ===== encoding/cbor.cc =====
+
+using namespace cbor;
+
+namespace {
+
+// See RFC 7049 Section 2.3, Table 2.
+static constexpr uint8_t kEncodedTrue =
+ EncodeInitialByte(MajorType::SIMPLE_VALUE, 21);
+static constexpr uint8_t kEncodedFalse =
+ EncodeInitialByte(MajorType::SIMPLE_VALUE, 20);
+static constexpr uint8_t kEncodedNull =
+ EncodeInitialByte(MajorType::SIMPLE_VALUE, 22);
+static constexpr uint8_t kInitialByteForDouble =
+ EncodeInitialByte(MajorType::SIMPLE_VALUE, 27);
+
+} // namespace
+
+uint8_t EncodeTrue() { return kEncodedTrue; }
+uint8_t EncodeFalse() { return kEncodedFalse; }
+uint8_t EncodeNull() { return kEncodedNull; }
+
+uint8_t EncodeIndefiniteLengthArrayStart() {
+ return kInitialByteIndefiniteLengthArray;
+}
+
+uint8_t EncodeIndefiniteLengthMapStart() {
+ return kInitialByteIndefiniteLengthMap;
+}
+
+uint8_t EncodeStop() { return kStopByte; }
+
+namespace {
+// See RFC 7049 Table 3 and Section 2.4.4.2. This is used as a prefix for
+// arbitrary binary data encoded as BYTE_STRING.
+static constexpr uint8_t kExpectedConversionToBase64Tag =
+ EncodeInitialByte(MajorType::TAG, 22);
+
+// When parsing CBOR, we limit recursion depth for objects and arrays
+// to this constant.
+static constexpr int kStackLimit = 1000;
+
+// Writes the bytes for |v| to |out|, starting with the most significant byte.
+// See also: https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
+template <typename T>
+void WriteBytesMostSignificantByteFirst(T v, std::vector<uint8_t>* out) {
+ for (int shift_bytes = sizeof(T) - 1; shift_bytes >= 0; --shift_bytes)
+ out->push_back(0xff & (v >> (shift_bytes * 8)));
+}
+} // namespace
+
+namespace cbor_internals {
+// Writes the start of a token with |type|. The |value| may indicate the size,
+// or it may be the payload if the value is an unsigned integer.
+void WriteTokenStart(MajorType type, uint64_t value,
+ std::vector<uint8_t>* encoded) {
+ if (value < 24) {
+ // Values 0-23 are encoded directly into the additional info of the
+ // initial byte.
+ encoded->push_back(EncodeInitialByte(type, /*additional_info=*/value));
+ return;
+ }
+ if (value <= std::numeric_limits<uint8_t>::max()) {
+ // Values 24-255 are encoded with one initial byte, followed by the value.
+ encoded->push_back(EncodeInitialByte(type, kAdditionalInformation1Byte));
+ encoded->push_back(value);
+ return;
+ }
+ if (value <= std::numeric_limits<uint16_t>::max()) {
+ // Values 256-65535: 1 initial byte + 2 bytes payload.
+ encoded->push_back(EncodeInitialByte(type, kAdditionalInformation2Bytes));
+ WriteBytesMostSignificantByteFirst<uint16_t>(value, encoded);
+ return;
+ }
+ if (value <= std::numeric_limits<uint32_t>::max()) {
+ // 32 bit uint: 1 initial byte + 4 bytes payload.
+ encoded->push_back(EncodeInitialByte(type, kAdditionalInformation4Bytes));
+ WriteBytesMostSignificantByteFirst<uint32_t>(static_cast<uint32_t>(value),
+ encoded);
+ return;
+ }
+ // 64 bit uint: 1 initial byte + 8 bytes payload.
+ encoded->push_back(EncodeInitialByte(type, kAdditionalInformation8Bytes));
+ WriteBytesMostSignificantByteFirst<uint64_t>(value, encoded);
+}
+} // namespace cbor_internals
+
+namespace {
+// Extracts sizeof(T) bytes from |in| to extract a value of type T
+// (e.g. uint64_t, uint32_t, ...), most significant byte first.
+// See also: https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
+template <typename T>
+T ReadBytesMostSignificantByteFirst(span<uint8_t> in) {
+ assert(static_cast<std::size_t>(in.size()) >= sizeof(T));
+ T result = 0;
+ for (std::size_t shift_bytes = 0; shift_bytes < sizeof(T); ++shift_bytes)
+ result |= T(in[sizeof(T) - 1 - shift_bytes]) << (shift_bytes * 8);
+ return result;
+}
+} // namespace
+
+namespace cbor_internals {
+int8_t ReadTokenStart(span<uint8_t> bytes, MajorType* type, uint64_t* value) {
+ if (bytes.empty()) return -1;
+ uint8_t initial_byte = bytes[0];
+ *type = MajorType((initial_byte & kMajorTypeMask) >> kMajorTypeBitShift);
+
+ uint8_t additional_information = initial_byte & kAdditionalInformationMask;
+ if (additional_information < 24) {
+ // Values 0-23 are encoded directly into the additional info of the
+ // initial byte.
+ *value = additional_information;
+ return 1;
+ }
+ if (additional_information == kAdditionalInformation1Byte) {
+ // Values 24-255 are encoded with one initial byte, followed by the value.
+ if (bytes.size() < 2) return -1;
+ *value = ReadBytesMostSignificantByteFirst<uint8_t>(bytes.subspan(1));
+ return 2;
+ }
+ if (additional_information == kAdditionalInformation2Bytes) {
+ // Values 256-65535: 1 initial byte + 2 bytes payload.
+ if (static_cast<std::size_t>(bytes.size()) < 1 + sizeof(uint16_t))
+ return -1;
+ *value = ReadBytesMostSignificantByteFirst<uint16_t>(bytes.subspan(1));
+ return 3;
+ }
+ if (additional_information == kAdditionalInformation4Bytes) {
+ // 32 bit uint: 1 initial byte + 4 bytes payload.
+ if (static_cast<std::size_t>(bytes.size()) < 1 + sizeof(uint32_t))
+ return -1;
+ *value = ReadBytesMostSignificantByteFirst<uint32_t>(bytes.subspan(1));
+ return 5;
+ }
+ if (additional_information == kAdditionalInformation8Bytes) {
+ // 64 bit uint: 1 initial byte + 8 bytes payload.
+ if (static_cast<std::size_t>(bytes.size()) < 1 + sizeof(uint64_t))
+ return -1;
+ *value = ReadBytesMostSignificantByteFirst<uint64_t>(bytes.subspan(1));
+ return 9;
+ }
+ return -1;
+}
+} // namespace cbor_internals
+
+using cbor_internals::WriteTokenStart;
+using cbor_internals::ReadTokenStart;
+
+void EncodeInt32(int32_t value, std::vector<uint8_t>* out) {
+ if (value >= 0) {
+ WriteTokenStart(MajorType::UNSIGNED, value, out);
+ } else {
+ uint64_t representation = static_cast<uint64_t>(-(value + 1));
+ WriteTokenStart(MajorType::NEGATIVE, representation, out);
+ }
+}
+
+void EncodeString16(span<uint16_t> in, std::vector<uint8_t>* out) {
+ uint64_t byte_length = static_cast<uint64_t>(in.size_bytes());
+ WriteTokenStart(MajorType::BYTE_STRING, byte_length, out);
+ // When emitting UTF16 characters, we always write the least significant byte
+ // first; this is because it's the native representation for X86.
+ // TODO(johannes): Implement a more efficient thing here later, e.g.
+ // casting *iff* the machine has this byte order.
+ // The wire format for UTF16 chars will probably remain the same
+ // (least significant byte first) since this way we can have
+ // golden files, unittests, etc. that port easily and universally.
+ // See also:
+ // https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
+ for (const uint16_t two_bytes : in) {
+ out->push_back(two_bytes);
+ out->push_back(two_bytes >> 8);
+ }
+}
+
+void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out) {
+ WriteTokenStart(MajorType::STRING, static_cast<uint64_t>(in.size_bytes()),
+ out);
+ out->insert(out->end(), in.begin(), in.end());
+}
+
+void EncodeBinary(span<uint8_t> in, std::vector<uint8_t>* out) {
+ out->push_back(kExpectedConversionToBase64Tag);
+ uint64_t byte_length = static_cast<uint64_t>(in.size_bytes());
+ WriteTokenStart(MajorType::BYTE_STRING, byte_length, out);
+ out->insert(out->end(), in.begin(), in.end());
+}
+
+// A double is encoded with a specific initial byte
+// (kInitialByteForDouble) plus the 64 bits of payload for its value.
+constexpr std::ptrdiff_t kEncodedDoubleSize = 1 + sizeof(uint64_t);
+
+// An envelope is encoded with a specific initial byte
+// (kInitialByteForEnvelope), plus the start byte for a BYTE_STRING with a 32
+// bit wide length, plus a 32 bit length for that string.
+constexpr std::ptrdiff_t kEncodedEnvelopeHeaderSize = 1 + 1 + sizeof(uint32_t);
+
+void EncodeDouble(double value, std::vector<uint8_t>* out) {
+ // The additional_info=27 indicates 64 bits for the double follow.
+ // See RFC 7049 Section 2.3, Table 1.
+ out->push_back(kInitialByteForDouble);
+ union {
+ double from_double;
+ uint64_t to_uint64;
+ } reinterpret;
+ reinterpret.from_double = value;
+ WriteBytesMostSignificantByteFirst<uint64_t>(reinterpret.to_uint64, out);
+}
+
+void EnvelopeEncoder::EncodeStart(std::vector<uint8_t>* out) {
+ assert(byte_size_pos_ == 0);
+ out->push_back(kInitialByteForEnvelope);
+ out->push_back(kInitialByteFor32BitLengthByteString);
+ byte_size_pos_ = out->size();
+ out->resize(out->size() + sizeof(uint32_t));
+}
+
+bool EnvelopeEncoder::EncodeStop(std::vector<uint8_t>* out) {
+ assert(byte_size_pos_ != 0);
+ // The byte size is the size of the payload, that is, all the
+ // bytes that were written past the byte size position itself.
+ uint64_t byte_size = out->size() - (byte_size_pos_ + sizeof(uint32_t));
+ // We store exactly 4 bytes, so at most INT32MAX, with most significant
+ // byte first.
+ if (byte_size > std::numeric_limits<uint32_t>::max()) return false;
+ for (int shift_bytes = sizeof(uint32_t) - 1; shift_bytes >= 0;
+ --shift_bytes) {
+ (*out)[byte_size_pos_++] = 0xff & (byte_size >> (shift_bytes * 8));
+ }
+ return true;
+}
+
+namespace {
+class JSONToCBOREncoder : public JSONParserHandler {
+ public:
+ JSONToCBOREncoder(std::vector<uint8_t>* out, Status* status)
+ : out_(out), status_(status) {
+ *status_ = Status();
+ }
+
+ void HandleObjectBegin() override {
+ envelopes_.emplace_back();
+ envelopes_.back().EncodeStart(out_);
+ out_->push_back(kInitialByteIndefiniteLengthMap);
+ }
+
+ void HandleObjectEnd() override {
+ out_->push_back(kStopByte);
+ assert(!envelopes_.empty());
+ envelopes_.back().EncodeStop(out_);
+ envelopes_.pop_back();
+ }
+
+ void HandleArrayBegin() override {
+ envelopes_.emplace_back();
+ envelopes_.back().EncodeStart(out_);
+ out_->push_back(kInitialByteIndefiniteLengthArray);
+ }
+
+ void HandleArrayEnd() override {
+ out_->push_back(kStopByte);
+ assert(!envelopes_.empty());
+ envelopes_.back().EncodeStop(out_);
+ envelopes_.pop_back();
+ }
+
+ void HandleString16(std::vector<uint16_t> chars) override {
+ for (uint16_t ch : chars) {
+ if (ch >= 0x7f) {
+ // If there's at least one non-7bit character, we encode as UTF16.
+ EncodeString16(span<uint16_t>(chars.data(), chars.size()), out_);
+ return;
+ }
+ }
+ std::vector<uint8_t> sevenbit_chars(chars.begin(), chars.end());
+ EncodeString8(span<uint8_t>(sevenbit_chars.data(), sevenbit_chars.size()),
+ out_);
+ }
+
+ void HandleBinary(std::vector<uint8_t> bytes) override {
+ EncodeBinary(span<uint8_t>(bytes.data(), bytes.size()), out_);
+ }
+
+ void HandleDouble(double value) override { EncodeDouble(value, out_); }
+
+ void HandleInt32(int32_t value) override { EncodeInt32(value, out_); }
+
+ void HandleBool(bool value) override {
+ // See RFC 7049 Section 2.3, Table 2.
+ out_->push_back(value ? kEncodedTrue : kEncodedFalse);
+ }
+
+ void HandleNull() override {
+ // See RFC 7049 Section 2.3, Table 2.
+ out_->push_back(kEncodedNull);
+ }
+
+ void HandleError(Status error) override {
+ assert(!error.ok());
+ *status_ = error;
+ out_->clear();
+ }
+
+ private:
+ std::vector<uint8_t>* out_;
+ std::vector<EnvelopeEncoder> envelopes_;
+ Status* status_;
+};
+} // namespace
+
+std::unique_ptr<JSONParserHandler> NewJSONToCBOREncoder(
+ std::vector<uint8_t>* out, Status* status) {
+ return std::unique_ptr<JSONParserHandler>(new JSONToCBOREncoder(out, status));
+}
+
+namespace {
+// Below are three parsing routines for CBOR, which cover enough
+// to roundtrip JSON messages.
+bool ParseMap(int32_t stack_depth, CBORTokenizer* tokenizer,
+ JSONParserHandler* out);
+bool ParseArray(int32_t stack_depth, CBORTokenizer* tokenizer,
+ JSONParserHandler* out);
+bool ParseValue(int32_t stack_depth, CBORTokenizer* tokenizer,
+ JSONParserHandler* out);
+
+void ParseUTF16String(CBORTokenizer* tokenizer, JSONParserHandler* out) {
+ std::vector<uint16_t> value;
+ span<uint8_t> rep = tokenizer->GetString16WireRep();
+ for (std::ptrdiff_t ii = 0; ii < rep.size(); ii += 2)
+ value.push_back((rep[ii + 1] << 8) | rep[ii]);
+ out->HandleString16(std::move(value));
+ tokenizer->Next();
+}
+
+// For now this method only covers US-ASCII. Later, we may allow UTF8.
+bool ParseASCIIString(CBORTokenizer* tokenizer, JSONParserHandler* out) {
+ assert(tokenizer->TokenTag() == CBORTokenTag::STRING8);
+ std::vector<uint16_t> value16;
+ for (uint8_t ch : tokenizer->GetString8()) {
+ // We only accept us-ascii (7 bit) strings here. Other strings must
+ // be encoded with 16 bit (the BYTE_STRING case).
+ if (ch >= 0x7f) {
+ out->HandleError(
+ Status{Error::CBOR_STRING8_MUST_BE_7BIT, tokenizer->Status().pos});
+ return false;
+ }
+ value16.push_back(ch);
+ }
+ out->HandleString16(std::move(value16));
+ tokenizer->Next();
+ return true;
+}
+
+bool ParseValue(int32_t stack_depth, CBORTokenizer* tokenizer,
+ JSONParserHandler* out) {
+ if (stack_depth > kStackLimit) {
+ out->HandleError(
+ Status{Error::CBOR_STACK_LIMIT_EXCEEDED, tokenizer->Status().pos});
+ return false;
+ }
+ // Skip past the envelope to get to what's inside.
+ if (tokenizer->TokenTag() == CBORTokenTag::ENVELOPE)
+ tokenizer->EnterEnvelope();
+ switch (tokenizer->TokenTag()) {
+ case CBORTokenTag::ERROR_VALUE:
+ out->HandleError(tokenizer->Status());
+ return false;
+ case CBORTokenTag::DONE:
+ out->HandleError(Status{Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE,
+ tokenizer->Status().pos});
+ return false;
+ case CBORTokenTag::TRUE_VALUE:
+ out->HandleBool(true);
+ tokenizer->Next();
+ return true;
+ case CBORTokenTag::FALSE_VALUE:
+ out->HandleBool(false);
+ tokenizer->Next();
+ return true;
+ case CBORTokenTag::NULL_VALUE:
+ out->HandleNull();
+ tokenizer->Next();
+ return true;
+ case CBORTokenTag::INT32:
+ out->HandleInt32(tokenizer->GetInt32());
+ tokenizer->Next();
+ return true;
+ case CBORTokenTag::DOUBLE:
+ out->HandleDouble(tokenizer->GetDouble());
+ tokenizer->Next();
+ return true;
+ case CBORTokenTag::STRING8:
+ return ParseASCIIString(tokenizer, out);
+ case CBORTokenTag::STRING16:
+ ParseUTF16String(tokenizer, out);
+ return true;
+ case CBORTokenTag::BINARY: {
+ span<uint8_t> binary = tokenizer->GetBinary();
+ out->HandleBinary(std::vector<uint8_t>(binary.begin(), binary.end()));
+ tokenizer->Next();
+ return true;
+ }
+ case CBORTokenTag::MAP_START:
+ return ParseMap(stack_depth + 1, tokenizer, out);
+ case CBORTokenTag::ARRAY_START:
+ return ParseArray(stack_depth + 1, tokenizer, out);
+ default:
+ out->HandleError(
+ Status{Error::CBOR_UNSUPPORTED_VALUE, tokenizer->Status().pos});
+ return false;
+ }
+}
+
+// |bytes| must start with the indefinite length array byte, so basically,
+// ParseArray may only be called after an indefinite length array has been
+// detected.
+bool ParseArray(int32_t stack_depth, CBORTokenizer* tokenizer,
+ JSONParserHandler* out) {
+ assert(tokenizer->TokenTag() == CBORTokenTag::ARRAY_START);
+ tokenizer->Next();
+ out->HandleArrayBegin();
+ while (tokenizer->TokenTag() != CBORTokenTag::STOP) {
+ if (tokenizer->TokenTag() == CBORTokenTag::DONE) {
+ out->HandleError(
+ Status{Error::CBOR_UNEXPECTED_EOF_IN_ARRAY, tokenizer->Status().pos});
+ return false;
+ }
+ if (tokenizer->TokenTag() == CBORTokenTag::ERROR_VALUE) {
+ out->HandleError(tokenizer->Status());
+ return false;
+ }
+ // Parse value.
+ if (!ParseValue(stack_depth, tokenizer, out)) return false;
+ }
+ out->HandleArrayEnd();
+ tokenizer->Next();
+ return true;
+}
+
+// |bytes| must start with the indefinite length array byte, so basically,
+// ParseArray may only be called after an indefinite length array has been
+// detected.
+bool ParseMap(int32_t stack_depth, CBORTokenizer* tokenizer,
+ JSONParserHandler* out) {
+ assert(tokenizer->TokenTag() == CBORTokenTag::MAP_START);
+ out->HandleObjectBegin();
+ tokenizer->Next();
+ while (tokenizer->TokenTag() != CBORTokenTag::STOP) {
+ if (tokenizer->TokenTag() == CBORTokenTag::DONE) {
+ out->HandleError(
+ Status{Error::CBOR_UNEXPECTED_EOF_IN_MAP, tokenizer->Status().pos});
+ return false;
+ }
+ if (tokenizer->TokenTag() == CBORTokenTag::ERROR_VALUE) {
+ out->HandleError(tokenizer->Status());
+ return false;
+ }
+ // Parse key.
+ if (tokenizer->TokenTag() == CBORTokenTag::STRING8) {
+ if (!ParseASCIIString(tokenizer, out)) return false;
+ } else if (tokenizer->TokenTag() == CBORTokenTag::STRING16) {
+ ParseUTF16String(tokenizer, out);
+ } else {
+ out->HandleError(
+ Status{Error::CBOR_INVALID_MAP_KEY, tokenizer->Status().pos});
+ return false;
+ }
+ // Parse value.
+ if (!ParseValue(stack_depth, tokenizer, out)) return false;
+ }
+ out->HandleObjectEnd();
+ tokenizer->Next();
+ return true;
+}
+} // namespace
+
+void ParseCBOR(span<uint8_t> bytes, JSONParserHandler* json_out) {
+ if (bytes.empty()) {
+ json_out->HandleError(Status{Error::CBOR_NO_INPUT, 0});
+ return;
+ }
+ if (bytes[0] != kInitialByteForEnvelope) {
+ json_out->HandleError(Status{Error::CBOR_INVALID_START_BYTE, 0});
+ return;
+ }
+ CBORTokenizer tokenizer(bytes);
+ if (tokenizer.TokenTag() == CBORTokenTag::ERROR_VALUE) {
+ json_out->HandleError(tokenizer.Status());
+ return;
+ }
+ // We checked for the envelope start byte above, so the tokenizer
+ // must agree here, since it's not an error.
+ assert(tokenizer.TokenTag() == CBORTokenTag::ENVELOPE);
+ tokenizer.EnterEnvelope();
+ if (tokenizer.TokenTag() != CBORTokenTag::MAP_START) {
+ json_out->HandleError(
+ Status{Error::CBOR_MAP_START_EXPECTED, tokenizer.Status().pos});
+ return;
+ }
+ if (!ParseMap(/*stack_depth=*/1, &tokenizer, json_out)) return;
+ if (tokenizer.TokenTag() == CBORTokenTag::DONE) return;
+ if (tokenizer.TokenTag() == CBORTokenTag::ERROR_VALUE) {
+ json_out->HandleError(tokenizer.Status());
+ return;
+ }
+ json_out->HandleError(
+ Status{Error::CBOR_TRAILING_JUNK, tokenizer.Status().pos});
+}
+
+CBORTokenizer::CBORTokenizer(span<uint8_t> bytes) : bytes_(bytes) {
+ ReadNextToken(/*enter_envelope=*/false);
+}
+CBORTokenizer::~CBORTokenizer() {}
+
+CBORTokenTag CBORTokenizer::TokenTag() const { return token_tag_; }
+
+void CBORTokenizer::Next() {
+ if (token_tag_ == CBORTokenTag::ERROR_VALUE || token_tag_ == CBORTokenTag::DONE)
+ return;
+ ReadNextToken(/*enter_envelope=*/false);
+}
+
+void CBORTokenizer::EnterEnvelope() {
+ assert(token_tag_ == CBORTokenTag::ENVELOPE);
+ ReadNextToken(/*enter_envelope=*/true);
+}
+
+Status CBORTokenizer::Status() const { return status_; }
+
+int32_t CBORTokenizer::GetInt32() const {
+ assert(token_tag_ == CBORTokenTag::INT32);
+ // The range checks happen in ::ReadNextToken().
+ return static_cast<uint32_t>(
+ token_start_type_ == MajorType::UNSIGNED
+ ? token_start_internal_value_
+ : -static_cast<int64_t>(token_start_internal_value_) - 1);
+}
+
+double CBORTokenizer::GetDouble() const {
+ assert(token_tag_ == CBORTokenTag::DOUBLE);
+ union {
+ uint64_t from_uint64;
+ double to_double;
+ } reinterpret;
+ reinterpret.from_uint64 = ReadBytesMostSignificantByteFirst<uint64_t>(
+ bytes_.subspan(status_.pos + 1));
+ return reinterpret.to_double;
+}
+
+span<uint8_t> CBORTokenizer::GetString8() const {
+ assert(token_tag_ == CBORTokenTag::STRING8);
+ auto length = static_cast<std::ptrdiff_t>(token_start_internal_value_);
+ return bytes_.subspan(status_.pos + (token_byte_length_ - length), length);
+}
+
+span<uint8_t> CBORTokenizer::GetString16WireRep() const {
+ assert(token_tag_ == CBORTokenTag::STRING16);
+ auto length = static_cast<std::ptrdiff_t>(token_start_internal_value_);
+ return bytes_.subspan(status_.pos + (token_byte_length_ - length), length);
+}
+
+span<uint8_t> CBORTokenizer::GetBinary() const {
+ assert(token_tag_ == CBORTokenTag::BINARY);
+ auto length = static_cast<std::ptrdiff_t>(token_start_internal_value_);
+ return bytes_.subspan(status_.pos + (token_byte_length_ - length), length);
+}
+
+void CBORTokenizer::ReadNextToken(bool enter_envelope) {
+ if (enter_envelope) {
+ status_.pos += kEncodedEnvelopeHeaderSize;
+ } else {
+ status_.pos =
+ status_.pos == Status::npos() ? 0 : status_.pos + token_byte_length_;
+ }
+ status_.error = Error::OK;
+ if (status_.pos >= bytes_.size()) {
+ token_tag_ = CBORTokenTag::DONE;
+ return;
+ }
+ switch (bytes_[status_.pos]) {
+ case kStopByte:
+ SetToken(CBORTokenTag::STOP, 1);
+ return;
+ case kInitialByteIndefiniteLengthMap:
+ SetToken(CBORTokenTag::MAP_START, 1);
+ return;
+ case kInitialByteIndefiniteLengthArray:
+ SetToken(CBORTokenTag::ARRAY_START, 1);
+ return;
+ case kEncodedTrue:
+ SetToken(CBORTokenTag::TRUE_VALUE, 1);
+ return;
+ case kEncodedFalse:
+ SetToken(CBORTokenTag::FALSE_VALUE, 1);
+ return;
+ case kEncodedNull:
+ SetToken(CBORTokenTag::NULL_VALUE, 1);
+ return;
+ case kExpectedConversionToBase64Tag: { // BINARY
+ int8_t bytes_read =
+ ReadTokenStart(bytes_.subspan(status_.pos + 1), &token_start_type_,
+ &token_start_internal_value_);
+ int64_t token_byte_length = 1 + bytes_read + token_start_internal_value_;
+ if (-1 == bytes_read || token_start_type_ != MajorType::BYTE_STRING ||
+ status_.pos + token_byte_length > bytes_.size()) {
+ SetError(Error::CBOR_INVALID_BINARY);
+ return;
+ }
+ SetToken(CBORTokenTag::BINARY,
+ static_cast<std::ptrdiff_t>(token_byte_length));
+ return;
+ }
+ case kInitialByteForDouble: { // DOUBLE
+ if (status_.pos + kEncodedDoubleSize > bytes_.size()) {
+ SetError(Error::CBOR_INVALID_DOUBLE);
+ return;
+ }
+ SetToken(CBORTokenTag::DOUBLE, kEncodedDoubleSize);
+ return;
+ }
+ case kInitialByteForEnvelope: { // ENVELOPE
+ if (status_.pos + kEncodedEnvelopeHeaderSize > bytes_.size()) {
+ SetError(Error::CBOR_INVALID_ENVELOPE);
+ return;
+ }
+ // The envelope must be a byte string with 32 bit length.
+ if (bytes_[status_.pos + 1] != kInitialByteFor32BitLengthByteString) {
+ SetError(Error::CBOR_INVALID_ENVELOPE);
+ return;
+ }
+ // Read the length of the byte string.
+ token_start_internal_value_ = ReadBytesMostSignificantByteFirst<uint32_t>(
+ bytes_.subspan(status_.pos + 2));
+ // Make sure the payload is contained within the message.
+ if (token_start_internal_value_ + kEncodedEnvelopeHeaderSize +
+ status_.pos >
+ static_cast<std::size_t>(bytes_.size())) {
+ SetError(Error::CBOR_INVALID_ENVELOPE);
+ return;
+ }
+ auto length = static_cast<std::ptrdiff_t>(token_start_internal_value_);
+ SetToken(CBORTokenTag::ENVELOPE,
+ kEncodedEnvelopeHeaderSize + length);
+ return;
+ }
+ default: {
+ span<uint8_t> remainder =
+ bytes_.subspan(status_.pos, bytes_.size() - status_.pos);
+ assert(!remainder.empty());
+ int8_t token_start_length = ReadTokenStart(remainder, &token_start_type_,
+ &token_start_internal_value_);
+ bool success = token_start_length != -1;
+ switch (token_start_type_) {
+ case MajorType::UNSIGNED: // INT32.
+ if (!success || std::numeric_limits<int32_t>::max() <
+ token_start_internal_value_) {
+ SetError(Error::CBOR_INVALID_INT32);
+ return;
+ }
+ SetToken(CBORTokenTag::INT32, token_start_length);
+ return;
+ case MajorType::NEGATIVE: // INT32.
+ if (!success ||
+ std::numeric_limits<int32_t>::min() >
+ -static_cast<int64_t>(token_start_internal_value_) - 1) {
+ SetError(Error::CBOR_INVALID_INT32);
+ return;
+ }
+ SetToken(CBORTokenTag::INT32, token_start_length);
+ return;
+ case MajorType::STRING: { // STRING8.
+ if (!success || remainder.size() < static_cast<int64_t>(
+ token_start_internal_value_)) {
+ SetError(Error::CBOR_INVALID_STRING8);
+ return;
+ }
+ auto length = static_cast<std::ptrdiff_t>(token_start_internal_value_);
+ SetToken(CBORTokenTag::STRING8, token_start_length + length);
+ return;
+ }
+ case MajorType::BYTE_STRING: { // STRING16.
+ if (!success ||
+ remainder.size() <
+ static_cast<int64_t>(token_start_internal_value_) ||
+ // Must be divisible by 2 since UTF16 is 2 bytes per character.
+ token_start_internal_value_ & 1) {
+ SetError(Error::CBOR_INVALID_STRING16);
+ return;
+ }
+ auto length = static_cast<std::ptrdiff_t>(token_start_internal_value_);
+ SetToken(CBORTokenTag::STRING16, token_start_length + length);
+ return;
+ }
+ case MajorType::ARRAY:
+ case MajorType::MAP:
+ case MajorType::TAG:
+ case MajorType::SIMPLE_VALUE:
+ SetError(Error::CBOR_UNSUPPORTED_VALUE);
+ return;
+ }
+ }
+ }
+}
+
+void CBORTokenizer::SetToken(CBORTokenTag token_tag,
+ std::ptrdiff_t token_byte_length) {
+ token_tag_ = token_tag;
+ token_byte_length_ = token_byte_length;
+}
+
+void CBORTokenizer::SetError(Error error) {
+ token_tag_ = CBORTokenTag::ERROR_VALUE;
+ status_.error = error;
+}
+
+#if 0
+void DumpCBOR(span<uint8_t> cbor) {
+ std::string indent;
+ CBORTokenizer tokenizer(cbor);
+ while (true) {
+ fprintf(stderr, "%s", indent.c_str());
+ switch (tokenizer.TokenTag()) {
+ case CBORTokenTag::ERROR_VALUE:
+ fprintf(stderr, "ERROR {status.error=%d, status.pos=%ld}\n",
+ tokenizer.Status().error, tokenizer.Status().pos);
+ return;
+ case CBORTokenTag::DONE:
+ fprintf(stderr, "DONE\n");
+ return;
+ case CBORTokenTag::TRUE_VALUE:
+ fprintf(stderr, "TRUE_VALUE\n");
+ break;
+ case CBORTokenTag::FALSE_VALUE:
+ fprintf(stderr, "FALSE_VALUE\n");
+ break;
+ case CBORTokenTag::NULL_VALUE:
+ fprintf(stderr, "NULL_VALUE\n");
+ break;
+ case CBORTokenTag::INT32:
+ fprintf(stderr, "INT32 [%d]\n", tokenizer.GetInt32());
+ break;
+ case CBORTokenTag::DOUBLE:
+ fprintf(stderr, "DOUBLE [%lf]\n", tokenizer.GetDouble());
+ break;
+ case CBORTokenTag::STRING8: {
+ span<uint8_t> v = tokenizer.GetString8();
+ std::string t(v.begin(), v.end());
+ fprintf(stderr, "STRING8 [%s]\n", t.c_str());
+ break;
+ }
+ case CBORTokenTag::STRING16: {
+ span<uint8_t> v = tokenizer.GetString16WireRep();
+ std::string t(v.begin(), v.end());
+ fprintf(stderr, "STRING16 [%s]\n", t.c_str());
+ break;
+ }
+ case CBORTokenTag::BINARY: {
+ span<uint8_t> v = tokenizer.GetBinary();
+ std::string t(v.begin(), v.end());
+ fprintf(stderr, "BINARY [%s]\n", t.c_str());
+ break;
+ }
+ case CBORTokenTag::MAP_START:
+ fprintf(stderr, "MAP_START\n");
+ indent += " ";
+ break;
+ case CBORTokenTag::ARRAY_START:
+ fprintf(stderr, "ARRAY_START\n");
+ indent += " ";
+ break;
+ case CBORTokenTag::STOP:
+ fprintf(stderr, "STOP\n");
+ indent.erase(0, 2);
+ break;
+ case CBORTokenTag::ENVELOPE:
+ fprintf(stderr, "ENVELOPE\n");
+ tokenizer.EnterEnvelope();
+ continue;
+ }
+ tokenizer.Next();
+ }
+}
+#endif
+
+
+{% for namespace in config.protocol.namespace %}
+} // namespace {{namespace}}
+{% endfor %}
diff --git a/tools/inspector_protocol/lib/CBOR_h.template b/tools/inspector_protocol/lib/CBOR_h.template
new file mode 100644
index 0000000000..dd637f19e7
--- /dev/null
+++ b/tools/inspector_protocol/lib/CBOR_h.template
@@ -0,0 +1,416 @@
+{# This template is generated by gen_cbor_templates.py. #}
+// Generated by lib/CBOR_h.template.
+
+// Copyright 2019 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.
+
+#ifndef {{"_".join(config.protocol.namespace)}}_CBOR_h
+#define {{"_".join(config.protocol.namespace)}}_CBOR_h
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+{% for namespace in config.protocol.namespace %}
+namespace {{namespace}} {
+{% endfor %}
+
+// ===== encoding/status.h =====
+
+// Error codes.
+enum class Error {
+ OK = 0,
+ // JSON parsing errors - json_parser.{h,cc}.
+ JSON_PARSER_UNPROCESSED_INPUT_REMAINS = 0x01,
+ JSON_PARSER_STACK_LIMIT_EXCEEDED = 0x02,
+ JSON_PARSER_NO_INPUT = 0x03,
+ JSON_PARSER_INVALID_TOKEN = 0x04,
+ JSON_PARSER_INVALID_NUMBER = 0x05,
+ JSON_PARSER_INVALID_STRING = 0x06,
+ JSON_PARSER_UNEXPECTED_ARRAY_END = 0x07,
+ JSON_PARSER_COMMA_OR_ARRAY_END_EXPECTED = 0x08,
+ JSON_PARSER_STRING_LITERAL_EXPECTED = 0x09,
+ JSON_PARSER_COLON_EXPECTED = 0x0a,
+ JSON_PARSER_UNEXPECTED_OBJECT_END = 0x0b,
+ JSON_PARSER_COMMA_OR_OBJECT_END_EXPECTED = 0x0c,
+ JSON_PARSER_VALUE_EXPECTED = 0x0d,
+
+ CBOR_INVALID_INT32 = 0x0e,
+ CBOR_INVALID_DOUBLE = 0x0f,
+ CBOR_INVALID_ENVELOPE = 0x10,
+ CBOR_INVALID_STRING8 = 0x11,
+ CBOR_INVALID_STRING16 = 0x12,
+ CBOR_INVALID_BINARY = 0x13,
+ CBOR_UNSUPPORTED_VALUE = 0x14,
+ CBOR_NO_INPUT = 0x15,
+ CBOR_INVALID_START_BYTE = 0x16,
+ CBOR_UNEXPECTED_EOF_EXPECTED_VALUE = 0x17,
+ CBOR_UNEXPECTED_EOF_IN_ARRAY = 0x18,
+ CBOR_UNEXPECTED_EOF_IN_MAP = 0x19,
+ CBOR_INVALID_MAP_KEY = 0x1a,
+ CBOR_STACK_LIMIT_EXCEEDED = 0x1b,
+ CBOR_STRING8_MUST_BE_7BIT = 0x1c,
+ CBOR_TRAILING_JUNK = 0x1d,
+ CBOR_MAP_START_EXPECTED = 0x1e,
+};
+
+// A status value with position that can be copied. The default status
+// is OK. Usually, error status values should come with a valid position.
+struct Status {
+ static constexpr std::ptrdiff_t npos() { return -1; }
+
+ bool ok() const { return error == Error::OK; }
+
+ Error error = Error::OK;
+ std::ptrdiff_t pos = npos();
+ Status(Error error, std::ptrdiff_t pos) : error(error), pos(pos) {}
+ Status() = default;
+};
+
+// ===== encoding/span.h =====
+
+// This template is similar to std::span, which will be included in C++20. Like
+// std::span it uses ptrdiff_t, which is signed (and thus a bit annoying
+// sometimes when comparing with size_t), but other than this it's much simpler.
+template <typename T>
+class span {
+ public:
+ using index_type = std::ptrdiff_t;
+
+ span() : data_(nullptr), size_(0) {}
+ span(const T* data, index_type size) : data_(data), size_(size) {}
+
+ const T* data() const { return data_; }
+
+ const T* begin() const { return data_; }
+ const T* end() const { return data_ + size_; }
+
+ const T& operator[](index_type idx) const { return data_[idx]; }
+
+ span<T> subspan(index_type offset, index_type count) const {
+ return span(data_ + offset, count);
+ }
+
+ span<T> subspan(index_type offset) const {
+ return span(data_ + offset, size_ - offset);
+ }
+
+ bool empty() const { return size_ == 0; }
+
+ index_type size() const { return size_; }
+ index_type size_bytes() const { return size_ * sizeof(T); }
+
+ private:
+ const T* data_;
+ index_type size_;
+};
+
+// ===== encoding/json_parser_handler.h =====
+
+// Handler interface for JSON parser events. See also json_parser.h.
+class JSONParserHandler {
+ public:
+ virtual ~JSONParserHandler() = default;
+ virtual void HandleObjectBegin() = 0;
+ virtual void HandleObjectEnd() = 0;
+ virtual void HandleArrayBegin() = 0;
+ virtual void HandleArrayEnd() = 0;
+ // TODO(johannes): Support utf8 (requires utf16->utf8 conversion
+ // internally, including handling mismatched surrogate pairs).
+ virtual void HandleString16(std::vector<uint16_t> chars) = 0;
+ virtual void HandleBinary(std::vector<uint8_t> bytes) = 0;
+ virtual void HandleDouble(double value) = 0;
+ virtual void HandleInt32(int32_t value) = 0;
+ virtual void HandleBool(bool value) = 0;
+ virtual void HandleNull() = 0;
+
+ // The parser may send one error even after other events have already
+ // been received. Client code is reponsible to then discard the
+ // already processed events.
+ // |error| must be an eror, as in, |error.is_ok()| can't be true.
+ virtual void HandleError(Status error) = 0;
+};
+
+// ===== encoding/cbor_internals.h =====
+
+namespace cbor {
+enum class MajorType;
+}
+
+namespace cbor_internals {
+
+// Reads the start of a token with definitive size from |bytes|.
+// |type| is the major type as specified in RFC 7049 Section 2.1.
+// |value| is the payload (e.g. for MajorType::UNSIGNED) or is the size
+// (e.g. for BYTE_STRING).
+// If successful, returns the number of bytes read. Otherwise returns -1.
+int8_t ReadTokenStart(span<uint8_t> bytes, cbor::MajorType* type,
+ uint64_t* value);
+
+// Writes the start of a token with |type|. The |value| may indicate the size,
+// or it may be the payload if the value is an unsigned integer.
+void WriteTokenStart(cbor::MajorType type, uint64_t value,
+ std::vector<uint8_t>* encoded);
+} // namespace cbor_internals
+
+// ===== encoding/cbor.h =====
+
+
+namespace cbor {
+
+// The major types from RFC 7049 Section 2.1.
+enum class MajorType {
+ UNSIGNED = 0,
+ NEGATIVE = 1,
+ BYTE_STRING = 2,
+ STRING = 3,
+ ARRAY = 4,
+ MAP = 5,
+ TAG = 6,
+ SIMPLE_VALUE = 7
+};
+
+// Indicates the number of bits the "initial byte" needs to be shifted to the
+// right after applying |kMajorTypeMask| to produce the major type in the
+// lowermost bits.
+static constexpr uint8_t kMajorTypeBitShift = 5u;
+// Mask selecting the low-order 5 bits of the "initial byte", which is where
+// the additional information is encoded.
+static constexpr uint8_t kAdditionalInformationMask = 0x1f;
+// Mask selecting the high-order 3 bits of the "initial byte", which indicates
+// the major type of the encoded value.
+static constexpr uint8_t kMajorTypeMask = 0xe0;
+// Indicates the integer is in the following byte.
+static constexpr uint8_t kAdditionalInformation1Byte = 24u;
+// Indicates the integer is in the next 2 bytes.
+static constexpr uint8_t kAdditionalInformation2Bytes = 25u;
+// Indicates the integer is in the next 4 bytes.
+static constexpr uint8_t kAdditionalInformation4Bytes = 26u;
+// Indicates the integer is in the next 8 bytes.
+static constexpr uint8_t kAdditionalInformation8Bytes = 27u;
+
+// Encodes the initial byte, consisting of the |type| in the first 3 bits
+// followed by 5 bits of |additional_info|.
+constexpr uint8_t EncodeInitialByte(MajorType type, uint8_t additional_info) {
+ return (static_cast<uint8_t>(type) << kMajorTypeBitShift) |
+ (additional_info & kAdditionalInformationMask);
+}
+
+// TAG 24 indicates that what follows is a byte string which is
+// encoded in CBOR format. We use this as a wrapper for
+// maps and arrays, allowing us to skip them, because the
+// byte string carries its size (byte length).
+// https://tools.ietf.org/html/rfc7049#section-2.4.4.1
+static constexpr uint8_t kInitialByteForEnvelope =
+ EncodeInitialByte(MajorType::TAG, 24);
+// The initial byte for a byte string with at most 2^32 bytes
+// of payload. This is used for envelope encoding, even if
+// the byte string is shorter.
+static constexpr uint8_t kInitialByteFor32BitLengthByteString =
+ EncodeInitialByte(MajorType::BYTE_STRING, 26);
+
+// See RFC 7049 Section 2.2.1, indefinite length arrays / maps have additional
+// info = 31.
+static constexpr uint8_t kInitialByteIndefiniteLengthArray =
+ EncodeInitialByte(MajorType::ARRAY, 31);
+static constexpr uint8_t kInitialByteIndefiniteLengthMap =
+ EncodeInitialByte(MajorType::MAP, 31);
+// See RFC 7049 Section 2.3, Table 1; this is used for finishing indefinite
+// length maps / arrays.
+static constexpr uint8_t kStopByte =
+ EncodeInitialByte(MajorType::SIMPLE_VALUE, 31);
+
+} // namespace cbor
+
+// The binary encoding for the inspector protocol follows the CBOR specification
+// (RFC 7049). Additional constraints:
+// - Only indefinite length maps and arrays are supported.
+// - Maps and arrays are wrapped with an envelope, that is, a
+// CBOR tag with value 24 followed by a byte string specifying
+// the byte length of the enclosed map / array. The byte string
+// must use a 32 bit wide length.
+// - At the top level, a message must be an indefinite length map
+// wrapped by an envelope.
+// - Maximal size for messages is 2^32 (4 GB).
+// - For scalars, we support only the int32_t range, encoded as
+// UNSIGNED/NEGATIVE (major types 0 / 1).
+// - UTF16 strings, including with unbalanced surrogate pairs, are encoded
+// as CBOR BYTE_STRING (major type 2). For such strings, the number of
+// bytes encoded must be even.
+// - UTF8 strings (major type 3) may only have ASCII characters
+// (7 bit US-ASCII).
+// - Arbitrary byte arrays, in the inspector protocol called 'binary',
+// are encoded as BYTE_STRING (major type 2), prefixed with a byte
+// indicating base64 when rendered as JSON.
+
+// Encodes |value| as |UNSIGNED| (major type 0) iff >= 0, or |NEGATIVE|
+// (major type 1) iff < 0.
+void EncodeInt32(int32_t value, std::vector<uint8_t>* out);
+
+// Encodes a UTF16 string as a BYTE_STRING (major type 2). Each utf16
+// character in |in| is emitted with most significant byte first,
+// appending to |out|.
+void EncodeString16(span<uint16_t> in, std::vector<uint8_t>* out);
+
+// Encodes a UTF8 string |in| as STRING (major type 3).
+void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out);
+
+// Encodes arbitrary binary data in |in| as a BYTE_STRING (major type 2) with
+// definitive length, prefixed with tag 22 indicating expected conversion to
+// base64 (see RFC 7049, Table 3 and Section 2.4.4.2).
+void EncodeBinary(span<uint8_t> in, std::vector<uint8_t>* out);
+
+// Encodes / decodes a double as Major type 7 (SIMPLE_VALUE),
+// with additional info = 27, followed by 8 bytes in big endian.
+void EncodeDouble(double value, std::vector<uint8_t>* out);
+
+// Some constants for CBOR tokens that only take a single byte on the wire.
+uint8_t EncodeTrue();
+uint8_t EncodeFalse();
+uint8_t EncodeNull();
+uint8_t EncodeIndefiniteLengthArrayStart();
+uint8_t EncodeIndefiniteLengthMapStart();
+uint8_t EncodeStop();
+
+// An envelope indicates the byte length of a wrapped item.
+// We use this for maps and array, which allows the decoder
+// to skip such (nested) values whole sale.
+// It's implemented as a CBOR tag (major type 6) with additional
+// info = 24, followed by a byte string with a 32 bit length value;
+// so the maximal structure that we can wrap is 2^32 bits long.
+// See also: https://tools.ietf.org/html/rfc7049#section-2.4.4.1
+class EnvelopeEncoder {
+ public:
+ // Emits the envelope start bytes and records the position for the
+ // byte size in |byte_size_pos_|. Also emits empty bytes for the
+ // byte sisze so that encoding can continue.
+ void EncodeStart(std::vector<uint8_t>* out);
+ // This records the current size in |out| at position byte_size_pos_.
+ // Returns true iff successful.
+ bool EncodeStop(std::vector<uint8_t>* out);
+
+ private:
+ std::size_t byte_size_pos_ = 0;
+};
+
+// This can be used to convert from JSON to CBOR, by passing the
+// return value to the routines in json_parser.h. The handler will encode into
+// |out|, and iff an error occurs it will set |status| to an error and clear
+// |out|. Otherwise, |status.ok()| will be |true|.
+std::unique_ptr<JSONParserHandler> NewJSONToCBOREncoder(
+ std::vector<uint8_t>* out, Status* status);
+
+// Parses a CBOR encoded message from |bytes|, sending JSON events to
+// |json_out|. If an error occurs, sends |out->HandleError|, and parsing stops.
+// The client is responsible for discarding the already received information in
+// that case.
+void ParseCBOR(span<uint8_t> bytes, JSONParserHandler* json_out);
+
+// Tags for the tokens within a CBOR message that CBORStream understands.
+// Note that this is not the same terminology as the CBOR spec (RFC 7049),
+// but rather, our adaptation. For instance, we lump unsigned and signed
+// major type into INT32 here (and disallow values outside the int32_t range).
+enum class CBORTokenTag {
+ // Encountered an error in the structure of the message. Consult
+ // status() for details.
+ ERROR_VALUE,
+ // Booleans and NULL.
+ TRUE_VALUE,
+ FALSE_VALUE,
+ NULL_VALUE,
+ // An int32_t (signed 32 bit integer).
+ INT32,
+ // A double (64 bit floating point).
+ DOUBLE,
+ // A UTF8 string.
+ STRING8,
+ // A UTF16 string.
+ STRING16,
+ // A binary string.
+ BINARY,
+ // Starts an indefinite length map; after the map start we expect
+ // alternating keys and values, followed by STOP.
+ MAP_START,
+ // Starts an indefinite length array; after the array start we
+ // expect values, followed by STOP.
+ ARRAY_START,
+ // Ends a map or an array.
+ STOP,
+ // An envelope indicator, wrapping a map or array.
+ // Internally this carries the byte length of the wrapped
+ // map or array. While CBORTokenizer::Next() will read / skip the entire
+ // envelope, CBORTokenizer::EnterEnvelope() reads the tokens
+ // inside of it.
+ ENVELOPE,
+ // We've reached the end there is nothing else to read.
+ DONE,
+};
+
+// CBORTokenizer segments a CBOR message, presenting the tokens therein as
+// numbers, strings, etc. This is not a complete CBOR parser, but makes it much
+// easier to implement one (e.g. ParseCBOR, above). It can also be used to parse
+// messages partially.
+class CBORTokenizer {
+ public:
+ explicit CBORTokenizer(span<uint8_t> bytes);
+ ~CBORTokenizer();
+
+ // Identifies the current token that we're looking at,
+ // or ERROR_VALUE (in which ase ::Status() has details)
+ // or DONE (if we're past the last token).
+ CBORTokenTag TokenTag() const;
+
+ // Advances to the next token.
+ void Next();
+ // Can only be called if TokenTag() == CBORTokenTag::ENVELOPE.
+ // While Next() would skip past the entire envelope / what it's
+ // wrapping, EnterEnvelope positions the cursor inside of the envelope,
+ // letting the client explore the nested structure.
+ void EnterEnvelope();
+
+ // If TokenTag() is CBORTokenTag::ERROR_VALUE, then Status().error describes
+ // the error more precisely; otherwise it'll be set to Error::OK.
+ // In either case, Status().pos is the current position.
+ struct Status Status() const;
+
+ // The following methods retrieve the token values. They can only
+ // be called if TokenTag() matches.
+
+ // To be called only if ::TokenTag() == CBORTokenTag::INT32.
+ int32_t GetInt32() const;
+
+ // To be called only if ::TokenTag() == CBORTokenTag::DOUBLE.
+ double GetDouble() const;
+
+ // To be called only if ::TokenTag() == CBORTokenTag::STRING8.
+ span<uint8_t> GetString8() const;
+
+ // Wire representation for STRING16 is low byte first (little endian).
+ // To be called only if ::TokenTag() == CBORTokenTag::STRING16.
+ span<uint8_t> GetString16WireRep() const;
+
+ // To be called only if ::TokenTag() == CBORTokenTag::BINARY.
+ span<uint8_t> GetBinary() const;
+
+ private:
+ void ReadNextToken(bool enter_envelope);
+ void SetToken(CBORTokenTag token, std::ptrdiff_t token_byte_length);
+ void SetError(Error error);
+
+ span<uint8_t> bytes_;
+ CBORTokenTag token_tag_;
+ struct Status status_;
+ std::ptrdiff_t token_byte_length_;
+ cbor::MajorType token_start_type_;
+ uint64_t token_start_internal_value_;
+};
+
+void DumpCBOR(span<uint8_t> cbor);
+
+
+{% for namespace in config.protocol.namespace %}
+} // namespace {{namespace}}
+{% endfor %}
+#endif // !defined({{"_".join(config.protocol.namespace)}}_CBOR_h)
diff --git a/tools/inspector_protocol/lib/DispatcherBase_cpp.template b/tools/inspector_protocol/lib/DispatcherBase_cpp.template
index cecef743bf..11843f4330 100644
--- a/tools/inspector_protocol/lib/DispatcherBase_cpp.template
+++ b/tools/inspector_protocol/lib/DispatcherBase_cpp.template
@@ -1,3 +1,5 @@
+// This file is generated by DispatcherBase_cpp.template.
+
// 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.
@@ -68,10 +70,11 @@ DispatcherBase::WeakPtr::~WeakPtr()
m_dispatcher->m_weakPtrs.erase(this);
}
-DispatcherBase::Callback::Callback(std::unique_ptr<DispatcherBase::WeakPtr> backendImpl, int callId, int callbackId)
+DispatcherBase::Callback::Callback(std::unique_ptr<DispatcherBase::WeakPtr> backendImpl, int callId, const String& method, const ProtocolMessage& message)
: m_backendImpl(std::move(backendImpl))
, m_callId(callId)
- , m_callbackId(callbackId) { }
+ , m_method(method)
+ , m_message(message) { }
DispatcherBase::Callback::~Callback() = default;
@@ -92,32 +95,18 @@ void DispatcherBase::Callback::fallThroughIfActive()
{
if (!m_backendImpl || !m_backendImpl->get())
return;
- m_backendImpl->get()->markFallThrough(m_callbackId);
+ m_backendImpl->get()->channel()->fallThrough(m_callId, m_method, m_message);
m_backendImpl = nullptr;
}
DispatcherBase::DispatcherBase(FrontendChannel* frontendChannel)
- : m_frontendChannel(frontendChannel)
- , m_lastCallbackId(0)
- , m_lastCallbackFallThrough(false) { }
+ : m_frontendChannel(frontendChannel) { }
DispatcherBase::~DispatcherBase()
{
clearFrontend();
}
-int DispatcherBase::nextCallbackId()
-{
- m_lastCallbackFallThrough = false;
- return ++m_lastCallbackId;
-}
-
-void DispatcherBase::markFallThrough(int callbackId)
-{
- DCHECK(callbackId == m_lastCallbackId);
- m_lastCallbackFallThrough = true;
-}
-
void DispatcherBase::sendResponse(int callId, const DispatchResponse& response, std::unique_ptr<protocol::DictionaryValue> result)
{
if (!m_frontendChannel)
@@ -153,18 +142,14 @@ public:
return std::unique_ptr<ProtocolError>(new ProtocolError(code, errorMessage));
}
- String serialize() override
+ String serializeToJSON() override
{
- std::unique_ptr<protocol::DictionaryValue> error = DictionaryValue::create();
- error->setInteger("code", m_code);
- error->setString("message", m_errorMessage);
- if (m_data.length())
- error->setString("data", m_data);
- std::unique_ptr<protocol::DictionaryValue> message = DictionaryValue::create();
- message->setObject("error", std::move(error));
- if (m_hasCallId)
- message->setInteger("id", m_callId);
- return message->serialize();
+ return serialize()->serializeToJSON();
+ }
+
+ std::vector<uint8_t> serializeToBinary() override
+ {
+ return serialize()->serializeToBinary();
}
~ProtocolError() override {}
@@ -176,6 +161,19 @@ private:
{
}
+ std::unique_ptr<DictionaryValue> serialize() {
+ std::unique_ptr<protocol::DictionaryValue> error = DictionaryValue::create();
+ error->setInteger("code", m_code);
+ error->setString("message", m_errorMessage);
+ if (m_data.length())
+ error->setString("data", m_data);
+ std::unique_ptr<protocol::DictionaryValue> message = DictionaryValue::create();
+ message->setObject("error", std::move(error));
+ if (m_hasCallId)
+ message->setInteger("id", m_callId);
+ return message;
+ }
+
DispatchResponse::ErrorCode m_code;
String m_errorMessage;
String m_data;
@@ -218,100 +216,87 @@ std::unique_ptr<DispatcherBase::WeakPtr> DispatcherBase::weakPtr()
}
UberDispatcher::UberDispatcher(FrontendChannel* frontendChannel)
- : m_frontendChannel(frontendChannel)
- , m_fallThroughForNotFound(false) { }
-
-void UberDispatcher::setFallThroughForNotFound(bool fallThroughForNotFound)
-{
- m_fallThroughForNotFound = fallThroughForNotFound;
-}
+ : m_frontendChannel(frontendChannel) { }
void UberDispatcher::registerBackend(const String& name, std::unique_ptr<protocol::DispatcherBase> dispatcher)
{
m_dispatchers[name] = std::move(dispatcher);
}
-void UberDispatcher::setupRedirects(const HashMap<String, String>& redirects)
+void UberDispatcher::setupRedirects(const std::unordered_map<String, String>& redirects)
{
for (const auto& pair : redirects)
m_redirects[pair.first] = pair.second;
}
-DispatchResponse::Status UberDispatcher::dispatch(std::unique_ptr<Value> parsedMessage, int* outCallId, String* outMethod)
-{
+bool UberDispatcher::parseCommand(Value* parsedMessage, int* outCallId, String* outMethod) {
if (!parsedMessage) {
reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kParseError, "Message must be a valid JSON");
- return DispatchResponse::kError;
+ return false;
}
- std::unique_ptr<protocol::DictionaryValue> messageObject = DictionaryValue::cast(std::move(parsedMessage));
+ protocol::DictionaryValue* messageObject = DictionaryValue::cast(parsedMessage);
if (!messageObject) {
reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kInvalidRequest, "Message must be an object");
- return DispatchResponse::kError;
+ return false;
}
int callId = 0;
protocol::Value* callIdValue = messageObject->get("id");
bool success = callIdValue && callIdValue->asInteger(&callId);
- if (outCallId)
- *outCallId = callId;
if (!success) {
reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kInvalidRequest, "Message must have integer 'id' property");
- return DispatchResponse::kError;
+ return false;
}
+ if (outCallId)
+ *outCallId = callId;
protocol::Value* methodValue = messageObject->get("method");
String method;
success = methodValue && methodValue->asString(&method);
- if (outMethod)
- *outMethod = method;
if (!success) {
reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kInvalidRequest, "Message must have string 'method' property", nullptr);
- return DispatchResponse::kError;
+ return false;
}
+ if (outMethod)
+ *outMethod = method;
+ return true;
+}
- HashMap<String, String>::iterator redirectIt = m_redirects.find(method);
- if (redirectIt != m_redirects.end())
- method = redirectIt->second;
-
+protocol::DispatcherBase* UberDispatcher::findDispatcher(const String& method) {
size_t dotIndex = StringUtil::find(method, ".");
- if (dotIndex == StringUtil::kNotFound) {
- if (m_fallThroughForNotFound)
- return DispatchResponse::kFallThrough;
- reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kMethodNotFound, "'" + method + "' wasn't found", nullptr);
- return DispatchResponse::kError;
- }
+ if (dotIndex == StringUtil::kNotFound)
+ return nullptr;
String domain = StringUtil::substring(method, 0, dotIndex);
auto it = m_dispatchers.find(domain);
- if (it == m_dispatchers.end()) {
- if (m_fallThroughForNotFound)
- return DispatchResponse::kFallThrough;
- reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kMethodNotFound, "'" + method + "' wasn't found", nullptr);
- return DispatchResponse::kError;
- }
- return it->second->dispatch(callId, method, std::move(messageObject));
+ if (it == m_dispatchers.end())
+ return nullptr;
+ if (!it->second->canDispatch(method))
+ return nullptr;
+ return it->second.get();
}
-bool UberDispatcher::getCommandName(const String& message, String* method, std::unique_ptr<protocol::DictionaryValue>* parsedMessage)
+bool UberDispatcher::canDispatch(const String& in_method)
{
- std::unique_ptr<protocol::Value> value = StringUtil::parseJSON(message);
- if (!value) {
- reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kParseError, "Message must be a valid JSON");
- return false;
- }
-
- protocol::DictionaryValue* object = DictionaryValue::cast(value.get());
- if (!object) {
- reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kInvalidRequest, "Message must be an object");
- return false;
- }
+ String method = in_method;
+ auto redirectIt = m_redirects.find(method);
+ if (redirectIt != m_redirects.end())
+ method = redirectIt->second;
+ return !!findDispatcher(method);
+}
- if (!object->getString("method", method)) {
- reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kInvalidRequest, "Message must have string 'method' property");
- return false;
+void UberDispatcher::dispatch(int callId, const String& in_method, std::unique_ptr<Value> parsedMessage, const ProtocolMessage& rawMessage)
+{
+ String method = in_method;
+ auto redirectIt = m_redirects.find(method);
+ if (redirectIt != m_redirects.end())
+ method = redirectIt->second;
+ protocol::DispatcherBase* dispatcher = findDispatcher(method);
+ if (!dispatcher) {
+ reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kMethodNotFound, "'" + method + "' wasn't found", nullptr);
+ return;
}
-
- parsedMessage->reset(DictionaryValue::cast(value.release()));
- return true;
+ std::unique_ptr<protocol::DictionaryValue> messageObject = DictionaryValue::cast(std::move(parsedMessage));
+ dispatcher->dispatch(callId, method, rawMessage, std::move(messageObject));
}
UberDispatcher::~UberDispatcher() = default;
@@ -328,18 +313,32 @@ std::unique_ptr<InternalResponse> InternalResponse::createNotification(const Str
return std::unique_ptr<InternalResponse>(new InternalResponse(0, notification, std::move(params)));
}
-String InternalResponse::serialize()
+String InternalResponse::serializeToJSON()
+{
+ std::unique_ptr<DictionaryValue> result = DictionaryValue::create();
+ std::unique_ptr<Serializable> params(m_params ? std::move(m_params) : DictionaryValue::create());
+ if (m_notification.length()) {
+ result->setString("method", m_notification);
+ result->setValue("params", SerializedValue::fromJSON(params->serializeToJSON()));
+ } else {
+ result->setInteger("id", m_callId);
+ result->setValue("result", SerializedValue::fromJSON(params->serializeToJSON()));
+ }
+ return result->serializeToJSON();
+}
+
+std::vector<uint8_t> InternalResponse::serializeToBinary()
{
std::unique_ptr<DictionaryValue> result = DictionaryValue::create();
std::unique_ptr<Serializable> params(m_params ? std::move(m_params) : DictionaryValue::create());
if (m_notification.length()) {
result->setString("method", m_notification);
- result->setValue("params", SerializedValue::create(params->serialize()));
+ result->setValue("params", SerializedValue::fromBinary(params->serializeToBinary()));
} else {
result->setInteger("id", m_callId);
- result->setValue("result", SerializedValue::create(params->serialize()));
+ result->setValue("result", SerializedValue::fromBinary(params->serializeToBinary()));
}
- return result->serialize();
+ return result->serializeToBinary();
}
InternalResponse::InternalResponse(int callId, const String& notification, std::unique_ptr<Serializable> params)
diff --git a/tools/inspector_protocol/lib/DispatcherBase_h.template b/tools/inspector_protocol/lib/DispatcherBase_h.template
index d70a4afe71..7d859c4f27 100644
--- a/tools/inspector_protocol/lib/DispatcherBase_h.template
+++ b/tools/inspector_protocol/lib/DispatcherBase_h.template
@@ -1,3 +1,5 @@
+// This file is generated by DispatcherBase_h.template.
+
// 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.
@@ -5,9 +7,8 @@
#ifndef {{"_".join(config.protocol.namespace)}}_DispatcherBase_h
#define {{"_".join(config.protocol.namespace)}}_DispatcherBase_h
-//#include "Collections.h"
-//#include "ErrorSupport.h"
//#include "Forward.h"
+//#include "ErrorSupport.h"
//#include "Values.h"
{% for namespace in config.protocol.namespace %}
@@ -22,7 +23,6 @@ public:
kSuccess = 0,
kError = 1,
kFallThrough = 2,
- kAsync = 3
};
enum ErrorCode {
@@ -68,7 +68,7 @@ public:
class {{config.lib.export_macro}} Callback {
public:
- Callback(std::unique_ptr<WeakPtr> backendImpl, int callId, int callbackId);
+ Callback(std::unique_ptr<WeakPtr> backendImpl, int callId, const String& method, const ProtocolMessage& message);
virtual ~Callback();
void dispose();
@@ -79,13 +79,16 @@ public:
private:
std::unique_ptr<WeakPtr> m_backendImpl;
int m_callId;
- int m_callbackId;
+ String m_method;
+ ProtocolMessage m_message;
};
explicit DispatcherBase(FrontendChannel*);
virtual ~DispatcherBase();
- virtual DispatchResponse::Status dispatch(int callId, const String& method, std::unique_ptr<protocol::DictionaryValue> messageObject) = 0;
+ virtual bool canDispatch(const String& method) = 0;
+ virtual void dispatch(int callId, const String& method, const ProtocolMessage& rawMessage, std::unique_ptr<protocol::DictionaryValue> messageObject) = 0;
+ FrontendChannel* channel() { return m_frontendChannel; }
void sendResponse(int callId, const DispatchResponse&, std::unique_ptr<protocol::DictionaryValue> result);
void sendResponse(int callId, const DispatchResponse&);
@@ -95,15 +98,9 @@ public:
std::unique_ptr<WeakPtr> weakPtr();
- int nextCallbackId();
- void markFallThrough(int callbackId);
- bool lastCallbackFallThrough() { return m_lastCallbackFallThrough; }
-
private:
FrontendChannel* m_frontendChannel;
- protocol::HashSet<WeakPtr*> m_weakPtrs;
- int m_lastCallbackId;
- bool m_lastCallbackFallThrough;
+ std::unordered_set<WeakPtr*> m_weakPtrs;
};
class {{config.lib.export_macro}} UberDispatcher {
@@ -111,19 +108,18 @@ class {{config.lib.export_macro}} UberDispatcher {
public:
explicit UberDispatcher(FrontendChannel*);
void registerBackend(const String& name, std::unique_ptr<protocol::DispatcherBase>);
- void setupRedirects(const HashMap<String, String>&);
- DispatchResponse::Status dispatch(std::unique_ptr<Value> message, int* callId = nullptr, String* method = nullptr);
+ void setupRedirects(const std::unordered_map<String, String>&);
+ bool parseCommand(Value* message, int* callId, String* method);
+ bool canDispatch(const String& method);
+ void dispatch(int callId, const String& method, std::unique_ptr<Value> message, const ProtocolMessage& rawMessage);
FrontendChannel* channel() { return m_frontendChannel; }
- bool fallThroughForNotFound() { return m_fallThroughForNotFound; }
- void setFallThroughForNotFound(bool);
- bool getCommandName(const String& message, String* method, std::unique_ptr<protocol::DictionaryValue>* parsedMessage);
virtual ~UberDispatcher();
private:
+ protocol::DispatcherBase* findDispatcher(const String& method);
FrontendChannel* m_frontendChannel;
- bool m_fallThroughForNotFound;
- HashMap<String, String> m_redirects;
- protocol::HashMap<String, std::unique_ptr<protocol::DispatcherBase>> m_dispatchers;
+ std::unordered_map<String, String> m_redirects;
+ std::unordered_map<String, std::unique_ptr<protocol::DispatcherBase>> m_dispatchers;
};
class InternalResponse : public Serializable {
@@ -132,7 +128,8 @@ public:
static std::unique_ptr<InternalResponse> createResponse(int callId, std::unique_ptr<Serializable> params);
static std::unique_ptr<InternalResponse> createNotification(const String& notification, std::unique_ptr<Serializable> params = nullptr);
- String serialize() override;
+ String serializeToJSON() override;
+ std::vector<uint8_t> serializeToBinary() override;
~InternalResponse() override {}
@@ -146,24 +143,36 @@ private:
class InternalRawNotification : public Serializable {
public:
- static std::unique_ptr<InternalRawNotification> create(const String& notification)
+ static std::unique_ptr<InternalRawNotification> fromJSON(String notification)
+ {
+ return std::unique_ptr<InternalRawNotification>(new InternalRawNotification(std::move(notification)));
+ }
+
+ static std::unique_ptr<InternalRawNotification> fromBinary(std::vector<uint8_t> notification)
{
- return std::unique_ptr<InternalRawNotification>(new InternalRawNotification(notification));
+ return std::unique_ptr<InternalRawNotification>(new InternalRawNotification(std::move(notification)));
}
+
~InternalRawNotification() override {}
- String serialize() override
+ String serializeToJSON() override
+ {
+ return std::move(m_jsonNotification);
+ }
+
+ std::vector<uint8_t> serializeToBinary() override
{
- return m_notification;
+ return std::move(m_binaryNotification);
}
private:
- explicit InternalRawNotification(const String& notification)
- : m_notification(notification)
- {
- }
+ explicit InternalRawNotification(String notification)
+ : m_jsonNotification(std::move(notification)) { }
+ explicit InternalRawNotification(std::vector<uint8_t> notification)
+ : m_binaryNotification(std::move(notification)) { }
- String m_notification;
+ String m_jsonNotification;
+ std::vector<uint8_t> m_binaryNotification;
};
{% for namespace in config.protocol.namespace %}
diff --git a/tools/inspector_protocol/lib/ErrorSupport_cpp.template b/tools/inspector_protocol/lib/ErrorSupport_cpp.template
index 7b858b8dc4..a5c2a79bbd 100644
--- a/tools/inspector_protocol/lib/ErrorSupport_cpp.template
+++ b/tools/inspector_protocol/lib/ErrorSupport_cpp.template
@@ -1,3 +1,5 @@
+// This file is generated by ErrorSupport_cpp.template.
+
// 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.
diff --git a/tools/inspector_protocol/lib/ErrorSupport_h.template b/tools/inspector_protocol/lib/ErrorSupport_h.template
index 083f2a5eb0..f317a3cfb4 100644
--- a/tools/inspector_protocol/lib/ErrorSupport_h.template
+++ b/tools/inspector_protocol/lib/ErrorSupport_h.template
@@ -1,3 +1,5 @@
+// This file is generated by ErrorSupport_h.template.
+
// 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.
@@ -5,7 +7,7 @@
#ifndef {{"_".join(config.protocol.namespace)}}_ErrorSupport_h
#define {{"_".join(config.protocol.namespace)}}_ErrorSupport_h
-//#include "Forward.h"
+#include {{format_include(config.protocol.package, "Forward")}}
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
diff --git a/tools/inspector_protocol/lib/Forward_h.template b/tools/inspector_protocol/lib/Forward_h.template
index 34d1c0d3e9..ff5e685863 100644
--- a/tools/inspector_protocol/lib/Forward_h.template
+++ b/tools/inspector_protocol/lib/Forward_h.template
@@ -1,3 +1,5 @@
+// This file is generated by Forward_h.template.
+
// 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.
@@ -10,7 +12,11 @@
{% endif %}
#include {{format_include(config.lib.string_header)}}
+#include <cstddef>
+#include <memory>
#include <vector>
+#include <unordered_map>
+#include <unordered_set>
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
diff --git a/tools/inspector_protocol/lib/FrontendChannel_h.template b/tools/inspector_protocol/lib/FrontendChannel_h.template
index 0454978b0c..df104debad 100644
--- a/tools/inspector_protocol/lib/FrontendChannel_h.template
+++ b/tools/inspector_protocol/lib/FrontendChannel_h.template
@@ -1,3 +1,5 @@
+// This file is generated by FrontendChannel_h.template.
+
// 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.
@@ -11,7 +13,14 @@ namespace {{namespace}} {
class {{config.lib.export_macro}} Serializable {
public:
- virtual String serialize() = 0;
+ ProtocolMessage serialize(bool binary) {
+ if (binary)
+ return StringUtil::binaryToMessage(serializeToBinary());
+ else
+ return StringUtil::jsonToMessage(serializeToJSON());
+ }
+ virtual String serializeToJSON() = 0;
+ virtual std::vector<uint8_t> serializeToBinary() = 0;
virtual ~Serializable() = default;
};
@@ -20,6 +29,7 @@ public:
virtual ~FrontendChannel() { }
virtual void sendProtocolResponse(int callId, std::unique_ptr<Serializable> message) = 0;
virtual void sendProtocolNotification(std::unique_ptr<Serializable> message) = 0;
+ virtual void fallThrough(int callId, const String& method, const ProtocolMessage& message) = 0;
virtual void flushProtocolNotifications() = 0;
};
diff --git a/tools/inspector_protocol/lib/Maybe_h.template b/tools/inspector_protocol/lib/Maybe_h.template
index 71593acd0e..22cfac6b24 100644
--- a/tools/inspector_protocol/lib/Maybe_h.template
+++ b/tools/inspector_protocol/lib/Maybe_h.template
@@ -1,3 +1,5 @@
+// This file is generated by Maybe_h.template.
+
// 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.
@@ -5,6 +7,41 @@
#ifndef {{"_".join(config.protocol.namespace)}}_Maybe_h
#define {{"_".join(config.protocol.namespace)}}_Maybe_h
+// This macro allows to test for the version of the GNU C++ compiler.
+// Note that this also applies to compilers that masquerade as GCC,
+// for example clang and the Intel C++ compiler for Linux.
+// Use like:
+// #if IP_GNUC_PREREQ(4, 3, 1)
+// ...
+// #endif
+#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
+#define IP_GNUC_PREREQ(major, minor, patchlevel) \
+ ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= \
+ ((major)*10000 + (minor)*100 + (patchlevel)))
+#elif defined(__GNUC__) && defined(__GNUC_MINOR__)
+#define IP_GNUC_PREREQ(major, minor, patchlevel) \
+ ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= \
+ ((major)*10000 + (minor)*100 + (patchlevel)))
+#else
+#define IP_GNUC_PREREQ(major, minor, patchlevel) 0
+#endif
+
+#if defined(__mips64)
+#define IP_TARGET_ARCH_MIPS64 1
+#elif defined(__MIPSEB__) || defined(__MIPSEL__)
+#define IP_TARGET_ARCH_MIPS 1
+#endif
+
+// Allowing the use of noexcept by removing the keyword on older compilers that
+// do not support adding noexcept to default members.
+#if ((IP_GNUC_PREREQ(4, 9, 0) && !defined(IP_TARGET_ARCH_MIPS) && \
+ !defined(IP_TARGET_ARCH_MIPS64)) || \
+ (defined(__clang__) && __cplusplus > 201300L))
+#define IP_NOEXCEPT noexcept
+#else
+#define IP_NOEXCEPT
+#endif
+
//#include "Forward.h"
{% for namespace in config.protocol.namespace %}
@@ -16,7 +53,7 @@ class Maybe {
public:
Maybe() : m_value() { }
Maybe(std::unique_ptr<T> value) : m_value(std::move(value)) { }
- Maybe(Maybe&& other) : m_value(std::move(other.m_value)) { }
+ Maybe(Maybe&& other) IP_NOEXCEPT : m_value(std::move(other.m_value)) {}
void operator=(std::unique_ptr<T> value) { m_value = std::move(value); }
T* fromJust() const { DCHECK(m_value); return m_value.get(); }
T* fromMaybe(T* defaultValue) const { return m_value ? m_value.get() : defaultValue; }
@@ -31,7 +68,9 @@ class MaybeBase {
public:
MaybeBase() : m_isJust(false) { }
MaybeBase(T value) : m_isJust(true), m_value(value) { }
- MaybeBase(MaybeBase&& other) : m_isJust(other.m_isJust), m_value(std::move(other.m_value)) { }
+ MaybeBase(MaybeBase&& other) IP_NOEXCEPT
+ : m_isJust(other.m_isJust),
+ m_value(std::move(other.m_value)) {}
void operator=(T value) { m_value = value; m_isJust = true; }
T fromJust() const { DCHECK(m_isJust); return m_value; }
T fromMaybe(const T& defaultValue) const { return m_isJust ? m_value : defaultValue; }
@@ -46,27 +85,27 @@ protected:
template<>
class Maybe<bool> : public MaybeBase<bool> {
public:
- Maybe() { }
+ Maybe() { m_value = false; }
Maybe(bool value) : MaybeBase(value) { }
- Maybe(Maybe&& other) : MaybeBase(std::move(other)) { }
+ Maybe(Maybe&& other) IP_NOEXCEPT : MaybeBase(std::move(other)) {}
using MaybeBase::operator=;
};
template<>
class Maybe<int> : public MaybeBase<int> {
public:
- Maybe() { }
+ Maybe() { m_value = 0; }
Maybe(int value) : MaybeBase(value) { }
- Maybe(Maybe&& other) : MaybeBase(std::move(other)) { }
+ Maybe(Maybe&& other) IP_NOEXCEPT : MaybeBase(std::move(other)) {}
using MaybeBase::operator=;
};
template<>
class Maybe<double> : public MaybeBase<double> {
public:
- Maybe() { }
+ Maybe() { m_value = 0; }
Maybe(double value) : MaybeBase(value) { }
- Maybe(Maybe&& other) : MaybeBase(std::move(other)) { }
+ Maybe(Maybe&& other) IP_NOEXCEPT : MaybeBase(std::move(other)) {}
using MaybeBase::operator=;
};
@@ -75,7 +114,16 @@ class Maybe<String> : public MaybeBase<String> {
public:
Maybe() { }
Maybe(const String& value) : MaybeBase(value) { }
- Maybe(Maybe&& other) : MaybeBase(std::move(other)) { }
+ Maybe(Maybe&& other) IP_NOEXCEPT : MaybeBase(std::move(other)) {}
+ using MaybeBase::operator=;
+};
+
+template<>
+class Maybe<Binary> : public MaybeBase<Binary> {
+public:
+ Maybe() { }
+ Maybe(Binary value) : MaybeBase(value) { }
+ Maybe(Maybe&& other) IP_NOEXCEPT : MaybeBase(std::move(other)) {}
using MaybeBase::operator=;
};
@@ -83,4 +131,9 @@ public:
} // namespace {{namespace}}
{% endfor %}
+#undef IP_GNUC_PREREQ
+#undef IP_TARGET_ARCH_MIPS64
+#undef IP_TARGET_ARCH_MIPS
+#undef IP_NOEXCEPT
+
#endif // !defined({{"_".join(config.protocol.namespace)}}_Maybe_h)
diff --git a/tools/inspector_protocol/lib/Object_cpp.template b/tools/inspector_protocol/lib/Object_cpp.template
index 91723a71e2..1640a11127 100644
--- a/tools/inspector_protocol/lib/Object_cpp.template
+++ b/tools/inspector_protocol/lib/Object_cpp.template
@@ -1,3 +1,5 @@
+// This file is generated by Object_cpp.template.
+
// 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.
diff --git a/tools/inspector_protocol/lib/Object_h.template b/tools/inspector_protocol/lib/Object_h.template
index f6ffc57659..ec953d0d48 100644
--- a/tools/inspector_protocol/lib/Object_h.template
+++ b/tools/inspector_protocol/lib/Object_h.template
@@ -1,3 +1,5 @@
+// This file is generated by Object_h.template.
+
// 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.
@@ -16,12 +18,12 @@ namespace {{namespace}} {
class {{config.lib.export_macro}} Object {
public:
static std::unique_ptr<Object> fromValue(protocol::Value*, ErrorSupport*);
+ explicit Object(std::unique_ptr<protocol::DictionaryValue>);
~Object();
std::unique_ptr<protocol::DictionaryValue> toValue() const;
std::unique_ptr<Object> clone() const;
private:
- explicit Object(std::unique_ptr<protocol::DictionaryValue>);
std::unique_ptr<protocol::DictionaryValue> m_object;
};
diff --git a/tools/inspector_protocol/lib/Parser_cpp.template b/tools/inspector_protocol/lib/Parser_cpp.template
index f3dde5ac21..ea7ecc5a1a 100644
--- a/tools/inspector_protocol/lib/Parser_cpp.template
+++ b/tools/inspector_protocol/lib/Parser_cpp.template
@@ -1,3 +1,5 @@
+// This file is generated by Parser_cpp.template.
+
// 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.
@@ -425,9 +427,8 @@ std::unique_ptr<Value> buildValue(const Char* start, const Char* end, const Char
double value = charactersToDouble(tokenStart, tokenEnd - tokenStart, &ok);
if (!ok)
return nullptr;
- int number = static_cast<int>(value);
- if (number == value)
- result = FundamentalValue::create(number);
+ if (value >= INT_MIN && value <= INT_MAX && static_cast<int>(value) == value)
+ result = FundamentalValue::create(static_cast<int>(value));
else
result = FundamentalValue::create(value);
break;
diff --git a/tools/inspector_protocol/lib/Parser_h.template b/tools/inspector_protocol/lib/Parser_h.template
index 8397d3f5d6..1832c2e972 100644
--- a/tools/inspector_protocol/lib/Parser_h.template
+++ b/tools/inspector_protocol/lib/Parser_h.template
@@ -1,3 +1,5 @@
+// This file is generated by Parser_h.template.
+
// 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.
diff --git a/tools/inspector_protocol/lib/Protocol_cpp.template b/tools/inspector_protocol/lib/Protocol_cpp.template
index 901656373a..88303a27ab 100644
--- a/tools/inspector_protocol/lib/Protocol_cpp.template
+++ b/tools/inspector_protocol/lib/Protocol_cpp.template
@@ -1,4 +1,4 @@
-// This file is generated.
+// This file is generated by Protocol_cpp.template.
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
@@ -7,6 +7,6 @@
#include {{format_include(config.protocol.package, "Protocol")}}
#include <algorithm>
+#include <climits>
#include <cmath>
-
#include <cstring>
diff --git a/tools/inspector_protocol/lib/ValueConversions_h.template b/tools/inspector_protocol/lib/ValueConversions_h.template
index 4d64ec9091..2ee5b72454 100644
--- a/tools/inspector_protocol/lib/ValueConversions_h.template
+++ b/tools/inspector_protocol/lib/ValueConversions_h.template
@@ -1,3 +1,5 @@
+// This file is generated by ValueConversions_h.template.
+
// 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.
@@ -100,6 +102,33 @@ struct ValueConversions<String> {
};
template<>
+struct ValueConversions<Binary> {
+ static Binary fromValue(protocol::Value* value, ErrorSupport* errors)
+ {
+ if (!value ||
+ (value->type() != Value::TypeBinary && value->type() != Value::TypeString)) {
+ errors->addError("Either string base64 or binary value expected");
+ return Binary();
+ }
+ Binary binary;
+ if (value->asBinary(&binary))
+ return binary;
+ String result;
+ value->asString(&result);
+ bool success;
+ Binary out = Binary::fromBase64(result, &success);
+ if (!success)
+ errors->addError("base64 decoding error");
+ return out;
+ }
+
+ static std::unique_ptr<protocol::Value> toValue(const Binary& value)
+ {
+ return BinaryValue::create(value);
+ }
+};
+
+template<>
struct ValueConversions<Value> {
static std::unique_ptr<Value> fromValue(protocol::Value* value, ErrorSupport* errors)
{
diff --git a/tools/inspector_protocol/lib/Values_cpp.template b/tools/inspector_protocol/lib/Values_cpp.template
index b9f061346b..4b4ba99415 100644
--- a/tools/inspector_protocol/lib/Values_cpp.template
+++ b/tools/inspector_protocol/lib/Values_cpp.template
@@ -1,3 +1,5 @@
+// This file is generated by Values_cpp.template.
+
// 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.
@@ -58,8 +60,162 @@ void escapeStringForJSONInternal(const Char* str, unsigned len,
}
}
+// When parsing CBOR, we limit recursion depth for objects and arrays
+// to this constant.
+static constexpr int kStackLimitValues = 1000;
+
+// Below are three parsing routines for CBOR, which cover enough
+// to roundtrip JSON messages.
+std::unique_ptr<DictionaryValue> parseMap(int32_t stack_depth, CBORTokenizer* tokenizer);
+std::unique_ptr<ListValue> parseArray(int32_t stack_depth, CBORTokenizer* tokenizer);
+std::unique_ptr<Value> parseValue(int32_t stack_depth, CBORTokenizer* tokenizer);
+
+// |bytes| must start with the indefinite length array byte, so basically,
+// ParseArray may only be called after an indefinite length array has been
+// detected.
+std::unique_ptr<ListValue> parseArray(int32_t stack_depth, CBORTokenizer* tokenizer) {
+ DCHECK(tokenizer->TokenTag() == CBORTokenTag::ARRAY_START);
+ tokenizer->Next();
+ auto list = ListValue::create();
+ while (tokenizer->TokenTag() != CBORTokenTag::STOP) {
+ // Error::CBOR_UNEXPECTED_EOF_IN_ARRAY
+ if (tokenizer->TokenTag() == CBORTokenTag::DONE) return nullptr;
+ if (tokenizer->TokenTag() == CBORTokenTag::ERROR_VALUE) return nullptr;
+ // Parse value.
+ auto value = parseValue(stack_depth, tokenizer);
+ if (!value) return nullptr;
+ list->pushValue(std::move(value));
+ }
+ tokenizer->Next();
+ return list;
+}
+
+std::unique_ptr<Value> parseValue(
+ int32_t stack_depth, CBORTokenizer* tokenizer) {
+ // Error::CBOR_STACK_LIMIT_EXCEEDED
+ if (stack_depth > kStackLimitValues) return nullptr;
+ // Skip past the envelope to get to what's inside.
+ if (tokenizer->TokenTag() == CBORTokenTag::ENVELOPE)
+ tokenizer->EnterEnvelope();
+ switch (tokenizer->TokenTag()) {
+ case CBORTokenTag::ERROR_VALUE:
+ return nullptr;
+ case CBORTokenTag::DONE:
+ // Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE
+ return nullptr;
+ case CBORTokenTag::TRUE_VALUE: {
+ std::unique_ptr<Value> value = FundamentalValue::create(true);
+ tokenizer->Next();
+ return value;
+ }
+ case CBORTokenTag::FALSE_VALUE: {
+ std::unique_ptr<Value> value = FundamentalValue::create(false);
+ tokenizer->Next();
+ return value;
+ }
+ case CBORTokenTag::NULL_VALUE: {
+ std::unique_ptr<Value> value = FundamentalValue::null();
+ tokenizer->Next();
+ return value;
+ }
+ case CBORTokenTag::INT32: {
+ std::unique_ptr<Value> value = FundamentalValue::create(tokenizer->GetInt32());
+ tokenizer->Next();
+ return value;
+ }
+ case CBORTokenTag::DOUBLE: {
+ std::unique_ptr<Value> value = FundamentalValue::create(tokenizer->GetDouble());
+ tokenizer->Next();
+ return value;
+ }
+ case CBORTokenTag::STRING8: {
+ span<uint8_t> str = tokenizer->GetString8();
+ std::unique_ptr<Value> value = StringValue::create(StringUtil::fromUTF8(str.data(), str.size()));
+ tokenizer->Next();
+ return value;
+ }
+ case CBORTokenTag::STRING16:
+ // NOT SUPPORTED YET.
+ return nullptr;
+ case CBORTokenTag::BINARY: {
+ span<uint8_t> payload = tokenizer->GetBinary();
+ tokenizer->Next();
+ return BinaryValue::create(Binary::fromSpan(payload.data(), payload.size()));
+ }
+ case CBORTokenTag::MAP_START:
+ return parseMap(stack_depth + 1, tokenizer);
+ case CBORTokenTag::ARRAY_START:
+ return parseArray(stack_depth + 1, tokenizer);
+ default:
+ // Error::CBOR_UNSUPPORTED_VALUE
+ return nullptr;
+ }
+}
+
+// |bytes| must start with the indefinite length array byte, so basically,
+// ParseArray may only be called after an indefinite length array has been
+// detected.
+std::unique_ptr<DictionaryValue> parseMap(
+ int32_t stack_depth, CBORTokenizer* tokenizer) {
+ auto dict = DictionaryValue::create();
+ tokenizer->Next();
+ while (tokenizer->TokenTag() != CBORTokenTag::STOP) {
+ if (tokenizer->TokenTag() == CBORTokenTag::DONE) {
+ // Error::CBOR_UNEXPECTED_EOF_IN_MAP
+ return nullptr;
+ }
+ if (tokenizer->TokenTag() == CBORTokenTag::ERROR_VALUE) return nullptr;
+ // Parse key.
+ String key;
+ if (tokenizer->TokenTag() == CBORTokenTag::STRING8) {
+ span<uint8_t> key_span = tokenizer->GetString8();
+ key = StringUtil::fromUTF8(key_span.data(), key_span.size());
+ tokenizer->Next();
+ } else if (tokenizer->TokenTag() == CBORTokenTag::STRING16) {
+ return nullptr; // STRING16 not supported yet.
+ } else {
+ // Error::CBOR_INVALID_MAP_KEY
+ return nullptr;
+ }
+ // Parse value.
+ auto value = parseValue(stack_depth, tokenizer);
+ if (!value) return nullptr;
+ dict->setValue(key, std::move(value));
+ }
+ tokenizer->Next();
+ return dict;
+}
+
} // anonymous namespace
+// static
+std::unique_ptr<Value> Value::parseBinary(const uint8_t* data, size_t size) {
+ span<uint8_t> bytes(data, size);
+
+ // Error::CBOR_NO_INPUT
+ if (bytes.empty()) return nullptr;
+
+ // Error::CBOR_INVALID_START_BYTE
+ // TODO(johannes): EncodeInitialByteForEnvelope() method.
+ if (bytes[0] != 0xd8) return nullptr;
+
+ CBORTokenizer tokenizer(bytes);
+ if (tokenizer.TokenTag() == CBORTokenTag::ERROR_VALUE) return nullptr;
+
+ // We checked for the envelope start byte above, so the tokenizer
+ // must agree here, since it's not an error.
+ DCHECK(tokenizer.TokenTag() == CBORTokenTag::ENVELOPE);
+ tokenizer.EnterEnvelope();
+ // Error::MAP_START_EXPECTED
+ if (tokenizer.TokenTag() != CBORTokenTag::MAP_START) return nullptr;
+ std::unique_ptr<Value> result = parseMap(/*stack_depth=*/1, &tokenizer);
+ if (!result) return nullptr;
+ if (tokenizer.TokenTag() == CBORTokenTag::DONE) return result;
+ if (tokenizer.TokenTag() == CBORTokenTag::ERROR_VALUE) return nullptr;
+ // Error::CBOR_TRAILING_JUNK
+ return nullptr;
+}
+
bool Value::asBoolean(bool*) const
{
return false;
@@ -80,7 +236,7 @@ bool Value::asString(String*) const
return false;
}
-bool Value::asSerialized(String*) const
+bool Value::asBinary(Binary*) const
{
return false;
}
@@ -91,12 +247,17 @@ void Value::writeJSON(StringBuilder* output) const
StringUtil::builderAppend(*output, nullValueString, 4);
}
+void Value::writeBinary(std::vector<uint8_t>* bytes) const {
+ DCHECK(m_type == TypeNull);
+ bytes->push_back(EncodeNull());
+}
+
std::unique_ptr<Value> Value::clone() const
{
return Value::null();
}
-String Value::serialize()
+String Value::toJSONString() const
{
StringBuilder result;
StringUtil::builderReserve(result, 512);
@@ -104,6 +265,16 @@ String Value::serialize()
return StringUtil::builderToString(result);
}
+String Value::serializeToJSON() {
+ return toJSONString();
+}
+
+std::vector<uint8_t> Value::serializeToBinary() {
+ std::vector<uint8_t> bytes;
+ writeBinary(&bytes);
+ return bytes;
+}
+
bool FundamentalValue::asBoolean(bool* output) const
{
if (type() != TypeBoolean)
@@ -152,6 +323,22 @@ void FundamentalValue::writeJSON(StringBuilder* output) const
}
}
+void FundamentalValue::writeBinary(std::vector<uint8_t>* bytes) const {
+ switch (type()) {
+ case TypeDouble:
+ EncodeDouble(m_doubleValue, bytes);
+ return;
+ case TypeInteger:
+ EncodeInt32(m_integerValue, bytes);
+ return;
+ case TypeBoolean:
+ bytes->push_back(m_boolValue ? EncodeTrue() : EncodeFalse());
+ return;
+ default:
+ DCHECK(false);
+ }
+}
+
std::unique_ptr<Value> FundamentalValue::clone() const
{
switch (type()) {
@@ -176,26 +363,53 @@ void StringValue::writeJSON(StringBuilder* output) const
StringUtil::builderAppendQuotedString(*output, m_stringValue);
}
+void StringValue::writeBinary(std::vector<uint8_t>* bytes) const {
+ StringUTF8Adapter utf8(m_stringValue);
+ EncodeString8(span<uint8_t>(reinterpret_cast<const uint8_t*>(utf8.Data()),
+ utf8.length()), bytes);
+}
+
std::unique_ptr<Value> StringValue::clone() const
{
return StringValue::create(m_stringValue);
}
-bool SerializedValue::asSerialized(String* output) const
+bool BinaryValue::asBinary(Binary* output) const
{
- *output = m_serializedValue;
+ *output = m_binaryValue;
return true;
}
+void BinaryValue::writeJSON(StringBuilder* output) const
+{
+ DCHECK(type() == TypeBinary);
+ StringUtil::builderAppendQuotedString(*output, m_binaryValue.toBase64());
+}
+
+void BinaryValue::writeBinary(std::vector<uint8_t>* bytes) const {
+ EncodeBinary(span<uint8_t>(m_binaryValue.data(), m_binaryValue.size()), bytes);
+}
+
+std::unique_ptr<Value> BinaryValue::clone() const
+{
+ return BinaryValue::create(m_binaryValue);
+}
+
void SerializedValue::writeJSON(StringBuilder* output) const
{
DCHECK(type() == TypeSerialized);
- StringUtil::builderAppend(*output, m_serializedValue);
+ StringUtil::builderAppend(*output, m_serializedJSON);
+}
+
+void SerializedValue::writeBinary(std::vector<uint8_t>* output) const
+{
+ DCHECK(type() == TypeSerialized);
+ output->insert(output->end(), m_serializedBinary.begin(), m_serializedBinary.end());
}
std::unique_ptr<Value> SerializedValue::clone() const
{
- return SerializedValue::create(m_serializedValue);
+ return std::unique_ptr<SerializedValue>(new SerializedValue(m_serializedJSON, m_serializedBinary));
}
DictionaryValue::~DictionaryValue()
@@ -335,6 +549,23 @@ void DictionaryValue::writeJSON(StringBuilder* output) const
StringUtil::builderAppend(*output, '}');
}
+void DictionaryValue::writeBinary(std::vector<uint8_t>* bytes) const {
+ EnvelopeEncoder encoder;
+ encoder.EncodeStart(bytes);
+ bytes->push_back(EncodeIndefiniteLengthMapStart());
+ for (size_t i = 0; i < m_order.size(); ++i) {
+ const String& key = m_order[i];
+ Dictionary::const_iterator value = m_data.find(key);
+ DCHECK(value != m_data.cend() && value->second);
+ StringUTF8Adapter utf8(key);
+ EncodeString8(span<uint8_t>(reinterpret_cast<const uint8_t*>(utf8.Data()),
+ utf8.length()), bytes);
+ value->second->writeBinary(bytes);
+ }
+ bytes->push_back(EncodeStop());
+ encoder.EncodeStop(bytes);
+}
+
std::unique_ptr<Value> DictionaryValue::clone() const
{
std::unique_ptr<DictionaryValue> result = DictionaryValue::create();
@@ -369,6 +600,17 @@ void ListValue::writeJSON(StringBuilder* output) const
StringUtil::builderAppend(*output, ']');
}
+void ListValue::writeBinary(std::vector<uint8_t>* bytes) const {
+ EnvelopeEncoder encoder;
+ encoder.EncodeStart(bytes);
+ bytes->push_back(EncodeIndefiniteLengthArrayStart());
+ for (size_t i = 0; i < m_data.size(); ++i) {
+ m_data[i]->writeBinary(bytes);
+ }
+ bytes->push_back(EncodeStop());
+ encoder.EncodeStop(bytes);
+}
+
std::unique_ptr<Value> ListValue::clone() const
{
std::unique_ptr<ListValue> result = ListValue::create();
diff --git a/tools/inspector_protocol/lib/Values_h.template b/tools/inspector_protocol/lib/Values_h.template
index 3638b34b4e..4a2e58f4cd 100644
--- a/tools/inspector_protocol/lib/Values_h.template
+++ b/tools/inspector_protocol/lib/Values_h.template
@@ -1,3 +1,5 @@
+// This file is generated by Values_h.template.
+
// 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.
@@ -6,7 +8,6 @@
#define {{"_".join(config.protocol.namespace)}}_Values_h
//#include "Allocator.h"
-//#include "Collections.h"
//#include "Forward.h"
{% for namespace in config.protocol.namespace %}
@@ -27,15 +28,19 @@ public:
return std::unique_ptr<Value>(new Value());
}
+ static std::unique_ptr<Value> parseBinary(const uint8_t* data, size_t size);
+
enum ValueType {
TypeNull = 0,
TypeBoolean,
TypeInteger,
TypeDouble,
TypeString,
+ TypeBinary,
TypeObject,
TypeArray,
- TypeSerialized
+ TypeSerialized,
+ TypeImported
};
ValueType type() const { return m_type; }
@@ -46,11 +51,14 @@ public:
virtual bool asDouble(double* output) const;
virtual bool asInteger(int* output) const;
virtual bool asString(String* output) const;
- virtual bool asSerialized(String* output) const;
+ virtual bool asBinary(Binary* output) const;
virtual void writeJSON(StringBuilder* output) const;
+ virtual void writeBinary(std::vector<uint8_t>* bytes) const;
virtual std::unique_ptr<Value> clone() const;
- String serialize() override;
+ String toJSONString() const;
+ String serializeToJSON() override;
+ std::vector<uint8_t> serializeToBinary() override;
protected:
Value() : m_type(TypeNull) { }
@@ -84,6 +92,7 @@ public:
bool asDouble(double* output) const override;
bool asInteger(int* output) const override;
void writeJSON(StringBuilder* output) const override;
+ void writeBinary(std::vector<uint8_t>* bytes) const override;
std::unique_ptr<Value> clone() const override;
private:
@@ -112,6 +121,7 @@ public:
bool asString(String* output) const override;
void writeJSON(StringBuilder* output) const override;
+ void writeBinary(std::vector<uint8_t>* bytes) const override;
std::unique_ptr<Value> clone() const override;
private:
@@ -121,21 +131,47 @@ private:
String m_stringValue;
};
+class {{config.lib.export_macro}} BinaryValue : public Value {
+public:
+ static std::unique_ptr<BinaryValue> create(const Binary& value)
+ {
+ return std::unique_ptr<BinaryValue>(new BinaryValue(value));
+ }
+
+ bool asBinary(Binary* output) const override;
+ void writeJSON(StringBuilder* output) const override;
+ void writeBinary(std::vector<uint8_t>* bytes) const override;
+ std::unique_ptr<Value> clone() const override;
+
+private:
+ explicit BinaryValue(const Binary& value) : Value(TypeBinary), m_binaryValue(value) { }
+
+ Binary m_binaryValue;
+};
+
class {{config.lib.export_macro}} SerializedValue : public Value {
public:
- static std::unique_ptr<SerializedValue> create(const String& value)
+ static std::unique_ptr<SerializedValue> fromJSON(const String& value)
{
return std::unique_ptr<SerializedValue>(new SerializedValue(value));
}
- bool asSerialized(String* output) const override;
+ static std::unique_ptr<SerializedValue> fromBinary(std::vector<uint8_t> value)
+ {
+ return std::unique_ptr<SerializedValue>(new SerializedValue(std::move(value)));
+ }
+
void writeJSON(StringBuilder* output) const override;
+ void writeBinary(std::vector<uint8_t>* bytes) const override;
std::unique_ptr<Value> clone() const override;
private:
- explicit SerializedValue(const String& value) : Value(TypeSerialized), m_serializedValue(value) { }
-
- String m_serializedValue;
+ explicit SerializedValue(const String& json) : Value(TypeSerialized), m_serializedJSON(json) { }
+ explicit SerializedValue(std::vector<uint8_t> binary) : Value(TypeSerialized), m_serializedBinary(std::move(binary)) { }
+ SerializedValue(const String& json, const std::vector<uint8_t>& binary)
+ : Value(TypeSerialized), m_serializedJSON(json), m_serializedBinary(binary) { }
+ String m_serializedJSON;
+ std::vector<uint8_t> m_serializedBinary;
};
class {{config.lib.export_macro}} DictionaryValue : public Value {
@@ -159,6 +195,7 @@ public:
}
void writeJSON(StringBuilder* output) const override;
+ void writeBinary(std::vector<uint8_t>* bytes) const override;
std::unique_ptr<Value> clone() const override;
size_t size() const { return m_data.size(); }
@@ -200,7 +237,7 @@ private:
m_order.push_back(key);
}
- using Dictionary = protocol::HashMap<String, std::unique_ptr<Value>>;
+ using Dictionary = std::unordered_map<String, std::unique_ptr<Value>>;
Dictionary m_data;
std::vector<String> m_order;
};
@@ -227,6 +264,7 @@ public:
~ListValue() override;
void writeJSON(StringBuilder* output) const override;
+ void writeBinary(std::vector<uint8_t>* bytes) const override;
std::unique_ptr<Value> clone() const override;
void pushValue(std::unique_ptr<Value>);
diff --git a/tools/inspector_protocol/lib/base_string_adapter_cc.template b/tools/inspector_protocol/lib/base_string_adapter_cc.template
new file mode 100644
index 0000000000..ed3316446f
--- /dev/null
+++ b/tools/inspector_protocol/lib/base_string_adapter_cc.template
@@ -0,0 +1,304 @@
+// This file is generated by DispatcherBase_cpp.template.
+
+// Copyright 2019 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.
+
+#include {{format_include(config.protocol.package, "base_string_adapter")}}
+#include {{format_include(config.protocol.package, "Protocol")}}
+
+#include <utility>
+#include "base/base64.h"
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+
+{% for namespace in config.protocol.namespace %}
+namespace {{namespace}} {
+{% endfor %}
+
+std::unique_ptr<protocol::Value> toProtocolValue(
+ const base::Value* value, int depth) {
+ if (!value || !depth)
+ return nullptr;
+ if (value->is_none())
+ return protocol::Value::null();
+ if (value->is_bool()) {
+ bool inner;
+ value->GetAsBoolean(&inner);
+ return protocol::FundamentalValue::create(inner);
+ }
+ if (value->is_int()) {
+ int inner;
+ value->GetAsInteger(&inner);
+ return protocol::FundamentalValue::create(inner);
+ }
+ if (value->is_double()) {
+ double inner;
+ value->GetAsDouble(&inner);
+ return protocol::FundamentalValue::create(inner);
+ }
+ if (value->is_string()) {
+ std::string inner;
+ value->GetAsString(&inner);
+ return protocol::StringValue::create(inner);
+ }
+ if (value->is_list()) {
+ const base::ListValue* list = nullptr;
+ value->GetAsList(&list);
+ std::unique_ptr<protocol::ListValue> result = protocol::ListValue::create();
+ for (size_t i = 0; i < list->GetSize(); i++) {
+ const base::Value* item = nullptr;
+ list->Get(i, &item);
+ std::unique_ptr<protocol::Value> converted =
+ toProtocolValue(item, depth - 1);
+ if (converted)
+ result->pushValue(std::move(converted));
+ }
+ return std::move(result);
+ }
+ if (value->is_dict()) {
+ const base::DictionaryValue* dictionary = nullptr;
+ value->GetAsDictionary(&dictionary);
+ std::unique_ptr<protocol::DictionaryValue> result =
+ protocol::DictionaryValue::create();
+ for (base::DictionaryValue::Iterator it(*dictionary);
+ !it.IsAtEnd(); it.Advance()) {
+ std::unique_ptr<protocol::Value> converted =
+ toProtocolValue(&it.value(), depth - 1);
+ if (converted)
+ result->setValue(it.key(), std::move(converted));
+ }
+ return std::move(result);
+ }
+ return nullptr;
+}
+
+std::unique_ptr<base::Value> toBaseValue(Value* value, int depth) {
+ if (!value || !depth)
+ return nullptr;
+ if (value->type() == Value::TypeNull)
+ return std::make_unique<base::Value>();
+ if (value->type() == Value::TypeBoolean) {
+ bool inner;
+ value->asBoolean(&inner);
+ return base::WrapUnique(new base::Value(inner));
+ }
+ if (value->type() == Value::TypeInteger) {
+ int inner;
+ value->asInteger(&inner);
+ return base::WrapUnique(new base::Value(inner));
+ }
+ if (value->type() == Value::TypeDouble) {
+ double inner;
+ value->asDouble(&inner);
+ return base::WrapUnique(new base::Value(inner));
+ }
+ if (value->type() == Value::TypeString) {
+ std::string inner;
+ value->asString(&inner);
+ return base::WrapUnique(new base::Value(inner));
+ }
+ if (value->type() == Value::TypeArray) {
+ ListValue* list = ListValue::cast(value);
+ std::unique_ptr<base::ListValue> result(new base::ListValue());
+ for (size_t i = 0; i < list->size(); i++) {
+ std::unique_ptr<base::Value> converted =
+ toBaseValue(list->at(i), depth - 1);
+ if (converted)
+ result->Append(std::move(converted));
+ }
+ return std::move(result);
+ }
+ if (value->type() == Value::TypeObject) {
+ DictionaryValue* dict = DictionaryValue::cast(value);
+ std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
+ for (size_t i = 0; i < dict->size(); i++) {
+ DictionaryValue::Entry entry = dict->at(i);
+ std::unique_ptr<base::Value> converted =
+ toBaseValue(entry.second, depth - 1);
+ if (converted)
+ result->SetWithoutPathExpansion(entry.first, std::move(converted));
+ }
+ return std::move(result);
+ }
+ return nullptr;
+}
+
+// static
+std::unique_ptr<Value> StringUtil::parseMessage(
+ const std::string& message, bool binary) {
+ if (binary) {
+ return Value::parseBinary(
+ reinterpret_cast<const uint8_t*>(message.data()),
+ message.length());
+ }
+ std::unique_ptr<base::Value> value = base::JSONReader::Read(message);
+ return toProtocolValue(value.get(), 1000);
+}
+
+// static
+ProtocolMessage StringUtil::jsonToMessage(String message) {
+ return message;
+}
+
+// static
+ProtocolMessage StringUtil::binaryToMessage(std::vector<uint8_t> message) {
+ // TODO(pfeldman): figure out what to do with this copy.
+ return std::string(reinterpret_cast<const char*>(message.data()), message.size());
+}
+
+StringBuilder::StringBuilder() {}
+
+StringBuilder::~StringBuilder() {}
+
+void StringBuilder::append(const std::string& s) {
+ string_ += s;
+}
+
+void StringBuilder::append(char c) {
+ string_ += c;
+}
+
+void StringBuilder::append(const char* characters, size_t length) {
+ string_.append(characters, length);
+}
+
+// static
+void StringUtil::builderAppendQuotedString(StringBuilder& builder,
+ const String& str) {
+ builder.append('"');
+ base::string16 str16 = base::UTF8ToUTF16(str);
+ escapeWideStringForJSON(reinterpret_cast<const uint16_t*>(&str16[0]),
+ str16.length(), &builder);
+ builder.append('"');
+}
+
+std::string StringBuilder::toString() {
+ return string_;
+}
+
+void StringBuilder::reserveCapacity(size_t capacity) {
+ string_.reserve(capacity);
+}
+
+Binary::Binary() : bytes_(new base::RefCountedBytes) {}
+Binary::Binary(const Binary& binary) : bytes_(binary.bytes_) {}
+Binary::Binary(scoped_refptr<base::RefCountedMemory> bytes) : bytes_(bytes) {}
+Binary::~Binary() {}
+
+String Binary::toBase64() const {
+ std::string encoded;
+ base::Base64Encode(
+ base::StringPiece(reinterpret_cast<const char*>(bytes_->front()),
+ bytes_->size()),
+ &encoded);
+ return encoded;
+}
+
+// static
+Binary Binary::fromBase64(const String& base64, bool* success) {
+ std::string decoded;
+ *success = base::Base64Decode(base::StringPiece(base64), &decoded);
+ if (*success) {
+ return Binary::fromString(std::move(decoded));
+ }
+ return Binary();
+}
+
+// static
+Binary Binary::fromRefCounted(scoped_refptr<base::RefCountedMemory> memory) {
+ return Binary(memory);
+}
+
+// static
+Binary Binary::fromVector(std::vector<uint8_t> data) {
+ return Binary(base::RefCountedBytes::TakeVector(&data));
+}
+
+// static
+Binary Binary::fromString(std::string data) {
+ return Binary(base::RefCountedString::TakeString(&data));
+}
+
+// static
+Binary Binary::fromSpan(const uint8_t* data, size_t size) {
+ return Binary(scoped_refptr<base::RefCountedBytes>(
+ new base::RefCountedBytes(data, size)));
+}
+
+namespace {
+int32_t ReadEnvelopeSize(const uint8_t* in) {
+ return (in[0] << 24) + (in[1] << 16) + (in[2] << 8) + in[3];
+}
+
+void WriteEnvelopeSize(uint32_t value, uint8_t* out) {
+ *(out++) = (value >> 24) & 0xFF;
+ *(out++) = (value >> 16) & 0xFF;
+ *(out++) = (value >> 8) & 0xFF;
+ *(out++) = (value) & 0xFF;
+}
+
+}
+
+bool AppendStringValueToMapBinary(base::StringPiece in,
+ base::StringPiece key, base::StringPiece value, std::string* out) {
+ if (in.size() < 1 + 1 + 4 + 1 + 1)
+ return false;
+ const uint8_t* envelope = reinterpret_cast<const uint8_t*>(in.data());
+ if (cbor::kInitialByteForEnvelope != envelope[0])
+ return false;
+ if (cbor::kInitialByteFor32BitLengthByteString != envelope[1])
+ return false;
+ if (cbor::kInitialByteIndefiniteLengthMap != envelope[6])
+ return false;
+
+ uint32_t envelope_size = ReadEnvelopeSize(envelope + 2);
+ if (envelope_size + 2 + 4 != in.size())
+ return false;
+ if (cbor::kStopByte != static_cast<uint8_t>(*in.rbegin()))
+ return false;
+
+ std::vector<uint8_t> encoded_entry;
+ encoded_entry.reserve(1 + 4 + key.size() + 1 + 4 + value.size());
+ span<uint8_t> key_span(
+ reinterpret_cast<const uint8_t*>(key.data()), key.size());
+ EncodeString8(key_span, &encoded_entry);
+ span<uint8_t> value_span(
+ reinterpret_cast<const uint8_t*>(value.data()), value.size());
+ EncodeString8(value_span, &encoded_entry);
+
+ out->clear();
+ out->reserve(in.size() + encoded_entry.size());
+ out->append(in.begin(), in.end() - 1);
+ out->append(reinterpret_cast<const char*>(encoded_entry.data()),
+ encoded_entry.size());
+ out->append(1, static_cast<char>(cbor::kStopByte));
+ std::size_t new_size = envelope_size + out->size() - in.size();
+ if (new_size > static_cast<std::size_t>(
+ std::numeric_limits<uint32_t>::max())) {
+ return false;
+ }
+ WriteEnvelopeSize(new_size, reinterpret_cast<uint8_t*>(&*out->begin() + 2));
+ return true;
+}
+
+bool AppendStringValueToMapJSON(base::StringPiece in,
+ base::StringPiece key, base::StringPiece value, std::string* out) {
+ if (!in.length() || *in.rbegin() != '}')
+ return false;
+ std::string suffix =
+ base::StringPrintf(", \"%s\": \"%s\"}", key.begin(), value.begin());
+ out->clear();
+ out->reserve(in.length() + suffix.length() - 1);
+ out->append(in.data(), in.length() - 1);
+ out->append(suffix);
+ return true;
+}
+
+{% for namespace in config.protocol.namespace %}
+} // namespace {{namespace}}
+{% endfor %}
diff --git a/tools/inspector_protocol/lib/base_string_adapter_h.template b/tools/inspector_protocol/lib/base_string_adapter_h.template
new file mode 100644
index 0000000000..b0215e0745
--- /dev/null
+++ b/tools/inspector_protocol/lib/base_string_adapter_h.template
@@ -0,0 +1,150 @@
+// This file is generated by Parser_h.template.
+
+// Copyright 2019 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.
+
+#ifndef {{"_".join(config.protocol.namespace)}}_BASE_STRING_ADAPTER_H
+#define {{"_".join(config.protocol.namespace)}}_BASE_STRING_ADAPTER_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/strings/string_number_conversions.h"
+{% if config.lib.export_header %}
+#include "{{config.lib.export_header}}"
+{% endif %}
+
+namespace base {
+class Value;
+}
+
+{% for namespace in config.protocol.namespace %}
+namespace {{namespace}} {
+{% endfor %}
+
+class Value;
+
+using String = std::string;
+using ProtocolMessage = std::string;
+
+class {{config.lib.export_macro}} StringUTF8Adapter {
+ public:
+ StringUTF8Adapter(const std::string& string) : string_(string) { }
+ const char* Data() const { return string_.data(); }
+ size_t length() const { return string_.length(); }
+
+ private:
+ const std::string& string_;
+};
+
+class {{config.lib.export_macro}} StringBuilder {
+ public:
+ StringBuilder();
+ ~StringBuilder();
+ void append(const String&);
+ void append(char);
+ void append(const char*, size_t);
+ String toString();
+ void reserveCapacity(size_t);
+
+ private:
+ std::string string_;
+};
+
+class {{config.lib.export_macro}} StringUtil {
+ public:
+ static String substring(const String& s, unsigned pos, unsigned len) {
+ return s.substr(pos, len);
+ }
+ static String fromInteger(int number) { return base::NumberToString(number); }
+ static String fromDouble(double number) {
+ String s = base::NumberToString(number);
+ if (!s.empty()) { // .123 -> 0.123; -.123 -> -0.123 for valid JSON.
+ if (s[0] == '.')
+ s.insert(/*index=*/ 0, /*count=*/ 1, /*ch=*/ '0');
+ else if (s[0] == '-' && s.size() >= 2 && s[1] == '.')
+ s.insert(/*index=*/ 1, /*count=*/ 1, /*ch=*/ '0');
+ }
+ return s;
+ }
+ static double toDouble(const char* s, size_t len, bool* ok) {
+ double v = 0.0;
+ *ok = base::StringToDouble(std::string(s, len), &v);
+ return *ok ? v : 0.0;
+ }
+ static size_t find(const String& s, const char* needle) {
+ return s.find(needle);
+ }
+ static size_t find(const String& s, const String& needle) {
+ return s.find(needle);
+ }
+ static const size_t kNotFound = static_cast<size_t>(-1);
+ static void builderAppend(StringBuilder& builder, const String& s) {
+ builder.append(s);
+ }
+ static void builderAppend(StringBuilder& builder, char c) {
+ builder.append(c);
+ }
+ static void builderAppend(StringBuilder& builder, const char* s, size_t len) {
+ builder.append(s, len);
+ }
+ static void builderAppendQuotedString(StringBuilder& builder,
+ const String& str);
+ static void builderReserve(StringBuilder& builder, unsigned capacity) {
+ builder.reserveCapacity(capacity);
+ }
+ static String builderToString(StringBuilder& builder) {
+ return builder.toString();
+ }
+
+ static std::unique_ptr<Value> parseMessage(const std::string& message, bool binary);
+ static ProtocolMessage jsonToMessage(String message);
+ static ProtocolMessage binaryToMessage(std::vector<uint8_t> message);
+
+ static String fromUTF8(const uint8_t* data, size_t length) {
+ return std::string(reinterpret_cast<const char*>(data), length);
+ }
+};
+
+// A read-only sequence of uninterpreted bytes with reference-counted storage.
+class {{config.lib.export_macro}} Binary {
+ public:
+ Binary(const Binary&);
+ Binary();
+ ~Binary();
+
+ const uint8_t* data() const { return bytes_->front(); }
+ size_t size() const { return bytes_->size(); }
+ scoped_refptr<base::RefCountedMemory> bytes() const { return bytes_; }
+
+ String toBase64() const;
+
+ static Binary fromBase64(const String& base64, bool* success);
+ static Binary fromRefCounted(scoped_refptr<base::RefCountedMemory> memory);
+ static Binary fromVector(std::vector<uint8_t> data);
+ static Binary fromString(std::string data);
+ static Binary fromSpan(const uint8_t* data, size_t size);
+
+ private:
+ explicit Binary(scoped_refptr<base::RefCountedMemory> bytes);
+ scoped_refptr<base::RefCountedMemory> bytes_;
+};
+
+std::unique_ptr<Value> toProtocolValue(const base::Value* value, int depth);
+std::unique_ptr<base::Value> toBaseValue(Value* value, int depth);
+
+bool AppendStringValueToMapBinary(base::StringPiece in,
+ base::StringPiece key, base::StringPiece value, std::string* out);
+bool AppendStringValueToMapJSON(base::StringPiece in,
+ base::StringPiece key, base::StringPiece value, std::string* out);
+
+{% for namespace in config.protocol.namespace %}
+} // namespace {{namespace}}
+{% endfor %}
+
+#endif // !defined({{"_".join(config.protocol.namespace)}}_BASE_STRING_ADAPTER_H)
diff --git a/tools/inspector_protocol/ConvertProtocolToJSON.py b/tools/inspector_protocol/pdl.py
index 56fc09d78c..43111e944b 100644
--- a/tools/inspector_protocol/ConvertProtocolToJSON.py
+++ b/tools/inspector_protocol/pdl.py
@@ -1,28 +1,31 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
+# Copyright 2018 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.
+from __future__ import print_function
import collections
import json
import os.path
import re
import sys
-file_name = None
description = ''
-primitiveTypes = ['integer', 'number', 'boolean', 'string', 'object', 'any', 'array']
+primitiveTypes = ['integer', 'number', 'boolean', 'string', 'object', 'any', 'array', 'binary']
-def assignType(item, type, isArray=False):
- if isArray:
+
+def assignType(item, type, is_array=False, map_binary_to_string=False):
+ if is_array:
item['type'] = 'array'
item['items'] = collections.OrderedDict()
- assignType(item['items'], type)
+ assignType(item['items'], type, False, map_binary_to_string)
return
if type == 'enum':
type = 'string'
+ if map_binary_to_string and type == 'binary':
+ type = 'string'
if type in primitiveTypes:
item['type'] = type
else:
@@ -43,7 +46,7 @@ def createItem(d, experimental, deprecated, name=None):
return result
-def parse(data):
+def parse(data, file_name, map_binary_to_string=False):
protocol = collections.OrderedDict()
protocol['version'] = collections.OrderedDict()
protocol['domains'] = []
@@ -89,7 +92,7 @@ def parse(data):
if 'types' not in domain:
domain['types'] = []
item = createItem({'id': match.group(3)}, match.group(1), match.group(2))
- assignType(item, match.group(5), match.group(4))
+ assignType(item, match.group(5), match.group(4), map_binary_to_string)
domain['types'].append(item)
continue
@@ -116,7 +119,7 @@ def parse(data):
param = createItem({}, match.group(1), match.group(2), match.group(6))
if match.group(3):
param['optional'] = True
- assignType(param, match.group(5), match.group(4))
+ assignType(param, match.group(5), match.group(4), map_binary_to_string)
if match.group(5) == 'enum':
enumliterals = param['enum'] = []
subitems.append(param)
@@ -157,27 +160,12 @@ def parse(data):
enumliterals.append(trimLine)
continue
- print 'Error in %s:%s, illegal token: \t%s' % (file_name, i, line)
+ print('Error in %s:%s, illegal token: \t%s' % (file_name, i, line))
sys.exit(1)
return protocol
-def main(argv):
- if len(argv) < 2:
- sys.stderr.write("Usage: %s <protocol.pdl> <protocol.json>\n" % sys.argv[0])
- return 1
- global file_name
- file_name = os.path.normpath(argv[0])
- input_file = open(file_name, "r")
- pdl_string = input_file.read()
- protocol = parse(pdl_string)
- output_file = open(argv[0].replace('.pdl', '.json'), 'wb')
- json.dump(protocol, output_file, indent=4, separators=(',', ': '))
- output_file.close()
-
- output_file = open(os.path.normpath(argv[1]), 'wb')
- json.dump(protocol, output_file, indent=4, separators=(',', ': '))
- output_file.close()
-
-
-if __name__ == '__main__':
- sys.exit(main(sys.argv[1:]))
+
+def loads(data, file_name, map_binary_to_string=False):
+ if file_name.endswith(".pdl"):
+ return parse(data, file_name, map_binary_to_string)
+ return json.loads(data)
diff --git a/tools/inspector_protocol/templates/Exported_h.template b/tools/inspector_protocol/templates/Exported_h.template
index 3d36ecffae..765f6c2135 100644
--- a/tools/inspector_protocol/templates/Exported_h.template
+++ b/tools/inspector_protocol/templates/Exported_h.template
@@ -1,4 +1,4 @@
-// This file is generated
+// This file is generated by Exported_h.template.
// Copyright (c) 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
@@ -15,6 +15,17 @@
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
{% endfor %}
+
+#ifndef {{"_".join(config.protocol.namespace)}}_exported_api_h
+#define {{"_".join(config.protocol.namespace)}}_exported_api_h
+class {{config.exported.export_macro}} Exported {
+public:
+ virtual {{config.exported.string_out}} toJSONString() const = 0;
+ virtual void writeBinary(std::vector<uint8_t>* out) const = 0;
+ virtual ~Exported() { }
+};
+#endif // !defined({{"_".join(config.protocol.namespace)}}_exported_api_h)
+
namespace {{domain.domain}} {
namespace API {
@@ -48,11 +59,10 @@ namespace {{param.name | to_title_case}}Enum {
{% for type in domain.types %}
{% if not (type.type == "object") or not ("properties" in type) or not protocol.is_exported(domain.domain, type.id) %}{% continue %}{% endif %}
-class {{config.exported.export_macro}} {{type.id}} {
+class {{config.exported.export_macro}} {{type.id}} : public Exported {
public:
- virtual {{config.exported.string_out}} toJSONString() const = 0;
- virtual ~{{type.id}}() { }
static std::unique_ptr<protocol::{{domain.domain}}::API::{{type.id}}> fromJSONString(const {{config.exported.string_in}}& json);
+ static std::unique_ptr<protocol::{{domain.domain}}::API::{{type.id}}> fromBinary(const uint8_t* data, size_t length);
};
{% endfor %}
diff --git a/tools/inspector_protocol/templates/Imported_h.template b/tools/inspector_protocol/templates/Imported_h.template
index 4c9d24bd5f..f2e576a9c4 100644
--- a/tools/inspector_protocol/templates/Imported_h.template
+++ b/tools/inspector_protocol/templates/Imported_h.template
@@ -1,4 +1,4 @@
-// This file is generated
+// This file is generated by Imported_h.template.
// Copyright (c) 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
@@ -17,6 +17,37 @@
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
{% endfor %}
+
+using Exported = {{"::".join(config.imported.namespace)}}::Exported;
+
+#ifndef {{"_".join(config.protocol.namespace)}}_imported_imported_h
+#define {{"_".join(config.protocol.namespace)}}_imported_imported_h
+
+class {{config.lib.export_macro}} ImportedValue : public Value {
+public:
+ static std::unique_ptr<ImportedValue> fromExported(const Exported* value) {
+ return std::unique_ptr<ImportedValue>(new ImportedValue(value));
+ }
+
+ void writeJSON(StringBuilder* output) const override {
+ auto json = m_exported->toJSONString();
+ String local_json = ({{config.imported.from_imported_string % "std::move(json)"}});
+ StringUtil::builderAppend(*output, local_json);
+ }
+ void writeBinary(std::vector<uint8_t>* output) const override {
+ m_exported->writeBinary(output);
+ }
+ std::unique_ptr<Value> clone() const override {
+ return std::unique_ptr<Value>(new ImportedValue(m_exported));
+ }
+
+private:
+ explicit ImportedValue(const Exported* exported) : Value(TypeImported), m_exported(exported) { }
+ const Exported* m_exported;
+};
+
+#endif // !defined({{"_".join(config.protocol.namespace)}}_imported_imported_h)
+
{% for type in domain.types %}
{% if not (type.type == "object") or not ("properties" in type) or not protocol.is_imported(domain.domain, type.id) %}{% continue %}{% endif %}
@@ -28,17 +59,18 @@ struct ValueConversions<{{"::".join(config.imported.namespace)}}::{{domain.domai
errors->addError("value expected");
return nullptr;
}
- String json = value->serialize();
- auto result = {{"::".join(config.imported.namespace)}}::{{domain.domain}}::API::{{type.id}}::fromJSONString({{config.imported.to_imported_string % "json"}});
+
+ std::vector<uint8_t> binary;
+ value->writeBinary(&binary);
+ auto result = {{"::".join(config.imported.namespace)}}::{{domain.domain}}::API::{{type.id}}::fromBinary(binary.data(), binary.size());
if (!result)
errors->addError("cannot parse");
return result;
}
- static std::unique_ptr<protocol::Value> toValue(const {{"::".join(config.imported.namespace)}}::{{domain.domain}}::API::{{type.id}}* value)
+ static std::unique_ptr<protocol::Value> toValue(const {{"::".join(config.imported.namespace)}}::{{domain.domain}}::API::{{type.id}}* exported)
{
- auto json = value->toJSONString();
- return SerializedValue::create({{config.imported.from_imported_string % "std::move(json)"}});
+ return ImportedValue::fromExported(exported);
}
static std::unique_ptr<protocol::Value> toValue(const std::unique_ptr<{{"::".join(config.imported.namespace)}}::{{domain.domain}}::API::{{type.id}}>& value)
@@ -46,6 +78,7 @@ struct ValueConversions<{{"::".join(config.imported.namespace)}}::{{domain.domai
return toValue(value.get());
}
};
+
{% endfor %}
{% for namespace in config.protocol.namespace %}
diff --git a/tools/inspector_protocol/templates/TypeBuilder_cpp.template b/tools/inspector_protocol/templates/TypeBuilder_cpp.template
index 026c1cdb8d..4ef60a6ea2 100644
--- a/tools/inspector_protocol/templates/TypeBuilder_cpp.template
+++ b/tools/inspector_protocol/templates/TypeBuilder_cpp.template
@@ -1,10 +1,10 @@
-// This file is generated
+// This file is generated by TypeBuilder_cpp.template.
// Copyright (c) 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.
-#include {{format_include(config.protocol.package, domain.domain)}}
+#include {{format_domain_include(config.protocol.package, domain.domain)}}
#include {{format_include(config.protocol.package, "Protocol")}}
@@ -24,7 +24,7 @@ const char Metainfo::version[] = "{{domain.version}}";
namespace {{type.id}}Enum {
{% for literal in type.enum %}
-const char* {{ literal | dash_to_camelcase}} = "{{literal}}";
+const char {{ literal | dash_to_camelcase}}[] = "{{literal}}";
{% endfor %}
} // namespace {{type.id}}Enum
{% if protocol.is_exported(domain.domain, type.id) %}
@@ -101,10 +101,15 @@ std::unique_ptr<{{type.id}}> {{type.id}}::clone() const
{{config.exported.string_out}} {{type.id}}::toJSONString() const
{
- String json = toValue()->serialize();
+ String json = toValue()->serializeToJSON();
return {{config.exported.to_string_out % "json"}};
}
+void {{type.id}}::writeBinary(std::vector<uint8_t>* out) const
+{
+ toValue()->writeBinary(out);
+}
+
// static
std::unique_ptr<API::{{type.id}}> API::{{type.id}}::fromJSONString(const {{config.exported.string_in}}& json)
{
@@ -114,6 +119,17 @@ std::unique_ptr<API::{{type.id}}> API::{{type.id}}::fromJSONString(const {{confi
return nullptr;
return protocol::{{domain.domain}}::{{type.id}}::fromValue(value.get(), &errors);
}
+
+// static
+std::unique_ptr<API::{{type.id}}> API::{{type.id}}::fromBinary(const uint8_t* data, size_t length)
+{
+ ErrorSupport errors;
+ std::unique_ptr<Value> value = Value::parseBinary(data, length);
+ if (!value)
+ return nullptr;
+ return protocol::{{domain.domain}}::{{type.id}}::fromValue(value.get(), &errors);
+}
+
{% endif %}
{% endfor %}
@@ -187,19 +203,23 @@ void Frontend::flush()
m_frontendChannel->flushProtocolNotifications();
}
-void Frontend::sendRawNotification(const String& notification)
+void Frontend::sendRawNotification(String notification)
+{
+ m_frontendChannel->sendProtocolNotification(InternalRawNotification::fromJSON(std::move(notification)));
+}
+
+void Frontend::sendRawNotification(std::vector<uint8_t> notification)
{
- m_frontendChannel->sendProtocolNotification(InternalRawNotification::create(notification));
+ m_frontendChannel->sendProtocolNotification(InternalRawNotification::fromBinary(std::move(notification)));
}
// --------------------- Dispatcher.
class DispatcherImpl : public protocol::DispatcherBase {
public:
- DispatcherImpl(FrontendChannel* frontendChannel, Backend* backend, bool fallThroughForNotFound)
+ DispatcherImpl(FrontendChannel* frontendChannel, Backend* backend)
: DispatcherBase(frontendChannel)
- , m_backend(backend)
- , m_fallThroughForNotFound(fallThroughForNotFound) {
+ , m_backend(backend) {
{% for command in domain.commands %}
{% if "redirect" in command %}
m_redirects["{{domain.domain}}.{{command.name}}"] = "{{command.redirect}}.{{command.name}}";
@@ -210,37 +230,35 @@ public:
{% endfor %}
}
~DispatcherImpl() override { }
- DispatchResponse::Status dispatch(int callId, const String& method, std::unique_ptr<protocol::DictionaryValue> messageObject) override;
- HashMap<String, String>& redirects() { return m_redirects; }
+ bool canDispatch(const String& method) override;
+ void dispatch(int callId, const String& method, const ProtocolMessage& message, std::unique_ptr<protocol::DictionaryValue> messageObject) override;
+ std::unordered_map<String, String>& redirects() { return m_redirects; }
protected:
- using CallHandler = DispatchResponse::Status (DispatcherImpl::*)(int callId, std::unique_ptr<DictionaryValue> messageObject, ErrorSupport* errors);
- using DispatchMap = protocol::HashMap<String, CallHandler>;
+ using CallHandler = void (DispatcherImpl::*)(int callId, const String& method, const ProtocolMessage& message, std::unique_ptr<DictionaryValue> messageObject, ErrorSupport* errors);
+ using DispatchMap = std::unordered_map<String, CallHandler>;
DispatchMap m_dispatchMap;
- HashMap<String, String> m_redirects;
+ std::unordered_map<String, String> m_redirects;
{% for command in domain.commands %}
{% if "redirect" in command %}{% continue %}{% endif %}
{% if not protocol.generate_command(domain.domain, command.name) %}{% continue %}{% endif %}
- DispatchResponse::Status {{command.name}}(int callId, std::unique_ptr<DictionaryValue> requestMessageObject, ErrorSupport*);
+ void {{command.name}}(int callId, const String& method, const ProtocolMessage& message, std::unique_ptr<DictionaryValue> requestMessageObject, ErrorSupport*);
{% endfor %}
Backend* m_backend;
- bool m_fallThroughForNotFound;
};
-DispatchResponse::Status DispatcherImpl::dispatch(int callId, const String& method, std::unique_ptr<protocol::DictionaryValue> messageObject)
-{
- protocol::HashMap<String, CallHandler>::iterator it = m_dispatchMap.find(method);
- if (it == m_dispatchMap.end()) {
- if (m_fallThroughForNotFound)
- return DispatchResponse::kFallThrough;
- reportProtocolError(callId, DispatchResponse::kMethodNotFound, "'" + method + "' wasn't found", nullptr);
- return DispatchResponse::kError;
- }
+bool DispatcherImpl::canDispatch(const String& method) {
+ return m_dispatchMap.find(method) != m_dispatchMap.end();
+}
+void DispatcherImpl::dispatch(int callId, const String& method, const ProtocolMessage& message, std::unique_ptr<protocol::DictionaryValue> messageObject)
+{
+ std::unordered_map<String, CallHandler>::iterator it = m_dispatchMap.find(method);
+ DCHECK(it != m_dispatchMap.end());
protocol::ErrorSupport errors;
- return (this->*(it->second))(callId, std::move(messageObject), &errors);
+ (this->*(it->second))(callId, method, message, std::move(messageObject), &errors);
}
{% for command in domain.commands %}
@@ -251,8 +269,8 @@ DispatchResponse::Status DispatcherImpl::dispatch(int callId, const String& meth
class {{command_name_title}}CallbackImpl : public Backend::{{command_name_title}}Callback, public DispatcherBase::Callback {
public:
- {{command_name_title}}CallbackImpl(std::unique_ptr<DispatcherBase::WeakPtr> backendImpl, int callId, int callbackId)
- : DispatcherBase::Callback(std::move(backendImpl), callId, callbackId) { }
+ {{command_name_title}}CallbackImpl(std::unique_ptr<DispatcherBase::WeakPtr> backendImpl, int callId, const String& method, const ProtocolMessage& message)
+ : DispatcherBase::Callback(std::move(backendImpl), callId, method, message) { }
void sendSuccess(
{%- for parameter in command.returns -%}
@@ -289,7 +307,7 @@ public:
};
{% endif %}
-DispatchResponse::Status DispatcherImpl::{{command.name}}(int callId, std::unique_ptr<DictionaryValue> requestMessageObject, ErrorSupport* errors)
+void DispatcherImpl::{{command.name}}(int callId, const String& method, const ProtocolMessage& message, std::unique_ptr<DictionaryValue> requestMessageObject, ErrorSupport* errors)
{
{% if "parameters" in command %}
// Prepare input parameters.
@@ -312,7 +330,7 @@ DispatchResponse::Status DispatcherImpl::{{command.name}}(int callId, std::uniqu
errors->pop();
if (errors->hasErrors()) {
reportProtocolError(callId, DispatchResponse::kInvalidParams, kInvalidParamsString, errors);
- return DispatchResponse::kError;
+ return;
}
{% endif %}
{% if "returns" in command and not protocol.is_async_command(domain.domain, command.name) %}
@@ -343,8 +361,10 @@ DispatchResponse::Status DispatcherImpl::{{command.name}}(int callId, std::uniqu
&out_{{parameter.name}}
{%- endfor %}
{% endif %});
- if (response.status() == DispatchResponse::kFallThrough)
- return response.status();
+ if (response.status() == DispatchResponse::kFallThrough) {
+ channel()->fallThrough(callId, method, message);
+ return;
+ }
{% if "returns" in command %}
std::unique_ptr<protocol::DictionaryValue> result = DictionaryValue::create();
if (response.status() == DispatchResponse::kSuccess) {
@@ -363,10 +383,10 @@ DispatchResponse::Status DispatcherImpl::{{command.name}}(int callId, std::uniqu
if (weak->get())
weak->get()->sendResponse(callId, response);
{% endif %}
- return response.status();
+ return;
{% else %}
std::unique_ptr<DispatcherBase::WeakPtr> weak = weakPtr();
- std::unique_ptr<{{command_name_title}}CallbackImpl> callback(new {{command.name | to_title_case}}CallbackImpl(weakPtr(), callId, nextCallbackId()));
+ std::unique_ptr<{{command_name_title}}CallbackImpl> callback(new {{command.name | to_title_case}}CallbackImpl(weakPtr(), callId, method, message));
m_backend->{{command.name | to_method_case}}(
{%- for property in command.parameters -%}
{%- if not loop.first -%}, {% endif -%}
@@ -378,7 +398,7 @@ DispatchResponse::Status DispatcherImpl::{{command.name}}(int callId, std::uniqu
{%- endfor -%}
{%- if command.parameters -%}, {% endif -%}
std::move(callback));
- return (weak->get() && weak->get()->lastCallbackFallThrough()) ? DispatchResponse::kFallThrough : DispatchResponse::kAsync;
+ return;
{% endif %}
}
{% endfor %}
@@ -386,7 +406,7 @@ DispatchResponse::Status DispatcherImpl::{{command.name}}(int callId, std::uniqu
// static
void Dispatcher::wire(UberDispatcher* uber, Backend* backend)
{
- std::unique_ptr<DispatcherImpl> dispatcher(new DispatcherImpl(uber->channel(), backend, uber->fallThroughForNotFound()));
+ std::unique_ptr<DispatcherImpl> dispatcher(new DispatcherImpl(uber->channel(), backend));
uber->setupRedirects(dispatcher->redirects());
uber->registerBackend("{{domain.domain}}", std::move(dispatcher));
}
diff --git a/tools/inspector_protocol/templates/TypeBuilder_h.template b/tools/inspector_protocol/templates/TypeBuilder_h.template
index 744d496026..c670d65c46 100644
--- a/tools/inspector_protocol/templates/TypeBuilder_h.template
+++ b/tools/inspector_protocol/templates/TypeBuilder_h.template
@@ -1,4 +1,4 @@
-// This file is generated
+// This file is generated by TypeBuilder_h.template.
// Copyright (c) 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
@@ -15,7 +15,7 @@
// and include Domain::API version from there.
{% for name in domain.dependencies %}
{% if protocol.is_imported_dependency(name) %}
-#include {{format_include(config.protocol.package, name)}}
+#include {{format_domain_include(config.protocol.package, name)}}
{% endif %}
{% endfor %}
{% if protocol.is_exported_domain(domain.domain) %}
@@ -46,7 +46,7 @@ using {{type.id}} = {{protocol.resolve_type(type).type}};
namespace {{type.id}}Enum {
{% for literal in type.enum %}
-{{config.protocol.export_macro}} extern const char* {{ literal | dash_to_camelcase}};
+{{config.protocol.export_macro}} extern const char {{ literal | dash_to_camelcase}}[];
{% endfor %}
} // namespace {{type.id}}Enum
{% endif %}
@@ -100,10 +100,13 @@ public:
{% endfor %}
std::unique_ptr<protocol::DictionaryValue> toValue() const;
- String serialize() override { return toValue()->serialize(); }
+ String serializeToJSON() override { return toValue()->serializeToJSON(); }
+ std::vector<uint8_t> serializeToBinary() override { return toValue()->serializeToBinary(); }
+ String toJSON() const { return toValue()->toJSONString(); }
std::unique_ptr<{{type.id}}> clone() const;
{% if protocol.is_exported(domain.domain, type.id) %}
{{config.exported.string_out}} toJSONString() const override;
+ void writeBinary(std::vector<uint8_t>* out) const override;
{% endif %}
template<int STATE>
@@ -266,7 +269,8 @@ public:
{% endfor %}
void flush();
- void sendRawNotification(const String&);
+ void sendRawNotification(String);
+ void sendRawNotification(std::vector<uint8_t>);
private:
FrontendChannel* m_frontendChannel;
};