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/+/2277142 Reviewed-by: Yang Guo <yangguo@chromium.org> Commit-Queue: Andrey Kosyakov <caseq@chromium.org> Cr-Commit-Position: refs/heads/master@{#68657}
This commit is contained in:
parent
4769e1586b
commit
3573d5e0fa
@ -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>
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
]);
|
||||
|
||||
|
4
third_party/inspector_protocol/BUILD.gn
vendored
4
third_party/inspector_protocol/BUILD.gn
vendored
@ -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
third_party/inspector_protocol/README.v8
vendored
2
third_party/inspector_protocol/README.v8
vendored
@ -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
|
||||
|
18
third_party/inspector_protocol/code_generator.py
vendored
18
third_party/inspector_protocol/code_generator.py
vendored
@ -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 = [
|
||||
|
28
third_party/inspector_protocol/crdtp/dispatch.cc
vendored
28
third_party/inspector_protocol/crdtp/dispatch.cc
vendored
@ -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_; }
|
||||
|
||||
|
104
third_party/inspector_protocol/crdtp/maybe.h
vendored
Normal file
104
third_party/inspector_protocol/crdtp/maybe.h
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
// 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_
|
44
third_party/inspector_protocol/crdtp/maybe_test.cc
vendored
Normal file
44
third_party/inspector_protocol/crdtp/maybe_test.cc
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
// 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
|
288
third_party/inspector_protocol/crdtp/protocol_core.cc
vendored
Normal file
288
third_party/inspector_protocol/crdtp/protocol_core.cc
vendored
Normal file
@ -0,0 +1,288 @@
|
||||
// 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
|
406
third_party/inspector_protocol/crdtp/protocol_core.h
vendored
Normal file
406
third_party/inspector_protocol/crdtp/protocol_core.h
vendored
Normal file
@ -0,0 +1,406 @@
|
||||
// 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.
|
||||
|
||||
#ifndef V8_CRDTP_PROTOCOL_CORE_H_
|
||||
#define V8_CRDTP_PROTOCOL_CORE_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "cbor.h"
|
||||
#include "maybe.h"
|
||||
#include "serializable.h"
|
||||
#include "span.h"
|
||||
#include "status.h"
|
||||
|
||||
namespace v8_crdtp {
|
||||
|
||||
class DeserializerState {
|
||||
public:
|
||||
using Storage = std::shared_ptr<const std::vector<uint8_t>>;
|
||||
|
||||
// Creates a state from the raw bytes received from the peer.
|
||||
explicit DeserializerState(std::vector<uint8_t> bytes);
|
||||
// Creates the state from the part of another message.
|
||||
DeserializerState(Storage storage, span<uint8_t> span);
|
||||
DeserializerState(const DeserializerState& r) = delete;
|
||||
DeserializerState(DeserializerState&& r) = default;
|
||||
|
||||
// Registers |error|, unless the tokenizer's status is already an error.
|
||||
void RegisterError(Error error);
|
||||
// Registers |name| as a segment of the field path.
|
||||
void RegisterFieldPath(span<char> name);
|
||||
|
||||
// Produces an error message considering |tokenizer.Status()|,
|
||||
// status_, and field_path_.
|
||||
std::string ErrorMessage(span<char> message_name) const;
|
||||
Status status() const;
|
||||
const Storage& storage() const { return storage_; }
|
||||
cbor::CBORTokenizer* tokenizer() { return &tokenizer_; }
|
||||
|
||||
private:
|
||||
const Storage storage_;
|
||||
cbor::CBORTokenizer tokenizer_;
|
||||
Status status_;
|
||||
std::vector<span<char>> field_path_;
|
||||
};
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct ProtocolTypeTraits {};
|
||||
|
||||
template <>
|
||||
struct ProtocolTypeTraits<bool> {
|
||||
static bool Deserialize(DeserializerState* state, bool* value);
|
||||
static void Serialize(bool value, std::vector<uint8_t>* bytes);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ProtocolTypeTraits<int32_t> {
|
||||
static bool Deserialize(DeserializerState* state, int* value);
|
||||
static void Serialize(int value, std::vector<uint8_t>* bytes);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ProtocolTypeTraits<double> {
|
||||
static bool Deserialize(DeserializerState* state, double* value);
|
||||
static void Serialize(double value, std::vector<uint8_t>* bytes);
|
||||
};
|
||||
|
||||
class ContainerSerializer {
|
||||
public:
|
||||
ContainerSerializer(std::vector<uint8_t>* bytes, uint8_t tag);
|
||||
|
||||
template <typename T>
|
||||
void AddField(span<char> field_name, const T& value) {
|
||||
cbor::EncodeString8(
|
||||
span<uint8_t>(reinterpret_cast<const uint8_t*>(field_name.data()),
|
||||
field_name.size()),
|
||||
bytes_);
|
||||
ProtocolTypeTraits<T>::Serialize(value, bytes_);
|
||||
}
|
||||
template <typename T>
|
||||
void AddField(span<char> field_name, const detail::ValueMaybe<T>& value) {
|
||||
if (!value.isJust())
|
||||
return;
|
||||
AddField(field_name, value.fromJust());
|
||||
}
|
||||
template <typename T>
|
||||
void AddField(span<char> field_name, const detail::PtrMaybe<T>& value) {
|
||||
if (!value.isJust())
|
||||
return;
|
||||
AddField(field_name, *value.fromJust());
|
||||
}
|
||||
|
||||
void EncodeStop();
|
||||
|
||||
private:
|
||||
std::vector<uint8_t>* const bytes_;
|
||||
cbor::EnvelopeEncoder envelope_;
|
||||
};
|
||||
|
||||
class ObjectSerializer {
|
||||
public:
|
||||
ObjectSerializer();
|
||||
~ObjectSerializer();
|
||||
|
||||
template <typename T>
|
||||
void AddField(span<char> name, const T& field) {
|
||||
serializer_.AddField(name, field);
|
||||
}
|
||||
std::unique_ptr<Serializable> Finish();
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> owned_bytes_;
|
||||
ContainerSerializer serializer_;
|
||||
};
|
||||
|
||||
class DeserializerDescriptor {
|
||||
public:
|
||||
struct Field {
|
||||
span<char> name;
|
||||
bool is_optional;
|
||||
bool (*deserializer)(DeserializerState* state, void* obj);
|
||||
};
|
||||
|
||||
DeserializerDescriptor(const Field* fields, size_t field_count);
|
||||
|
||||
bool Deserialize(DeserializerState* state, void* obj) const;
|
||||
|
||||
private:
|
||||
bool DeserializeField(DeserializerState* state,
|
||||
span<char> name,
|
||||
int* seen_mandatory_fields,
|
||||
void* obj) const;
|
||||
|
||||
const Field* const fields_;
|
||||
const size_t field_count_;
|
||||
const int mandatory_field_mask_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ProtocolTypeTraits<std::vector<T>> {
|
||||
static bool Deserialize(DeserializerState* state, std::vector<T>* value) {
|
||||
auto* tokenizer = state->tokenizer();
|
||||
if (tokenizer->TokenTag() == cbor::CBORTokenTag::ENVELOPE)
|
||||
tokenizer->EnterEnvelope();
|
||||
if (tokenizer->TokenTag() != cbor::CBORTokenTag::ARRAY_START) {
|
||||
state->RegisterError(Error::CBOR_ARRAY_START_EXPECTED);
|
||||
return false;
|
||||
}
|
||||
assert(value->empty());
|
||||
tokenizer->Next();
|
||||
for (; tokenizer->TokenTag() != cbor::CBORTokenTag::STOP;
|
||||
tokenizer->Next()) {
|
||||
value->emplace_back();
|
||||
if (!ProtocolTypeTraits<T>::Deserialize(state, &value->back()))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Serialize(const std::vector<T>& value,
|
||||
std::vector<uint8_t>* bytes) {
|
||||
ContainerSerializer container_serializer(
|
||||
bytes, cbor::EncodeIndefiniteLengthArrayStart());
|
||||
for (const auto& item : value)
|
||||
ProtocolTypeTraits<T>::Serialize(item, bytes);
|
||||
container_serializer.EncodeStop();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ProtocolTypeTraits<std::unique_ptr<std::vector<T>>> {
|
||||
static bool Deserialize(DeserializerState* state,
|
||||
std::unique_ptr<std::vector<T>>* value) {
|
||||
auto res = std::make_unique<std::vector<T>>();
|
||||
if (!ProtocolTypeTraits<std::vector<T>>::Deserialize(state, res.get()))
|
||||
return false;
|
||||
*value = std::move(res);
|
||||
return true;
|
||||
}
|
||||
static void Serialize(const std::unique_ptr<std::vector<T>>& value,
|
||||
std::vector<uint8_t>* bytes) {
|
||||
ProtocolTypeTraits<std::vector<T>>::Serialize(*value, bytes);
|
||||
}
|
||||
};
|
||||
|
||||
class DeferredMessage : public Serializable {
|
||||
public:
|
||||
static std::unique_ptr<DeferredMessage> FromSerializable(
|
||||
std::unique_ptr<Serializable> serializeable);
|
||||
static std::unique_ptr<DeferredMessage> FromSpan(span<uint8_t> bytes);
|
||||
|
||||
~DeferredMessage() override = default;
|
||||
virtual DeserializerState MakeDeserializer() const = 0;
|
||||
|
||||
protected:
|
||||
DeferredMessage() = default;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ProtocolTypeTraits<std::unique_ptr<DeferredMessage>> {
|
||||
static bool Deserialize(DeserializerState* state,
|
||||
std::unique_ptr<DeferredMessage>* value);
|
||||
static void Serialize(const std::unique_ptr<DeferredMessage>& value,
|
||||
std::vector<uint8_t>* bytes);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ProtocolTypeTraits<detail::ValueMaybe<T>> {
|
||||
static bool Deserialize(DeserializerState* state,
|
||||
detail::ValueMaybe<T>* value) {
|
||||
T res;
|
||||
if (!ProtocolTypeTraits<T>::Deserialize(state, &res))
|
||||
return false;
|
||||
*value = std::move(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Serialize(const detail::ValueMaybe<T>& value,
|
||||
std::vector<uint8_t>* bytes) {
|
||||
ProtocolTypeTraits<T>::Serialize(value.fromJust(), bytes);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ProtocolTypeTraits<detail::PtrMaybe<T>> {
|
||||
static bool Deserialize(DeserializerState* state,
|
||||
detail::PtrMaybe<T>* value) {
|
||||
std::unique_ptr<T> res;
|
||||
if (!ProtocolTypeTraits<std::unique_ptr<T>>::Deserialize(state, &res))
|
||||
return false;
|
||||
*value = std::move(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Serialize(const detail::PtrMaybe<T>& value,
|
||||
std::vector<uint8_t>* bytes) {
|
||||
ProtocolTypeTraits<T>::Serialize(*value.fromJust(), bytes);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class DeserializableProtocolObject {
|
||||
public:
|
||||
static StatusOr<std::unique_ptr<T>> ReadFrom(
|
||||
const DeferredMessage& deferred_message) {
|
||||
auto state = deferred_message.MakeDeserializer();
|
||||
if (auto res = Deserialize(&state))
|
||||
return StatusOr<std::unique_ptr<T>>(std::move(res));
|
||||
return StatusOr<std::unique_ptr<T>>(state.status());
|
||||
}
|
||||
|
||||
static StatusOr<std::unique_ptr<T>> ReadFrom(std::vector<uint8_t> bytes) {
|
||||
auto state = DeserializerState(std::move(bytes));
|
||||
if (auto res = Deserialize(&state))
|
||||
return StatusOr<std::unique_ptr<T>>(std::move(res));
|
||||
return StatusOr<std::unique_ptr<T>>(state.status());
|
||||
}
|
||||
|
||||
// Short-hand for legacy clients. This would swallow any errors, consider
|
||||
// using ReadFrom.
|
||||
static std::unique_ptr<T> FromBinary(const uint8_t* bytes, size_t size) {
|
||||
std::unique_ptr<T> value(new T());
|
||||
auto deserializer = DeferredMessage::FromSpan(span<uint8_t>(bytes, size))
|
||||
->MakeDeserializer();
|
||||
Deserialize(&deserializer, value.get());
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool Deserialize(DeserializerState* state, T* value) {
|
||||
return T::deserializer_descriptor().Deserialize(state, value);
|
||||
}
|
||||
|
||||
protected:
|
||||
// This is for the sake of the macros used by derived classes thay may be in
|
||||
// a different namespace;
|
||||
using ProtocolType = T;
|
||||
using DeserializerDescriptorType = DeserializerDescriptor;
|
||||
template <typename U>
|
||||
using DeserializableBase = DeserializableProtocolObject<U>;
|
||||
|
||||
DeserializableProtocolObject() = default;
|
||||
~DeserializableProtocolObject() = default;
|
||||
|
||||
private:
|
||||
friend struct ProtocolTypeTraits<std::unique_ptr<T>>;
|
||||
|
||||
static std::unique_ptr<T> Deserialize(DeserializerState* state) {
|
||||
std::unique_ptr<T> value(new T());
|
||||
if (Deserialize(state, value.get()))
|
||||
return value;
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ProtocolObject : public Serializable,
|
||||
public DeserializableProtocolObject<T> {
|
||||
public:
|
||||
std::unique_ptr<T> Clone() const {
|
||||
std::vector<uint8_t> serialized;
|
||||
AppendSerialized(&serialized);
|
||||
return T::ReadFrom(std::move(serialized)).value();
|
||||
}
|
||||
// TODO(caseq): compatibility only, remove.
|
||||
std::unique_ptr<T> clone() const { return Clone(); }
|
||||
|
||||
protected:
|
||||
using ProtocolType = T;
|
||||
|
||||
ProtocolObject() = default;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ProtocolTypeTraits<
|
||||
T,
|
||||
typename std::enable_if<
|
||||
std::is_base_of<ProtocolObject<T>, T>::value>::type> {
|
||||
static bool Deserialize(DeserializerState* state, T* value) {
|
||||
return T::Deserialize(state, value);
|
||||
}
|
||||
|
||||
static void Serialize(const T& value, std::vector<uint8_t>* bytes) {
|
||||
value.AppendSerialized(bytes);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ProtocolTypeTraits<
|
||||
std::unique_ptr<T>,
|
||||
typename std::enable_if<
|
||||
std::is_base_of<ProtocolObject<T>, T>::value>::type> {
|
||||
static bool Deserialize(DeserializerState* state, std::unique_ptr<T>* value) {
|
||||
std::unique_ptr<T> res = T::Deserialize(state);
|
||||
if (!res)
|
||||
return false;
|
||||
*value = std::move(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Serialize(const std::unique_ptr<T>& value,
|
||||
std::vector<uint8_t>* bytes) {
|
||||
ProtocolTypeTraits<T>::Serialize(*value, bytes);
|
||||
}
|
||||
};
|
||||
|
||||
#define DECLARE_DESERIALIZATION_SUPPORT() \
|
||||
friend DeserializableBase<ProtocolType>; \
|
||||
static const DeserializerDescriptorType& deserializer_descriptor()
|
||||
|
||||
#define DECLARE_SERIALIZATION_SUPPORT() \
|
||||
public: \
|
||||
void AppendSerialized(std::vector<uint8_t>* bytes) const override; \
|
||||
\
|
||||
private: \
|
||||
friend DeserializableBase<ProtocolType>; \
|
||||
static const DeserializerDescriptorType& deserializer_descriptor()
|
||||
|
||||
#define V8_CRDTP_DESERIALIZE_FILED_IMPL(name, field, is_optional) \
|
||||
{ \
|
||||
MakeSpan(name), is_optional, \
|
||||
[](DeserializerState* __state, void* __obj) -> bool { \
|
||||
return ProtocolTypeTraits<decltype(field)>::Deserialize( \
|
||||
__state, &static_cast<ProtocolType*>(__obj)->field); \
|
||||
} \
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
#define V8_CRDTP_BEGIN_DESERIALIZER(type) \
|
||||
const type::DeserializerDescriptorType& type::deserializer_descriptor() { \
|
||||
using namespace v8_crdtp; \
|
||||
static const DeserializerDescriptorType::Field fields[] = {
|
||||
|
||||
#define V8_CRDTP_END_DESERIALIZER() \
|
||||
}; \
|
||||
static const DeserializerDescriptorType s_desc( \
|
||||
fields, sizeof fields / sizeof fields[0]); \
|
||||
return s_desc; \
|
||||
}
|
||||
|
||||
#define V8_CRDTP_DESERIALIZE_FIELD(name, field) \
|
||||
V8_CRDTP_DESERIALIZE_FILED_IMPL(name, field, false)
|
||||
#define V8_CRDTP_DESERIALIZE_FIELD_OPT(name, field) \
|
||||
V8_CRDTP_DESERIALIZE_FILED_IMPL(name, field, true)
|
||||
|
||||
#define V8_CRDTP_BEGIN_SERIALIZER(type) \
|
||||
void type::AppendSerialized(std::vector<uint8_t>* bytes) const { \
|
||||
using namespace v8_crdtp; \
|
||||
ContainerSerializer __serializer(bytes, \
|
||||
cbor::EncodeIndefiniteLengthMapStart());
|
||||
|
||||
#define V8_CRDTP_SERIALIZE_FIELD(name, field) \
|
||||
__serializer.AddField(MakeSpan(name), field)
|
||||
|
||||
#define V8_CRDTP_END_SERIALIZER() \
|
||||
__serializer.EncodeStop(); \
|
||||
} class __cddtp_dummy_name
|
||||
// clang-format on
|
||||
|
||||
} // namespace v8_crdtp
|
||||
|
||||
#endif // V8_CRDTP_PROTOCOL_CORE_H_
|
497
third_party/inspector_protocol/crdtp/protocol_core_test.cc
vendored
Normal file
497
third_party/inspector_protocol/crdtp/protocol_core_test.cc
vendored
Normal file
@ -0,0 +1,497 @@
|
||||
// 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 <memory>
|
||||
|
||||
#include "cbor.h"
|
||||
#include "maybe.h"
|
||||
#include "status_test_support.h"
|
||||
#include "test_platform.h"
|
||||
|
||||
namespace v8_crdtp {
|
||||
|
||||
// Test-only. Real-life bindings use UTF8/16 conversions as needed.
|
||||
template <>
|
||||
struct ProtocolTypeTraits<std::string> {
|
||||
static bool Deserialize(DeserializerState* state, std::string* value) {
|
||||
if (state->tokenizer()->TokenTag() == cbor::CBORTokenTag::STRING8) {
|
||||
auto cbor_span = state->tokenizer()->GetString8();
|
||||
value->assign(reinterpret_cast<const char*>(cbor_span.data()),
|
||||
cbor_span.size());
|
||||
return true;
|
||||
}
|
||||
state->RegisterError(Error::BINDINGS_STRING8_VALUE_EXPECTED);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void Serialize(const std::string& value, std::vector<uint8_t>* bytes) {
|
||||
cbor::EncodeString8(
|
||||
span<uint8_t>(reinterpret_cast<const uint8_t*>(value.data()),
|
||||
value.size()),
|
||||
bytes);
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
using ::testing::Eq;
|
||||
|
||||
template <typename TResult, typename TArg>
|
||||
std::unique_ptr<TResult> RoundtripToType(const TArg& obj) {
|
||||
std::vector<uint8_t> bytes;
|
||||
obj.AppendSerialized(&bytes);
|
||||
|
||||
StatusOr<std::unique_ptr<TResult>> result =
|
||||
TResult::ReadFrom(std::move(bytes));
|
||||
return std::move(result).value();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::unique_ptr<T> Roundtrip(const T& obj) {
|
||||
return RoundtripToType<T, T>(obj);
|
||||
}
|
||||
|
||||
// These TestTypeFOO classes below would normally be generated
|
||||
// by the protocol generator.
|
||||
|
||||
class TestTypeBasic : public ProtocolObject<TestTypeBasic> {
|
||||
public:
|
||||
TestTypeBasic() = default;
|
||||
|
||||
const std::string& GetValue() const { return value_; }
|
||||
void SetValue(std::string value) { value_ = std::move(value); }
|
||||
|
||||
private:
|
||||
DECLARE_SERIALIZATION_SUPPORT();
|
||||
|
||||
std::string value_;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
V8_CRDTP_BEGIN_DESERIALIZER(TestTypeBasic)
|
||||
V8_CRDTP_DESERIALIZE_FIELD("value", value_)
|
||||
V8_CRDTP_END_DESERIALIZER()
|
||||
|
||||
V8_CRDTP_BEGIN_SERIALIZER(TestTypeBasic)
|
||||
V8_CRDTP_SERIALIZE_FIELD("value", value_);
|
||||
V8_CRDTP_END_SERIALIZER();
|
||||
// clang-format on
|
||||
|
||||
TEST(ProtocolCoreTest, Basic) {
|
||||
TestTypeBasic obj1;
|
||||
obj1.SetValue("foo");
|
||||
|
||||
auto obj2 = Roundtrip(obj1);
|
||||
ASSERT_THAT(obj2, Not(testing::IsNull()));
|
||||
EXPECT_THAT(obj2->GetValue(), Eq("foo"));
|
||||
}
|
||||
|
||||
TEST(ProtocolCoreTest, FailedToDeserializeTestTypeBasic) {
|
||||
std::vector<uint8_t> garbage = {'g', 'a', 'r', 'b', 'a', 'g', 'e'};
|
||||
StatusOr<std::unique_ptr<TestTypeBasic>> result =
|
||||
TestTypeBasic::ReadFrom(std::move(garbage));
|
||||
EXPECT_THAT(result.status(), StatusIs(Error::CBOR_INVALID_STRING8, 0));
|
||||
}
|
||||
|
||||
class TestTypeBasicDouble : public ProtocolObject<TestTypeBasicDouble> {
|
||||
public:
|
||||
TestTypeBasicDouble() = default;
|
||||
|
||||
double GetValue() const { return value_; }
|
||||
void SetValue(double value) { value_ = value; }
|
||||
|
||||
private:
|
||||
DECLARE_SERIALIZATION_SUPPORT();
|
||||
|
||||
double value_;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
V8_CRDTP_BEGIN_DESERIALIZER(TestTypeBasicDouble)
|
||||
V8_CRDTP_DESERIALIZE_FIELD("value", value_)
|
||||
V8_CRDTP_END_DESERIALIZER()
|
||||
|
||||
V8_CRDTP_BEGIN_SERIALIZER(TestTypeBasicDouble)
|
||||
V8_CRDTP_SERIALIZE_FIELD("value", value_);
|
||||
V8_CRDTP_END_SERIALIZER();
|
||||
// clang-format on
|
||||
|
||||
TEST(TestBasicDouble, ParserAllowsAllowsDoubleEncodedAsInt) {
|
||||
// We allow double's encoded as INT32, because this is what a roundtrip via
|
||||
// JSON would produce.
|
||||
std::vector<uint8_t> encoded;
|
||||
crdtp::cbor::EnvelopeEncoder envelope;
|
||||
envelope.EncodeStart(&encoded);
|
||||
encoded.push_back(crdtp::cbor::EncodeIndefiniteLengthMapStart());
|
||||
crdtp::cbor::EncodeString8(crdtp::SpanFrom("value"), &encoded);
|
||||
crdtp::cbor::EncodeInt32(
|
||||
42, &encoded); // It's a double field, but we encode an int.
|
||||
encoded.push_back(crdtp::cbor::EncodeStop());
|
||||
envelope.EncodeStop(&encoded);
|
||||
auto obj = TestTypeBasicDouble::ReadFrom(encoded).value();
|
||||
ASSERT_THAT(obj, Not(testing::IsNull()));
|
||||
EXPECT_THAT(obj->GetValue(), Eq(42));
|
||||
}
|
||||
|
||||
class TestTypeComposite : public ProtocolObject<TestTypeComposite> {
|
||||
public:
|
||||
bool GetBoolField() const { return bool_field_; }
|
||||
void SetBoolField(bool value) { bool_field_ = value; }
|
||||
|
||||
int GetIntField() const { return int_field_; }
|
||||
void SetIntField(int value) { int_field_ = value; }
|
||||
|
||||
double GetDoubleField() const { return double_field_; }
|
||||
void SetDoubleField(double value) { double_field_ = value; }
|
||||
|
||||
const std::string& GetStrField() const { return str_field_; }
|
||||
void SetStrField(std::string value) { str_field_ = std::move(value); }
|
||||
|
||||
const TestTypeBasic* GetTestTypeBasicField() {
|
||||
return test_type1_field_.get();
|
||||
}
|
||||
void SetTestTypeBasicField(std::unique_ptr<TestTypeBasic> value) {
|
||||
test_type1_field_ = std::move(value);
|
||||
}
|
||||
|
||||
private:
|
||||
DECLARE_SERIALIZATION_SUPPORT();
|
||||
|
||||
bool bool_field_ = false;
|
||||
int int_field_ = 0;
|
||||
double double_field_ = 0.0;
|
||||
std::string str_field_;
|
||||
std::unique_ptr<TestTypeBasic> test_type1_field_;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
V8_CRDTP_BEGIN_DESERIALIZER(TestTypeComposite)
|
||||
V8_CRDTP_DESERIALIZE_FIELD("bool_field", bool_field_),
|
||||
V8_CRDTP_DESERIALIZE_FIELD("double_field", double_field_),
|
||||
V8_CRDTP_DESERIALIZE_FIELD("int_field", int_field_),
|
||||
V8_CRDTP_DESERIALIZE_FIELD("str_field", str_field_),
|
||||
V8_CRDTP_DESERIALIZE_FIELD("test_type1_field", test_type1_field_),
|
||||
V8_CRDTP_END_DESERIALIZER()
|
||||
|
||||
V8_CRDTP_BEGIN_SERIALIZER(TestTypeComposite)
|
||||
V8_CRDTP_SERIALIZE_FIELD("bool_field", bool_field_),
|
||||
V8_CRDTP_SERIALIZE_FIELD("double_field", double_field_),
|
||||
V8_CRDTP_SERIALIZE_FIELD("int_field", int_field_),
|
||||
V8_CRDTP_SERIALIZE_FIELD("str_field", str_field_),
|
||||
V8_CRDTP_SERIALIZE_FIELD("test_type1_field", test_type1_field_),
|
||||
V8_CRDTP_END_SERIALIZER();
|
||||
// clang-format on
|
||||
|
||||
TEST(ProtocolCoreTest, Composite) {
|
||||
TestTypeComposite obj1;
|
||||
obj1.SetBoolField(true);
|
||||
obj1.SetIntField(42);
|
||||
obj1.SetDoubleField(2.718281828);
|
||||
obj1.SetStrField("bar");
|
||||
auto val1 = std::make_unique<TestTypeBasic>();
|
||||
val1->SetValue("bazzzz");
|
||||
obj1.SetTestTypeBasicField(std::move(val1));
|
||||
|
||||
auto obj2 = Roundtrip(obj1);
|
||||
ASSERT_THAT(obj2, Not(testing::IsNull()));
|
||||
EXPECT_THAT(obj2->GetBoolField(), Eq(true));
|
||||
EXPECT_THAT(obj2->GetIntField(), Eq(42));
|
||||
EXPECT_THAT(obj2->GetDoubleField(), Eq(2.718281828));
|
||||
EXPECT_THAT(obj2->GetStrField(), Eq("bar"));
|
||||
EXPECT_THAT(obj2->GetTestTypeBasicField()->GetValue(), Eq("bazzzz"));
|
||||
}
|
||||
|
||||
class CompositeParsingTest : public testing::Test {
|
||||
public:
|
||||
CompositeParsingTest() {
|
||||
TestTypeComposite top;
|
||||
top.SetIntField(42);
|
||||
top.SetBoolField(true);
|
||||
top.SetIntField(42);
|
||||
top.SetDoubleField(2.718281828);
|
||||
top.SetStrField("junk");
|
||||
auto child = std::make_unique<TestTypeBasic>();
|
||||
child->SetValue("child_value");
|
||||
top.SetTestTypeBasicField(std::move(child));
|
||||
|
||||
// Let's establish that |serialized_| is a properly serialized
|
||||
// representation of |top|, by checking that it deserializes ok.
|
||||
top.AppendSerialized(&serialized_);
|
||||
TestTypeComposite::ReadFrom(serialized_).value();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t> serialized_;
|
||||
};
|
||||
|
||||
TEST_F(CompositeParsingTest, DecodingFailure_CBORTokenizer) {
|
||||
// Mutates |serialized_| so that it won't parse correctly. In this case,
|
||||
// we're changing a string value so that it's invalid, making CBORTokenizer
|
||||
// unhappy.
|
||||
size_t position =
|
||||
std::string(reinterpret_cast<const char*>(serialized_.data()),
|
||||
serialized_.size())
|
||||
.find("child_value");
|
||||
EXPECT_GT(position, 0ul);
|
||||
// We override the byte just before so that it's still a string
|
||||
// (3 << 5), but the length is encoded in the bytes that follows.
|
||||
// So, we override that with 0xff (255), which exceeds the length
|
||||
// of the message and thereby makes the string8 invalid.
|
||||
--position;
|
||||
serialized_[position] = 3 << 5 | // major type: STRING
|
||||
25; // length in encoded in byte that follows.
|
||||
serialized_[position + 1] = 0xff; // length
|
||||
auto result = TestTypeComposite::ReadFrom(serialized_);
|
||||
|
||||
EXPECT_THAT(result.status(), StatusIs(Error::CBOR_INVALID_STRING8, position));
|
||||
}
|
||||
|
||||
TEST_F(CompositeParsingTest, DecodingFailure_MandatoryFieldMissingShallow) {
|
||||
// We're changing the string key "int_field" to something else ("lnt_field"),
|
||||
// so that the mandatory field value won't be found. Unknown fields are
|
||||
// ignored for compatibility, so that's why this simple technique works here.
|
||||
size_t position =
|
||||
std::string(reinterpret_cast<const char*>(serialized_.data()),
|
||||
serialized_.size())
|
||||
.find("int_field");
|
||||
serialized_[position] = 'l'; // Change 'i' to 'l'.
|
||||
// serialized_.size() - 1 is the STOP character for the entire message,
|
||||
size_t expected_error_pos = serialized_.size() - 1;
|
||||
auto result = TestTypeComposite::ReadFrom(serialized_);
|
||||
EXPECT_THAT(result.status(), StatusIs(Error::BINDINGS_MANDATORY_FIELD_MISSING,
|
||||
expected_error_pos));
|
||||
}
|
||||
|
||||
TEST_F(CompositeParsingTest, DecodingFailure_MandatoryFieldMissingNested) {
|
||||
// We're changing the string key "value" to something else ("falue"), so that
|
||||
// the mandatory field value in TestTypeBasic in the child won't be found.
|
||||
size_t position =
|
||||
std::string(reinterpret_cast<const char*>(serialized_.data()),
|
||||
serialized_.size())
|
||||
.find("value");
|
||||
serialized_[position] = 'f'; // Change 'v' to 'f'.
|
||||
// serialized_.size() - 1 is the STOP character for the enclosing message,
|
||||
// and serialized_.size() - 2 is the STOP character for TestTypeBasic.
|
||||
size_t expected_error_pos = serialized_.size() - 2;
|
||||
auto result = TestTypeComposite::ReadFrom(serialized_);
|
||||
EXPECT_THAT(result.status(), StatusIs(Error::BINDINGS_MANDATORY_FIELD_MISSING,
|
||||
expected_error_pos));
|
||||
}
|
||||
|
||||
TEST_F(CompositeParsingTest, DecodingFailure_BoolValueExpected) {
|
||||
// We're changing the bool value (true) to null; we do this by looking
|
||||
// for bool_field, and searching from there for TRUE; both TRUE and null
|
||||
// are just one byte in the serialized buffer, so this swap is convenient.
|
||||
std::string serialized_view(reinterpret_cast<const char*>(serialized_.data()),
|
||||
serialized_.size());
|
||||
size_t position = serialized_view.find("bool_field");
|
||||
for (; position < serialized_.size(); ++position) {
|
||||
if (serialized_[position] == crdtp::cbor::EncodeTrue()) {
|
||||
serialized_[position] = crdtp::cbor::EncodeNull();
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto result = TestTypeComposite::ReadFrom(serialized_);
|
||||
EXPECT_THAT(result.status(),
|
||||
StatusIs(Error::BINDINGS_BOOL_VALUE_EXPECTED, position));
|
||||
}
|
||||
|
||||
class TestTypeArrays : public ProtocolObject<TestTypeArrays> {
|
||||
public:
|
||||
const std::vector<int>* GetIntArray() const { return &int_array_; }
|
||||
void SetIntArray(std::vector<int> value) { int_array_ = std::move(value); }
|
||||
|
||||
const std::vector<double>* GetDoubleArray() const { return &double_array_; }
|
||||
void SetDoubleArray(std::vector<double> value) {
|
||||
double_array_ = std::move(value);
|
||||
}
|
||||
|
||||
const std::vector<std::string>* GetStrArray() const { return &str_array_; }
|
||||
void SetStrArray(std::vector<std::string> value) {
|
||||
str_array_ = std::move(value);
|
||||
}
|
||||
|
||||
const std::vector<std::unique_ptr<TestTypeBasic>>* GetTestTypeBasicArray()
|
||||
const {
|
||||
return &test_type_basic_array_;
|
||||
}
|
||||
|
||||
void SetTestTypeBasicArray(
|
||||
std::vector<std::unique_ptr<TestTypeBasic>> value) {
|
||||
test_type_basic_array_ = std::move(value);
|
||||
}
|
||||
|
||||
private:
|
||||
DECLARE_SERIALIZATION_SUPPORT();
|
||||
|
||||
std::vector<int> int_array_;
|
||||
std::vector<double> double_array_;
|
||||
std::vector<std::string> str_array_;
|
||||
std::vector<std::unique_ptr<TestTypeBasic>> test_type_basic_array_;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
V8_CRDTP_BEGIN_DESERIALIZER(TestTypeArrays)
|
||||
V8_CRDTP_DESERIALIZE_FIELD("int_array", int_array_),
|
||||
V8_CRDTP_DESERIALIZE_FIELD("str_array", str_array_),
|
||||
V8_CRDTP_DESERIALIZE_FIELD("test_type_basic_array", test_type_basic_array_),
|
||||
V8_CRDTP_END_DESERIALIZER()
|
||||
|
||||
V8_CRDTP_BEGIN_SERIALIZER(TestTypeArrays)
|
||||
V8_CRDTP_SERIALIZE_FIELD("int_array", int_array_),
|
||||
V8_CRDTP_SERIALIZE_FIELD("str_array", str_array_),
|
||||
V8_CRDTP_SERIALIZE_FIELD("test_type_basic_array", test_type_basic_array_),
|
||||
V8_CRDTP_END_SERIALIZER();
|
||||
// clang-format on
|
||||
|
||||
TEST_F(CompositeParsingTest, Arrays) {
|
||||
TestTypeArrays obj1;
|
||||
obj1.SetIntArray(std::vector<int>{1, 3, 5, 7});
|
||||
std::vector<std::string> strs;
|
||||
strs.emplace_back("foo");
|
||||
strs.emplace_back(std::string("bar"));
|
||||
obj1.SetStrArray(std::move(strs));
|
||||
auto val1 = std::make_unique<TestTypeBasic>();
|
||||
val1->SetValue("bazzzz");
|
||||
std::vector<std::unique_ptr<TestTypeBasic>> vec1;
|
||||
vec1.emplace_back(std::move(val1));
|
||||
obj1.SetTestTypeBasicArray(std::move(vec1));
|
||||
|
||||
auto obj2 = Roundtrip(obj1);
|
||||
ASSERT_THAT(obj2, Not(testing::IsNull()));
|
||||
EXPECT_THAT(*obj2->GetIntArray(), testing::ElementsAre(1, 3, 5, 7));
|
||||
EXPECT_THAT(*obj2->GetStrArray(), testing::ElementsAre("foo", "bar"));
|
||||
EXPECT_THAT(obj2->GetDoubleArray()->size(), Eq(0ul));
|
||||
EXPECT_THAT(obj2->GetTestTypeBasicArray()->size(), Eq(1ul));
|
||||
EXPECT_THAT(obj2->GetTestTypeBasicArray()->front()->GetValue(), Eq("bazzzz"));
|
||||
}
|
||||
|
||||
class TestTypeOptional : public ProtocolObject<TestTypeOptional> {
|
||||
public:
|
||||
TestTypeOptional() = default;
|
||||
|
||||
bool HasIntField() const { return int_field_.isJust(); }
|
||||
int GetIntField() const { return int_field_.fromJust(); }
|
||||
void SetIntField(int value) { int_field_ = value; }
|
||||
|
||||
bool HasStrField() { return str_field_.isJust(); }
|
||||
const std::string& GetStrField() const { return str_field_.fromJust(); }
|
||||
void SetStrField(std::string value) { str_field_ = std::move(value); }
|
||||
|
||||
bool HasTestTypeBasicField() { return test_type_basic_field_.isJust(); }
|
||||
const TestTypeBasic* GetTestTypeBasicField() const {
|
||||
return test_type_basic_field_.isJust() ? test_type_basic_field_.fromJust()
|
||||
: nullptr;
|
||||
}
|
||||
void SetTestTypeBasicField(std::unique_ptr<TestTypeBasic> value) {
|
||||
test_type_basic_field_ = std::move(value);
|
||||
}
|
||||
|
||||
private:
|
||||
DECLARE_SERIALIZATION_SUPPORT();
|
||||
|
||||
Maybe<int> int_field_;
|
||||
Maybe<std::string> str_field_;
|
||||
Maybe<TestTypeBasic> test_type_basic_field_;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
V8_CRDTP_BEGIN_DESERIALIZER(TestTypeOptional)
|
||||
V8_CRDTP_DESERIALIZE_FIELD_OPT("int_field", int_field_),
|
||||
V8_CRDTP_DESERIALIZE_FIELD_OPT("str_field", str_field_),
|
||||
V8_CRDTP_DESERIALIZE_FIELD_OPT("test_type_basic_field", test_type_basic_field_),
|
||||
V8_CRDTP_END_DESERIALIZER()
|
||||
|
||||
V8_CRDTP_BEGIN_SERIALIZER(TestTypeOptional)
|
||||
V8_CRDTP_SERIALIZE_FIELD("int_field", int_field_),
|
||||
V8_CRDTP_SERIALIZE_FIELD("str_field", str_field_),
|
||||
V8_CRDTP_SERIALIZE_FIELD("test_type_basic_field", test_type_basic_field_),
|
||||
V8_CRDTP_END_SERIALIZER();
|
||||
// clang-format on
|
||||
|
||||
TEST(ProtocolCoreTest, OptionalAbsent) {
|
||||
TestTypeOptional obj1;
|
||||
auto obj2 = Roundtrip(obj1);
|
||||
ASSERT_THAT(obj2, Not(testing::IsNull()));
|
||||
|
||||
EXPECT_THAT(obj2->HasIntField(), Eq(false));
|
||||
EXPECT_THAT(obj2->HasStrField(), Eq(false));
|
||||
EXPECT_THAT(obj2->HasTestTypeBasicField(), Eq(false));
|
||||
}
|
||||
|
||||
TEST(ProtocolCoreTest, OptionalPresent) {
|
||||
TestTypeOptional obj1;
|
||||
obj1.SetIntField(42);
|
||||
obj1.SetStrField("foo");
|
||||
|
||||
auto val1 = std::make_unique<TestTypeBasic>();
|
||||
val1->SetValue("bar");
|
||||
obj1.SetTestTypeBasicField(std::move(val1));
|
||||
|
||||
auto obj2 = Roundtrip(obj1);
|
||||
ASSERT_THAT(obj2, Not(testing::IsNull()));
|
||||
|
||||
EXPECT_THAT(obj2->HasIntField(), Eq(true));
|
||||
EXPECT_THAT(obj2->GetIntField(), Eq(42));
|
||||
EXPECT_THAT(obj2->HasStrField(), Eq(true));
|
||||
EXPECT_THAT(obj2->GetStrField(), Eq("foo"));
|
||||
EXPECT_THAT(obj2->HasTestTypeBasicField(), Eq(true));
|
||||
EXPECT_THAT(obj2->GetTestTypeBasicField()->GetValue(), Eq("bar"));
|
||||
}
|
||||
|
||||
class TestTypeLazy : public ProtocolObject<TestTypeLazy> {
|
||||
public:
|
||||
TestTypeLazy() = default;
|
||||
|
||||
const std::string& GetStrField() const { return str_field_; }
|
||||
void SetStrField(std::string value) { str_field_ = std::move(value); }
|
||||
|
||||
const DeferredMessage* deferred_test_type1_field() const {
|
||||
return test_type1_field_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
DECLARE_SERIALIZATION_SUPPORT();
|
||||
|
||||
std::string str_field_;
|
||||
std::unique_ptr<DeferredMessage> test_type1_field_;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
V8_CRDTP_BEGIN_DESERIALIZER(TestTypeLazy)
|
||||
V8_CRDTP_DESERIALIZE_FIELD("str_field", str_field_),
|
||||
V8_CRDTP_DESERIALIZE_FIELD_OPT("test_type1_field", test_type1_field_),
|
||||
V8_CRDTP_END_DESERIALIZER()
|
||||
|
||||
V8_CRDTP_BEGIN_SERIALIZER(TestTypeLazy)
|
||||
V8_CRDTP_SERIALIZE_FIELD("str_field", str_field_),
|
||||
V8_CRDTP_SERIALIZE_FIELD("test_type1_field", test_type1_field_),
|
||||
V8_CRDTP_END_SERIALIZER();
|
||||
// clang-format on
|
||||
|
||||
TEST(ProtocolCoreTest, TestDeferredMessage) {
|
||||
TestTypeComposite obj1;
|
||||
obj1.SetStrField("bar");
|
||||
auto val1 = std::make_unique<TestTypeBasic>();
|
||||
val1->SetValue("bazzzz");
|
||||
obj1.SetTestTypeBasicField(std::move(val1));
|
||||
|
||||
auto obj2 = RoundtripToType<TestTypeLazy>(obj1);
|
||||
EXPECT_THAT(obj2->GetStrField(), Eq("bar"));
|
||||
|
||||
TestTypeBasic basic_val;
|
||||
auto deserializer = obj2->deferred_test_type1_field()->MakeDeserializer();
|
||||
EXPECT_THAT(TestTypeBasic::Deserialize(&deserializer, &basic_val), Eq(true));
|
||||
EXPECT_THAT(basic_val.GetValue(), Eq("bazzzz"));
|
||||
|
||||
StatusOr<std::unique_ptr<TestTypeBasic>> maybe_parsed =
|
||||
TestTypeBasic::ReadFrom(*obj2->deferred_test_type1_field());
|
||||
ASSERT_THAT(maybe_parsed.status(), StatusIsOk());
|
||||
ASSERT_THAT((*maybe_parsed), Not(testing::IsNull()));
|
||||
ASSERT_EQ((*maybe_parsed)->GetValue(), "bazzzz");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace v8_crdtp
|
@ -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);
|
||||
|
15
third_party/inspector_protocol/crdtp/span.cc
vendored
15
third_party/inspector_protocol/crdtp/span.cc
vendored
@ -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
|
||||
|
13
third_party/inspector_protocol/crdtp/span.h
vendored
13
third_party/inspector_protocol/crdtp/span.h
vendored
@ -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);
|
||||
|
30
third_party/inspector_protocol/crdtp/status.h
vendored
30
third_party/inspector_protocol/crdtp/status.h
vendored
@ -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;
|
||||
};
|
||||
|
||||
|
123
third_party/inspector_protocol/lib/ValueConversions_cpp.template
vendored
Normal file
123
third_party/inspector_protocol/lib/ValueConversions_cpp.template
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
// 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}}
|
@ -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)
|
||||
|
7
third_party/inspector_protocol/roll.py
vendored
7
third_party/inspector_protocol/roll.py
vendored
@ -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 %}
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
#include "{{config.crdtp.dir}}/cbor.h"
|
||||
#include "{{config.crdtp.dir}}/find_by_first.h"
|
||||
#include "{{config.crdtp.dir}}/serializer_traits.h"
|
||||
#include "{{config.crdtp.dir}}/span.h"
|
||||
|
||||
{% for namespace in config.protocol.namespace %}
|
||||
@ -18,6 +17,9 @@ namespace {{namespace}} {
|
||||
{% endfor %}
|
||||
namespace {{domain.domain}} {
|
||||
|
||||
using {{config.crdtp.namespace}}::DeserializerState;
|
||||
using {{config.crdtp.namespace}}::ProtocolTypeTraits;
|
||||
|
||||
// ------------- Enum values from types.
|
||||
|
||||
const char Metainfo::domainName[] = "{{domain.domain}}";
|
||||
@ -43,6 +45,7 @@ const char* {{ literal | dash_to_camelcase}} = "{{literal}}";
|
||||
} // namespace API
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% for property in type.properties %}
|
||||
{% if "enum" in property %}
|
||||
|
||||
@ -52,80 +55,28 @@ const char* {{type.id}}::{{property.name | to_title_case}}Enum::{{literal | dash
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if not (type.type == "object") or not ("properties" in type) %}{% continue %}{% endif %}
|
||||
|
||||
std::unique_ptr<{{type.id}}> {{type.id}}::fromValue(protocol::Value* value, ErrorSupport* errors)
|
||||
{
|
||||
if (!value || value->type() != protocol::Value::TypeObject) {
|
||||
errors->AddError("object expected");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<{{type.id}}> result(new {{type.id}}());
|
||||
protocol::DictionaryValue* object = DictionaryValue::cast(value);
|
||||
errors->Push();
|
||||
{% for property in type.properties %}
|
||||
protocol::Value* {{property.name}}Value = object->get("{{property.name}}");
|
||||
V8_CRDTP_BEGIN_DESERIALIZER({{type.id}})
|
||||
{% for property in type.properties | sort(attribute = 'name', case_sensitive=True) %}
|
||||
{% if property.optional %}
|
||||
if ({{property.name}}Value) {
|
||||
errors->SetName("{{property.name}}");
|
||||
result->m_{{property.name}} = ValueConversions<{{protocol.resolve_type(property).raw_type}}>::fromValue({{property.name}}Value, errors);
|
||||
}
|
||||
V8_CRDTP_DESERIALIZE_FIELD_OPT("{{property.name}}", m_{{property.name}}),
|
||||
{% else %}
|
||||
errors->SetName("{{property.name}}");
|
||||
result->m_{{property.name}} = ValueConversions<{{protocol.resolve_type(property).raw_type}}>::fromValue({{property.name}}Value, errors);
|
||||
V8_CRDTP_DESERIALIZE_FIELD("{{property.name}}", m_{{property.name}}),
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
errors->Pop();
|
||||
if (!errors->Errors().empty())
|
||||
return nullptr;
|
||||
return result;
|
||||
}
|
||||
{% endfor %}
|
||||
V8_CRDTP_END_DESERIALIZER()
|
||||
|
||||
std::unique_ptr<protocol::DictionaryValue> {{type.id}}::toValue() const
|
||||
{
|
||||
std::unique_ptr<protocol::DictionaryValue> result = DictionaryValue::create();
|
||||
{% for property in type.properties %}
|
||||
{% set property_type = protocol.resolve_type(property) %}
|
||||
{% set property_field = "m_" + property.name %}
|
||||
{% if property.optional %}
|
||||
if ({{property_field}}.isJust())
|
||||
result->setValue("{{property.name}}", ValueConversions<{{property_type.raw_type}}>::toValue({{property_field}}.fromJust()));
|
||||
{% else %}
|
||||
result->setValue("{{property.name}}", ValueConversions<{{property_type.raw_type}}>::toValue({{property_type.to_raw_type % property_field}}));
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
return result;
|
||||
}
|
||||
V8_CRDTP_BEGIN_SERIALIZER({{type.id}})
|
||||
{% for property in type.properties %}
|
||||
V8_CRDTP_SERIALIZE_FIELD("{{property.name}}", m_{{property.name}});
|
||||
{% endfor %}
|
||||
V8_CRDTP_END_SERIALIZER();
|
||||
|
||||
void {{type.id}}::AppendSerialized(std::vector<uint8_t>* out) const {
|
||||
{{config.crdtp.namespace}}::cbor::EnvelopeEncoder envelope_encoder;
|
||||
envelope_encoder.EncodeStart(out);
|
||||
out->push_back({{config.crdtp.namespace}}::cbor::EncodeIndefiniteLengthMapStart());
|
||||
{% for property in type.properties %}
|
||||
{% set property_field = "m_" + property.name %}
|
||||
{{config.crdtp.namespace}}::SerializeField({{config.crdtp.namespace}}::SpanFrom("{{property.name}}"), {{property_field}}, out);
|
||||
{% endfor %}
|
||||
out->push_back({{config.crdtp.namespace}}::cbor::EncodeStop());
|
||||
envelope_encoder.EncodeStop(out);
|
||||
}
|
||||
|
||||
std::unique_ptr<{{type.id}}> {{type.id}}::clone() const
|
||||
{
|
||||
ErrorSupport errors;
|
||||
return fromValue(toValue().get(), &errors);
|
||||
}
|
||||
{% if protocol.is_exported(domain.domain, type.id) %}
|
||||
|
||||
// static
|
||||
std::unique_ptr<API::{{type.id}}> API::{{type.id}}::fromBinary(const uint8_t* data, size_t length)
|
||||
{
|
||||
ErrorSupport errors;
|
||||
std::unique_ptr<Value> value = Value::parseBinary(data, length);
|
||||
if (!value)
|
||||
return nullptr;
|
||||
return protocol::{{domain.domain}}::{{type.id}}::fromValue(value.get(), &errors);
|
||||
return protocol::{{domain.domain}}::{{type.id}}::FromBinary(data, length);
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
@ -174,20 +125,11 @@ void Frontend::{{event.name | to_method_case}}(
|
||||
if (!frontend_channel_)
|
||||
return;
|
||||
{% if event.parameters %}
|
||||
std::unique_ptr<{{event.name | to_title_case}}Notification> messageData = {{event.name | to_title_case}}Notification::{{"create" | to_method_case}}()
|
||||
{{config.crdtp.namespace}}::ObjectSerializer serializer;
|
||||
{% for parameter in event.parameters %}
|
||||
{% if not "optional" in parameter %}
|
||||
.{{"set" | to_method_case}}{{parameter.name | to_title_case}}({{protocol.resolve_type(parameter).to_pass_type % parameter.name}})
|
||||
{% endif %}
|
||||
serializer.AddField({{config.crdtp.namespace}}::MakeSpan("{{parameter.name}}"), {{parameter.name}});
|
||||
{% endfor %}
|
||||
.{{ "build" | to_method_case }}();
|
||||
{% for parameter in event.parameters %}
|
||||
{% if "optional" in parameter %}
|
||||
if ({{parameter.name}}.isJust())
|
||||
messageData->{{"set" | to_method_case}}{{parameter.name | to_title_case}}(std::move({{parameter.name}}).takeJust());
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
frontend_channel_->SendProtocolNotification({{config.crdtp.namespace}}::CreateNotification("{{domain.domain}}.{{event.name}}", std::move(messageData)));
|
||||
frontend_channel_->SendProtocolNotification({{config.crdtp.namespace}}::CreateNotification("{{domain.domain}}.{{event.name}}", serializer.Finish()));
|
||||
{% else %}
|
||||
frontend_channel_->SendProtocolNotification({{config.crdtp.namespace}}::CreateNotification("{{domain.domain}}.{{event.name}}"));
|
||||
{% endif %}
|
||||
@ -213,14 +155,14 @@ public:
|
||||
, m_backend(backend) {}
|
||||
~DomainDispatcherImpl() override { }
|
||||
|
||||
using CallHandler = void (DomainDispatcherImpl::*)(const {{config.crdtp.namespace}}::Dispatchable& dispatchable, DictionaryValue* params, ErrorSupport* errors);
|
||||
using CallHandler = void (DomainDispatcherImpl::*)(const {{config.crdtp.namespace}}::Dispatchable& dispatchable);
|
||||
|
||||
std::function<void(const {{config.crdtp.namespace}}::Dispatchable&)> Dispatch({{config.crdtp.namespace}}::span<uint8_t> command_name) override;
|
||||
|
||||
{% for command in domain.commands %}
|
||||
{% if "redirect" in command %}{% continue %}{% endif %}
|
||||
{% if not protocol.generate_command(domain.domain, command.name) %}{% continue %}{% endif %}
|
||||
void {{command.name}}(const {{config.crdtp.namespace}}::Dispatchable& dispatchable, DictionaryValue* params, ErrorSupport* errors);
|
||||
void {{command.name}}(const {{config.crdtp.namespace}}::Dispatchable& dispatchable);
|
||||
{% endfor %}
|
||||
protected:
|
||||
Backend* m_backend;
|
||||
@ -252,13 +194,9 @@ DomainDispatcherImpl::CallHandler CommandByName({{config.crdtp.namespace}}::span
|
||||
std::function<void(const {{config.crdtp.namespace}}::Dispatchable&)> DomainDispatcherImpl::Dispatch({{config.crdtp.namespace}}::span<uint8_t> command_name) {
|
||||
CallHandler handler = CommandByName(command_name);
|
||||
if (!handler) return nullptr;
|
||||
return [this, handler](const {{config.crdtp.namespace}}::Dispatchable& dispatchable){
|
||||
std::unique_ptr<DictionaryValue> params =
|
||||
DictionaryValue::cast(protocol::Value::parseBinary(dispatchable.Params().data(),
|
||||
dispatchable.Params().size()));
|
||||
ErrorSupport errors;
|
||||
errors.Push();
|
||||
(this->*handler)(dispatchable, params.get(), &errors);
|
||||
|
||||
return [this, handler](const {{config.crdtp.namespace}}::Dispatchable& dispatchable) {
|
||||
(this->*handler)(dispatchable);
|
||||
};
|
||||
}
|
||||
|
||||
@ -284,16 +222,11 @@ public:
|
||||
{%- if not loop.last -%}, {% endif -%}
|
||||
{%- endfor -%}) override
|
||||
{
|
||||
std::vector<uint8_t> result_buffer;
|
||||
{{config.crdtp.namespace}}::cbor::EnvelopeEncoder envelope_encoder;
|
||||
envelope_encoder.EncodeStart(&result_buffer);
|
||||
result_buffer.push_back({{config.crdtp.namespace}}::cbor::EncodeIndefiniteLengthMapStart());
|
||||
{{config.crdtp.namespace}}::ObjectSerializer serializer;
|
||||
{% for parameter in command.returns %}
|
||||
{{config.crdtp.namespace}}::SerializeField({{config.crdtp.namespace}}::SpanFrom("{{parameter.name}}"), {{parameter.name}}, &result_buffer);
|
||||
serializer.AddField({{config.crdtp.namespace}}::MakeSpan("{{parameter.name}}"), {{parameter.name}});
|
||||
{% endfor %}
|
||||
result_buffer.push_back({{config.crdtp.namespace}}::cbor::EncodeStop());
|
||||
envelope_encoder.EncodeStop(&result_buffer);
|
||||
sendIfActive({{config.crdtp.namespace}}::Serializable::From(std::move(result_buffer)), DispatchResponse::Success());
|
||||
sendIfActive(serializer.Finish(), DispatchResponse::Success());
|
||||
}
|
||||
|
||||
void fallThrough() override
|
||||
@ -309,26 +242,45 @@ public:
|
||||
};
|
||||
{% endif %}
|
||||
|
||||
void DomainDispatcherImpl::{{command.name}}(const {{config.crdtp.namespace}}::Dispatchable& dispatchable, DictionaryValue* params, ErrorSupport* errors)
|
||||
{
|
||||
namespace {
|
||||
|
||||
{% if "parameters" in command %}
|
||||
// Prepare input parameters.
|
||||
struct {{command.name}}Params : public {{config.crdtp.namespace}}::DeserializableProtocolObject<{{command.name}}Params> {
|
||||
{% for parameter in command.parameters %}
|
||||
{% set parameter_type = protocol.resolve_type(parameter) %}
|
||||
protocol::Value* {{parameter.name}}Value = params ? params->get("{{parameter.name}}") : nullptr;
|
||||
{% if parameter.optional %}
|
||||
Maybe<{{parameter_type.raw_type}}> in_{{parameter.name}};
|
||||
if ({{parameter.name}}Value) {
|
||||
errors->SetName("{{parameter.name}}");
|
||||
in_{{parameter.name}} = ValueConversions<{{parameter_type.raw_type}}>::fromValue({{parameter.name}}Value, errors);
|
||||
}
|
||||
Maybe<{{parameter_type.raw_type}}> {{parameter.name}};
|
||||
{% else %}
|
||||
errors->SetName("{{parameter.name}}");
|
||||
{{parameter_type.type}} in_{{parameter.name}} = ValueConversions<{{parameter_type.raw_type}}>::fromValue({{parameter.name}}Value, errors);
|
||||
{{parameter_type.type}} {{parameter.name}};
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
if (MaybeReportInvalidParams(dispatchable, *errors)) return;
|
||||
DECLARE_DESERIALIZATION_SUPPORT();
|
||||
};
|
||||
|
||||
V8_CRDTP_BEGIN_DESERIALIZER({{command.name}}Params)
|
||||
{% for parameter in command.parameters | sort(attribute = 'name', case_sensitive=True) %}
|
||||
{% if parameter.optional %}
|
||||
V8_CRDTP_DESERIALIZE_FIELD_OPT("{{parameter.name}}", {{parameter.name}}),
|
||||
{% else %}
|
||||
V8_CRDTP_DESERIALIZE_FIELD("{{parameter.name}}", {{parameter.name}}),
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
V8_CRDTP_END_DESERIALIZER()
|
||||
{% endif %}
|
||||
|
||||
} // namespace
|
||||
|
||||
void DomainDispatcherImpl::{{command.name}}(const {{config.crdtp.namespace}}::Dispatchable& dispatchable)
|
||||
{
|
||||
// Prepare input parameters.
|
||||
{% if "parameters" in command %}
|
||||
auto deserializer = {{config.crdtp.namespace}}::DeferredMessage::FromSpan(dispatchable.Params())->MakeDeserializer();
|
||||
{{command.name}}Params params;
|
||||
{{command.name}}Params::Deserialize(&deserializer, ¶ms);
|
||||
if (MaybeReportInvalidParams(dispatchable, deserializer))
|
||||
return;
|
||||
{% endif %}
|
||||
|
||||
{% if "returns" in command and not protocol.is_async_command(domain.domain, command.name) %}
|
||||
// Declare output parameters.
|
||||
{% for parameter in command.returns %}
|
||||
@ -346,9 +298,9 @@ void DomainDispatcherImpl::{{command.name}}(const {{config.crdtp.namespace}}::Di
|
||||
{%- for parameter in command.parameters -%}
|
||||
{%- if not loop.first -%}, {% endif -%}
|
||||
{%- if "optional" in parameter -%}
|
||||
std::move(in_{{parameter.name}})
|
||||
std::move(params.{{parameter.name}})
|
||||
{%- else -%}
|
||||
{{protocol.resolve_type(parameter).to_pass_type % ("in_" + parameter.name)}}
|
||||
{{protocol.resolve_type(parameter).to_pass_type % ("params." + parameter.name)}}
|
||||
{%- endif -%}
|
||||
{%- endfor %}
|
||||
{%- if "returns" in command %}
|
||||
@ -363,18 +315,17 @@ void DomainDispatcherImpl::{{command.name}}(const {{config.crdtp.namespace}}::Di
|
||||
}
|
||||
{% if "returns" in command %}
|
||||
if (weak->get()) {
|
||||
std::vector<uint8_t> result;
|
||||
std::unique_ptr<{{config.crdtp.namespace}}::Serializable> result;
|
||||
if (response.IsSuccess()) {
|
||||
{{config.crdtp.namespace}}::cbor::EnvelopeEncoder envelope_encoder;
|
||||
envelope_encoder.EncodeStart(&result);
|
||||
result.push_back({{config.crdtp.namespace}}::cbor::EncodeIndefiniteLengthMapStart());
|
||||
{{config.crdtp.namespace}}::ObjectSerializer serializer;
|
||||
{% for parameter in command.returns %}
|
||||
{{config.crdtp.namespace}}::SerializeField({{config.crdtp.namespace}}::SpanFrom("{{parameter.name}}"), out_{{parameter.name}}, &result);
|
||||
serializer.AddField({{config.crdtp.namespace}}::MakeSpan("{{parameter.name}}"), out_{{parameter.name}});
|
||||
{% endfor %}
|
||||
result.push_back({{config.crdtp.namespace}}::cbor::EncodeStop());
|
||||
envelope_encoder.EncodeStop(&result);
|
||||
result = serializer.Finish();
|
||||
} else {
|
||||
result = Serializable::From({});
|
||||
}
|
||||
weak->get()->sendResponse(dispatchable.CallId(), response, {{config.crdtp.namespace}}::Serializable::From(std::move(result)));
|
||||
weak->get()->sendResponse(dispatchable.CallId(), response, std::move(result));
|
||||
}
|
||||
{% else %}
|
||||
if (weak->get())
|
||||
@ -386,9 +337,9 @@ void DomainDispatcherImpl::{{command.name}}(const {{config.crdtp.namespace}}::Di
|
||||
{%- for property in command.parameters -%}
|
||||
{%- if not loop.first -%}, {% endif -%}
|
||||
{%- if "optional" in property -%}
|
||||
std::move(in_{{property.name}})
|
||||
std::move(params.{{property.name}})
|
||||
{%- else -%}
|
||||
{{protocol.resolve_type(property).to_pass_type % ("in_" + property.name)}}
|
||||
{{protocol.resolve_type(property).to_pass_type % ("params." + property.name)}}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- if command.parameters -%}, {% endif -%}
|
||||
|
@ -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 %}
|
||||
|
Loading…
Reference in New Issue
Block a user