flutter_flutter/shell/platform/common/cpp/json_method_codec.cc
stuartmorgan 60b8d00865
Use references for C++ MethodResult and EventSink (#20651)
The response APIs for method channels and event channels used pointers
for optional parameters; this kept the API surface simple, but meant
that they couldn't take rvalues. As a result, returning success values
or error details often took an extra line, declaring a variable for the
result just to have something to pass the address of.

This converts them to using references, with function overloading to
allow for optional parameters, so that values can be inlined.

For now the pointer versions are still present, so that conversion can
be done before it becomes a breaking change; they will be removed soon.

Part of https://github.com/flutter/flutter/issues/63975
2020-08-20 15:10:28 -07:00

159 lines
5.4 KiB
C++

// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// FLUTTER_NOLINT
#include "flutter/shell/platform/common/cpp/json_method_codec.h"
#include "flutter/shell/platform/common/cpp/json_message_codec.h"
namespace flutter {
namespace {
// Keys used in MethodCall encoding.
constexpr char kMessageMethodKey[] = "method";
constexpr char kMessageArgumentsKey[] = "args";
// Returns a new document containing only |element|, which must be an element
// in |document|. This is a move rather than a copy, so it is efficient but
// destructive to the data in |document|.
std::unique_ptr<rapidjson::Document> ExtractElement(
rapidjson::Document* document,
rapidjson::Value* subtree) {
auto extracted = std::make_unique<rapidjson::Document>();
// Pull the subtree up to the root of the document.
document->Swap(*subtree);
// Swap the entire document into |extracted|. Unlike the swap above this moves
// the allocator ownership, so the data won't be deleted when |document| is
// destroyed.
extracted->Swap(*document);
return extracted;
}
} // namespace
// static
const JsonMethodCodec& JsonMethodCodec::GetInstance() {
static JsonMethodCodec sInstance;
return sInstance;
}
std::unique_ptr<MethodCall<rapidjson::Document>>
JsonMethodCodec::DecodeMethodCallInternal(const uint8_t* message,
size_t message_size) const {
std::unique_ptr<rapidjson::Document> json_message =
JsonMessageCodec::GetInstance().DecodeMessage(message, message_size);
if (!json_message) {
return nullptr;
}
auto method_name_iter = json_message->FindMember(kMessageMethodKey);
if (method_name_iter == json_message->MemberEnd()) {
return nullptr;
}
if (!method_name_iter->value.IsString()) {
return nullptr;
}
std::string method_name(method_name_iter->value.GetString());
auto arguments_iter = json_message->FindMember(kMessageArgumentsKey);
std::unique_ptr<rapidjson::Document> arguments;
if (arguments_iter != json_message->MemberEnd()) {
arguments = ExtractElement(json_message.get(), &(arguments_iter->value));
}
return std::make_unique<MethodCall<rapidjson::Document>>(
method_name, std::move(arguments));
}
std::unique_ptr<std::vector<uint8_t>> JsonMethodCodec::EncodeMethodCallInternal(
const MethodCall<rapidjson::Document>& method_call) const {
// TODO: Consider revisiting the codec APIs to avoid the need to copy
// everything when doing encoding (e.g., by having a version that takes
// owership of the object to encode, so that it can be moved instead).
rapidjson::Document message(rapidjson::kObjectType);
auto& allocator = message.GetAllocator();
rapidjson::Value name(method_call.method_name(), allocator);
rapidjson::Value arguments;
if (method_call.arguments()) {
arguments.CopyFrom(*method_call.arguments(), allocator);
}
message.AddMember(kMessageMethodKey, name, allocator);
message.AddMember(kMessageArgumentsKey, arguments, allocator);
return JsonMessageCodec::GetInstance().EncodeMessage(message);
}
std::unique_ptr<std::vector<uint8_t>>
JsonMethodCodec::EncodeSuccessEnvelopeInternal(
const rapidjson::Document* result) const {
rapidjson::Document envelope;
envelope.SetArray();
rapidjson::Value result_value;
if (result) {
result_value.CopyFrom(*result, envelope.GetAllocator());
}
envelope.PushBack(result_value, envelope.GetAllocator());
return JsonMessageCodec::GetInstance().EncodeMessage(envelope);
}
std::unique_ptr<std::vector<uint8_t>>
JsonMethodCodec::EncodeErrorEnvelopeInternal(
const std::string& error_code,
const std::string& error_message,
const rapidjson::Document* error_details) const {
rapidjson::Document envelope(rapidjson::kArrayType);
auto& allocator = envelope.GetAllocator();
envelope.PushBack(rapidjson::Value(error_code, allocator), allocator);
envelope.PushBack(rapidjson::Value(error_message, allocator), allocator);
rapidjson::Value details_value;
if (error_details) {
details_value.CopyFrom(*error_details, allocator);
}
envelope.PushBack(details_value, allocator);
return JsonMessageCodec::GetInstance().EncodeMessage(envelope);
}
bool JsonMethodCodec::DecodeAndProcessResponseEnvelopeInternal(
const uint8_t* response,
size_t response_size,
MethodResult<rapidjson::Document>* result) const {
std::unique_ptr<rapidjson::Document> json_response =
JsonMessageCodec::GetInstance().DecodeMessage(response, response_size);
if (!json_response) {
return false;
}
if (!json_response->IsArray()) {
return false;
}
switch (json_response->Size()) {
case 1: {
std::unique_ptr<rapidjson::Document> value =
ExtractElement(json_response.get(), &((*json_response)[0]));
if (value->IsNull()) {
result->Success();
} else {
result->Success(*value);
}
return true;
}
case 3: {
std::string code = (*json_response)[0].GetString();
std::string message = (*json_response)[1].GetString();
std::unique_ptr<rapidjson::Document> details =
ExtractElement(json_response.get(), &((*json_response)[2]));
if (details->IsNull()) {
result->Error(code, message);
} else {
result->Error(code, message, *details);
}
return true;
}
default:
return false;
}
}
} // namespace flutter