Skip to content

Commit

Permalink
Implement StandardMethodCodec for C++ shells (flutter#8598)
Browse files Browse the repository at this point in the history
Adds StandardMethodCodec support to the C++ client wrapper. This makes it
substantially easier to add Windows and Linux support for existing plugins, as
StandardMethodCodec is the default plugin protocol.

Fixes flutter/flutter#30670

Does not include extensibility for the codec, which will be added later.
  • Loading branch information
stuartmorgan authored Apr 17, 2019
1 parent 388124f commit 4805d72
Show file tree
Hide file tree
Showing 15 changed files with 1,046 additions and 8 deletions.
7 changes: 7 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,7 @@ FILE: ../../../flutter/shell/platform/android/platform_view_android_jni.cc
FILE: ../../../flutter/shell/platform/android/platform_view_android_jni.h
FILE: ../../../flutter/shell/platform/android/vsync_waiter_android.cc
FILE: ../../../flutter/shell/platform/android/vsync_waiter_android.h
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/byte_stream_wrappers.h
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/encodable_value_unittests.cc
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/engine_method_result.cc
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h
Expand All @@ -575,11 +576,17 @@ FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_codec.h
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_result.h
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_message_codec.h
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/json_message_codec.cc
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/json_method_codec.cc
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/method_call_unittests.cc
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_codec.cc
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_codec_serializer.h
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_message_codec_unittests.cc
FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_method_codec_unittests.cc
FILE: ../../../flutter/shell/platform/common/cpp/incoming_message_dispatcher.cc
FILE: ../../../flutter/shell/platform/common/cpp/incoming_message_dispatcher.h
FILE: ../../../flutter/shell/platform/common/cpp/public/flutter_export.h
Expand Down
13 changes: 11 additions & 2 deletions shell/platform/common/cpp/client_wrapper/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,21 @@ _wrapper_includes = [
"include/flutter/method_codec.h",
"include/flutter/method_result.h",
"include/flutter/plugin_registrar.h",
"include/flutter/standard_message_codec.h",
"include/flutter/standard_method_codec.h",
]

# TODO: Once the wrapper API is more stable, consolidate to as few files as is
# reasonable (without forcing different kinds of clients to take unnecessary
# code) to simplify use.
_wrapper_sources = [
"byte_stream_wrappers.h",
"engine_method_result.cc",
"json_message_codec.cc",
"json_method_codec.cc",
"json_message_codec.cc", # TODO combine into a single json_codec.cc.
"json_method_codec.cc", # TODO combine into a single json_codec.cc.
"plugin_registrar.cc",
"standard_codec_serializer.h",
"standard_codec.cc",
]

# Client library build for internal use by the shell implementation.
Expand Down Expand Up @@ -76,6 +81,10 @@ executable("client_wrapper_unittests") {
"encodable_value_unittests.cc",
"method_call_unittests.cc",
"plugin_registrar_unittests.cc",
"standard_message_codec_unittests.cc",
"standard_method_codec_unittests.cc",
"testing/encodable_value_utils.cc",
"testing/encodable_value_utils.h",
]

deps = [
Expand Down
105 changes: 105 additions & 0 deletions shell/platform/common/cpp/client_wrapper/byte_stream_wrappers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// 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.

#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_BYTE_STREAM_WRAPPERS_H_
#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_BYTE_STREAM_WRAPPERS_H_

// Utility classes for interacting with a buffer of bytes as a stream, for use
// in message channel codecs.

#include <cstdint>
#include <cstring>
#include <iostream>
#include <vector>

namespace flutter {

// Wraps an array of bytes with utility methods for treating it as a readable
// stream.
class ByteBufferStreamReader {
public:
// Createa a reader reading from |bytes|, which must have a length of |size|.
// |bytes| must remain valid for the lifetime of this object.
explicit ByteBufferStreamReader(const uint8_t* bytes, size_t size)
: bytes_(bytes), size_(size) {}

// Reads and returns the next byte from the stream.
uint8_t ReadByte() {
if (location_ >= size_) {
std::cerr << "Invalid read in StandardCodecByteStreamReader" << std::endl;
return 0;
}
return bytes_[location_++];
}

// Reads the next |length| bytes from the stream into |buffer|. The caller
// is responsible for ensuring that |buffer| is large enough.
void ReadBytes(uint8_t* buffer, size_t length) {
if (location_ + length > size_) {
std::cerr << "Invalid read in StandardCodecByteStreamReader" << std::endl;
return;
}
std::memcpy(buffer, &bytes_[location_], length);
location_ += length;
}

// Advances the read cursor to the next multiple of |alignment| relative to
// the start of the wrapped byte buffer, unless it is already aligned.
void ReadAlignment(uint8_t alignment) {
uint8_t mod = location_ % alignment;
if (mod) {
location_ += alignment - mod;
}
}

private:
// The buffer to read from.
const uint8_t* bytes_;
// The total size of the buffer.
size_t size_;
// The current read location.
size_t location_ = 0;
};

// Wraps an array of bytes with utility methods for treating it as a writable
// stream.
class ByteBufferStreamWriter {
public:
// Createa a writter that writes into |buffer|.
// |buffer| must remain valid for the lifetime of this object.
explicit ByteBufferStreamWriter(std::vector<uint8_t>* buffer)
: bytes_(buffer) {
assert(buffer);
}

// Writes |byte| to the wrapped buffer.
void WriteByte(uint8_t byte) { bytes_->push_back(byte); }

// Writes the next |length| bytes from |bytes| into the wrapped buffer.
// The caller is responsible for ensuring that |buffer| is large enough.
void WriteBytes(const uint8_t* bytes, size_t length) {
assert(length > 0);
bytes_->insert(bytes_->end(), bytes, bytes + length);
}

// Writes 0s until the next multiple of |alignment| relative to
// the start of the wrapped byte buffer, unless the write positition is
// already aligned.
void WriteAlignment(uint8_t alignment) {
uint8_t mod = bytes_->size() % alignment;
if (mod) {
for (int i = 0; i < alignment - mod; ++i) {
WriteByte(0);
}
}
}

private:
// The buffer to write to.
std::vector<uint8_t>* bytes_;
};

} // namespace flutter

#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_BYTE_STREAM_WRAPPERS_H_
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ class BasicMessageChannel {
public:
// Creates an instance that sends and receives method calls on the channel
// named |name|, encoded with |codec| and dispatched via |messenger|.
//
// TODO: Make codec optional once the standard codec is supported (Issue #67).
BasicMessageChannel(BinaryMessenger* messenger,
const std::string& name,
const MessageCodec<T>* codec)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,20 @@ class MessageCodec {

// Returns the message encoded in |binary_message|, or nullptr if it cannot be
// decoded by this codec.
// TODO: Consider adding absl as a dependency and using absl::Span.
std::unique_ptr<T> DecodeMessage(const uint8_t* binary_message,
const size_t message_size) const {
return std::move(DecodeMessageInternal(binary_message, message_size));
}

// Returns the message encoded in |binary_message|, or nullptr if it cannot be
// decoded by this codec.
std::unique_ptr<T> DecodeMessage(
const std::vector<uint8_t>& binary_message) const {
size_t size = binary_message.size();
const uint8_t* data = size > 0 ? &binary_message[0] : nullptr;
return std::move(DecodeMessageInternal(data, size));
}

// Returns a binary encoding of the given |message|, or nullptr if the
// message cannot be serialized by this codec.
std::unique_ptr<std::vector<uint8_t>> EncodeMessage(const T& message) const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ class MethodChannel {
public:
// Creates an instance that sends and receives method calls on the channel
// named |name|, encoded with |codec| and dispatched via |messenger|.
//
// TODO: Make codec optional once the standard codec is supported (Issue #67).
MethodChannel(BinaryMessenger* messenger,
const std::string& name,
const MethodCodec<T>* codec)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,21 @@ class MethodCodec {

// Returns the MethodCall encoded in |message|, or nullptr if it cannot be
// decoded.
// TODO: Consider adding absl as a dependency and using absl::Span.
std::unique_ptr<MethodCall<T>> DecodeMethodCall(
const uint8_t* message,
const size_t message_size) const {
return std::move(DecodeMethodCallInternal(message, message_size));
}

// Returns the MethodCall encoded in |message|, or nullptr if it cannot be
// decoded.
std::unique_ptr<MethodCall<T>> DecodeMethodCall(
const std::vector<uint8_t>& message) const {
size_t size = message.size();
const uint8_t* data = size > 0 ? &message[0] : nullptr;
return std::move(DecodeMethodCallInternal(data, size));
}

// Returns a binary encoding of the given |method_call|, or nullptr if the
// method call cannot be serialized by this codec.
std::unique_ptr<std::vector<uint8_t>> EncodeMethodCall(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 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.

#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_MESSAGE_CODEC_H_
#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_MESSAGE_CODEC_H_

#include "encodable_value.h"
#include "message_codec.h"

namespace flutter {

// A binary message encoding/decoding mechanism for communications to/from the
// Flutter engine via message channels.
class StandardMessageCodec : public MessageCodec<EncodableValue> {
public:
// Returns the shared instance of the codec.
static const StandardMessageCodec& GetInstance();

~StandardMessageCodec();

// Prevent copying.
StandardMessageCodec(StandardMessageCodec const&) = delete;
StandardMessageCodec& operator=(StandardMessageCodec const&) = delete;

protected:
// Instances should be obtained via GetInstance.
StandardMessageCodec();

// |flutter::MessageCodec|
std::unique_ptr<EncodableValue> DecodeMessageInternal(
const uint8_t* binary_message,
const size_t message_size) const override;

// |flutter::MessageCodec|
std::unique_ptr<std::vector<uint8_t>> EncodeMessageInternal(
const EncodableValue& message) const override;
};

} // namespace flutter

#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_MESSAGE_CODEC_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// 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.

#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_METHOD_CODEC_H_
#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_METHOD_CODEC_H_

#include "encodable_value.h"
#include "method_call.h"
#include "method_codec.h"

namespace flutter {

// An implementation of MethodCodec that uses a binary serialization.
class StandardMethodCodec : public MethodCodec<EncodableValue> {
public:
// Returns the shared instance of the codec.
static const StandardMethodCodec& GetInstance();

~StandardMethodCodec() = default;

// Prevent copying.
StandardMethodCodec(StandardMethodCodec const&) = delete;
StandardMethodCodec& operator=(StandardMethodCodec const&) = delete;

protected:
// Instances should be obtained via GetInstance.
StandardMethodCodec() = default;

// |flutter::MethodCodec|
std::unique_ptr<MethodCall<EncodableValue>> DecodeMethodCallInternal(
const uint8_t* message,
const size_t message_size) const override;

// |flutter::MethodCodec|
std::unique_ptr<std::vector<uint8_t>> EncodeMethodCallInternal(
const MethodCall<EncodableValue>& method_call) const override;

// |flutter::MethodCodec|
std::unique_ptr<std::vector<uint8_t>> EncodeSuccessEnvelopeInternal(
const EncodableValue* result) const override;

// |flutter::MethodCodec|
std::unique_ptr<std::vector<uint8_t>> EncodeErrorEnvelopeInternal(
const std::string& error_code,
const std::string& error_message,
const EncodableValue* error_details) const override;
};

} // namespace flutter

#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_METHOD_CODEC_H_
Loading

0 comments on commit 4805d72

Please sign in to comment.