tools: update inspector_protocol to 0aafd2

Co-authored-by: Ben Noordhuis <info@bnoordhuis.nl>
PR-URL: https://github.com/nodejs/node/pull/27770
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Eugene Ostroukhov <eostroukhov@google.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
Michaël Zasso 2019-05-19 09:54:52 +02:00 committed by Rich Trott
parent aa8b820aaa
commit 5aaa7fee2e
29 changed files with 7733 additions and 1459 deletions

View File

@ -15,9 +15,12 @@
'node_protocol_files': [
'<(protocol_tool_path)/lib/Allocator_h.template',
'<(protocol_tool_path)/lib/Array_h.template',
'<(protocol_tool_path)/lib/Collections_h.template',
'<(protocol_tool_path)/lib/base_string_adapter_cc.template',
'<(protocol_tool_path)/lib/base_string_adapter_h.template',
'<(protocol_tool_path)/lib/DispatcherBase_cpp.template',
'<(protocol_tool_path)/lib/DispatcherBase_h.template',
'<(protocol_tool_path)/lib/encoding_cpp.template',
'<(protocol_tool_path)/lib/encoding_h.template',
'<(protocol_tool_path)/lib/ErrorSupport_cpp.template',
'<(protocol_tool_path)/lib/ErrorSupport_h.template',
'<(protocol_tool_path)/lib/Forward_h.template',

View File

@ -107,6 +107,22 @@ String fromUTF8(const uint8_t* data, size_t length) {
return std::string(reinterpret_cast<const char*>(data), length);
}
String fromUTF16(const uint16_t* data, size_t length) {
icu::UnicodeString utf16(reinterpret_cast<const char16_t*>(data), length);
std::string result;
return utf16.toUTF8String(result);
}
const uint8_t* CharactersUTF8(const String& s) {
return reinterpret_cast<const uint8_t*>(s.data());
}
size_t CharacterCount(const String& s) {
icu::UnicodeString utf16 =
icu::UnicodeString::fromUTF8(icu::StringPiece(s.data(), s.length()));
return utf16.countChar32();
}
} // namespace StringUtil
} // namespace protocol
} // namespace inspector

View File

