Commit 3573d5e0 authored by Andrey Kosyakov's avatar Andrey Kosyakov Committed by Commit Bot

Roll inspector_protocol library to inculude unified (de)serialization support

Note that changes in test expectation come from a more verbose
error diagnostics for expected errors around input parameter
validation.

Original change: https://chromium-review.googlesource.com/c/deps/inspector_protocol/+/2270757

Bug: chromium:1099809

Change-Id: I4fc2efc9c89d0af645dad937d719fa36e1d33489
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2277142Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Andrey Kosyakov <caseq@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68657}
parent 4769e158
......@@ -247,8 +247,7 @@ class InjectedScript::ProtocolPromiseHandler {
// we try to capture a fresh stack trace.
if (maybeMessage.ToLocal(&message)) {
v8::Local<v8::Value> exception = result;
protocol::detail::PtrMaybe<protocol::Runtime::ExceptionDetails>
exceptionDetails;
protocol::PtrMaybe<protocol::Runtime::ExceptionDetails> exceptionDetails;
response = scope.injectedScript()->createExceptionDetails(
message, exception, m_objectGroup, &exceptionDetails);
if (!response.IsSuccess()) {
......
......@@ -246,7 +246,66 @@ String16 stackTraceIdToString(uintptr_t id) {
} // namespace v8_inspector
namespace v8_crdtp {
void SerializerTraits<v8_inspector::protocol::Binary>::Serialize(
using v8_inspector::String16;
using v8_inspector::protocol::Binary;
using v8_inspector::protocol::StringUtil;
// static
bool ProtocolTypeTraits<String16>::Deserialize(DeserializerState* state,
String16* value) {
auto* tokenizer = state->tokenizer();
if (tokenizer->TokenTag() == cbor::CBORTokenTag::STRING8) {
const auto str = tokenizer->GetString8();
*value = StringUtil::fromUTF8(str.data(), str.size());
return true;
}
if (tokenizer->TokenTag() == cbor::CBORTokenTag::STRING16) {
const auto str = tokenizer->GetString16WireRep();
*value = StringUtil::fromUTF16LE(
reinterpret_cast<const uint16_t*>(str.data()), str.size() / 2);
return true;
}
state->RegisterError(Error::BINDINGS_STRING_VALUE_EXPECTED);
return false;
}
// static
void ProtocolTypeTraits<String16>::Serialize(const String16& value,
std::vector<uint8_t>* bytes) {
cbor::EncodeFromUTF16(
span<uint16_t>(reinterpret_cast<const uint16_t*>(value.characters16()),
value.length()),
bytes);
}
// static
bool ProtocolTypeTraits<Binary>::Deserialize(DeserializerState* state,
Binary* value) {
auto* tokenizer = state->tokenizer();
if (tokenizer->TokenTag() == cbor::CBORTokenTag::BINARY) {
const span<uint8_t> bin = tokenizer->GetBinary();
*value = Binary::fromSpan(bin.data(), bin.size());
return true;
}
if (tokenizer->TokenTag() == cbor::CBORTokenTag::STRING8) {
const auto str_span = tokenizer->GetString8();
auto str = StringUtil::fromUTF8(str_span.data(), str_span.size());
bool success = false;
*value = Binary::fromBase64(str, &success);
return success;
}
state->RegisterError(Error::BINDINGS_BINARY_VALUE_EXPECTED);
return false;
}
// static
void ProtocolTypeTraits<Binary>::Serialize(const Binary& value,
std::vector<uint8_t>* bytes) {
cbor::EncodeBinary(span<uint8_t>(value.data(), value.size()), bytes);
}
void SerializerTraits<Binary>::Serialize(
const v8_inspector::protocol::Binary& binary, std::vector<uint8_t>* out) {
cbor::EncodeBinary(span<uint8_t>(binary.data(), binary.size()), out);
}
......
......@@ -6,14 +6,15 @@
#define V8_INSPECTOR_STRING_UTIL_H_
#include <stdint.h>
#include <memory>
#include "../../third_party/inspector_protocol/crdtp/protocol_core.h"
#include "include/v8-inspector.h"
#include "src/base/logging.h"
#include "src/base/macros.h"
#include "src/inspector/string-16.h"
#include "include/v8-inspector.h"
namespace v8_inspector {
namespace protocol {
......@@ -86,11 +87,42 @@ String16 stackTraceIdToString(uintptr_t id);
// See third_party/inspector_protocol/crdtp/serializer_traits.h.
namespace v8_crdtp {
template <>
struct ProtocolTypeTraits<v8_inspector::String16> {
static bool Deserialize(DeserializerState* state,
v8_inspector::String16* value);
static void Serialize(const v8_inspector::String16& value,
std::vector<uint8_t>* bytes);
};
template <>
struct ProtocolTypeTraits<v8_inspector::protocol::Binary> {
static bool Deserialize(DeserializerState* state,
v8_inspector::protocol::Binary* value);
static void Serialize(const v8_inspector::protocol::Binary& value,
std::vector<uint8_t>* bytes);
};
template <>
struct SerializerTraits<v8_inspector::protocol::Binary> {
static void Serialize(const v8_inspector::protocol::Binary& binary,
std::vector<uint8_t>* out);
};
namespace detail {
template <>
struct MaybeTypedef<v8_inspector::String16> {
typedef ValueMaybe<v8_inspector::String16> type;
};
template <>
struct MaybeTypedef<v8_inspector::protocol::Binary> {
typedef ValueMaybe<v8_inspector::protocol::Binary> type;
};
} // namespace detail
} // namespace v8_crdtp
#endif // V8_INSPECTOR_STRING_UTIL_H_
......@@ -1552,10 +1552,9 @@ void V8DebuggerAgentImpl::didParseSource(
String16 scriptId = script->scriptId();
String16 scriptURL = script->sourceURL();
String16 scriptLanguage = getScriptLanguage(*script);
Maybe<int> codeOffset =
script->getLanguage() == V8DebuggerScript::Language::JavaScript
? Maybe<int>()
: script->codeOffset();
Maybe<int> codeOffset;
if (script->getLanguage() == V8DebuggerScript::Language::WebAssembly)
codeOffset = script->codeOffset();
std::unique_ptr<protocol::Debugger::DebugSymbols> debugSymbols =
getDebugSymbols(*script);
......@@ -1727,10 +1726,11 @@ void V8DebuggerAgentImpl::didPause(
WrapMode::kNoPreview, &obj);
std::unique_ptr<protocol::DictionaryValue> breakAuxData;
if (obj) {
breakAuxData = obj->toValue();
std::vector<uint8_t> serialized;
obj->AppendSerialized(&serialized);
breakAuxData = protocol::DictionaryValue::cast(
protocol::Value::parseBinary(serialized.data(), serialized.size()));
breakAuxData->setBoolean("uncaught", isUncaught);
} else {
breakAuxData = nullptr;
}
hitReasons.push_back(
std::make_pair(breakReason, std::move(breakAuxData)));
......
......@@ -108,14 +108,14 @@ Running test: testConsoleLog
functionName : eval
lineNumber : 0
scriptId : <scriptId>
url :
url :
}
[1] : {
columnNumber : 0
functionName :
functionName :
lineNumber : 0
scriptId : <scriptId>
url :
url :
}
]
}
......@@ -232,7 +232,7 @@ ReleaseObject with invalid params.
{
error : {
code : -32602
data : objectId: string value expected
data : Failed to deserialize params.objectId - BINDINGS: mandatory field missing at <some position>
message : Invalid parameters
}
id : <messageId>
......@@ -299,7 +299,7 @@ ReleaseObjectGroup with invalid params
{
error : {
code : -32602
data : objectGroup: string value expected
data : Failed to deserialize params.objectGroup - BINDINGS: mandatory field missing at <some position>
message : Invalid parameters
}
id : <messageId>
......@@ -327,7 +327,7 @@ Running test: testCallFrameIdTypeError
{
error : {
code : -32602
data : callFrameId: string value expected
data : Failed to deserialize params.callFrameId - BINDINGS: string value expected at <some position>
message : Invalid parameters
}
id : <messageId>
......@@ -347,7 +347,7 @@ Running test: testNullExpression
{
error : {
code : -32602
data : expression: string value expected
data : Failed to deserialize params.expression - BINDINGS: string value expected at <some position>
message : Invalid parameters
}
id : <messageId>
......
......@@ -66,7 +66,7 @@ const {Protocol} = InspectorTest.start(
async function testReleaseObjectInvalid() {
const releaseObjectResult = await Protocol.Runtime.releaseObject({});
InspectorTest.log('ReleaseObject with invalid params.');
InspectorTest.logMessage(releaseObjectResult);
InspectorTest.logMessage(InspectorTest.trimErrorMessage(releaseObjectResult));
},
async function testObjectGroups() {
await Protocol.Runtime.evaluate({ expression: 'var a = {x:3};', callFrameId });
......@@ -89,7 +89,7 @@ const {Protocol} = InspectorTest.start(
async function testReleaseObjectGroupInvalid() {
const releaseObjectGroupResult = await Protocol.Runtime.releaseObjectGroup({});
InspectorTest.log('ReleaseObjectGroup with invalid params');
InspectorTest.logMessage(releaseObjectGroupResult);
InspectorTest.logMessage(InspectorTest.trimErrorMessage(releaseObjectGroupResult));
},
async function testEvaluateSyntaxError() {
const result = await Protocol.Debugger.evaluateOnCallFrame({ expression: `[]]`, callFrameId });
......@@ -101,7 +101,7 @@ const {Protocol} = InspectorTest.start(
},
async function testCallFrameIdTypeError() {
const result = await Protocol.Debugger.evaluateOnCallFrame({ expression: `console.log(42)`, callFrameId: {} });
InspectorTest.logMessage(result);
InspectorTest.logMessage(InspectorTest.trimErrorMessage(result));
},
async function testCallFrameIdInvalidInput() {
InspectorTest.log('Testing evaluateOnCallFrame with non-existent callFrameId');
......@@ -115,7 +115,7 @@ const {Protocol} = InspectorTest.start(
async function evalAndLog(expression, callFrameId, returnByValue) {
const result = await Protocol.Debugger.evaluateOnCallFrame({ expression, callFrameId, returnByValue });
InspectorTest.logMessage(result);
InspectorTest.logMessage(InspectorTest.trimErrorMessage(result));
}
// Helper function that calls a function on all objects with ids in objectIds, then returns
......
......@@ -3,5 +3,5 @@ setBreakpointByUrl error: undefined
setBreakpoint error: {
"code": -32602,
"message": "Invalid parameters",
"data": "location: object expected"
"data": "Failed to deserialize params.location - BINDINGS: mandatory field missing at <some position>"
}
......@@ -8,12 +8,14 @@ Protocol.Debugger.setBreakpointByUrl({ url: "http://example.com", lineNumber: 10
function didSetBreakpointByUrlBeforeEnable(message)
{
InspectorTest.log("setBreakpointByUrl error: " + JSON.stringify(message.error, null, 2));
InspectorTest.log("setBreakpointByUrl error: " + JSON.stringify(
InspectorTest.trimErrorMessage(message).error, null, 2));
Protocol.Debugger.setBreakpoint().then(didSetBreakpointBeforeEnable);
}
function didSetBreakpointBeforeEnable(message)
{
InspectorTest.log("setBreakpoint error: " + JSON.stringify(message.error, null, 2));
InspectorTest.log("setBreakpoint error: " + JSON.stringify(
InspectorTest.trimErrorMessage(message).error, null, 2));
InspectorTest.completeTest();
}
......@@ -218,7 +218,7 @@ setVariableValue with invalid scopeNumber
{
error : {
code : -32602
data : scopeNumber: integer value expected
data : Failed to deserialize params.scopeNumber - BINDINGS: int32 value expected at <some position>
message : Invalid parameters
}
id : <messageId>
......@@ -253,8 +253,8 @@ setVariableValue with invalid objectId
{
error : {
code : -32602
data : newValue.objectId: string value expected
data : Failed to deserialize params.newValue.objectId - BINDINGS: string value expected at <some position>
message : Invalid parameters
}
id : <messageId>
}
\ No newline at end of file
}
......@@ -66,24 +66,24 @@ const { contextGroup, Protocol } = InspectorTest.start(
async function testInvalidFrame() {
let result = await Protocol.Debugger.setVariableValue({ scopeNumber: 0, variableName: 'num', newValue: { unserializableValue: 'NaN' }, callFrameId: 'fakeCallFrame' });
InspectorTest.log('setVariableValue with invalid callFrameId');
InspectorTest.logMessage(result);
InspectorTest.logMessage(InspectorTest.trimErrorMessage(result));
result = await Protocol.Debugger.setVariableValue({ scopeNumber: 'invalidScopeType', variableName: 'num', newValue: { unserializableValue: 'NaN' }, callFrameId });
InspectorTest.log('setVariableValue with invalid scopeNumber')
InspectorTest.logMessage(result);
InspectorTest.logMessage(InspectorTest.trimErrorMessage(result));
result = await Protocol.Debugger.setVariableValue({ scopeNumber: 1000, variableName: 'num', newValue: { unserializableValue: 'NaN' }, callFrameId });
InspectorTest.log('setVariableValue with invalid scopeNumber');
InspectorTest.logMessage(result);
result = await Protocol.Debugger.setVariableValue({ scopeNumber: 0, variableName: 'FakeObjectName', newValue: { unserializableValue: 'NaN' }, callFrameId });
InspectorTest.log('setVariableValue with invalid variableName');
InspectorTest.logMessage(result);
InspectorTest.logMessage(InspectorTest.trimErrorMessage(result));
},
async function testNewValueErrors() {
let result = await Protocol.Debugger.setVariableValue({ scopeNumber: 0, variableName: 'num', newValue: { unserializableValue: 'not unserializable value' }, callFrameId });
InspectorTest.log('setVariableValue with invalid unserializableValue');
InspectorTest.logMessage(result);
InspectorTest.logMessage(InspectorTest.trimErrorMessage(result));
result = await Protocol.Debugger.setVariableValue({ scopeNumber: 0, variableName: 'num', newValue: { objectId: 2000 }, callFrameId });
InspectorTest.log('setVariableValue with invalid objectId');
InspectorTest.logMessage(result);
InspectorTest.logMessage(InspectorTest.trimErrorMessage(result));
}
]);
......
......@@ -129,6 +129,14 @@ InspectorTest.decodeBase64 = function(base64) {
return bytes;
}
InspectorTest.trimErrorMessage = function(message) {
if (!message.error || !message.error.data)
return message;
message.error.data = message.error.data.replace(/at position \d+/,
'at <some position>');
return message;
}
InspectorTest.ContextGroup = class {
constructor() {
this.id = utils.createContextGroup();
......
......@@ -71,7 +71,7 @@ ReleaseObject with invalid params.
{
error : {
code : -32602
data : objectId: string value expected
data : Failed to deserialize params.objectId - BINDINGS: mandatory field missing at <some position>
message : Invalid parameters
}
id : <messageId>
......@@ -150,8 +150,8 @@ ReleaseObjectGroup with invalid params
{
error : {
code : -32602
data : objectGroup: string value expected
data : Failed to deserialize params.objectGroup - BINDINGS: mandatory field missing at <some position>
message : Invalid parameters
}
id : <messageId>
}
\ No newline at end of file
}
......@@ -31,7 +31,7 @@ const {Protocol} = InspectorTest.start(
async function testReleaseObjectInvalid() {
const releaseObjectResult = await Protocol.Runtime.releaseObject({});
InspectorTest.log('ReleaseObject with invalid params.');
InspectorTest.logMessage(releaseObjectResult);
InspectorTest.logMessage(InspectorTest.trimErrorMessage(releaseObjectResult));
},
async function testObjectGroups() {
await logAndEvaluate('var a = {x:3};');
......@@ -58,7 +58,7 @@ const {Protocol} = InspectorTest.start(
async function testReleaseObjectGroupInvalid() {
const releaseObjectGroupResult = await Protocol.Runtime.releaseObjectGroup({});
InspectorTest.log('ReleaseObjectGroup with invalid params');
InspectorTest.logMessage(releaseObjectGroupResult);
InspectorTest.logMessage(InspectorTest.trimErrorMessage(releaseObjectGroupResult));
}
]);
......
......@@ -20,10 +20,12 @@ v8_source_set("crdtp") {
"crdtp/error_support.h",
"crdtp/export.h",
"crdtp/find_by_first.h",
"crdtp/glue.h",
"crdtp/json.cc",
"crdtp/json.h",
"crdtp/maybe.h",
"crdtp/parser_handler.h",
"crdtp/protocol_core.cc",
"crdtp/protocol_core.h",
"crdtp/serializable.cc",
"crdtp/serializable.h",
"crdtp/serializer_traits.h",
......
......@@ -2,7 +2,7 @@ Name: inspector protocol
Short Name: inspector_protocol
URL: https://chromium.googlesource.com/deps/inspector_protocol/
Version: 0
Revision: b7cda08cd6e522df2159413ba5f29d2a953cc1c4
Revision: 22455bf37cd7a1913918e626a1997cbe77f32d1a
License: BSD
License File: LICENSE
Security Critical: no
......
......@@ -371,7 +371,6 @@ class Protocol(object):
for rule in config.imported.options]
self.patch_full_qualified_refs()
self.create_notification_types()
self.create_type_definitions()
self.generate_used_types()
......@@ -434,8 +433,6 @@ class Protocol(object):
for event in domain["events"]:
if self.generate_event(domain_name, event["name"]):
all_refs |= self.all_references(event)
all_refs.add('%s.%sNotification' % (domain_name,
to_title_case(event["name"])))
dependencies = self.generate_type_dependencies()
queue = set(all_refs)
......@@ -457,20 +454,6 @@ class Protocol(object):
dependencies[domain_name + "." + type["id"]] = related_types
return dependencies
def create_notification_types(self):
for domain in self.json_api["domains"]:
if "events" in domain:
for event in domain["events"]:
event_type = dict()
event_type["description"] = "Wrapper for notification params"
event_type["type"] = "object"
event_type["id"] = to_title_case(event["name"]) + "Notification"
if "parameters" in event:
event_type["properties"] = copy.deepcopy(event["parameters"])
if "types" not in domain:
domain["types"] = list()
domain["types"].append(event_type)
def create_type_definitions(self):
imported_namespace = ""
if self.config.imported:
......@@ -664,6 +647,7 @@ def main():
"Protocol_cpp.template",
"Values_cpp.template",
"Object_cpp.template",
"ValueConversions_cpp.template",
]
forward_h_templates = [
......
......@@ -9,6 +9,7 @@
#include "error_support.h"
#include "find_by_first.h"
#include "frontend_channel.h"
#include "protocol_core.h"
namespace v8_crdtp {
// =============================================================================
......@@ -321,6 +322,18 @@ std::unique_ptr<Serializable> CreateErrorResponse(
return protocol_error;
}
std::unique_ptr<Serializable> CreateErrorResponse(
int call_id,
DispatchResponse dispatch_response,
const DeserializerState& state) {
auto protocol_error =
std::make_unique<ProtocolError>(std::move(dispatch_response));
protocol_error->SetCallId(call_id);
// TODO(caseq): should we plumb the call name here?
protocol_error->SetData(state.ErrorMessage(MakeSpan("params")));
return protocol_error;
}
std::unique_ptr<Serializable> CreateErrorNotification(
DispatchResponse dispatch_response) {
return std::make_unique<ProtocolError>(std::move(dispatch_response));
......@@ -475,6 +488,21 @@ bool DomainDispatcher::MaybeReportInvalidParams(
return true;
}
bool DomainDispatcher::MaybeReportInvalidParams(
const Dispatchable& dispatchable,
const DeserializerState& state) {
if (state.status().ok())
return false;
if (frontend_channel_) {
frontend_channel_->SendProtocolResponse(
dispatchable.CallId(),
CreateErrorResponse(
dispatchable.CallId(),
DispatchResponse::InvalidParams("Invalid parameters"), state));
}
return true;
}
void DomainDispatcher::clearFrontend() {
frontend_channel_ = nullptr;
for (auto& weak : weak_ptrs_)
......
......@@ -16,8 +16,9 @@
#include "status.h"
namespace v8_crdtp {
class FrontendChannel;
class DeserializerState;
class ErrorSupport;
class FrontendChannel;
namespace cbor {
class CBORTokenizer;
} // namespace cbor
......@@ -234,6 +235,8 @@ class DomainDispatcher {
// optimized for code size of the callee.
bool MaybeReportInvalidParams(const Dispatchable& dispatchable,
const ErrorSupport& errors);
bool MaybeReportInvalidParams(const Dispatchable& dispatchable,
const DeserializerState& state);
FrontendChannel* channel() { return frontend_channel_; }
......
// 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_CRDTP_MAYBE_H_
#define V8_CRDTP_MAYBE_H_
#include <cassert>
#include <memory>
namespace v8_crdtp {
// =============================================================================
// detail::PtrMaybe, detail::ValueMaybe, templates for optional
// pointers / values which are used in ../lib/Forward_h.template.
// =============================================================================
namespace detail {
template <typename T>
class PtrMaybe {
public:
PtrMaybe() = default;
PtrMaybe(std::unique_ptr<T> value) : value_(std::move(value)) {}
PtrMaybe(PtrMaybe&& other) noexcept : value_(std::move(other.value_)) {}
void operator=(std::unique_ptr<T> value) { value_ = std::move(value); }
T* fromJust() const {
assert(value_);
return value_.get();
}
T* fromMaybe(T* default_value) const {
return value_ ? value_.get() : default_value;
}
bool isJust() const { return value_ != nullptr; }
std::unique_ptr<T> takeJust() {
assert(value_);
return std::move(value_);
}
private:
std::unique_ptr<T> value_;
};
template <typename T>
class ValueMaybe {
public:
ValueMaybe() : is_just_(false), value_() {}
ValueMaybe(T value) : is_just_(true), value_(std::move(value)) {}
ValueMaybe(ValueMaybe&& other) noexcept
: is_just_(other.is_just_), value_(std::move(other.value_)) {}
void operator=(T value) {
value_ = value;
is_just_ = true;
}
const T& fromJust() const {
assert(is_just_);
return value_;
}
const T& fromMaybe(const T& default_value) const {
return is_just_ ? value_ : default_value;
}
bool isJust() const { return is_just_; }
T takeJust() {
assert(is_just_);
is_just_ = false;
return std::move(value_);
}
private:
bool is_just_;
T value_;
};
template <typename T>
struct MaybeTypedef {
typedef PtrMaybe<T> type;
};
template <>
struct MaybeTypedef<bool> {
typedef ValueMaybe<bool> type;
};
template <>
struct MaybeTypedef<int> {
typedef ValueMaybe<int> type;
};
template <>
struct MaybeTypedef<double> {
typedef ValueMaybe<double> type;
};
template <>
struct MaybeTypedef<std::string> {
typedef ValueMaybe<std::string> type;
};
} // namespace detail
template <typename T>
using Maybe = typename detail::MaybeTypedef<T>::type;
} // namespace v8_crdtp
#endif // V8_CRDTP_MAYBE_H_
// 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 "maybe.h"
#include <string>
#include <vector>
#include "test_platform.h"
namespace v8_crdtp {
// =============================================================================
// detail::PtrMaybe, detail::ValueMaybe, templates for optional
// pointers / values which are used in ../lib/Forward_h.template.
// =============================================================================
TEST(PtrMaybeTest, SmokeTest) {
detail::PtrMaybe<std::vector<uint32_t>> example;
EXPECT_FALSE(example.isJust());
EXPECT_TRUE(nullptr == example.fromMaybe(nullptr));
std::unique_ptr<std::vector<uint32_t>> v(new std::vector<uint32_t>);
v->push_back(42);
v->push_back(21);
example = std::move(v);
EXPECT_TRUE(example.isJust());
EXPECT_THAT(*example.fromJust(), testing::ElementsAre(42, 21));
std::unique_ptr<std::vector<uint32_t>> out = example.takeJust();
EXPECT_FALSE(example.isJust());
EXPECT_THAT(*out, testing::ElementsAre(42, 21));
}
TEST(PtrValueTest, SmokeTest) {
detail::ValueMaybe<int32_t> example;
EXPECT_FALSE(example.isJust());
EXPECT_EQ(-1, example.fromMaybe(-1));
example = 42;
EXPECT_TRUE(example.isJust());
EXPECT_EQ(42, example.fromJust());
int32_t out = example.takeJust();
EXPECT_EQ(out, 42);
}
} // namespace v8_crdtp
// Copyright 2020 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 "protocol_core.h"
#include <cassert>
#include <string>
namespace v8_crdtp {
DeserializerState::DeserializerState(std::vector<uint8_t> bytes)
: storage_(new std::vector<uint8_t>(std::move(bytes))),
tokenizer_(span<uint8_t>(storage_->data(), storage_->size())) {}
DeserializerState::DeserializerState(Storage storage, span<uint8_t> span)
: storage_(std::move(storage)), tokenizer_(span) {}
void DeserializerState::RegisterError(Error error) {
assert(Error::OK != error);
if (tokenizer_.Status().ok())
status_ = Status{error, tokenizer_.Status().pos};
}
void DeserializerState::RegisterFieldPath(span<char> name) {
field_path_.push_back(name);
}
std::string DeserializerState::ErrorMessage(span<char> message_name) const {
std::string msg = "Failed to deserialize ";
msg.append(message_name.begin(), message_name.end());
for (int field = static_cast<int>(field_path_.size()) - 1; field >= 0;
--field) {
msg.append(".");
msg.append(field_path_[field].begin(), field_path_[field].end());
}
Status s = status();
if (!s.ok())
msg += " - " + s.ToASCIIString();
return msg;
}
Status DeserializerState::status() const {
if (!tokenizer_.Status().ok())
return tokenizer_.Status();
return status_;
}
namespace {
constexpr int32_t GetMandatoryFieldMask(
const DeserializerDescriptor::Field* fields,
size_t count) {
int32_t mask = 0;
for (size_t i = 0; i < count; ++i) {
if (!fields[i].is_optional)
mask |= (1 << i);
}
return mask;
}
} // namespace
DeserializerDescriptor::DeserializerDescriptor(const Field* fields,
size_t field_count)
: fields_(fields),
field_count_(field_count),
mandatory_field_mask_(GetMandatoryFieldMask(fields, field_count)) {}
bool DeserializerDescriptor::Deserialize(DeserializerState* state,
void* obj) const {
auto* tokenizer = state->tokenizer();
// As a special compatibility quirk, allow empty objects if
// no mandatory fields are required.
if (tokenizer->TokenTag() == cbor::CBORTokenTag::DONE &&
!mandatory_field_mask_) {
return true;
}
if (tokenizer->TokenTag() == cbor::CBORTokenTag::ENVELOPE)
tokenizer->EnterEnvelope();
if (tokenizer->TokenTag() != cbor::CBORTokenTag::MAP_START) {
state->RegisterError(Error::CBOR_MAP_START_EXPECTED);
return false;
}
tokenizer->Next();
int32_t seen_mandatory_fields = 0;
for (; tokenizer->TokenTag() != cbor::CBORTokenTag::STOP; tokenizer->Next()) {
if (tokenizer->TokenTag() != cbor::CBORTokenTag::STRING8) {
state->RegisterError(Error::CBOR_INVALID_MAP_KEY);
return false;
}
span<uint8_t> u_key = tokenizer->GetString8();
span<char> key(reinterpret_cast<const char*>(u_key.data()), u_key.size());
tokenizer->Next();
if (!DeserializeField(state, key, &seen_mandatory_fields, obj))
return false;
}
// Only compute mandatory fields once per type.
int32_t missing_fields = seen_mandatory_fields ^ mandatory_field_mask_;
if (missing_fields) {
int32_t idx = 0;
while ((missing_fields & 1) == 0) {
missing_fields >>= 1;
++idx;
}
state->RegisterError(Error::BINDINGS_MANDATORY_FIELD_MISSING);
state->RegisterFieldPath(fields_[idx].name);
return false;
}
return true;
}
bool DeserializerDescriptor::DeserializeField(DeserializerState* state,
span<char> name,
int* seen_mandatory_fields,
void* obj) const {
// TODO(caseq): consider checking if the sought field is the one
// after the last deserialized.
const auto* begin = fields_;
const auto* end = fields_ + field_count_;
auto entry = std::lower_bound(
begin, end, name, [](const Field& field_desc, span<char> field_name) {
return SpanLessThan(field_desc.name, field_name);
});
// Unknown field is not an error -- we may be working against an
// implementation of a later version of the protocol.
// TODO(caseq): support unknown arrays and maps not enclosed by an envelope.
if (entry == end || !SpanEquals(entry->name, name))
return true;
if (!entry->deserializer(state, obj)) {
state->RegisterFieldPath(name);
return false;
}
if (!entry->is_optional)
*seen_mandatory_fields |= 1 << (entry - begin);
return true;
}
bool ProtocolTypeTraits<bool>::Deserialize(DeserializerState* state,
bool* value) {
const auto tag = state->tokenizer()->TokenTag();
if (tag == cbor::CBORTokenTag::TRUE_VALUE) {
*value = true;
return true;
}
if (tag == cbor::CBORTokenTag::FALSE_VALUE) {
*value = false;
return true;
}
state->RegisterError(Error::BINDINGS_BOOL_VALUE_EXPECTED);
return false;
}
void ProtocolTypeTraits<bool>::Serialize(bool value,
std::vector<uint8_t>* bytes) {
bytes->push_back(value ? cbor::EncodeTrue() : cbor::EncodeFalse());
}
bool ProtocolTypeTraits<int32_t>::Deserialize(DeserializerState* state,
int32_t* value) {
if (state->tokenizer()->TokenTag() != cbor::CBORTokenTag::INT32) {
state->RegisterError(Error::BINDINGS_INT32_VALUE_EXPECTED);
return false;
}
*value = state->tokenizer()->GetInt32();
return true;
}
void ProtocolTypeTraits<int32_t>::Serialize(int32_t value,
std::vector<uint8_t>* bytes) {
cbor::EncodeInt32(value, bytes);
}
ContainerSerializer::ContainerSerializer(std::vector<uint8_t>* bytes,
uint8_t tag)
: bytes_(bytes) {
envelope_.EncodeStart(bytes_);
bytes_->push_back(tag);
}
void ContainerSerializer::EncodeStop() {
bytes_->push_back(cbor::EncodeStop());
envelope_.EncodeStop(bytes_);
}
ObjectSerializer::ObjectSerializer()
: serializer_(&owned_bytes_, cbor::EncodeIndefiniteLengthMapStart()) {}
ObjectSerializer::~ObjectSerializer() = default;
std::unique_ptr<Serializable> ObjectSerializer::Finish() {
serializer_.EncodeStop();
return Serializable::From(std::move(owned_bytes_));
}
bool ProtocolTypeTraits<double>::Deserialize(DeserializerState* state,
double* value) {
// Double values that round-trip through JSON may end up getting represented
// as an int32 (SIGNED, UNSIGNED) on the wire in CBOR. Therefore, we also
// accept an INT32 here.
if (state->tokenizer()->TokenTag() == cbor::CBORTokenTag::INT32) {
*value = state->tokenizer()->GetInt32();
return true;
}
if (state->tokenizer()->TokenTag() != cbor::CBORTokenTag::DOUBLE) {
state->RegisterError(Error::BINDINGS_DOUBLE_VALUE_EXPECTED);
return false;
}
*value = state->tokenizer()->GetDouble();
return true;
}
void ProtocolTypeTraits<double>::Serialize(double value,
std::vector<uint8_t>* bytes) {
cbor::EncodeDouble(value, bytes);
}
class IncomingDeferredMessage : public DeferredMessage {
public:
// Creates the state from the part of another message.
// Note storage is opaque and is mostly to retain ownership.
// It may be null in case caller owns the memory and will dispose
// of the message synchronously.
IncomingDeferredMessage(DeserializerState::Storage storage,
span<uint8_t> span)
: storage_(storage), span_(span) {}
private:
DeserializerState MakeDeserializer() const override {
return DeserializerState(storage_, span_);
}
void AppendSerialized(std::vector<uint8_t>* out) const override {
out->insert(out->end(), span_.begin(), span_.end());
}
DeserializerState::Storage storage_;
span<uint8_t> span_;
};
class OutgoingDeferredMessage : public DeferredMessage {
public:
OutgoingDeferredMessage() = default;
explicit OutgoingDeferredMessage(std::unique_ptr<Serializable> serializable)
: serializable_(std::move(serializable)) {
assert(!!serializable_);
}
private:
DeserializerState MakeDeserializer() const override {
return DeserializerState(serializable_->Serialize());
}
void AppendSerialized(std::vector<uint8_t>* out) const override {
serializable_->AppendSerialized(out);
}
std::unique_ptr<Serializable> serializable_;
};
// static
std::unique_ptr<DeferredMessage> DeferredMessage::FromSerializable(
std::unique_ptr<Serializable> serializeable) {
return std::make_unique<OutgoingDeferredMessage>(std::move(serializeable));
}
// static
std::unique_ptr<DeferredMessage> DeferredMessage::FromSpan(
span<uint8_t> bytes) {
return std::make_unique<IncomingDeferredMessage>(nullptr, bytes);
}
bool ProtocolTypeTraits<std::unique_ptr<DeferredMessage>>::Deserialize(
DeserializerState* state,
std::unique_ptr<DeferredMessage>* value) {
if (state->tokenizer()->TokenTag() != cbor::CBORTokenTag::ENVELOPE) {
state->RegisterError(Error::CBOR_INVALID_ENVELOPE);
return false;
}
*value = std::make_unique<IncomingDeferredMessage>(
state->storage(), state->tokenizer()->GetEnvelope());
return true;
}
void ProtocolTypeTraits<std::unique_ptr<DeferredMessage>>::Serialize(
const std::unique_ptr<DeferredMessage>& value,
std::vector<uint8_t>* bytes) {
value->AppendSerialized(bytes);
}
} // namespace v8_crdtp
This diff is collapsed.
This diff is collapsed.
......@@ -9,7 +9,7 @@
#include <string>
#include <vector>
#include "cbor.h"
#include "glue.h"
#include "maybe.h"
#include "span.h"
namespace v8_crdtp {
......@@ -124,9 +124,9 @@ struct FieldSerializerTraits {
};
template <typename T>
struct FieldSerializerTraits<glue::detail::PtrMaybe<T>> {
struct FieldSerializerTraits<detail::PtrMaybe<T>> {
static void Serialize(span<uint8_t> field_name,
const glue::detail::PtrMaybe<T>& field_value,
const detail::PtrMaybe<T>& field_value,
std::vector<uint8_t>* out) {
if (!field_value.isJust())
return;
......@@ -136,9 +136,9 @@ struct FieldSerializerTraits<glue::detail::PtrMaybe<T>> {
};
template <typename T>
struct FieldSerializerTraits<glue::detail::ValueMaybe<T>> {
struct FieldSerializerTraits<detail::ValueMaybe<T>> {
static void Serialize(span<uint8_t> field_name,
const glue::detail::ValueMaybe<T>& field_value,
const detail::ValueMaybe<T>& field_value,
std::vector<uint8_t>* out) {
if (!field_value.isJust())
return;
......
......@@ -172,12 +172,12 @@ TEST(FieldSerializerTraits, RequiredField) {
template <typename T>
class FieldSerializerTraits_MaybeTest : public ::testing::Test {};
using MaybeTypes =
::testing::Types<glue::detail::ValueMaybe<bool>,
glue::detail::ValueMaybe<double>,
glue::detail::ValueMaybe<int32_t>,
glue::detail::ValueMaybe<std::string>,
glue::detail::PtrMaybe<Foo>,
glue::detail::PtrMaybe<std::vector<std::unique_ptr<Foo>>>>;
::testing::Types<detail::ValueMaybe<bool>,
detail::ValueMaybe<double>,
detail::ValueMaybe<int32_t>,
detail::ValueMaybe<std::string>,
detail::PtrMaybe<Foo>,
detail::PtrMaybe<std::vector<std::unique_ptr<Foo>>>>;
TYPED_TEST_SUITE(FieldSerializerTraits_MaybeTest, MaybeTypes);
TYPED_TEST(FieldSerializerTraits_MaybeTest, NoOutputForFieldsIfNotJust) {
......@@ -188,9 +188,8 @@ TYPED_TEST(FieldSerializerTraits_MaybeTest, NoOutputForFieldsIfNotJust) {
TEST(FieldSerializerTraits, MaybeBool) {
std::vector<uint8_t> out;
SerializeField(SpanFrom("true"), glue::detail::ValueMaybe<bool>(true), &out);
SerializeField(SpanFrom("false"), glue::detail::ValueMaybe<bool>(false),
&out);
SerializeField(SpanFrom("true"), detail::ValueMaybe<bool>(true), &out);
SerializeField(SpanFrom("false"), detail::ValueMaybe<bool>(false), &out);
std::vector<uint8_t> expected;
cbor::EncodeString8(SpanFrom("true"), &expected);
......@@ -203,7 +202,7 @@ TEST(FieldSerializerTraits, MaybeBool) {
TEST(FieldSerializerTraits, MaybeDouble) {
std::vector<uint8_t> out;
SerializeField(SpanFrom("dbl"), glue::detail::ValueMaybe<double>(3.14), &out);
SerializeField(SpanFrom("dbl"), detail::ValueMaybe<double>(3.14), &out);
std::vector<uint8_t> expected;
cbor::EncodeString8(SpanFrom("dbl"), &expected);
......@@ -215,7 +214,7 @@ TEST(FieldSerializerTraits, MaybeDouble) {
TEST(FieldSerializerTraits, MaybePtrFoo) {
std::vector<uint8_t> out;
SerializeField(SpanFrom("foo"),
glue::detail::PtrMaybe<Foo>(std::make_unique<Foo>(42)), &out);
detail::PtrMaybe<Foo>(std::make_unique<Foo>(42)), &out);
std::vector<uint8_t> expected;
cbor::EncodeString8(SpanFrom("foo"), &expected);
......
......@@ -21,4 +21,19 @@ bool SpanEquals(span<uint8_t> x, span<uint8_t> y) noexcept {
return x.data() == y.data() || len == 0 ||
std::memcmp(x.data(), y.data(), len) == 0;
}
bool SpanLessThan(span<char> x, span<char> y) noexcept {
auto min_size = std::min(x.size(), y.size());
const int r = min_size == 0 ? 0 : memcmp(x.data(), y.data(), min_size);
return (r < 0) || (r == 0 && x.size() < y.size());
}
bool SpanEquals(span<char> x, span<char> y) noexcept {
auto len = x.size();
if (len != y.size())
return false;
return x.data() == y.data() || len == 0 ||
std::memcmp(x.data(), y.data(), len) == 0;
}
} // namespace v8_crdtp
......@@ -50,6 +50,11 @@ class span {
index_type size_;
};
template <size_t N>
constexpr span<char> MakeSpan(const char (&str)[N]) {
return span<char>(str, N - 1);
}
template <size_t N>
constexpr span<uint8_t> SpanFrom(const char (&str)[N]) {
return span<uint8_t>(reinterpret_cast<const uint8_t*>(str), N - 1);
......@@ -75,11 +80,15 @@ inline span<typename C::value_type> SpanFrom(const C& v) {
}
// Less than / equality comparison functions for sorting / searching for byte
// spans. These are similar to absl::string_view's < and == operators.
// spans.
bool SpanLessThan(span<uint8_t> x, span<uint8_t> y) noexcept;
bool SpanEquals(span<uint8_t> x, span<uint8_t> y) noexcept;
// Less than / equality comparison functions for sorting / searching for byte
// spans.
bool SpanLessThan(span<char> x, span<char> y) noexcept;
bool SpanEquals(span<char> x, span<char> y) noexcept;
struct SpanLt {
bool operator()(span<uint8_t> l, span<uint8_t> r) const {
return SpanLessThan(l, r);
......
......@@ -5,6 +5,7 @@
#ifndef V8_CRDTP_STATUS_H_
#define V8_CRDTP_STATUS_H_
#include <cassert>
#include <cstddef>
#include <limits>
#include <string>
......@@ -103,6 +104,35 @@ struct Status {
// includes the position.
std::string ToASCIIString() const;
};
template <typename T>
class StatusOr {
public:
explicit StatusOr(const T& value) : value_(value) {}
explicit StatusOr(T&& value) : value_(std::move(value)) {}
explicit StatusOr(const Status& status) : status_(status) {}
bool ok() const { return status_.ok(); }
T& operator*() & {
assert(ok());
return value_;
}
const T& operator*() const& { return value(); }
T&& operator*() && { return value(); }
const Status& status() const { return status_; }
T& value() & { return *this; }
T&& value() && {
assert(ok());
return std::move(value_);
}
const T& value() const& { return *this; }
private:
Status status_;
T value_;
};
} // namespace v8_crdtp
#endif // V8_CRDTP_STATUS_H_
......@@ -37,6 +37,7 @@ template("inspector_protocol_generate") {
"$inspector_protocol_dir/lib/Object_cpp.template",
"$inspector_protocol_dir/lib/Object_h.template",
"$inspector_protocol_dir/lib/Protocol_cpp.template",
"$inspector_protocol_dir/lib/ValueConversions_cpp.template",
"$inspector_protocol_dir/lib/ValueConversions_h.template",
"$inspector_protocol_dir/lib/Values_cpp.template",
"$inspector_protocol_dir/lib/Values_h.template",
......
......@@ -21,7 +21,7 @@
#include "{{config.crdtp.dir}}/error_support.h"
#include "{{config.crdtp.dir}}/dispatch.h"
#include "{{config.crdtp.dir}}/frontend_channel.h"
#include "{{config.crdtp.dir}}/glue.h"
#include "{{config.crdtp.dir}}/protocol_core.h"
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
......@@ -42,7 +42,14 @@ class SerializedValue;
class StringValue;
class Value;
using {{config.crdtp.namespace}}::detail::PtrMaybe;
using {{config.crdtp.namespace}}::detail::ValueMaybe;
template<typename T>
using Maybe = {{config.crdtp.namespace}}::Maybe<T>;
namespace detail {
template <typename T>
struct ArrayTypedef { typedef std::vector<std::unique_ptr<T>> type; };
......@@ -62,32 +69,6 @@ struct ArrayTypedef<bool> { typedef std::vector<bool> type; };
template <typename T>
using Array = typename detail::ArrayTypedef<T>::type;
namespace detail {
using {{config.crdtp.namespace}}::glue::detail::PtrMaybe;
using {{config.crdtp.namespace}}::glue::detail::ValueMaybe;
template <typename T>
struct MaybeTypedef { typedef PtrMaybe<T> type; };
template <>
struct MaybeTypedef<bool> { typedef ValueMaybe<bool> type; };
template <>
struct MaybeTypedef<int> { typedef ValueMaybe<int> type; };
template <>
struct MaybeTypedef<double> { typedef ValueMaybe<double> type; };
template <>
struct MaybeTypedef<String> { typedef ValueMaybe<String> type; };
template <>
struct MaybeTypedef<Binary> { typedef ValueMaybe<Binary> type; };
} // namespace detail
template <typename T>
using Maybe = typename detail::MaybeTypedef<T>::type;
{% for namespace in config.protocol.namespace %}
} // namespace {{namespace}}
{% endfor %}
......
......@@ -28,7 +28,11 @@ public:
std::unique_ptr<protocol::DictionaryValue> toValue() const;
std::unique_ptr<Object> clone() const;
private:
Object() = default;
friend struct {{config.crdtp.namespace}}::ProtocolTypeTraits<std::unique_ptr<Object>, void>;
std::unique_ptr<protocol::DictionaryValue> m_object;
};
......
// This file is generated by ValueConversions_cpp.template.
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include {{format_include(config.protocol.package, "Protocol")}}
#include <algorithm>
#include <climits>
#include <string>
//#include "ValueConversions.h"
//#include "Values.h"
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
{% endfor %}
{% for namespace in config.protocol.namespace %}
} // namespce
{% endfor %}
namespace {{config.crdtp.namespace}} {
namespace {
using {{"::".join(config.protocol.namespace)}}::Binary;
using {{"::".join(config.protocol.namespace)}}::Object;
using {{"::".join(config.protocol.namespace)}}::Value;
using {{"::".join(config.protocol.namespace)}}::String;
using {{"::".join(config.protocol.namespace)}}::DictionaryValue;
using {{"::".join(config.protocol.namespace)}}::FundamentalValue;
using {{"::".join(config.protocol.namespace)}}::StringValue;
using {{"::".join(config.protocol.namespace)}}::StringUtil;
//using {{"::".join(config.protocol.namespace)}}::EncodeString;
std::unique_ptr<Value> ReadValue(DeserializerState* state) {
cbor::CBORTokenizer* tokenizer = state->tokenizer();
switch (tokenizer->TokenTag()) {
case cbor::CBORTokenTag::TRUE_VALUE:
return FundamentalValue::create(true);
case cbor::CBORTokenTag::FALSE_VALUE:
return FundamentalValue::create(false);
case cbor::CBORTokenTag::NULL_VALUE:
return Value::null();
case cbor::CBORTokenTag::INT32:
return FundamentalValue::create(tokenizer->GetInt32());
case cbor::CBORTokenTag::DOUBLE:
return FundamentalValue::create(tokenizer->GetDouble());
case cbor::CBORTokenTag::STRING8: {
const auto str = tokenizer->GetString8();
return StringValue::create(StringUtil::fromUTF8(str.data(), str.size()));
}
case cbor::CBORTokenTag::STRING16: {
const auto str = tokenizer->GetString16WireRep();
return StringValue::create(StringUtil::fromUTF16LE(reinterpret_cast<const uint16_t*>(str.data()), str.size() / 2));
}
case cbor::CBORTokenTag::ENVELOPE: {
const auto env = tokenizer->GetEnvelope();
return Value::parseBinary(env.data(), env.size());
}
// Intentionally not supported.
case cbor::CBORTokenTag::BINARY:
// Should not be encountered outside of envelope.
case cbor::CBORTokenTag::MAP_START:
case cbor::CBORTokenTag::ARRAY_START:
default:
state->RegisterError(Error::CBOR_UNSUPPORTED_VALUE);
return nullptr;
}
}
} // namespace
// static
bool ProtocolTypeTraits<std::unique_ptr<Value>>::Deserialize(
DeserializerState* state, std::unique_ptr<Value>* value) {
auto result = ReadValue(state);
if (!result)
return false;
*value = std::move(result);
return true;
}
// static
void ProtocolTypeTraits<std::unique_ptr<Value>>::Serialize(
const std::unique_ptr<Value>& value, std::vector<uint8_t>* bytes) {
value->AppendSerialized(bytes);
}
// static
bool ProtocolTypeTraits<std::unique_ptr<DictionaryValue>>::Deserialize(
DeserializerState* state, std::unique_ptr<DictionaryValue>* value) {
std::unique_ptr<Value> res;
if (!ProtocolTypeTraits<std::unique_ptr<Value>>::Deserialize(state, &res))
return false;
*value = DictionaryValue::cast(std::move(res));
return true;
}
// static
void ProtocolTypeTraits<std::unique_ptr<DictionaryValue>>::Serialize(
const std::unique_ptr<DictionaryValue>& value, std::vector<uint8_t>* bytes) {
value->AppendSerialized(bytes);
}
// static
bool ProtocolTypeTraits<std::unique_ptr<Object>>::Deserialize(DeserializerState* state, std::unique_ptr<Object>* value) {
auto res = DictionaryValue::create();
if (ProtocolTypeTraits<std::unique_ptr<DictionaryValue>>::Deserialize(state, &res)) {
*value = std::make_unique<Object>(std::move(res));
return true;
}
return false;
}
void ProtocolTypeTraits<std::unique_ptr<Object>>::Serialize(const std::unique_ptr<Object>& value, std::vector<uint8_t>* bytes) {
value->AppendSerialized(bytes);
}
} // namespace {{config.crdtp.namespace}}
......@@ -259,8 +259,61 @@ struct ValueConversions<ListValue> {
}
};
template<typename T> struct ValueTypeConverter {
static std::unique_ptr<T> FromValue(const protocol::Value& value) {
std::vector<uint8_t> bytes;
value.AppendSerialized(&bytes);
return T::FromBinary(bytes.data(), bytes.size());
}
static std::unique_ptr<protocol::DictionaryValue> ToValue(const T& obj) {
std::vector<uint8_t> bytes;
obj.AppendSerialized(&bytes);
auto result = Value::parseBinary(bytes.data(), bytes.size());
return DictionaryValue::cast(std::move(result));
}
};
{% for namespace in config.protocol.namespace %}
} // namespace {{namespace}}
{% endfor %}
namespace {{config.crdtp.namespace}} {
template<typename T>
struct ProtocolTypeTraits<T,
typename std::enable_if<std::is_base_of<{{"::".join(config.protocol.namespace)}}::Value, T>::value>::type> {
static void Serialize(const {{"::".join(config.protocol.namespace)}}::Value& value, std::vector<uint8_t>* bytes) {
value.AppendSerialized(bytes);
}
};
template <>
struct ProtocolTypeTraits<std::unique_ptr<{{"::".join(config.protocol.namespace)}}::Value>> {
static bool Deserialize(DeserializerState* state, std::unique_ptr<{{"::".join(config.protocol.namespace)}}::Value>* value);
static void Serialize(const std::unique_ptr<{{"::".join(config.protocol.namespace)}}::Value>& value, std::vector<uint8_t>* bytes);
};
template <>
struct ProtocolTypeTraits<std::unique_ptr<{{"::".join(config.protocol.namespace)}}::DictionaryValue>> {
static bool Deserialize(DeserializerState* state, std::unique_ptr<{{"::".join(config.protocol.namespace)}}::DictionaryValue>* value);
static void Serialize(const std::unique_ptr<{{"::".join(config.protocol.namespace)}}::DictionaryValue>& value, std::vector<uint8_t>* bytes);
};
// TODO(caseq): get rid of it, it's just a DictionaryValue really.
template <>
struct ProtocolTypeTraits<std::unique_ptr<{{"::".join(config.protocol.namespace)}}::Object>> {
static bool Deserialize(DeserializerState* state, std::unique_ptr<{{"::".join(config.protocol.namespace)}}::Object>* value);
static void Serialize(const std::unique_ptr<{{"::".join(config.protocol.namespace)}}::Object>& value, std::vector<uint8_t>* bytes);
};
template<>
struct ProtocolTypeTraits<{{"::".join(config.protocol.namespace)}}::Object> {
static void Serialize(const {{"::".join(config.protocol.namespace)}}::Object& value, std::vector<uint8_t>* bytes) {
value.AppendSerialized(bytes);
}
};
} // namespace {{config.crdtp.namespace}}
#endif // !defined({{"_".join(config.protocol.namespace)}}_ValueConversions_h)
......@@ -62,7 +62,7 @@ class ValueParserHandler : public ParserHandler {
DCHECK(stack_.back().is_dict);
stack_.pop_back();
}
void HandleArrayBegin() override {
if (!status_.ok()) return;
std::unique_ptr<ListValue> list = ListValue::create();
......@@ -91,19 +91,19 @@ class ValueParserHandler : public ParserHandler {
AddValueToParent(
BinaryValue::create(Binary::fromSpan(bytes.data(), bytes.size())));
}
void HandleDouble(double value) override {
AddValueToParent(FundamentalValue::create(value));
}
void HandleInt32(int32_t value) override {
AddValueToParent(FundamentalValue::create(value));
}
void HandleBool(bool value) override {
AddValueToParent(FundamentalValue::create(value));
}
void HandleNull() override {
AddValueToParent(Value::null());
}
......@@ -318,7 +318,6 @@ void EncodeString(const String& s, std::vector<uint8_t>* out) {
}
}
} // namespace
void StringValue::AppendSerialized(std::vector<uint8_t>* bytes) const {
EncodeString(m_stringValue, bytes);
}
......
......@@ -20,6 +20,11 @@ class ListValue;
class DictionaryValue;
class Value;
#define PROTOCOL_DISALLOW_COPY(ClassName) \
private: \
ClassName(const ClassName&) = delete; \
ClassName& operator=(const ClassName&) = delete
class {{config.lib.export_macro}} Value : public Serializable {
PROTOCOL_DISALLOW_COPY(Value);
public:
......
......@@ -16,6 +16,13 @@
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "{{config.crdtp.dir}}/cbor.h"
#include "{{config.crdtp.dir}}/protocol_core.h"
using namespace {{config.crdtp.namespace}};
using {{"::".join(config.protocol.namespace)}}::Binary;
using {{"::".join(config.protocol.namespace)}}::String;
using {{"::".join(config.protocol.namespace)}}::StringUtil;
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
......@@ -138,6 +145,28 @@ String StringUtil::fromUTF16LE(const uint16_t* data, size_t length) {
return utf8;
}
bool StringUtil::ReadString(DeserializerState* state, String* value) {
auto* tokenizer = state->tokenizer();
if (tokenizer->TokenTag() == cbor::CBORTokenTag::STRING8) {
const auto str = tokenizer->GetString8();
*value = StringUtil::fromUTF8(str.data(), str.size());
return true;
}
if (tokenizer->TokenTag() == cbor::CBORTokenTag::STRING16) {
const auto str = tokenizer->GetString16WireRep();
*value = StringUtil::fromUTF16LE(reinterpret_cast<const uint16_t*>(str.data()), str.size() / 2);
return true;
}
state->RegisterError(Error::BINDINGS_STRING_VALUE_EXPECTED);
return false;
}
void StringUtil::WriteString(const String& str, std::vector<uint8_t>* bytes) {
cbor::EncodeString8(span<uint8_t>(StringUtil::CharactersUTF8(str),
StringUtil::CharacterCount(str)),
bytes);
}
Binary::Binary() : bytes_(new base::RefCountedBytes) {}
Binary::Binary(const Binary& binary) : bytes_(binary.bytes_) {}
Binary::Binary(scoped_refptr<base::RefCountedMemory> bytes) : bytes_(bytes) {}
......@@ -190,3 +219,31 @@ Binary Binary::fromSpan(const uint8_t* data, size_t size) {
{% for namespace in config.protocol.namespace %}
} // namespace {{namespace}}
{% endfor %}
namespace {{config.crdtp.namespace}} {
// static
bool ProtocolTypeTraits<Binary>::Deserialize(DeserializerState* state, Binary* value) {
auto* tokenizer = state->tokenizer();
if (tokenizer->TokenTag() == cbor::CBORTokenTag::BINARY) {
const span<uint8_t> bin = tokenizer->GetBinary();
*value = Binary::fromSpan(bin.data(), bin.size());
return true;
}
if (tokenizer->TokenTag() == cbor::CBORTokenTag::STRING8) {
const auto str_span = tokenizer->GetString8();
String str = StringUtil::fromUTF8(str_span.data(), str_span.size());
bool success = false;
*value = Binary::fromBase64(str, &success);
return success;
}
state->RegisterError(Error::BINDINGS_BINARY_VALUE_EXPECTED);
return false;
}
// static
void ProtocolTypeTraits<Binary>::Serialize(const Binary& value, std::vector<uint8_t>* bytes) {
value.AppendSerialized(bytes);
}
} // namespace {{config.crdtp.namespace}}
\ No newline at end of file
......@@ -15,6 +15,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted_memory.h"
#include "{{config.crdtp.dir}}/serializable.h"
#include "{{config.crdtp.dir}}/protocol_core.h"
{% if config.lib.export_header %}
#include "{{config.lib.export_header}}"
......@@ -24,6 +25,10 @@ namespace base {
class Value;
}
namespace {{config.crdtp.namespace}} {
class DeserializerState;
}
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
{% endfor %}
......@@ -46,6 +51,9 @@ class {{config.lib.export_macro}} StringUtil {
}
static const uint16_t* CharactersUTF16(const String& s) { return nullptr; }
static size_t CharacterCount(const String& s) { return s.size(); }
static bool ReadString({{config.crdtp.namespace}}::DeserializerState* state, String* str);
static void WriteString(const String& str, std::vector<uint8_t>* bytes);
};
// A read-only sequence of uninterpreted bytes with reference-counted storage.
......@@ -81,4 +89,24 @@ std::unique_ptr<base::Value> toBaseValue(Value* value, int depth);
} // namespace {{namespace}}
{% endfor %}
namespace {{config.crdtp.namespace}} {
template <>
struct ProtocolTypeTraits<{{"::".join(config.protocol.namespace)}}::String> {
static bool Deserialize(DeserializerState* state, {{"::".join(config.protocol.namespace)}}::String* value) {
return {{"::".join(config.protocol.namespace)}}::StringUtil::ReadString(state, value);
}
static void Serialize(const {{"::".join(config.protocol.namespace)}}::String& value, std::vector<uint8_t>* bytes) {
{{"::".join(config.protocol.namespace)}}::StringUtil::WriteString(value, bytes);
}
};
template <>
struct ProtocolTypeTraits<{{"::".join(config.protocol.namespace)}}::Binary> {
static bool Deserialize(DeserializerState* state, {{"::".join(config.protocol.namespace)}}::Binary* value);
static void Serialize(const {{"::".join(config.protocol.namespace)}}::Binary& value, std::vector<uint8_t>* bytes);
};
} // {{config.crdtp.namespace}}
#endif // !defined({{"_".join(config.protocol.namespace)}}_BASE_STRING_ADAPTER_H)
......@@ -31,13 +31,16 @@ FILES_TO_SYNC = [
'crdtp/find_by_first.h',
'crdtp/find_by_first_test.cc',
'crdtp/frontend_channel.h',
'crdtp/glue.h',
'crdtp/glue_test.cc',
'crdtp/maybe.h',
'crdtp/maybe_test.cc',
'crdtp/json.cc',
'crdtp/json.h',
'crdtp/json_platform.h',
'crdtp/json_test.cc',
'crdtp/parser_handler.h',
'crdtp/protocol_core_test.cc',
'crdtp/protocol_core.cc',
'crdtp/protocol_core.h',
'crdtp/serializable.h',
'crdtp/serializable.cc',
'crdtp/serializable_test.cc',
......
......@@ -14,6 +14,53 @@
#include {{format_include(config.imported.package, domain.domain)}}
{% endif %}
#ifndef {{"_".join(config.protocol.namespace)}}_imported_imported_h
namespace {{config.crdtp.namespace}} {
template <typename T>
struct ProtocolTypeTraits<
std::unique_ptr<T>,
typename std::enable_if<
std::is_base_of<{{"::".join(config.imported.namespace)}}::Exported, T>::value>::type> {
static bool Deserialize(DeserializerState* state, std::unique_ptr<T>* value) {
if (state->tokenizer()->TokenTag() != cbor::CBORTokenTag::ENVELOPE) {
state->RegisterError(Error::CBOR_INVALID_ENVELOPE);
return false;
}
span<uint8_t> env = state->tokenizer()->GetEnvelope();
auto res = T::fromBinary(env.data(), env.size());
if (!res) {
// TODO(caseq): properly plumb an error rather than returning a bogus code.
state->RegisterError(Error::MESSAGE_MUST_BE_AN_OBJECT);
return false;
}
*value = std::move(res);
return true;
}
static void Serialize(const std::unique_ptr<T>& value, std::vector<uint8_t>* bytes) {
// Use virtual method, so that outgoing protocol objects could be retained
// by a pointer to ProtocolObject.
value->AppendSerialized(bytes);
}
};
template <typename T>
struct ProtocolTypeTraits<
T,
typename std::enable_if<
std::is_base_of<{{"::".join(config.imported.namespace)}}::Exported, T>::value>::type> {
static void Serialize(const T& value, std::vector<uint8_t>* bytes) {
// Use virtual method, so that outgoing protocol objects could be retained
// by a pointer to ProtocolObject.
value.AppendSerialized(bytes);
}
};
} // namespace {{config.crdtp.namespace}}
#endif // {{"_".join(config.protocol.namespace)}}_imported_imported_h
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
{% endfor %}
......
......@@ -26,8 +26,6 @@
namespace {{namespace}} {
{% endfor %}
namespace {{domain.domain}} {
// ------------- Forward and enum declarations.
{% for type in domain.types %}
{% if not protocol.generate_type(domain.domain, type.id) %}{% continue %}{% endif %}
{% if type.type == "object" %}
......@@ -40,6 +38,8 @@ using {{type.id}} = Object;
using {{type.id}} = {{protocol.resolve_type(type).type}};
{% endif %}
{% endfor %}
// ------------- Forward and enum declarations.
{% for type in domain.types %}
{% if not protocol.generate_type(domain.domain, type.id) %}{% continue %}{% endif %}
{% if "enum" in type %}
......@@ -71,11 +71,9 @@ namespace {{param.name | to_title_case}}Enum {
{% if not protocol.generate_type(domain.domain, type.id) %}{% continue %}{% endif %}
{% if not (type.type == "object") or not ("properties" in type) %}{% continue %}{% endif %}
class {{config.protocol.export_macro}} {{type.id}} : public Serializable{% if protocol.is_exported(domain.domain, type.id) %}, public API::{{type.id}}{% endif %}{
PROTOCOL_DISALLOW_COPY({{type.id}});
class {{config.protocol.export_macro}} {{type.id}} : public ::{{config.crdtp.namespace}}::ProtocolObject<{{type.id}}>{% if protocol.is_exported(domain.domain, type.id) %},
public API::{{type.id}}{% endif %} {
public:
static std::unique_ptr<{{type.id}}> fromValue(protocol::Value* value, ErrorSupport* errors);
~{{type.id}}() override { }
{% for property in type.properties %}
{% set property_type = protocol.resolve_type(property) %}
......@@ -99,10 +97,6 @@ public:
void {{"set" | to_method_case}}{{property_name}}({{property_type.pass_type}} value) { {{property_field}} = {{property_type.to_rvalue % "value"}}; }
{% endfor %}
std::unique_ptr<protocol::DictionaryValue> toValue() const;
void AppendSerialized(std::vector<uint8_t>* out) const override;
std::unique_ptr<{{type.id}}> clone() const;
template<int STATE>
class {{type.id}}Builder {
public:
......@@ -160,6 +154,8 @@ public:
}
private:
DECLARE_SERIALIZATION_SUPPORT();
{{type.id}}()
{
{% for property in type.properties %}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment