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 { ...@@ -247,8 +247,7 @@ class InjectedScript::ProtocolPromiseHandler {
// we try to capture a fresh stack trace. // we try to capture a fresh stack trace.
if (maybeMessage.ToLocal(&message)) { if (maybeMessage.ToLocal(&message)) {
v8::Local<v8::Value> exception = result; v8::Local<v8::Value> exception = result;
protocol::detail::PtrMaybe<protocol::Runtime::ExceptionDetails> protocol::PtrMaybe<protocol::Runtime::ExceptionDetails> exceptionDetails;
exceptionDetails;
response = scope.injectedScript()->createExceptionDetails( response = scope.injectedScript()->createExceptionDetails(
message, exception, m_objectGroup, &exceptionDetails); message, exception, m_objectGroup, &exceptionDetails);
if (!response.IsSuccess()) { if (!response.IsSuccess()) {
......
...@@ -246,7 +246,66 @@ String16 stackTraceIdToString(uintptr_t id) { ...@@ -246,7 +246,66 @@ String16 stackTraceIdToString(uintptr_t id) {
} // namespace v8_inspector } // namespace v8_inspector
namespace v8_crdtp { 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) { const v8_inspector::protocol::Binary& binary, std::vector<uint8_t>* out) {
cbor::EncodeBinary(span<uint8_t>(binary.data(), binary.size()), out); cbor::EncodeBinary(span<uint8_t>(binary.data(), binary.size()), out);
} }
......
...@@ -6,14 +6,15 @@ ...@@ -6,14 +6,15 @@
#define V8_INSPECTOR_STRING_UTIL_H_ #define V8_INSPECTOR_STRING_UTIL_H_
#include <stdint.h> #include <stdint.h>
#include <memory> #include <memory>
#include "../../third_party/inspector_protocol/crdtp/protocol_core.h"
#include "include/v8-inspector.h"
#include "src/base/logging.h" #include "src/base/logging.h"
#include "src/base/macros.h" #include "src/base/macros.h"
#include "src/inspector/string-16.h" #include "src/inspector/string-16.h"
#include "include/v8-inspector.h"
namespace v8_inspector { namespace v8_inspector {
namespace protocol { namespace protocol {
...@@ -86,11 +87,42 @@ String16 stackTraceIdToString(uintptr_t id); ...@@ -86,11 +87,42 @@ String16 stackTraceIdToString(uintptr_t id);
// See third_party/inspector_protocol/crdtp/serializer_traits.h. // See third_party/inspector_protocol/crdtp/serializer_traits.h.
namespace v8_crdtp { 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 <> template <>
struct SerializerTraits<v8_inspector::protocol::Binary> { struct SerializerTraits<v8_inspector::protocol::Binary> {
static void Serialize(const v8_inspector::protocol::Binary& binary, static void Serialize(const v8_inspector::protocol::Binary& binary,
std::vector<uint8_t>* out); 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 } // namespace v8_crdtp
#endif // V8_INSPECTOR_STRING_UTIL_H_ #endif // V8_INSPECTOR_STRING_UTIL_H_
...@@ -1552,10 +1552,9 @@ void V8DebuggerAgentImpl::didParseSource( ...@@ -1552,10 +1552,9 @@ void V8DebuggerAgentImpl::didParseSource(
String16 scriptId = script->scriptId(); String16 scriptId = script->scriptId();
String16 scriptURL = script->sourceURL(); String16 scriptURL = script->sourceURL();
String16 scriptLanguage = getScriptLanguage(*script); String16 scriptLanguage = getScriptLanguage(*script);
Maybe<int> codeOffset = Maybe<int> codeOffset;
script->getLanguage() == V8DebuggerScript::Language::JavaScript if (script->getLanguage() == V8DebuggerScript::Language::WebAssembly)
? Maybe<int>() codeOffset = script->codeOffset();
: script->codeOffset();
std::unique_ptr<protocol::Debugger::DebugSymbols> debugSymbols = std::unique_ptr<protocol::Debugger::DebugSymbols> debugSymbols =
getDebugSymbols(*script); getDebugSymbols(*script);
...@@ -1727,10 +1726,11 @@ void V8DebuggerAgentImpl::didPause( ...@@ -1727,10 +1726,11 @@ void V8DebuggerAgentImpl::didPause(
WrapMode::kNoPreview, &obj); WrapMode::kNoPreview, &obj);
std::unique_ptr<protocol::DictionaryValue> breakAuxData; std::unique_ptr<protocol::DictionaryValue> breakAuxData;
if (obj) { 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); breakAuxData->setBoolean("uncaught", isUncaught);
} else {
breakAuxData = nullptr;
} }
hitReasons.push_back( hitReasons.push_back(
std::make_pair(breakReason, std::move(breakAuxData))); std::make_pair(breakReason, std::move(breakAuxData)));
......
...@@ -108,14 +108,14 @@ Running test: testConsoleLog ...@@ -108,14 +108,14 @@ Running test: testConsoleLog
functionName : eval functionName : eval
lineNumber : 0 lineNumber : 0
scriptId : <scriptId> scriptId : <scriptId>
url : url :
} }
[1] : { [1] : {
columnNumber : 0 columnNumber : 0
functionName : functionName :
lineNumber : 0 lineNumber : 0
scriptId : <scriptId> scriptId : <scriptId>
url : url :
} }
] ]
} }
...@@ -232,7 +232,7 @@ ReleaseObject with invalid params. ...@@ -232,7 +232,7 @@ ReleaseObject with invalid params.
{ {
error : { error : {
code : -32602 code : -32602
data : objectId: string value expected data : Failed to deserialize params.objectId - BINDINGS: mandatory field missing at <some position>
message : Invalid parameters message : Invalid parameters
} }
id : <messageId> id : <messageId>
...@@ -299,7 +299,7 @@ ReleaseObjectGroup with invalid params ...@@ -299,7 +299,7 @@ ReleaseObjectGroup with invalid params
{ {
error : { error : {
code : -32602 code : -32602
data : objectGroup: string value expected data : Failed to deserialize params.objectGroup - BINDINGS: mandatory field missing at <some position>
message : Invalid parameters message : Invalid parameters
} }
id : <messageId> id : <messageId>
...@@ -327,7 +327,7 @@ Running test: testCallFrameIdTypeError ...@@ -327,7 +327,7 @@ Running test: testCallFrameIdTypeError
{ {
error : { error : {
code : -32602 code : -32602
data : callFrameId: string value expected data : Failed to deserialize params.callFrameId - BINDINGS: string value expected at <some position>
message : Invalid parameters message : Invalid parameters
} }
id : <messageId> id : <messageId>
...@@ -347,7 +347,7 @@ Running test: testNullExpression ...@@ -347,7 +347,7 @@ Running test: testNullExpression
{ {
error : { error : {
code : -32602 code : -32602
data : expression: string value expected data : Failed to deserialize params.expression - BINDINGS: string value expected at <some position>
message : Invalid parameters message : Invalid parameters
} }
id : <messageId> id : <messageId>
......
...@@ -66,7 +66,7 @@ const {Protocol} = InspectorTest.start( ...@@ -66,7 +66,7 @@ const {Protocol} = InspectorTest.start(
async function testReleaseObjectInvalid() { async function testReleaseObjectInvalid() {
const releaseObjectResult = await Protocol.Runtime.releaseObject({}); const releaseObjectResult = await Protocol.Runtime.releaseObject({});
InspectorTest.log('ReleaseObject with invalid params.'); InspectorTest.log('ReleaseObject with invalid params.');
InspectorTest.logMessage(releaseObjectResult); InspectorTest.logMessage(InspectorTest.trimErrorMessage(releaseObjectResult));
}, },
async function testObjectGroups() { async function testObjectGroups() {
await Protocol.Runtime.evaluate({ expression: 'var a = {x:3};', callFrameId }); await Protocol.Runtime.evaluate({ expression: 'var a = {x:3};', callFrameId });
...@@ -89,7 +89,7 @@ const {Protocol} = InspectorTest.start( ...@@ -89,7 +89,7 @@ const {Protocol} = InspectorTest.start(
async function testReleaseObjectGroupInvalid() { async function testReleaseObjectGroupInvalid() {
const releaseObjectGroupResult = await Protocol.Runtime.releaseObjectGroup({}); const releaseObjectGroupResult = await Protocol.Runtime.releaseObjectGroup({});
InspectorTest.log('ReleaseObjectGroup with invalid params'); InspectorTest.log('ReleaseObjectGroup with invalid params');
InspectorTest.logMessage(releaseObjectGroupResult); InspectorTest.logMessage(InspectorTest.trimErrorMessage(releaseObjectGroupResult));
}, },
async function testEvaluateSyntaxError() { async function testEvaluateSyntaxError() {
const result = await Protocol.Debugger.evaluateOnCallFrame({ expression: `[]]`, callFrameId }); const result = await Protocol.Debugger.evaluateOnCallFrame({ expression: `[]]`, callFrameId });
...@@ -101,7 +101,7 @@ const {Protocol} = InspectorTest.start( ...@@ -101,7 +101,7 @@ const {Protocol} = InspectorTest.start(
}, },
async function testCallFrameIdTypeError() { async function testCallFrameIdTypeError() {
const result = await Protocol.Debugger.evaluateOnCallFrame({ expression: `console.log(42)`, callFrameId: {} }); const result = await Protocol.Debugger.evaluateOnCallFrame({ expression: `console.log(42)`, callFrameId: {} });
InspectorTest.logMessage(result); InspectorTest.logMessage(InspectorTest.trimErrorMessage(result));
}, },
async function testCallFrameIdInvalidInput() { async function testCallFrameIdInvalidInput() {
InspectorTest.log('Testing evaluateOnCallFrame with non-existent callFrameId'); InspectorTest.log('Testing evaluateOnCallFrame with non-existent callFrameId');
...@@ -115,7 +115,7 @@ const {Protocol} = InspectorTest.start( ...@@ -115,7 +115,7 @@ const {Protocol} = InspectorTest.start(
async function evalAndLog(expression, callFrameId, returnByValue) { async function evalAndLog(expression, callFrameId, returnByValue) {
const result = await Protocol.Debugger.evaluateOnCallFrame({ 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 // Helper function that calls a function on all objects with ids in objectIds, then returns
......
...@@ -3,5 +3,5 @@ setBreakpointByUrl error: undefined ...@@ -3,5 +3,5 @@ setBreakpointByUrl error: undefined
setBreakpoint error: { setBreakpoint error: {
"code": -32602, "code": -32602,
"message": "Invalid parameters", "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 ...@@ -8,12 +8,14 @@ Protocol.Debugger.setBreakpointByUrl({ url: "http://example.com", lineNumber: 10
function didSetBreakpointByUrlBeforeEnable(message) 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); Protocol.Debugger.setBreakpoint().then(didSetBreakpointBeforeEnable);
} }
function didSetBreakpointBeforeEnable(message) 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(); InspectorTest.completeTest();
} }
...@@ -218,7 +218,7 @@ setVariableValue with invalid scopeNumber ...@@ -218,7 +218,7 @@ setVariableValue with invalid scopeNumber
{ {
error : { error : {
code : -32602 code : -32602
data : scopeNumber: integer value expected data : Failed to deserialize params.scopeNumber - BINDINGS: int32 value expected at <some position>
message : Invalid parameters message : Invalid parameters
} }
id : <messageId> id : <messageId>
...@@ -253,8 +253,8 @@ setVariableValue with invalid objectId ...@@ -253,8 +253,8 @@ setVariableValue with invalid objectId
{ {
error : { error : {
code : -32602 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 message : Invalid parameters
} }
id : <messageId> id : <messageId>
} }
\ No newline at end of file
...@@ -66,24 +66,24 @@ const { contextGroup, Protocol } = InspectorTest.start( ...@@ -66,24 +66,24 @@ const { contextGroup, Protocol } = InspectorTest.start(
async function testInvalidFrame() { async function testInvalidFrame() {
let result = await Protocol.Debugger.setVariableValue({ scopeNumber: 0, variableName: 'num', newValue: { unserializableValue: 'NaN' }, callFrameId: 'fakeCallFrame' }); let result = await Protocol.Debugger.setVariableValue({ scopeNumber: 0, variableName: 'num', newValue: { unserializableValue: 'NaN' }, callFrameId: 'fakeCallFrame' });
InspectorTest.log('setVariableValue with invalid callFrameId'); 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 }); result = await Protocol.Debugger.setVariableValue({ scopeNumber: 'invalidScopeType', variableName: 'num', newValue: { unserializableValue: 'NaN' }, callFrameId });
InspectorTest.log('setVariableValue with invalid scopeNumber') 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 }); result = await Protocol.Debugger.setVariableValue({ scopeNumber: 1000, variableName: 'num', newValue: { unserializableValue: 'NaN' }, callFrameId });
InspectorTest.log('setVariableValue with invalid scopeNumber'); InspectorTest.log('setVariableValue with invalid scopeNumber');
InspectorTest.logMessage(result); InspectorTest.logMessage(result);
result = await Protocol.Debugger.setVariableValue({ scopeNumber: 0, variableName: 'FakeObjectName', newValue: { unserializableValue: 'NaN' }, callFrameId }); result = await Protocol.Debugger.setVariableValue({ scopeNumber: 0, variableName: 'FakeObjectName', newValue: { unserializableValue: 'NaN' }, callFrameId });
InspectorTest.log('setVariableValue with invalid variableName'); InspectorTest.log('setVariableValue with invalid variableName');
InspectorTest.logMessage(result); InspectorTest.logMessage(InspectorTest.trimErrorMessage(result));
}, },
async function testNewValueErrors() { async function testNewValueErrors() {
let result = await Protocol.Debugger.setVariableValue({ scopeNumber: 0, variableName: 'num', newValue: { unserializableValue: 'not unserializable value' }, callFrameId }); let result = await Protocol.Debugger.setVariableValue({ scopeNumber: 0, variableName: 'num', newValue: { unserializableValue: 'not unserializable value' }, callFrameId });
InspectorTest.log('setVariableValue with invalid unserializableValue'); 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 }); result = await Protocol.Debugger.setVariableValue({ scopeNumber: 0, variableName: 'num', newValue: { objectId: 2000 }, callFrameId });
InspectorTest.log('setVariableValue with invalid objectId'); InspectorTest.log('setVariableValue with invalid objectId');
InspectorTest.logMessage(result); InspectorTest.logMessage(InspectorTest.trimErrorMessage(result));
} }
]); ]);
......
...@@ -129,6 +129,14 @@ InspectorTest.decodeBase64 = function(base64) { ...@@ -129,6 +129,14 @@ InspectorTest.decodeBase64 = function(base64) {
return bytes; 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 { InspectorTest.ContextGroup = class {
constructor() { constructor() {
this.id = utils.createContextGroup(); this.id = utils.createContextGroup();
......
...@@ -71,7 +71,7 @@ ReleaseObject with invalid params. ...@@ -71,7 +71,7 @@ ReleaseObject with invalid params.
{ {
error : { error : {
code : -32602 code : -32602
data : objectId: string value expected data : Failed to deserialize params.objectId - BINDINGS: mandatory field missing at <some position>
message : Invalid parameters message : Invalid parameters
} }
id : <messageId> id : <messageId>
...@@ -150,8 +150,8 @@ ReleaseObjectGroup with invalid params ...@@ -150,8 +150,8 @@ ReleaseObjectGroup with invalid params
{ {
error : { error : {
code : -32602 code : -32602
data : objectGroup: string value expected data : Failed to deserialize params.objectGroup - BINDINGS: mandatory field missing at <some position>
message : Invalid parameters message : Invalid parameters
} }
id : <messageId> id : <messageId>
} }
\ No newline at end of file
...@@ -31,7 +31,7 @@ const {Protocol} = InspectorTest.start( ...@@ -31,7 +31,7 @@ const {Protocol} = InspectorTest.start(
async function testReleaseObjectInvalid() { async function testReleaseObjectInvalid() {
const releaseObjectResult = await Protocol.Runtime.releaseObject({}); const releaseObjectResult = await Protocol.Runtime.releaseObject({});
InspectorTest.log('ReleaseObject with invalid params.'); InspectorTest.log('ReleaseObject with invalid params.');
InspectorTest.logMessage(releaseObjectResult); InspectorTest.logMessage(InspectorTest.trimErrorMessage(releaseObjectResult));
}, },
async function testObjectGroups() { async function testObjectGroups() {
await logAndEvaluate('var a = {x:3};'); await logAndEvaluate('var a = {x:3};');
...@@ -58,7 +58,7 @@ const {Protocol} = InspectorTest.start( ...@@ -58,7 +58,7 @@ const {Protocol} = InspectorTest.start(
async function testReleaseObjectGroupInvalid() { async function testReleaseObjectGroupInvalid() {
const releaseObjectGroupResult = await Protocol.Runtime.releaseObjectGroup({}); const releaseObjectGroupResult = await Protocol.Runtime.releaseObjectGroup({});
InspectorTest.log('ReleaseObjectGroup with invalid params'); InspectorTest.log('ReleaseObjectGroup with invalid params');
InspectorTest.logMessage(releaseObjectGroupResult); InspectorTest.logMessage(InspectorTest.trimErrorMessage(releaseObjectGroupResult));
} }
]); ]);
......
...@@ -20,10 +20,12 @@ v8_source_set("crdtp") { ...@@ -20,10 +20,12 @@ v8_source_set("crdtp") {
"crdtp/error_support.h", "crdtp/error_support.h",
"crdtp/export.h", "crdtp/export.h",
"crdtp/find_by_first.h", "crdtp/find_by_first.h",
"crdtp/glue.h",
"crdtp/json.cc", "crdtp/json.cc",
"crdtp/json.h", "crdtp/json.h",
"crdtp/maybe.h",
"crdtp/parser_handler.h", "crdtp/parser_handler.h",
"crdtp/protocol_core.cc",
"crdtp/protocol_core.h",
"crdtp/serializable.cc", "crdtp/serializable.cc",
"crdtp/serializable.h", "crdtp/serializable.h",
"crdtp/serializer_traits.h", "crdtp/serializer_traits.h",
......
...@@ -2,7 +2,7 @@ Name: inspector protocol ...@@ -2,7 +2,7 @@ Name: inspector protocol
Short Name: inspector_protocol Short Name: inspector_protocol
URL: https://chromium.googlesource.com/deps/inspector_protocol/ URL: https://chromium.googlesource.com/deps/inspector_protocol/
Version: 0 Version: 0
Revision: b7cda08cd6e522df2159413ba5f29d2a953cc1c4 Revision: 22455bf37cd7a1913918e626a1997cbe77f32d1a
License: BSD License: BSD
License File: LICENSE License File: LICENSE
Security Critical: no Security Critical: no
......
...@@ -371,7 +371,6 @@ class Protocol(object): ...@@ -371,7 +371,6 @@ class Protocol(object):
for rule in config.imported.options] for rule in config.imported.options]
self.patch_full_qualified_refs() self.patch_full_qualified_refs()
self.create_notification_types()
self.create_type_definitions() self.create_type_definitions()
self.generate_used_types() self.generate_used_types()
...@@ -434,8 +433,6 @@ class Protocol(object): ...@@ -434,8 +433,6 @@ class Protocol(object):
for event in domain["events"]: for event in domain["events"]:
if self.generate_event(domain_name, event["name"]): if self.generate_event(domain_name, event["name"]):
all_refs |= self.all_references(event) all_refs |= self.all_references(event)
all_refs.add('%s.%sNotification' % (domain_name,
to_title_case(event["name"])))
dependencies = self.generate_type_dependencies() dependencies = self.generate_type_dependencies()
queue = set(all_refs) queue = set(all_refs)
...@@ -457,20 +454,6 @@ class Protocol(object): ...@@ -457,20 +454,6 @@ class Protocol(object):
dependencies[domain_name + "." + type["id"]] = related_types dependencies[domain_name + "." + type["id"]] = related_types
return dependencies 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): def create_type_definitions(self):
imported_namespace = "" imported_namespace = ""
if self.config.imported: if self.config.imported:
...@@ -664,6 +647,7 @@ def main(): ...@@ -664,6 +647,7 @@ def main():
"Protocol_cpp.template", "Protocol_cpp.template",
"Values_cpp.template", "Values_cpp.template",
"Object_cpp.template", "Object_cpp.template",
"ValueConversions_cpp.template",
] ]
forward_h_templates = [ forward_h_templates = [
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "error_support.h" #include "error_support.h"
#include "find_by_first.h" #include "find_by_first.h"
#include "frontend_channel.h" #include "frontend_channel.h"
#include "protocol_core.h"
namespace v8_crdtp { namespace v8_crdtp {
// ============================================================================= // =============================================================================
...@@ -321,6 +322,18 @@ std::unique_ptr<Serializable> CreateErrorResponse( ...@@ -321,6 +322,18 @@ std::unique_ptr<Serializable> CreateErrorResponse(
return protocol_error; 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( std::unique_ptr<Serializable> CreateErrorNotification(
DispatchResponse dispatch_response) { DispatchResponse dispatch_response) {
return std::make_unique<ProtocolError>(std::move(dispatch_response)); return std::make_unique<ProtocolError>(std::move(dispatch_response));
...@@ -475,6 +488,21 @@ bool DomainDispatcher::MaybeReportInvalidParams( ...@@ -475,6 +488,21 @@ bool DomainDispatcher::MaybeReportInvalidParams(
return true; 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() { void DomainDispatcher::clearFrontend() {
frontend_channel_ = nullptr; frontend_channel_ = nullptr;
for (auto& weak : weak_ptrs_) for (auto& weak : weak_ptrs_)
......
...@@ -16,8 +16,9 @@ ...@@ -16,8 +16,9 @@
#include "status.h" #include "status.h"
namespace v8_crdtp { namespace v8_crdtp {
class FrontendChannel; class DeserializerState;
class ErrorSupport; class ErrorSupport;
class FrontendChannel;
namespace cbor { namespace cbor {
class CBORTokenizer; class CBORTokenizer;
} // namespace cbor } // namespace cbor
...@@ -234,6 +235,8 @@ class DomainDispatcher { ...@@ -234,6 +235,8 @@ class DomainDispatcher {
// optimized for code size of the callee. // optimized for code size of the callee.
bool MaybeReportInvalidParams(const Dispatchable& dispatchable, bool MaybeReportInvalidParams(const Dispatchable& dispatchable,
const ErrorSupport& errors); const ErrorSupport& errors);
bool MaybeReportInvalidParams(const Dispatchable& dispatchable,
const DeserializerState& state);
FrontendChannel* channel() { return frontend_channel_; } 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 @@ ...@@ -9,7 +9,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "cbor.h" #include "cbor.h"
#include "glue.h" #include "maybe.h"
#include "span.h" #include "span.h"
namespace v8_crdtp { namespace v8_crdtp {
...@@ -124,9 +124,9 @@ struct FieldSerializerTraits { ...@@ -124,9 +124,9 @@ struct FieldSerializerTraits {
}; };
template <typename T> template <typename T>
struct FieldSerializerTraits<glue::detail::PtrMaybe<T>> { struct FieldSerializerTraits<detail::PtrMaybe<T>> {
static void Serialize(span<uint8_t> field_name, 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) { std::vector<uint8_t>* out) {
if (!field_value.isJust()) if (!field_value.isJust())
return; return;
...@@ -136,9 +136,9 @@ struct FieldSerializerTraits<glue::detail::PtrMaybe<T>> { ...@@ -136,9 +136,9 @@ struct FieldSerializerTraits<glue::detail::PtrMaybe<T>> {
}; };
template <typename T> template <typename T>
struct FieldSerializerTraits<glue::detail::ValueMaybe<T>> { struct FieldSerializerTraits<detail::ValueMaybe<T>> {
static void Serialize(span<uint8_t> field_name, 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) { std::vector<uint8_t>* out) {
if (!field_value.isJust()) if (!field_value.isJust())
return; return;
......
...@@ -172,12 +172,12 @@ TEST(FieldSerializerTraits, RequiredField) { ...@@ -172,12 +172,12 @@ TEST(FieldSerializerTraits, RequiredField) {
template <typename T> template <typename T>
class FieldSerializerTraits_MaybeTest : public ::testing::Test {}; class FieldSerializerTraits_MaybeTest : public ::testing::Test {};
using MaybeTypes = using MaybeTypes =
::testing::Types<glue::detail::ValueMaybe<bool>, ::testing::Types<detail::ValueMaybe<bool>,
glue::detail::ValueMaybe<double>, detail::ValueMaybe<double>,
glue::detail::ValueMaybe<int32_t>, detail::ValueMaybe<int32_t>,
glue::detail::ValueMaybe<std::string>, detail::ValueMaybe<std::string>,
glue::detail::PtrMaybe<Foo>, detail::PtrMaybe<Foo>,
glue::detail::PtrMaybe<std::vector<std::unique_ptr<Foo>>>>; detail::PtrMaybe<std::vector<std::unique_ptr<Foo>>>>;
TYPED_TEST_SUITE(FieldSerializerTraits_MaybeTest, MaybeTypes); TYPED_TEST_SUITE(FieldSerializerTraits_MaybeTest, MaybeTypes);
TYPED_TEST(FieldSerializerTraits_MaybeTest, NoOutputForFieldsIfNotJust) { TYPED_TEST(FieldSerializerTraits_MaybeTest, NoOutputForFieldsIfNotJust) {
...@@ -188,9 +188,8 @@ TYPED_TEST(FieldSerializerTraits_MaybeTest, NoOutputForFieldsIfNotJust) { ...@@ -188,9 +188,8 @@ TYPED_TEST(FieldSerializerTraits_MaybeTest, NoOutputForFieldsIfNotJust) {
TEST(FieldSerializerTraits, MaybeBool) { TEST(FieldSerializerTraits, MaybeBool) {
std::vector<uint8_t> out; std::vector<uint8_t> out;
SerializeField(SpanFrom("true"), glue::detail::ValueMaybe<bool>(true), &out); SerializeField(SpanFrom("true"), detail::ValueMaybe<bool>(true), &out);
SerializeField(SpanFrom("false"), glue::detail::ValueMaybe<bool>(false), SerializeField(SpanFrom("false"), detail::ValueMaybe<bool>(false), &out);
&out);
std::vector<uint8_t> expected; std::vector<uint8_t> expected;
cbor::EncodeString8(SpanFrom("true"), &expected); cbor::EncodeString8(SpanFrom("true"), &expected);
...@@ -203,7 +202,7 @@ TEST(FieldSerializerTraits, MaybeBool) { ...@@ -203,7 +202,7 @@ TEST(FieldSerializerTraits, MaybeBool) {
TEST(FieldSerializerTraits, MaybeDouble) { TEST(FieldSerializerTraits, MaybeDouble) {
std::vector<uint8_t> out; 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; std::vector<uint8_t> expected;
cbor::EncodeString8(SpanFrom("dbl"), &expected); cbor::EncodeString8(SpanFrom("dbl"), &expected);
...@@ -215,7 +214,7 @@ TEST(FieldSerializerTraits, MaybeDouble) { ...@@ -215,7 +214,7 @@ TEST(FieldSerializerTraits, MaybeDouble) {
TEST(FieldSerializerTraits, MaybePtrFoo) { TEST(FieldSerializerTraits, MaybePtrFoo) {
std::vector<uint8_t> out; std::vector<uint8_t> out;
SerializeField(SpanFrom("foo"), 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; std::vector<uint8_t> expected;
cbor::EncodeString8(SpanFrom("foo"), &expected); cbor::EncodeString8(SpanFrom("foo"), &expected);
......
...@@ -21,4 +21,19 @@ bool SpanEquals(span<uint8_t> x, span<uint8_t> y) noexcept { ...@@ -21,4 +21,19 @@ bool SpanEquals(span<uint8_t> x, span<uint8_t> y) noexcept {
return x.data() == y.data() || len == 0 || return x.data() == y.data() || len == 0 ||
std::memcmp(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 } // namespace v8_crdtp
...@@ -50,6 +50,11 @@ class span { ...@@ -50,6 +50,11 @@ class span {
index_type size_; 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> template <size_t N>
constexpr span<uint8_t> SpanFrom(const char (&str)[N]) { constexpr span<uint8_t> SpanFrom(const char (&str)[N]) {
return span<uint8_t>(reinterpret_cast<const uint8_t*>(str), N - 1); 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) { ...@@ -75,11 +80,15 @@ inline span<typename C::value_type> SpanFrom(const C& v) {
} }
// Less than / equality comparison functions for sorting / searching for byte // 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 SpanLessThan(span<uint8_t> x, span<uint8_t> y) noexcept;
bool SpanEquals(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 { struct SpanLt {
bool operator()(span<uint8_t> l, span<uint8_t> r) const { bool operator()(span<uint8_t> l, span<uint8_t> r) const {
return SpanLessThan(l, r); return SpanLessThan(l, r);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef V8_CRDTP_STATUS_H_ #ifndef V8_CRDTP_STATUS_H_
#define V8_CRDTP_STATUS_H_ #define V8_CRDTP_STATUS_H_
#include <cassert>
#include <cstddef> #include <cstddef>
#include <limits> #include <limits>
#include <string> #include <string>
...@@ -103,6 +104,35 @@ struct Status { ...@@ -103,6 +104,35 @@ struct Status {
// includes the position. // includes the position.
std::string ToASCIIString() const; 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 } // namespace v8_crdtp
#endif // V8_CRDTP_STATUS_H_ #endif // V8_CRDTP_STATUS_H_
...@@ -37,6 +37,7 @@ template("inspector_protocol_generate") { ...@@ -37,6 +37,7 @@ template("inspector_protocol_generate") {
"$inspector_protocol_dir/lib/Object_cpp.template", "$inspector_protocol_dir/lib/Object_cpp.template",
"$inspector_protocol_dir/lib/Object_h.template", "$inspector_protocol_dir/lib/Object_h.template",
"$inspector_protocol_dir/lib/Protocol_cpp.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/ValueConversions_h.template",
"$inspector_protocol_dir/lib/Values_cpp.template", "$inspector_protocol_dir/lib/Values_cpp.template",
"$inspector_protocol_dir/lib/Values_h.template", "$inspector_protocol_dir/lib/Values_h.template",
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
#include "{{config.crdtp.dir}}/error_support.h" #include "{{config.crdtp.dir}}/error_support.h"
#include "{{config.crdtp.dir}}/dispatch.h" #include "{{config.crdtp.dir}}/dispatch.h"
#include "{{config.crdtp.dir}}/frontend_channel.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 %} {% for namespace in config.protocol.namespace %}
namespace {{namespace}} { namespace {{namespace}} {
...@@ -42,7 +42,14 @@ class SerializedValue; ...@@ -42,7 +42,14 @@ class SerializedValue;
class StringValue; class StringValue;
class Value; 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 { namespace detail {
template <typename T> template <typename T>
struct ArrayTypedef { typedef std::vector<std::unique_ptr<T>> type; }; struct ArrayTypedef { typedef std::vector<std::unique_ptr<T>> type; };
...@@ -62,32 +69,6 @@ struct ArrayTypedef<bool> { typedef std::vector<bool> type; }; ...@@ -62,32 +69,6 @@ struct ArrayTypedef<bool> { typedef std::vector<bool> type; };
template <typename T> template <typename T>
using Array = typename detail::ArrayTypedef<T>::type; 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 %} {% for namespace in config.protocol.namespace %}
} // namespace {{namespace}} } // namespace {{namespace}}
{% endfor %} {% endfor %}
......
...@@ -28,7 +28,11 @@ public: ...@@ -28,7 +28,11 @@ public:
std::unique_ptr<protocol::DictionaryValue> toValue() const; std::unique_ptr<protocol::DictionaryValue> toValue() const;
std::unique_ptr<Object> clone() const; std::unique_ptr<Object> clone() const;
private: private:
Object() = default;
friend struct {{config.crdtp.namespace}}::ProtocolTypeTraits<std::unique_ptr<Object>, void>;
std::unique_ptr<protocol::DictionaryValue> m_object; 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> { ...@@ -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 %} {% for namespace in config.protocol.namespace %}
} // namespace {{namespace}} } // namespace {{namespace}}
{% endfor %} {% 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) #endif // !defined({{"_".join(config.protocol.namespace)}}_ValueConversions_h)
...@@ -62,7 +62,7 @@ class ValueParserHandler : public ParserHandler { ...@@ -62,7 +62,7 @@ class ValueParserHandler : public ParserHandler {
DCHECK(stack_.back().is_dict); DCHECK(stack_.back().is_dict);
stack_.pop_back(); stack_.pop_back();
} }
void HandleArrayBegin() override { void HandleArrayBegin() override {
if (!status_.ok()) return; if (!status_.ok()) return;
std::unique_ptr<ListValue> list = ListValue::create(); std::unique_ptr<ListValue> list = ListValue::create();
...@@ -91,19 +91,19 @@ class ValueParserHandler : public ParserHandler { ...@@ -91,19 +91,19 @@ class ValueParserHandler : public ParserHandler {
AddValueToParent( AddValueToParent(
BinaryValue::create(Binary::fromSpan(bytes.data(), bytes.size()))); BinaryValue::create(Binary::fromSpan(bytes.data(), bytes.size())));
} }
void HandleDouble(double value) override { void HandleDouble(double value) override {
AddValueToParent(FundamentalValue::create(value)); AddValueToParent(FundamentalValue::create(value));
} }
void HandleInt32(int32_t value) override { void HandleInt32(int32_t value) override {
AddValueToParent(FundamentalValue::create(value)); AddValueToParent(FundamentalValue::create(value));
} }
void HandleBool(bool value) override { void HandleBool(bool value) override {
AddValueToParent(FundamentalValue::create(value)); AddValueToParent(FundamentalValue::create(value));
} }
void HandleNull() override { void HandleNull() override {
AddValueToParent(Value::null()); AddValueToParent(Value::null());
} }
...@@ -318,7 +318,6 @@ void EncodeString(const String& s, std::vector<uint8_t>* out) { ...@@ -318,7 +318,6 @@ void EncodeString(const String& s, std::vector<uint8_t>* out) {
} }
} }
} // namespace } // namespace
void StringValue::AppendSerialized(std::vector<uint8_t>* bytes) const { void StringValue::AppendSerialized(std::vector<uint8_t>* bytes) const {
EncodeString(m_stringValue, bytes); EncodeString(m_stringValue, bytes);
} }
......
...@@ -20,6 +20,11 @@ class ListValue; ...@@ -20,6 +20,11 @@ class ListValue;
class DictionaryValue; class DictionaryValue;
class Value; 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 { class {{config.lib.export_macro}} Value : public Serializable {
PROTOCOL_DISALLOW_COPY(Value); PROTOCOL_DISALLOW_COPY(Value);
public: public:
......
...@@ -16,6 +16,13 @@ ...@@ -16,6 +16,13 @@
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/values.h" #include "base/values.h"
#include "{{config.crdtp.dir}}/cbor.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 %} {% for namespace in config.protocol.namespace %}
namespace {{namespace}} { namespace {{namespace}} {
...@@ -138,6 +145,28 @@ String StringUtil::fromUTF16LE(const uint16_t* data, size_t length) { ...@@ -138,6 +145,28 @@ String StringUtil::fromUTF16LE(const uint16_t* data, size_t length) {
return utf8; 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() : bytes_(new base::RefCountedBytes) {}
Binary::Binary(const Binary& binary) : bytes_(binary.bytes_) {} Binary::Binary(const Binary& binary) : bytes_(binary.bytes_) {}
Binary::Binary(scoped_refptr<base::RefCountedMemory> bytes) : bytes_(bytes) {} Binary::Binary(scoped_refptr<base::RefCountedMemory> bytes) : bytes_(bytes) {}
...@@ -190,3 +219,31 @@ Binary Binary::fromSpan(const uint8_t* data, size_t size) { ...@@ -190,3 +219,31 @@ Binary Binary::fromSpan(const uint8_t* data, size_t size) {
{% for namespace in config.protocol.namespace %} {% for namespace in config.protocol.namespace %}
} // namespace {{namespace}} } // namespace {{namespace}}
{% endfor %} {% 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 @@ ...@@ -15,6 +15,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted_memory.h" #include "base/memory/ref_counted_memory.h"
#include "{{config.crdtp.dir}}/serializable.h" #include "{{config.crdtp.dir}}/serializable.h"
#include "{{config.crdtp.dir}}/protocol_core.h"
{% if config.lib.export_header %} {% if config.lib.export_header %}
#include "{{config.lib.export_header}}" #include "{{config.lib.export_header}}"
...@@ -24,6 +25,10 @@ namespace base { ...@@ -24,6 +25,10 @@ namespace base {
class Value; class Value;
} }
namespace {{config.crdtp.namespace}} {
class DeserializerState;
}
{% for namespace in config.protocol.namespace %} {% for namespace in config.protocol.namespace %}
namespace {{namespace}} { namespace {{namespace}} {
{% endfor %} {% endfor %}
...@@ -46,6 +51,9 @@ class {{config.lib.export_macro}} StringUtil { ...@@ -46,6 +51,9 @@ class {{config.lib.export_macro}} StringUtil {
} }
static const uint16_t* CharactersUTF16(const String& s) { return nullptr; } static const uint16_t* CharactersUTF16(const String& s) { return nullptr; }
static size_t CharacterCount(const String& s) { return s.size(); } 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. // 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); ...@@ -81,4 +89,24 @@ std::unique_ptr<base::Value> toBaseValue(Value* value, int depth);
} // namespace {{namespace}} } // namespace {{namespace}}
{% endfor %} {% 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) #endif // !defined({{"_".join(config.protocol.namespace)}}_BASE_STRING_ADAPTER_H)
...@@ -31,13 +31,16 @@ FILES_TO_SYNC = [ ...@@ -31,13 +31,16 @@ FILES_TO_SYNC = [
'crdtp/find_by_first.h', 'crdtp/find_by_first.h',
'crdtp/find_by_first_test.cc', 'crdtp/find_by_first_test.cc',
'crdtp/frontend_channel.h', 'crdtp/frontend_channel.h',
'crdtp/glue.h', 'crdtp/maybe.h',
'crdtp/glue_test.cc', 'crdtp/maybe_test.cc',
'crdtp/json.cc', 'crdtp/json.cc',
'crdtp/json.h', 'crdtp/json.h',
'crdtp/json_platform.h', 'crdtp/json_platform.h',
'crdtp/json_test.cc', 'crdtp/json_test.cc',
'crdtp/parser_handler.h', 'crdtp/parser_handler.h',
'crdtp/protocol_core_test.cc',
'crdtp/protocol_core.cc',
'crdtp/protocol_core.h',
'crdtp/serializable.h', 'crdtp/serializable.h',
'crdtp/serializable.cc', 'crdtp/serializable.cc',
'crdtp/serializable_test.cc', 'crdtp/serializable_test.cc',
......
...@@ -14,6 +14,53 @@ ...@@ -14,6 +14,53 @@
#include {{format_include(config.imported.package, domain.domain)}} #include {{format_include(config.imported.package, domain.domain)}}
{% endif %} {% 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 %} {% for namespace in config.protocol.namespace %}
namespace {{namespace}} { namespace {{namespace}} {
{% endfor %} {% endfor %}
......
...@@ -26,8 +26,6 @@ ...@@ -26,8 +26,6 @@
namespace {{namespace}} { namespace {{namespace}} {
{% endfor %} {% endfor %}
namespace {{domain.domain}} { namespace {{domain.domain}} {
// ------------- Forward and enum declarations.
{% for type in domain.types %} {% for type in domain.types %}
{% if not protocol.generate_type(domain.domain, type.id) %}{% continue %}{% endif %} {% if not protocol.generate_type(domain.domain, type.id) %}{% continue %}{% endif %}
{% if type.type == "object" %} {% if type.type == "object" %}
...@@ -40,6 +38,8 @@ using {{type.id}} = Object; ...@@ -40,6 +38,8 @@ using {{type.id}} = Object;
using {{type.id}} = {{protocol.resolve_type(type).type}}; using {{type.id}} = {{protocol.resolve_type(type).type}};
{% endif %} {% endif %}
{% endfor %} {% endfor %}
// ------------- Forward and enum declarations.
{% for type in domain.types %} {% for type in domain.types %}
{% if not protocol.generate_type(domain.domain, type.id) %}{% continue %}{% endif %} {% if not protocol.generate_type(domain.domain, type.id) %}{% continue %}{% endif %}
{% if "enum" in type %} {% if "enum" in type %}
...@@ -71,11 +71,9 @@ namespace {{param.name | to_title_case}}Enum { ...@@ -71,11 +71,9 @@ namespace {{param.name | to_title_case}}Enum {
{% if not protocol.generate_type(domain.domain, type.id) %}{% continue %}{% endif %} {% if not protocol.generate_type(domain.domain, type.id) %}{% continue %}{% endif %}
{% if not (type.type == "object") or not ("properties" in type) %}{% 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 %}{ class {{config.protocol.export_macro}} {{type.id}} : public ::{{config.crdtp.namespace}}::ProtocolObject<{{type.id}}>{% if protocol.is_exported(domain.domain, type.id) %},
PROTOCOL_DISALLOW_COPY({{type.id}}); public API::{{type.id}}{% endif %} {
public: public:
static std::unique_ptr<{{type.id}}> fromValue(protocol::Value* value, ErrorSupport* errors);
~{{type.id}}() override { } ~{{type.id}}() override { }
{% for property in type.properties %} {% for property in type.properties %}
{% set property_type = protocol.resolve_type(property) %} {% set property_type = protocol.resolve_type(property) %}
...@@ -99,10 +97,6 @@ public: ...@@ -99,10 +97,6 @@ public:
void {{"set" | to_method_case}}{{property_name}}({{property_type.pass_type}} value) { {{property_field}} = {{property_type.to_rvalue % "value"}}; } void {{"set" | to_method_case}}{{property_name}}({{property_type.pass_type}} value) { {{property_field}} = {{property_type.to_rvalue % "value"}}; }
{% endfor %} {% 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> template<int STATE>
class {{type.id}}Builder { class {{type.id}}Builder {
public: public:
...@@ -160,6 +154,8 @@ public: ...@@ -160,6 +154,8 @@ public:
} }
private: private:
DECLARE_SERIALIZATION_SUPPORT();
{{type.id}}() {{type.id}}()
{ {
{% for property in type.properties %} {% 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