@ -20,16 +20,6 @@ using String = std::string;
using StringBuilder = std::ostringstream;
using ProtocolMessage = std::string;
class StringUTF8Adapter {
public:
explicit 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_;
};
namespace StringUtil {
// NOLINTNEXTLINE(runtime/references) This is V8 API...
inline void builderAppend(StringBuilder& builder, char c) {
@ -82,6 +72,13 @@ std::unique_ptr<Value> parseMessage(const std::string& message, bool binary);
ProtocolMessage jsonToMessage(String message);
ProtocolMessage binaryToMessage(std::vector<uint8_t> message);
String fromUTF8(const uint8_t* data, size_t length);
String fromUTF16(const uint16_t* data, size_t length);
const uint8_t* CharactersUTF8(const String& s);
size_t CharacterCount(const String& s);
// Unimplemented. The generated code will fall back to CharactersUTF8().
inline uint8_t* CharactersLatin1(const String& s) { return nullptr; }
inline const uint16_t* CharactersUTF16(const String& s) { return nullptr; }
extern size_t kNotFound;
} // namespace StringUtil

View File

@ -70,7 +70,7 @@ class SendMessageRequest : public Request {
if (frontend_wrapper == nullptr) return;
auto frontend = frontend_wrapper->get();
if (frontend != nullptr) {
frontend->sendRawNotification(message_);
frontend->sendRawJSONNotification(message_);
}
}

View File

@ -0,0 +1,36 @@
# Defines the Chromium style for automatic reformatting.
# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
BasedOnStyle: Chromium
# This defaults to 'Auto'. Explicitly set it for a while, so that
# 'vector<vector<int> >' in existing files gets formatted to
# 'vector<vector<int>>'. ('Auto' means that clang-format will only use
# 'int>>' if the file already contains at least one such instance.)
Standard: Cpp11
# Make sure code like:
# IPC_BEGIN_MESSAGE_MAP()
# IPC_MESSAGE_HANDLER(WidgetHostViewHost_Update, OnUpdate)
# IPC_END_MESSAGE_MAP()
# gets correctly indented.
MacroBlockBegin: "^\
BEGIN_MSG_MAP|\
BEGIN_MSG_MAP_EX|\
BEGIN_SAFE_MSG_MAP_EX|\
CR_BEGIN_MSG_MAP_EX|\
IPC_BEGIN_MESSAGE_MAP|\
IPC_BEGIN_MESSAGE_MAP_WITH_PARAM|\
IPC_PROTOBUF_MESSAGE_TRAITS_BEGIN|\
IPC_STRUCT_BEGIN|\
IPC_STRUCT_BEGIN_WITH_PARENT|\
IPC_STRUCT_TRAITS_BEGIN|\
POLPARAMS_BEGIN|\
PPAPI_BEGIN_MESSAGE_MAP$"
MacroBlockEnd: "^\
CR_END_MSG_MAP|\
END_MSG_MAP|\
IPC_END_MESSAGE_MAP|\
IPC_PROTOBUF_MESSAGE_TRAITS_END|\
IPC_STRUCT_END|\
IPC_STRUCT_TRAITS_END|\
POLPARAMS_END|\
PPAPI_END_MESSAGE_MAP$"

View File

@ -0,0 +1,34 @@
# Copyright 2019 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
static_library("encoding") {
sources = [
"encoding/encoding.cc",
"encoding/encoding.h",
]
}
# encoding_test is part of the unittests, defined in
# test/unittests/BUILD.gn.
import("../../gni/v8.gni")
v8_source_set("encoding_test") {
sources = [
"encoding/encoding_test.cc",
"encoding/encoding_test_helper.h",
]
configs = [
"../..:external_config",
"../..:internal_config_base",
]
deps = [
":encoding",
"../..:v8_libbase",
"../../src/inspector:inspector_string_conversions",
"//testing/gmock",
"//testing/gtest",
]
testonly = true
}

View File

@ -0,0 +1,33 @@
# Chromium inspector (devtools) protocol
This package contains code generators and templates for the Chromium
inspector protocol.
The canonical location of this package is at
https://chromium.googlesource.com/deps/inspector_protocol/
In the Chromium tree, it's rolled into
https://cs.chromium.org/chromium/src/third_party/inspector_protocol/
In the V8 tree, it's rolled into
https://cs.chromium.org/chromium/src/v8/third_party/inspector_protocol/
See also [Contributing to Chrome Devtools Protocol](https://docs.google.com/document/d/1c-COD2kaK__5iMM5SEx-PzNA7HFmgttcYfOHHX0HaOM/edit).
We're working on enabling standalone builds for parts of this package for
testing and development, please feel free to ignore this for now.
But, if you're familiar with
[Chromium's development process](https://www.chromium.org/developers/contributing-code)
and have the depot_tools installed, you may use these commands
to fetch the package (and dependencies) and build and run the tests:
fetch inspector_protocol
cd src
gn gen out/Release
ninja -C out/Release json_parser_test
out/Release/json_parser_test
You'll probably also need to install g++, since Clang uses this to find the
standard C++ headers. E.g.,
sudo apt-get install g++-8

View File

@ -2,7 +2,7 @@ Name: inspector protocol
Short Name: inspector_protocol
URL: https://chromium.googlesource.com/deps/inspector_protocol/
Version: 0
Revision: f67ec5180f476830e839226b5ca948e43070fdab
Revision: 0aafd2876f7485db7b07c513c0457b7cbbbe3304
License: BSD
License File: LICENSE
Security Critical: no

View File

@ -5,7 +5,7 @@
import os.path
import sys
import optparse
import argparse
import collections
import functools
import re
@ -17,6 +17,13 @@ except ImportError:
import pdl
try:
unicode
except NameError:
# Define unicode for Py3
def unicode(s, *_):
return s
# 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
@ -53,27 +60,16 @@ def read_config():
return collections.namedtuple('X', keys)(*values)
try:
cmdline_parser = optparse.OptionParser()
cmdline_parser.add_option("--output_base")
cmdline_parser.add_option("--jinja_dir")
cmdline_parser.add_option("--config")
cmdline_parser.add_option("--config_value", action="append", type="string")
arg_options, _ = cmdline_parser.parse_args()
cmdline_parser = argparse.ArgumentParser()
cmdline_parser.add_argument("--output_base", type=unicode, required=True)
cmdline_parser.add_argument("--jinja_dir", type=unicode, required=True)
cmdline_parser.add_argument("--config", type=unicode, required=True)
cmdline_parser.add_argument("--config_value", default=[], action="append")
arg_options = cmdline_parser.parse_args()
jinja_dir = arg_options.jinja_dir
if not jinja_dir:
raise Exception("jinja directory must be specified")
jinja_dir = jinja_dir.decode('utf8')
output_base = arg_options.output_base
if not output_base:
raise Exception("Base output directory must be specified")
output_base = output_base.decode('utf8')
config_file = arg_options.config
if not config_file:
raise Exception("Config file name must be specified")
config_file = config_file.decode('utf8')
config_values = arg_options.config_value
if not config_values:
config_values = []
except Exception:
# Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html
exc = sys.exc_info()[1]
@ -631,7 +627,7 @@ def main():
"Array_h.template",
"DispatcherBase_h.template",
"Parser_h.template",
"CBOR_h.template",
"encoding_h.template",
]
protocol_cpp_templates = [
@ -641,7 +637,7 @@ def main():
"Object_cpp.template",
"DispatcherBase_cpp.template",
"Parser_cpp.template",
"CBOR_cpp.template",
"encoding_cpp.template",
]
forward_h_templates = [

View File

@ -0,0 +1,6 @@
# This file is used by git-cl to get repository specific information.
CC_LIST: chromium-reviews@chromium.org
CODE_REVIEW_SERVER: codereview.chromium.org
GERRIT_HOST: True
PROJECT: inspector_protocol
VIEW_VC: https://chromium.googlesource.com/deps/inspector_protocol/+/

View File

@ -4,10 +4,8 @@
# found in the LICENSE file.
import argparse
import collections
import json
import os.path
import re
import sys
import pdl

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,510 @@
// 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 V8_INSPECTOR_PROTOCOL_ENCODING_ENCODING_H_
#define V8_INSPECTOR_PROTOCOL_ENCODING_ENCODING_H_
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>
#include <memory>
#include <string>
#include <vector>
namespace v8_inspector_protocol_encoding {
// =============================================================================
// span - sequence of bytes
// =============================================================================
// This template is similar to std::span, which will be included in C++20.
template <typename T>
class span {
public:
using index_type = size_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_;
};
template <typename T>
span<T> SpanFrom(const std::vector<T>& v) {
return span<T>(v.data(), v.size());
}
template <size_t N>
span<uint8_t> SpanFrom(const char (&str)[N]) {
return span<uint8_t>(reinterpret_cast<const uint8_t*>(str), N - 1);
}
inline span<uint8_t> SpanFrom(const char* str) {
return str ? span<uint8_t>(reinterpret_cast<const uint8_t*>(str), strlen(str))
: span<uint8_t>();
}
inline span<uint8_t> SpanFrom(const std::string& v) {
return span<uint8_t>(reinterpret_cast<const uint8_t*>(v.data()), v.size());
}
// =============================================================================
// Status and 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_MAP_END = 0x0b,
JSON_PARSER_COMMA_OR_MAP_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_TRAILING_JUNK = 0x1c,
CBOR_MAP_START_EXPECTED = 0x1d,
CBOR_MAP_STOP_EXPECTED = 0x1e,
CBOR_ENVELOPE_SIZE_LIMIT_EXCEEDED = 0x1f,
};
// 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 size_t npos() { return std::numeric_limits<size_t>::max(); }
bool ok() const { return error == Error::OK; }
Error error = Error::OK;
size_t pos = npos();
Status(Error error, size_t pos) : error(error), pos(pos) {}
Status() = default;
// Returns a 7 bit US-ASCII string, either "OK" or an error message
// that includes the position.
std::string ToASCIIString() const;
private:
std::string ToASCIIString(const char* msg) const;
};
// Handler interface for parser events emitted by a streaming parser.
// See cbor::NewCBOREncoder, cbor::ParseCBOR, json::NewJSONEncoder,
// json::ParseJSON.
class StreamingParserHandler {
public:
virtual ~StreamingParserHandler() = default;
virtual void HandleMapBegin() = 0;
virtual void HandleMapEnd() = 0;
virtual void HandleArrayBegin() = 0;
virtual void HandleArrayEnd() = 0;
virtual void HandleString8(span<uint8_t> chars) = 0;
virtual void HandleString16(span<uint16_t> chars) = 0;
virtual void HandleBinary(span<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;
};
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) are supported.
// - 7 bit US-ASCII strings must always be encoded as UTF8 strings, never
// as UTF16 strings.
// - 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.
// =============================================================================
// Detecting CBOR content
// =============================================================================
// The first byte for an envelope, which we use for wrapping dictionaries
// and arrays; and the byte that indicates a byte string with 32 bit length.
// These two bytes start an envelope, and thereby also any CBOR message
// produced or consumed by this protocol. See also |EnvelopeEncoder| below.
uint8_t InitialByteForEnvelope();
uint8_t InitialByteFor32BitLengthByteString();
// Checks whether |msg| is a cbor message.
bool IsCBORMessage(span<uint8_t> msg);
// =============================================================================
// Encoding individual CBOR items
// =============================================================================
// 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();
// 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);
void EncodeInt32(int32_t value, std::string* 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);
void EncodeString16(span<uint16_t> in, std::string* out);
// Encodes a UTF8 string |in| as STRING (major type 3).
void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out);
void EncodeString8(span<uint8_t> in, std::string* out);
// Encodes the given |latin1| string as STRING8.
// If any non-ASCII character is present, it will be represented
// as a 2 byte UTF8 sequence.
void EncodeFromLatin1(span<uint8_t> latin1, std::vector<uint8_t>* out);
void EncodeFromLatin1(span<uint8_t> latin1, std::string* out);
// Encodes the given |utf16| string as STRING8 if it's entirely US-ASCII.
// Otherwise, encodes as STRING16.
void EncodeFromUTF16(span<uint16_t> utf16, std::vector<uint8_t>* out);
void EncodeFromUTF16(span<uint16_t> utf16, std::string* 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);
void EncodeBinary(span<uint8_t> in, std::string* 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);
void EncodeDouble(double value, std::string* out);
// =============================================================================
// cbor::EnvelopeEncoder - for wrapping submessages
// =============================================================================
// 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);
void EncodeStart(std::string* out);
// This records the current size in |out| at position byte_size_pos_.
// Returns true iff successful.
bool EncodeStop(std::vector<uint8_t>* out);
bool EncodeStop(std::string* out);
private:
size_t byte_size_pos_ = 0;
};
// =============================================================================
// cbor::NewCBOREncoder - for encoding from a streaming parser
// =============================================================================
// This can be used to convert to CBOR, by passing the return value to a parser
// that drives it. 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<StreamingParserHandler> NewCBOREncoder(
std::vector<uint8_t>* out,
Status* status);
std::unique_ptr<StreamingParserHandler> NewCBOREncoder(std::string* out,
Status* status);
// =============================================================================
// cbor::CBORTokenizer - for parsing individual CBOR items
// =============================================================================
// Tags for the tokens within a CBOR message that CBORTokenizer 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,
};
// 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
};
// 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;
// To be called only if ::TokenTag() == CBORTokenTag::ENVELOPE.
span<uint8_t> GetEnvelopeContents() const;
private:
void ReadNextToken(bool enter_envelope);
void SetToken(CBORTokenTag token, size_t token_byte_length);
void SetError(Error error);
span<uint8_t> bytes_;
CBORTokenTag token_tag_;
struct Status status_;
size_t token_byte_length_;
MajorType token_start_type_;
uint64_t token_start_internal_value_;
};
// =============================================================================
// cbor::ParseCBOR - for receiving streaming parser events for CBOR messages
// =============================================================================
// Parses a CBOR encoded message from |bytes|, sending events to
// |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, StreamingParserHandler* out);
// =============================================================================
// cbor::AppendString8EntryToMap - for limited in-place editing of messages
// =============================================================================
// Modifies the |cbor| message by appending a new key/value entry at the end
// of the map. Patches up the envelope size; Status.ok() iff successful.
// If not successful, |cbor| may be corrupted after this call.
Status AppendString8EntryToCBORMap(span<uint8_t> string8_key,
span<uint8_t> string8_value,
std::vector<uint8_t>* cbor);
Status AppendString8EntryToCBORMap(span<uint8_t> string8_key,
span<uint8_t> string8_value,
std::string* cbor);
namespace internals { // Exposed only for writing tests.
int8_t ReadTokenStart(span<uint8_t> bytes,
cbor::MajorType* type,
uint64_t* value);
void WriteTokenStart(cbor::MajorType type,
uint64_t value,
std::vector<uint8_t>* encoded);
void WriteTokenStart(cbor::MajorType type,
uint64_t value,
std::string* encoded);
} // namespace internals
} // namespace cbor
namespace json {
// Client code must provide an instance. Implementation should delegate
// to whatever is appropriate.
class Platform {
public:
virtual ~Platform() = default;
// Parses |str| into |result|. Returns false iff there are
// leftover characters or parsing errors.
virtual bool StrToD(const char* str, double* result) const = 0;
// Prints |value| in a format suitable for JSON.
virtual std::unique_ptr<char[]> DToStr(double value) const = 0;
};
// =============================================================================
// json::NewJSONEncoder - for encoding streaming parser events as JSON
// =============================================================================
// Returns a handler object which will write ascii characters to |out|.
// |status->ok()| will be false iff the handler routine HandleError() is called.
// In that case, we'll stop emitting output.
// Except for calling the HandleError routine at any time, the client
// code must call the Handle* methods in an order in which they'd occur
// in valid JSON; otherwise we may crash (the code uses assert).
std::unique_ptr<StreamingParserHandler> NewJSONEncoder(
const Platform* platform,
std::vector<uint8_t>* out,
Status* status);
std::unique_ptr<StreamingParserHandler> NewJSONEncoder(const Platform* platform,
std::string* out,
Status* status);
// =============================================================================
// json::ParseJSON - for receiving streaming parser events for JSON
// =============================================================================
void ParseJSON(const Platform& platform,
span<uint8_t> chars,
StreamingParserHandler* handler);
void ParseJSON(const Platform& platform,
span<uint16_t> chars,
StreamingParserHandler* handler);
// =============================================================================
// json::ConvertCBORToJSON, json::ConvertJSONToCBOR - for transcoding
// =============================================================================
Status ConvertCBORToJSON(const Platform& platform,
span<uint8_t> cbor,
std::string* json);
Status ConvertCBORToJSON(const Platform& platform,
span<uint8_t> cbor,
std::vector<uint8_t>* json);
Status ConvertJSONToCBOR(const Platform& platform,
span<uint8_t> json,
std::vector<uint8_t>* cbor);
Status ConvertJSONToCBOR(const Platform& platform,
span<uint16_t> json,
std::vector<uint8_t>* cbor);
Status ConvertJSONToCBOR(const Platform& platform,
span<uint8_t> json,
std::string* cbor);
Status ConvertJSONToCBOR(const Platform& platform,
span<uint16_t> json,
std::string* cbor);
} // namespace json
} // namespace v8_inspector_protocol_encoding
#endif // V8_INSPECTOR_PROTOCOL_ENCODING_ENCODING_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,33 @@
// Copyright 2019 The V8 Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is V8 specific, to make encoding_test.cc work.
// It is not rolled from the upstream project.
#ifndef V8_INSPECTOR_PROTOCOL_ENCODING_ENCODING_TEST_HELPER_H_
#define V8_INSPECTOR_PROTOCOL_ENCODING_ENCODING_TEST_HELPER_H_
#include <string>
#include <vector>
#include "src/base/logging.h"
#include "src/inspector/v8-string-conversions.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8_inspector_protocol_encoding {
std::string UTF16ToUTF8(span<uint16_t> in) {
return v8_inspector::UTF16ToUTF8(in.data(), in.size());
}
std::vector<uint16_t> UTF8ToUTF16(span<uint8_t> in) {
std::basic_string<uint16_t> utf16 = v8_inspector::UTF8ToUTF16(
reinterpret_cast<const char*>(in.data()), in.size());
return std::vector<uint16_t>(utf16.begin(), utf16.end());
}
} // namespace v8_inspector_protocol_encoding
#endif // V8_INSPECTOR_PROTOCOL_ENCODING_ENCODING_TEST_HELPER_H_

View File

@ -27,13 +27,16 @@ template("inspector_protocol_generate") {
inspector_protocol_dir = invoker.inspector_protocol_dir
action(target_name) {
script = "$inspector_protocol_dir/CodeGenerator.py"
script = "$inspector_protocol_dir/code_generator.py"
inputs = [
invoker.config_file,
"$inspector_protocol_dir/lib/base_string_adapter_cc.template",
"$inspector_protocol_dir/lib/base_string_adapter_h.template",
"$inspector_protocol_dir/lib/encoding_h.template",
"$inspector_protocol_dir/lib/encoding_cpp.template",
"$inspector_protocol_dir/lib/Allocator_h.template",
"$inspector_protocol_dir/lib/Array_h.template",
"$inspector_protocol_dir/lib/Collections_h.template",
"$inspector_protocol_dir/lib/DispatcherBase_cpp.template",
"$inspector_protocol_dir/lib/DispatcherBase_h.template",
"$inspector_protocol_dir/lib/ErrorSupport_cpp.template",

View File

@ -5,9 +5,10 @@
{
'variables': {
'inspector_protocol_files': [
'lib/encoding_h.template',
'lib/encoding_cpp.template',
'lib/Allocator_h.template',
'lib/Array_h.template',
'lib/Collections_h.template',
'lib/DispatcherBase_cpp.template',
'lib/DispatcherBase_h.template',
'lib/ErrorSupport_cpp.template',
@ -27,7 +28,7 @@
'templates/Imported_h.template',
'templates/TypeBuilder_cpp.template',
'templates/TypeBuilder_h.template',
'CodeGenerator.py',
'code_generator.py',
]
}
}

View File

@ -1,803 +0,0 @@
{# 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 %}

View File

@ -1,416 +0,0 @@
{# 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)

View File

@ -1,43 +0,0 @@
// 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.
#ifndef {{"_".join(config.protocol.namespace)}}_Collections_h
#define {{"_".join(config.protocol.namespace)}}_Collections_h
#include {{format_include(config.protocol.package, "Forward")}}
#include <cstddef>
#if defined(__APPLE__) && !defined(_LIBCPP_VERSION)
#include <map>
#include <set>
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
{% endfor %}
template <class Key, class T> using HashMap = std::map<Key, T>;
template <class Key> using HashSet = std::set<Key>;
{% for namespace in config.protocol.namespace %}
} // namespace {{namespace}}
{% endfor %}
#else
#include <unordered_map>
#include <unordered_set>
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
{% endfor %}
template <class Key, class T> using HashMap = std::unordered_map<Key, T>;
template <class Key> using HashSet = std::unordered_set<Key>;
{% for namespace in config.protocol.namespace %}
} // namespace {{namespace}}
{% endfor %}
#endif // defined(__APPLE__) && !defined(_LIBCPP_VERSION)
#endif // !defined({{"_".join(config.protocol.namespace)}}_Collections_h)

View File

@ -66,21 +66,21 @@ 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);
std::unique_ptr<DictionaryValue> parseMap(int32_t stack_depth, cbor::CBORTokenizer* tokenizer);
std::unique_ptr<ListValue> parseArray(int32_t stack_depth, cbor::CBORTokenizer* tokenizer);
std::unique_ptr<Value> parseValue(int32_t stack_depth, cbor::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);
std::unique_ptr<ListValue> parseArray(int32_t stack_depth, cbor::CBORTokenizer* tokenizer) {
DCHECK(tokenizer->TokenTag() == cbor::CBORTokenTag::ARRAY_START);
tokenizer->Next();
auto list = ListValue::create();
while (tokenizer->TokenTag() != CBORTokenTag::STOP) {
while (tokenizer->TokenTag() != cbor::CBORTokenTag::STOP) {
// Error::CBOR_UNEXPECTED_EOF_IN_ARRAY
if (tokenizer->TokenTag() == CBORTokenTag::DONE) return nullptr;
if (tokenizer->TokenTag() == CBORTokenTag::ERROR_VALUE) return nullptr;
if (tokenizer->TokenTag() == cbor::CBORTokenTag::DONE) return nullptr;
if (tokenizer->TokenTag() == cbor::CBORTokenTag::ERROR_VALUE) return nullptr;
// Parse value.
auto value = parseValue(stack_depth, tokenizer);
if (!value) return nullptr;
@ -91,60 +91,66 @@ std::unique_ptr<ListValue> parseArray(int32_t stack_depth, CBORTokenizer* tokeni
}
std::unique_ptr<Value> parseValue(
int32_t stack_depth, CBORTokenizer* tokenizer) {
int32_t stack_depth, cbor::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)
if (tokenizer->TokenTag() == cbor::CBORTokenTag::ENVELOPE)
tokenizer->EnterEnvelope();
switch (tokenizer->TokenTag()) {
case CBORTokenTag::ERROR_VALUE:
case cbor::CBORTokenTag::ERROR_VALUE:
return nullptr;
case CBORTokenTag::DONE:
case cbor::CBORTokenTag::DONE:
// Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE
return nullptr;
case CBORTokenTag::TRUE_VALUE: {
case cbor::CBORTokenTag::TRUE_VALUE: {
std::unique_ptr<Value> value = FundamentalValue::create(true);
tokenizer->Next();
return value;
}
case CBORTokenTag::FALSE_VALUE: {
case cbor::CBORTokenTag::FALSE_VALUE: {
std::unique_ptr<Value> value = FundamentalValue::create(false);
tokenizer->Next();
return value;
}
case CBORTokenTag::NULL_VALUE: {
case cbor::CBORTokenTag::NULL_VALUE: {
std::unique_ptr<Value> value = FundamentalValue::null();
tokenizer->Next();
return value;
}
case CBORTokenTag::INT32: {
case cbor::CBORTokenTag::INT32: {
std::unique_ptr<Value> value = FundamentalValue::create(tokenizer->GetInt32());
tokenizer->Next();
return value;
}
case CBORTokenTag::DOUBLE: {
case cbor::CBORTokenTag::DOUBLE: {
std::unique_ptr<Value> value = FundamentalValue::create(tokenizer->GetDouble());
tokenizer->Next();
return value;
}
case CBORTokenTag::STRING8: {
case cbor::CBORTokenTag::STRING8: {
span<uint8_t> str = tokenizer->GetString8();
std::unique_ptr<Value> value = StringValue::create(StringUtil::fromUTF8(str.data(), str.size()));
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: {
case cbor::CBORTokenTag::STRING16: {
span<uint8_t> wire = tokenizer->GetString16WireRep();
DCHECK_EQ(wire.size() & 1, 0u);
std::unique_ptr<Value> value = StringValue::create(StringUtil::fromUTF16(
reinterpret_cast<const uint16_t*>(wire.data()), wire.size() / 2));
tokenizer->Next();
return value;
}
case cbor::CBORTokenTag::BINARY: {
span<uint8_t> payload = tokenizer->GetBinary();
tokenizer->Next();
return BinaryValue::create(Binary::fromSpan(payload.data(), payload.size()));
}
case CBORTokenTag::MAP_START:
case cbor::CBORTokenTag::MAP_START:
return parseMap(stack_depth + 1, tokenizer);
case CBORTokenTag::ARRAY_START:
case cbor::CBORTokenTag::ARRAY_START:
return parseArray(stack_depth + 1, tokenizer);
default:
// Error::CBOR_UNSUPPORTED_VALUE
@ -156,22 +162,22 @@ std::unique_ptr<Value> parseValue(
// ParseArray may only be called after an indefinite length array has been
// detected.
std::unique_ptr<DictionaryValue> parseMap(
int32_t stack_depth, CBORTokenizer* tokenizer) {
int32_t stack_depth, cbor::CBORTokenizer* tokenizer) {
auto dict = DictionaryValue::create();
tokenizer->Next();
while (tokenizer->TokenTag() != CBORTokenTag::STOP) {
if (tokenizer->TokenTag() == CBORTokenTag::DONE) {
while (tokenizer->TokenTag() != cbor::CBORTokenTag::STOP) {
if (tokenizer->TokenTag() == cbor::CBORTokenTag::DONE) {
// Error::CBOR_UNEXPECTED_EOF_IN_MAP
return nullptr;
}
if (tokenizer->TokenTag() == CBORTokenTag::ERROR_VALUE) return nullptr;
if (tokenizer->TokenTag() == cbor::CBORTokenTag::ERROR_VALUE) return nullptr;
// Parse key.
String key;
if (tokenizer->TokenTag() == CBORTokenTag::STRING8) {
if (tokenizer->TokenTag() == cbor::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) {
} else if (tokenizer->TokenTag() == cbor::CBORTokenTag::STRING16) {
return nullptr; // STRING16 not supported yet.
} else {
// Error::CBOR_INVALID_MAP_KEY
@ -196,22 +202,21 @@ std::unique_ptr<Value> Value::parseBinary(const uint8_t* data, size_t size) {
if (bytes.empty()) return nullptr;
// Error::CBOR_INVALID_START_BYTE
// TODO(johannes): EncodeInitialByteForEnvelope() method.
if (bytes[0] != 0xd8) return nullptr;
if (bytes[0] != cbor::InitialByteForEnvelope()) return nullptr;
CBORTokenizer tokenizer(bytes);
if (tokenizer.TokenTag() == CBORTokenTag::ERROR_VALUE) return nullptr;
cbor::CBORTokenizer tokenizer(bytes);
if (tokenizer.TokenTag() == cbor::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);
DCHECK(tokenizer.TokenTag() == cbor::CBORTokenTag::ENVELOPE);
tokenizer.EnterEnvelope();
// Error::MAP_START_EXPECTED
if (tokenizer.TokenTag() != CBORTokenTag::MAP_START) return nullptr;
if (tokenizer.TokenTag() != cbor::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;
if (tokenizer.TokenTag() == cbor::CBORTokenTag::DONE) return result;
if (tokenizer.TokenTag() == cbor::CBORTokenTag::ERROR_VALUE) return nullptr;
// Error::CBOR_TRAILING_JUNK
return nullptr;
}
@ -249,7 +254,7 @@ void Value::writeJSON(StringBuilder* output) const
void Value::writeBinary(std::vector<uint8_t>* bytes) const {
DCHECK(m_type == TypeNull);
bytes->push_back(EncodeNull());
bytes->push_back(cbor::EncodeNull());
}
std::unique_ptr<Value> Value::clone() const
@ -326,13 +331,13 @@ void FundamentalValue::writeJSON(StringBuilder* output) const
void FundamentalValue::writeBinary(std::vector<uint8_t>* bytes) const {
switch (type()) {
case TypeDouble:
EncodeDouble(m_doubleValue, bytes);
cbor::EncodeDouble(m_doubleValue, bytes);
return;
case TypeInteger:
EncodeInt32(m_integerValue, bytes);
cbor::EncodeInt32(m_integerValue, bytes);
return;
case TypeBoolean:
bytes->push_back(m_boolValue ? EncodeTrue() : EncodeFalse());
bytes->push_back(m_boolValue ? cbor::EncodeTrue() : cbor::EncodeFalse());
return;
default:
DCHECK(false);
@ -363,10 +368,37 @@ void StringValue::writeJSON(StringBuilder* output) const
StringUtil::builderAppendQuotedString(*output, m_stringValue);
}
namespace {
// This routine distinguishes between the current encoding for a given
// string |s|, and calls encoding routines that will
// - Ensure that all ASCII strings end up being encoded as UTF8 in
// the wire format - e.g., EncodeFromUTF16 will detect ASCII and
// do the (trivial) transcode to STRING8 on the wire, but if it's
// not ASCII it'll do STRING16.
// - Select a format that's cheap to convert to. E.g., we don't
// have LATIN1 on the wire, so we call EncodeFromLatin1 which
// transcodes to UTF8 if needed.
void EncodeString(const String& s, std::vector<uint8_t>* out) {
if (StringUtil::CharacterCount(s) == 0) {
cbor::EncodeString8(span<uint8_t>(nullptr, 0), out); // Empty string.
} else if (StringUtil::CharactersLatin1(s)) {
cbor::EncodeFromLatin1(span<uint8_t>(StringUtil::CharactersLatin1(s),
StringUtil::CharacterCount(s)),
out);
} else if (StringUtil::CharactersUTF16(s)) {
cbor::EncodeFromUTF16(span<uint16_t>(StringUtil::CharactersUTF16(s),
StringUtil::CharacterCount(s)),
out);
} else if (StringUtil::CharactersUTF8(s)) {
cbor::EncodeString8(span<uint8_t>(StringUtil::CharactersUTF8(s),
StringUtil::CharacterCount(s)),
out);
}
}
} // namespace
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);
EncodeString(m_stringValue, bytes);
}
std::unique_ptr<Value> StringValue::clone() const
@ -387,7 +419,8 @@ void BinaryValue::writeJSON(StringBuilder* output) const
}
void BinaryValue::writeBinary(std::vector<uint8_t>* bytes) const {
EncodeBinary(span<uint8_t>(m_binaryValue.data(), m_binaryValue.size()), bytes);
cbor::EncodeBinary(span<uint8_t>(m_binaryValue.data(),
m_binaryValue.size()), bytes);
}
std::unique_ptr<Value> BinaryValue::clone() const
@ -550,19 +583,17 @@ void DictionaryValue::writeJSON(StringBuilder* output) const
}
void DictionaryValue::writeBinary(std::vector<uint8_t>* bytes) const {
EnvelopeEncoder encoder;
cbor::EnvelopeEncoder encoder;
encoder.EncodeStart(bytes);
bytes->push_back(EncodeIndefiniteLengthMapStart());
bytes->push_back(cbor::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);
EncodeString(key, bytes);
value->second->writeBinary(bytes);
}
bytes->push_back(EncodeStop());
bytes->push_back(cbor::EncodeStop());
encoder.EncodeStop(bytes);
}
@ -601,13 +632,13 @@ void ListValue::writeJSON(StringBuilder* output) const
}
void ListValue::writeBinary(std::vector<uint8_t>* bytes) const {
EnvelopeEncoder encoder;
cbor::EnvelopeEncoder encoder;
encoder.EncodeStart(bytes);
bytes->push_back(EncodeIndefiniteLengthArrayStart());
bytes->push_back(cbor::EncodeIndefiniteLengthArrayStart());
for (size_t i = 0; i < m_data.size(); ++i) {
m_data[i]->writeBinary(bytes);
}
bytes->push_back(EncodeStop());
bytes->push_back(cbor::EncodeStop());
encoder.EncodeStop(bytes);
}

View File

@ -136,7 +136,7 @@ std::unique_ptr<Value> StringUtil::parseMessage(
reinterpret_cast<const uint8_t*>(message.data()),
message.length());
}
std::unique_ptr<base::Value> value = base::JSONReader::Read(message);
std::unique_ptr<base::Value> value = base::JSONReader::ReadDeprecated(message);
return toProtocolValue(value.get(), 1000);
}
@ -185,6 +185,13 @@ void StringBuilder::reserveCapacity(size_t capacity) {
string_.reserve(capacity);
}
// static
String StringUtil::fromUTF16(const uint16_t* data, size_t length) {
std::string utf8;
base::UTF16ToUTF8(reinterpret_cast<const base::char16*>(data), length, &utf8);
return utf8;
}
Binary::Binary() : bytes_(new base::RefCountedBytes) {}
Binary::Binary(const Binary& binary) : bytes_(binary.bytes_) {}
Binary::Binary(scoped_refptr<base::RefCountedMemory> bytes) : bytes_(bytes) {}
@ -230,75 +237,6 @@ Binary Binary::fromSpan(const uint8_t* data, size_t size) {
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 %}

View File

@ -32,16 +32,6 @@ 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();
@ -109,6 +99,15 @@ class {{config.lib.export_macro}} StringUtil {
static String fromUTF8(const uint8_t* data, size_t length) {
return std::string(reinterpret_cast<const char*>(data), length);
}
static String fromUTF16(const uint16_t* data, size_t length);
static const uint8_t* CharactersLatin1(const String& s) { return nullptr; }
static const uint8_t* CharactersUTF8(const String& s) {
return reinterpret_cast<const uint8_t*>(s.data());
}
static const uint16_t* CharactersUTF16(const String& s) { return nullptr; }
static size_t CharacterCount(const String& s) { return s.size(); }
};
// A read-only sequence of uninterpreted bytes with reference-counted storage.
@ -137,12 +136,6 @@ class {{config.lib.export_macro}} Binary {
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 %}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,520 @@
{# This template is generated by gen_cbor_templates.py. #}
// Generated by lib/encoding_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)}}_encoding_h
#define {{"_".join(config.protocol.namespace)}}_encoding_h
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>
#include <memory>
#include <string>
#include <vector>
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
{% endfor %}
// ===== encoding/encoding.h =====
// =============================================================================
// span - sequence of bytes
// =============================================================================
// This template is similar to std::span, which will be included in C++20.
template <typename T>
class span {
public:
using index_type = size_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_;
};
template <typename T>
span<T> SpanFrom(const std::vector<T>& v) {
return span<T>(v.data(), v.size());
}
template <size_t N>
span<uint8_t> SpanFrom(const char (&str)[N]) {
return span<uint8_t>(reinterpret_cast<const uint8_t*>(str), N - 1);
}
inline span<uint8_t> SpanFrom(const char* str) {
return str ? span<uint8_t>(reinterpret_cast<const uint8_t*>(str), strlen(str))
: span<uint8_t>();
}
inline span<uint8_t> SpanFrom(const std::string& v) {
return span<uint8_t>(reinterpret_cast<const uint8_t*>(v.data()), v.size());
}
// =============================================================================
// Status and 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_MAP_END = 0x0b,
JSON_PARSER_COMMA_OR_MAP_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_TRAILING_JUNK = 0x1c,
CBOR_MAP_START_EXPECTED = 0x1d,
CBOR_MAP_STOP_EXPECTED = 0x1e,
CBOR_ENVELOPE_SIZE_LIMIT_EXCEEDED = 0x1f,
};
// 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 size_t npos() { return std::numeric_limits<size_t>::max(); }
bool ok() const { return error == Error::OK; }
Error error = Error::OK;
size_t pos = npos();
Status(Error error, size_t pos) : error(error), pos(pos) {}
Status() = default;
// Returns a 7 bit US-ASCII string, either "OK" or an error message
// that includes the position.
std::string ToASCIIString() const;
private:
std::string ToASCIIString(const char* msg) const;
};
// Handler interface for parser events emitted by a streaming parser.
// See cbor::NewCBOREncoder, cbor::ParseCBOR, json::NewJSONEncoder,
// json::ParseJSON.
class StreamingParserHandler {
public:
virtual ~StreamingParserHandler() = default;
virtual void HandleMapBegin() = 0;
virtual void HandleMapEnd() = 0;
virtual void HandleArrayBegin() = 0;
virtual void HandleArrayEnd() = 0;
virtual void HandleString8(span<uint8_t> chars) = 0;
virtual void HandleString16(span<uint16_t> chars) = 0;
virtual void HandleBinary(span<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;
};
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) are supported.
// - 7 bit US-ASCII strings must always be encoded as UTF8 strings, never
// as UTF16 strings.
// - 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.
// =============================================================================
// Detecting CBOR content
// =============================================================================
// The first byte for an envelope, which we use for wrapping dictionaries
// and arrays; and the byte that indicates a byte string with 32 bit length.
// These two bytes start an envelope, and thereby also any CBOR message
// produced or consumed by this protocol. See also |EnvelopeEncoder| below.
uint8_t InitialByteForEnvelope();
uint8_t InitialByteFor32BitLengthByteString();
// Checks whether |msg| is a cbor message.
bool IsCBORMessage(span<uint8_t> msg);
// =============================================================================
// Encoding individual CBOR items
// =============================================================================
// 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();
// 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);
void EncodeInt32(int32_t value, std::string* 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);
void EncodeString16(span<uint16_t> in, std::string* out);
// Encodes a UTF8 string |in| as STRING (major type 3).
void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out);
void EncodeString8(span<uint8_t> in, std::string* out);
// Encodes the given |latin1| string as STRING8.
// If any non-ASCII character is present, it will be represented
// as a 2 byte UTF8 sequence.
void EncodeFromLatin1(span<uint8_t> latin1, std::vector<uint8_t>* out);
void EncodeFromLatin1(span<uint8_t> latin1, std::string* out);
// Encodes the given |utf16| string as STRING8 if it's entirely US-ASCII.
// Otherwise, encodes as STRING16.
void EncodeFromUTF16(span<uint16_t> utf16, std::vector<uint8_t>* out);
void EncodeFromUTF16(span<uint16_t> utf16, std::string* 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);
void EncodeBinary(span<uint8_t> in, std::string* 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);
void EncodeDouble(double value, std::string* out);
// =============================================================================
// cbor::EnvelopeEncoder - for wrapping submessages
// =============================================================================
// 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);
void EncodeStart(std::string* out);
// This records the current size in |out| at position byte_size_pos_.
// Returns true iff successful.
bool EncodeStop(std::vector<uint8_t>* out);
bool EncodeStop(std::string* out);
private:
size_t byte_size_pos_ = 0;
};
// =============================================================================
// cbor::NewCBOREncoder - for encoding from a streaming parser
// =============================================================================
// This can be used to convert to CBOR, by passing the return value to a parser
// that drives it. 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<StreamingParserHandler> NewCBOREncoder(
std::vector<uint8_t>* out,
Status* status);
std::unique_ptr<StreamingParserHandler> NewCBOREncoder(std::string* out,
Status* status);
// =============================================================================
// cbor::CBORTokenizer - for parsing individual CBOR items
// =============================================================================
// Tags for the tokens within a CBOR message that CBORTokenizer 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,
};
// 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
};
// 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;
// To be called only if ::TokenTag() == CBORTokenTag::ENVELOPE.
span<uint8_t> GetEnvelopeContents() const;
private:
void ReadNextToken(bool enter_envelope);
void SetToken(CBORTokenTag token, size_t token_byte_length);
void SetError(Error error);
span<uint8_t> bytes_;
CBORTokenTag token_tag_;
struct Status status_;
size_t token_byte_length_;
MajorType token_start_type_;
uint64_t token_start_internal_value_;
};
// =============================================================================
// cbor::ParseCBOR - for receiving streaming parser events for CBOR messages
// =============================================================================
// Parses a CBOR encoded message from |bytes|, sending events to
// |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, StreamingParserHandler* out);
// =============================================================================
// cbor::AppendString8EntryToMap - for limited in-place editing of messages
// =============================================================================
// Modifies the |cbor| message by appending a new key/value entry at the end
// of the map. Patches up the envelope size; Status.ok() iff successful.
// If not successful, |cbor| may be corrupted after this call.
Status AppendString8EntryToCBORMap(span<uint8_t> string8_key,
span<uint8_t> string8_value,
std::vector<uint8_t>* cbor);
Status AppendString8EntryToCBORMap(span<uint8_t> string8_key,
span<uint8_t> string8_value,
std::string* cbor);
namespace internals { // Exposed only for writing tests.
int8_t ReadTokenStart(span<uint8_t> bytes,
cbor::MajorType* type,
uint64_t* value);
void WriteTokenStart(cbor::MajorType type,
uint64_t value,
std::vector<uint8_t>* encoded);
void WriteTokenStart(cbor::MajorType type,
uint64_t value,
std::string* encoded);
} // namespace internals
} // namespace cbor
namespace json {
// Client code must provide an instance. Implementation should delegate
// to whatever is appropriate.
class Platform {
public:
virtual ~Platform() = default;
// Parses |str| into |result|. Returns false iff there are
// leftover characters or parsing errors.
virtual bool StrToD(const char* str, double* result) const = 0;
// Prints |value| in a format suitable for JSON.
virtual std::unique_ptr<char[]> DToStr(double value) const = 0;
};
// =============================================================================
// json::NewJSONEncoder - for encoding streaming parser events as JSON
// =============================================================================
// Returns a handler object which will write ascii characters to |out|.
// |status->ok()| will be false iff the handler routine HandleError() is called.
// In that case, we'll stop emitting output.
// Except for calling the HandleError routine at any time, the client
// code must call the Handle* methods in an order in which they'd occur
// in valid JSON; otherwise we may crash (the code uses assert).
std::unique_ptr<StreamingParserHandler> NewJSONEncoder(
const Platform* platform,
std::vector<uint8_t>* out,
Status* status);
std::unique_ptr<StreamingParserHandler> NewJSONEncoder(const Platform* platform,
std::string* out,
Status* status);
// =============================================================================
// json::ParseJSON - for receiving streaming parser events for JSON
// =============================================================================
void ParseJSON(const Platform& platform,
span<uint8_t> chars,
StreamingParserHandler* handler);
void ParseJSON(const Platform& platform,
span<uint16_t> chars,
StreamingParserHandler* handler);
// =============================================================================
// json::ConvertCBORToJSON, json::ConvertJSONToCBOR - for transcoding
// =============================================================================
Status ConvertCBORToJSON(const Platform& platform,
span<uint8_t> cbor,
std::string* json);
Status ConvertCBORToJSON(const Platform& platform,
span<uint8_t> cbor,
std::vector<uint8_t>* json);
Status ConvertJSONToCBOR(const Platform& platform,
span<uint8_t> json,
std::vector<uint8_t>* cbor);
Status ConvertJSONToCBOR(const Platform& platform,
span<uint16_t> json,
std::vector<uint8_t>* cbor);
Status ConvertJSONToCBOR(const Platform& platform,
span<uint8_t> json,
std::string* cbor);
Status ConvertJSONToCBOR(const Platform& platform,
span<uint16_t> json,
std::string* cbor);
} // namespace json
{% for namespace in config.protocol.namespace %}
} // namespace {{namespace}}
{% endfor %}
#endif // !defined({{"_".join(config.protocol.namespace)}}_encoding_h)

View File

@ -74,20 +74,20 @@ def parse(data, file_name, map_binary_to_string=False):
if len(trimLine) == 0:
continue
match = re.compile('^(experimental )?(deprecated )?domain (.*)').match(line)
match = re.compile(r'^(experimental )?(deprecated )?domain (.*)').match(line)
if match:
domain = createItem({'domain' : match.group(3)}, match.group(1), match.group(2))
protocol['domains'].append(domain)
continue
match = re.compile('^ depends on ([^\s]+)').match(line)
match = re.compile(r'^ depends on ([^\s]+)').match(line)
if match:
if 'dependencies' not in domain:
domain['dependencies'] = []
domain['dependencies'].append(match.group(1))
continue
match = re.compile('^ (experimental )?(deprecated )?type (.*) extends (array of )?([^\s]+)').match(line)
match = re.compile(r'^ (experimental )?(deprecated )?type (.*) extends (array of )?([^\s]+)').match(line)
if match:
if 'types' not in domain:
domain['types'] = []
@ -96,7 +96,7 @@ def parse(data, file_name, map_binary_to_string=False):
domain['types'].append(item)
continue
match = re.compile('^ (experimental )?(deprecated )?(command|event) (.*)').match(line)
match = re.compile(r'^ (experimental )?(deprecated )?(command|event) (.*)').match(line)
if match:
list = []
if match.group(3) == 'command':
@ -114,7 +114,7 @@ def parse(data, file_name, map_binary_to_string=False):
list.append(item)
continue
match = re.compile('^ (experimental )?(deprecated )?(optional )?(array of )?([^\s]+) ([^\s]+)').match(line)
match = re.compile(r'^ (experimental )?(deprecated )?(optional )?(array of )?([^\s]+) ([^\s]+)').match(line)
if match:
param = createItem({}, match.group(1), match.group(2), match.group(6))
if match.group(3):
@ -125,36 +125,36 @@ def parse(data, file_name, map_binary_to_string=False):
subitems.append(param)
continue
match = re.compile('^ (parameters|returns|properties)').match(line)
match = re.compile(r'^ (parameters|returns|properties)').match(line)
if match:
subitems = item[match.group(1)] = []
continue
match = re.compile('^ enum').match(line)
match = re.compile(r'^ enum').match(line)
if match:
enumliterals = item['enum'] = []
continue
match = re.compile('^version').match(line)
match = re.compile(r'^version').match(line)
if match:
continue
match = re.compile('^ major (\d+)').match(line)
match = re.compile(r'^ major (\d+)').match(line)
if match:
protocol['version']['major'] = match.group(1)
continue
match = re.compile('^ minor (\d+)').match(line)
match = re.compile(r'^ minor (\d+)').match(line)
if match:
protocol['version']['minor'] = match.group(1)
continue
match = re.compile('^ redirect ([^\s]+)').match(line)
match = re.compile(r'^ redirect ([^\s]+)').match(line)
if match:
item['redirect'] = match.group(1)
continue
match = re.compile('^ ( )?[^\s]+$').match(line)
match = re.compile(r'^ ( )?[^\s]+$').match(line)
if match:
# enum literal
enumliterals.append(trimLine)

View File

@ -0,0 +1,162 @@
#!/usr/bin/env python
# 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.
from __future__ import print_function
import argparse
import sys
import os
import subprocess
import glob
import shutil
FILES_TO_SYNC = [
'README.md',
'check_protocol_compatibility.py',
'code_generator.py',
'concatenate_protocols.py',
'convert_protocol_to_json.py',
'encoding/encoding.h',
'encoding/encoding.cc',
'encoding/encoding_test.cc',
'inspector_protocol.gni',
'inspector_protocol.gypi',
'lib/*',
'pdl.py',
'templates/*',
]
def RunCmd(cmd):
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
(stdoutdata, stderrdata) = p.communicate()
if p.returncode != 0:
raise Exception('%s: exit status %d', str(cmd), p.returncode)
return stdoutdata
def CheckRepoIsClean(path, suffix):
os.chdir(path) # As a side effect this also checks for existence of the dir.
# If path isn't a git repo, this will throw and exception.
# And if it is a git repo and 'git status' has anything interesting to say,
# then it's not clean (uncommitted files etc.)
if len(RunCmd(['git', 'status', '--porcelain'])) != 0:
raise Exception('%s is not a clean git repo (run git status)' % path)
if not path.endswith(suffix):
raise Exception('%s does not end with /%s' % (path, suffix))
def CheckRepoIsNotAtMasterBranch(path):
os.chdir(path)
stdout = RunCmd(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).strip()
if stdout == 'master':
raise Exception('%s is at master branch - refusing to copy there.' % path)
def CheckRepoIsV8Checkout(path):
os.chdir(path)
if (RunCmd(['git', 'config', '--get', 'remote.origin.url']).strip() !=
'https://chromium.googlesource.com/v8/v8.git'):
raise Exception('%s is not a proper V8 checkout.' % path)
def CheckRepoIsInspectorProtocolCheckout(path):
os.chdir(path)
if (RunCmd(['git', 'config', '--get', 'remote.origin.url']).strip() !=
'https://chromium.googlesource.com/deps/inspector_protocol.git'):
raise Exception('%s is not a proper inspector_protocol checkout.' % path)
def FindFilesToSyncIn(path):
files = []
for f in FILES_TO_SYNC:
files += glob.glob(os.path.join(path, f))
files = [os.path.relpath(f, path) for f in files]
return files
def FilesAreEqual(path1, path2):
# We check for permissions (useful for executable scripts) and contents.
return (os.stat(path1).st_mode == os.stat(path2).st_mode and
open(path1).read() == open(path2).read())
def GetHeadRevision(path):
os.chdir(path)
return RunCmd(['git', 'rev-parse', 'HEAD'])
def main(argv):
parser = argparse.ArgumentParser(description=(
"Rolls the inspector_protocol project (upstream) into V8's "
"third_party (downstream)."))
parser.add_argument("--ip_src_upstream",
help="The inspector_protocol (upstream) tree.",
default="~/ip/src")
parser.add_argument("--v8_src_downstream",
help="The V8 src tree.",
default="~/v8/v8")
parser.add_argument('--force', dest='force', action='store_true',
help=("Whether to carry out the modifications "
"in the destination tree."))
parser.set_defaults(force=False)
args = parser.parse_args(argv)
upstream = os.path.normpath(os.path.expanduser(args.ip_src_upstream))
downstream = os.path.normpath(os.path.expanduser(
args.v8_src_downstream))
CheckRepoIsClean(upstream, '/src')
CheckRepoIsClean(downstream, '/v8')
CheckRepoIsInspectorProtocolCheckout(upstream)
CheckRepoIsV8Checkout(downstream)
# Check that the destination Git repo isn't at the master branch - it's
# generally a bad idea to check into the master branch, so we catch this
# common pilot error here early.
CheckRepoIsNotAtMasterBranch(downstream)
src_dir = upstream
dest_dir = os.path.join(downstream, 'third_party/inspector_protocol')
print('Rolling %s into %s ...' % (src_dir, dest_dir))
src_files = set(FindFilesToSyncIn(src_dir))
dest_files = set(FindFilesToSyncIn(dest_dir))
to_add = [f for f in src_files if f not in dest_files]
to_delete = [f for f in dest_files if f not in src_files]
to_copy = [f for f in src_files
if (f in dest_files and not FilesAreEqual(
os.path.join(src_dir, f), os.path.join(dest_dir, f)))]
print('To add: %s' % to_add)
print('To delete: %s' % to_delete)
print('To copy: %s' % to_copy)
if not to_add and not to_delete and not to_copy:
print('Nothing to do. You\'re good.')
sys.exit(0)
if not args.force:
print('Rerun with --force if you wish the modifications to be done.')
sys.exit(1)
print('You said --force ... as you wish, modifying the destination.')
for f in to_add + to_copy:
contents = open(os.path.join(src_dir, f)).read()
contents = contents.replace(
'INSPECTOR_PROTOCOL_ENCODING_ENCODING_H_',
'V8_INSPECTOR_PROTOCOL_ENCODING_ENCODING_H_')
contents = contents.replace(
'namespace inspector_protocol_encoding',
'namespace v8_inspector_protocol_encoding')
open(os.path.join(dest_dir, f), 'w').write(contents)
shutil.copymode(os.path.join(src_dir, f), os.path.join(dest_dir, f))
for f in to_delete:
os.unlink(os.path.join(dest_dir, f))
head_revision = GetHeadRevision(upstream)
lines = open(os.path.join(dest_dir, 'README.v8')).readlines()
f = open(os.path.join(dest_dir, 'README.v8'), 'w')
for line in lines:
if line.startswith('Revision: '):
f.write('Revision: %s' % head_revision)
else:
f.write(line)
f.close()
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View File

@ -203,12 +203,12 @@ void Frontend::flush()
m_frontendChannel->flushProtocolNotifications();
}
void Frontend::sendRawNotification(String notification)
void Frontend::sendRawJSONNotification(String notification)
{
m_frontendChannel->sendProtocolNotification(InternalRawNotification::fromJSON(std::move(notification)));
}
void Frontend::sendRawNotification(std::vector<uint8_t> notification)
void Frontend::sendRawCBORNotification(std::vector<uint8_t> notification)
{
m_frontendChannel->sendProtocolNotification(InternalRawNotification::fromBinary(std::move(notification)));
}

View File

@ -269,8 +269,8 @@ public:
{% endfor %}
void flush();
void sendRawNotification(String);
void sendRawNotification(std::vector<uint8_t>);
void sendRawJSONNotification(String);
void sendRawCBORNotification(std::vector<uint8_t>);
private:
FrontendChannel* m_frontendChannel;
};