diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 30bbd785e60..2a1050540b9 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -560,9 +560,11 @@ 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/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 FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/encodable_value.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/engine_method_result.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_message_codec.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_method_codec.h diff --git a/shell/platform/common/cpp/client_wrapper/BUILD.gn b/shell/platform/common/cpp/client_wrapper/BUILD.gn index 1aacc758abe..f226c85a963 100644 --- a/shell/platform/common/cpp/client_wrapper/BUILD.gn +++ b/shell/platform/common/cpp/client_wrapper/BUILD.gn @@ -7,6 +7,7 @@ import("publish.gni") _wrapper_includes = [ "include/flutter/basic_message_channel.h", "include/flutter/binary_messenger.h", + "include/flutter/encodable_value.h", "include/flutter/engine_method_result.h", "include/flutter/json_message_codec.h", "include/flutter/json_method_codec.h", @@ -72,6 +73,7 @@ executable("client_wrapper_unittests") { # TODO: Add more unit tests. sources = [ + "encodable_value_unittests.cc", "method_call_unittests.cc", "plugin_registrar_unittests.cc", ] diff --git a/shell/platform/common/cpp/client_wrapper/encodable_value_unittests.cc b/shell/platform/common/cpp/client_wrapper/encodable_value_unittests.cc new file mode 100644 index 00000000000..6784fa4b775 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/encodable_value_unittests.cc @@ -0,0 +1,329 @@ +// 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. + +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/encodable_value.h" + +#include + +#include "gtest/gtest.h" + +namespace flutter { + +// Verifies that value.type() is |type|, and that of all the Is* methods, only +// the one that matches the type is true. +void VerifyType(EncodableValue& value, + EncodableValue::EncodableValue::Type type) { + EXPECT_EQ(value.type(), type); + + EXPECT_EQ(value.IsNull(), type == EncodableValue::Type::kNull); + EXPECT_EQ(value.IsBool(), type == EncodableValue::Type::kBool); + EXPECT_EQ(value.IsInt(), type == EncodableValue::Type::kInt); + EXPECT_EQ(value.IsLong(), type == EncodableValue::Type::kLong); + EXPECT_EQ(value.IsDouble(), type == EncodableValue::Type::kDouble); + EXPECT_EQ(value.IsString(), type == EncodableValue::Type::kString); + EXPECT_EQ(value.IsByteList(), type == EncodableValue::Type::kByteList); + EXPECT_EQ(value.IsIntList(), type == EncodableValue::Type::kIntList); + EXPECT_EQ(value.IsLongList(), type == EncodableValue::Type::kLongList); + EXPECT_EQ(value.IsDoubleList(), type == EncodableValue::Type::kDoubleList); + EXPECT_EQ(value.IsList(), type == EncodableValue::Type::kList); + EXPECT_EQ(value.IsMap(), type == EncodableValue::Type::kMap); +} + +TEST(EncodableValueTest, Null) { + EncodableValue value; + VerifyType(value, EncodableValue::Type::kNull); +} + +TEST(EncodableValueTest, Bool) { + EncodableValue value(false); + VerifyType(value, EncodableValue::Type::kBool); + + EXPECT_FALSE(value.BoolValue()); + value = true; + EXPECT_TRUE(value.BoolValue()); +} + +TEST(EncodableValueTest, Int) { + EncodableValue value(42); + VerifyType(value, EncodableValue::Type::kInt); + + EXPECT_EQ(value.IntValue(), 42); + value = std::numeric_limits::max(); + EXPECT_EQ(value.IntValue(), std::numeric_limits::max()); +} + +TEST(EncodableValueTest, LongValueFromInt) { + EncodableValue value(std::numeric_limits::max()); + EXPECT_EQ(value.LongValue(), std::numeric_limits::max()); +} + +TEST(EncodableValueTest, Long) { + EncodableValue value(42l); + VerifyType(value, EncodableValue::Type::kLong); + + EXPECT_EQ(value.LongValue(), 42); + value = std::numeric_limits::max(); + EXPECT_EQ(value.LongValue(), std::numeric_limits::max()); +} + +TEST(EncodableValueTest, Double) { + EncodableValue value(3.14); + VerifyType(value, EncodableValue::Type::kDouble); + + EXPECT_EQ(value.DoubleValue(), 3.14); + value = std::numeric_limits::max(); + EXPECT_EQ(value.DoubleValue(), std::numeric_limits::max()); +} + +TEST(EncodableValueTest, String) { + std::string hello("Hello, world!"); + EncodableValue value(hello); + VerifyType(value, EncodableValue::Type::kString); + + EXPECT_EQ(value.StringValue(), hello); + value = "Goodbye"; + EXPECT_EQ(value.StringValue(), "Goodbye"); +} + +TEST(EncodableValueTest, UInt8List) { + std::vector data = {0, 2}; + EncodableValue value(data); + VerifyType(value, EncodableValue::Type::kByteList); + + std::vector& list_value = value.ByteListValue(); + list_value.push_back(std::numeric_limits::max()); + EXPECT_EQ(list_value[0], 0); + EXPECT_EQ(list_value[1], 2); + + ASSERT_EQ(list_value.size(), 3u); + EXPECT_EQ(data.size(), 2u); + EXPECT_EQ(list_value[2], std::numeric_limits::max()); +} + +TEST(EncodableValueTest, Int32List) { + std::vector data = {-10, 2}; + EncodableValue value(data); + VerifyType(value, EncodableValue::Type::kIntList); + + std::vector& list_value = value.IntListValue(); + list_value.push_back(std::numeric_limits::max()); + EXPECT_EQ(list_value[0], -10); + EXPECT_EQ(list_value[1], 2); + + ASSERT_EQ(list_value.size(), 3u); + EXPECT_EQ(data.size(), 2u); + EXPECT_EQ(list_value[2], std::numeric_limits::max()); +} + +TEST(EncodableValueTest, Int64List) { + std::vector data = {-10, 2}; + EncodableValue value(data); + VerifyType(value, EncodableValue::Type::kLongList); + + std::vector& list_value = value.LongListValue(); + list_value.push_back(std::numeric_limits::max()); + EXPECT_EQ(list_value[0], -10); + EXPECT_EQ(list_value[1], 2); + + ASSERT_EQ(list_value.size(), 3u); + EXPECT_EQ(data.size(), 2u); + EXPECT_EQ(list_value[2], std::numeric_limits::max()); +} + +TEST(EncodableValueTest, DoubleList) { + std::vector data = {-10.0, 2.0}; + EncodableValue value(data); + VerifyType(value, EncodableValue::Type::kDoubleList); + + std::vector& list_value = value.DoubleListValue(); + list_value.push_back(std::numeric_limits::max()); + EXPECT_EQ(list_value[0], -10.0); + EXPECT_EQ(list_value[1], 2.0); + + ASSERT_EQ(list_value.size(), 3u); + EXPECT_EQ(data.size(), 2u); + EXPECT_EQ(list_value[2], std::numeric_limits::max()); +} + +TEST(EncodableValueTest, List) { + EncodableList encodables = { + EncodableValue(1), + EncodableValue(2.0), + EncodableValue("Three"), + }; + EncodableValue value(encodables); + VerifyType(value, EncodableValue::Type::kList); + + EncodableList& list_value = value.ListValue(); + EXPECT_EQ(list_value[0].IntValue(), 1); + EXPECT_EQ(list_value[1].DoubleValue(), 2.0); + EXPECT_EQ(list_value[2].StringValue(), "Three"); + + // Ensure that it's a modifiable copy of the original array. + list_value.push_back(EncodableValue(true)); + ASSERT_EQ(list_value.size(), 4u); + EXPECT_EQ(encodables.size(), 3u); + EXPECT_EQ(value.ListValue()[3].BoolValue(), true); +} + +TEST(EncodableValueTest, Map) { + EncodableMap encodables = { + {EncodableValue(), EncodableValue(std::vector{1, 2, 3})}, + {EncodableValue(1), EncodableValue(10000l)}, + {EncodableValue("two"), EncodableValue(7)}, + }; + EncodableValue value(encodables); + VerifyType(value, EncodableValue::Type::kMap); + + EncodableMap& map_value = value.MapValue(); + EXPECT_EQ(map_value[EncodableValue()].IsIntList(), true); + EXPECT_EQ(map_value[EncodableValue(1)].LongValue(), 10000l); + EXPECT_EQ(map_value[EncodableValue("two")].IntValue(), 7); + + // Ensure that it's a modifiable copy of the original map. + map_value[EncodableValue(true)] = EncodableValue(false); + ASSERT_EQ(map_value.size(), 4u); + EXPECT_EQ(encodables.size(), 3u); + EXPECT_EQ(map_value[EncodableValue(true)].BoolValue(), false); +} + +TEST(EncodableValueTest, EmptyTypeConstructor) { + EXPECT_TRUE(EncodableValue(EncodableValue::Type::kNull).IsNull()); + EXPECT_EQ(EncodableValue(EncodableValue::Type::kBool).BoolValue(), false); + EXPECT_EQ(EncodableValue(EncodableValue::Type::kInt).IntValue(), 0); + EXPECT_EQ(EncodableValue(EncodableValue::Type::kLong).LongValue(), 0l); + EXPECT_EQ(EncodableValue(EncodableValue::Type::kDouble).DoubleValue(), 0.0); + EXPECT_EQ(EncodableValue(EncodableValue::Type::kString).StringValue().size(), + 0u); + EXPECT_EQ( + EncodableValue(EncodableValue::Type::kByteList).ByteListValue().size(), + 0u); + EXPECT_EQ( + EncodableValue(EncodableValue::Type::kIntList).IntListValue().size(), 0u); + EXPECT_EQ( + EncodableValue(EncodableValue::Type::kLongList).LongListValue().size(), + 0u); + EXPECT_EQ(EncodableValue(EncodableValue::Type::kDoubleList) + .DoubleListValue() + .size(), + 0u); + EXPECT_EQ(EncodableValue(EncodableValue::Type::kList).ListValue().size(), 0u); + EXPECT_EQ(EncodableValue(EncodableValue::Type::kMap).MapValue().size(), 0u); +} + +// Tests that the < operator meets the requirements of using EncodableValue as +// a map key. +TEST(EncodableValueTest, Comparison) { + EncodableList values = { + // Null + EncodableValue(), + // Bool + EncodableValue(true), + EncodableValue(false), + // Int + EncodableValue(-7), + EncodableValue(0), + EncodableValue(100), + // Long + EncodableValue(-7l), + EncodableValue(0l), + EncodableValue(100l), + // Double + EncodableValue(-7.0), + EncodableValue(0.0), + EncodableValue(100.0), + // String + EncodableValue("one"), + EncodableValue("two"), + // ByteList + EncodableValue(std::vector{0, 1}), + EncodableValue(std::vector{0, 10}), + // IntList + EncodableValue(std::vector{0, 1}), + EncodableValue(std::vector{0, 100}), + // LongList + EncodableValue(std::vector{0, 1l}), + EncodableValue(std::vector{0, 100l}), + // DoubleList + EncodableValue(std::vector{0, 1l}), + EncodableValue(std::vector{0, 100l}), + // List + EncodableValue(EncodableList{EncodableValue(), EncodableValue(true)}), + EncodableValue(EncodableList{EncodableValue(), EncodableValue(1.0)}), + // Map + EncodableValue(EncodableMap{{EncodableValue(), EncodableValue(true)}, + {EncodableValue(7), EncodableValue(7.0)}}), + EncodableValue( + EncodableMap{{EncodableValue(), EncodableValue(1.0)}, + {EncodableValue("key"), EncodableValue("value")}}), + }; + + for (size_t i = 0; i < values.size(); ++i) { + const auto& a = values[i]; + for (size_t j = 0; j < values.size(); ++j) { + const auto& b = values[j]; + if (i == j) { + // Identical objects should always be equal. + EXPECT_FALSE(a < b); + EXPECT_FALSE(b < a); + } else { + // All other comparisons should be consistent, but the direction doesn't + // matter. + EXPECT_NE(a < b, b < a); + } + } + + // Different non-collection objects with the same value should be equal; + // different collections should always be unequal regardless of contents. + bool is_collection = a.IsByteList() || a.IsIntList() || a.IsLongList() || + a.IsDoubleList() || a.IsList() || a.IsMap(); + EncodableValue copy(a); + bool is_equal = !(a < copy || copy < a); + EXPECT_EQ(is_equal, !is_collection); + } +} + +// Tests that structures are deep-copied. +TEST(EncodableValueTest, DeepCopy) { + EncodableList encodables = { + EncodableValue(EncodableMap{ + {EncodableValue(), EncodableValue(std::vector{1, 2, 3})}, + {EncodableValue(1), EncodableValue(10000l)}, + {EncodableValue("two"), EncodableValue(7)}, + }), + EncodableValue(EncodableList{ + EncodableValue(), + EncodableValue(), + EncodableValue( + EncodableMap{{EncodableValue("a"), EncodableValue("b")}}), + }), + }; + + EncodableValue value(encodables); + ASSERT_TRUE(value.IsList()); + + // Spot-check innermost collection values. + EXPECT_EQ(value.ListValue()[0].MapValue()[EncodableValue("two")].IntValue(), + 7); + EXPECT_EQ(value.ListValue()[1] + .ListValue()[2] + .MapValue()[EncodableValue("a")] + .StringValue(), + "b"); + + // Modify those values in the original structure. + encodables[0].MapValue()[EncodableValue("two")] = EncodableValue(); + encodables[1].ListValue()[2].MapValue()[EncodableValue("a")] = 99; + + // Re-check innermost collection values to ensure that they haven't changed. + EXPECT_EQ(value.ListValue()[0].MapValue()[EncodableValue("two")].IntValue(), + 7); + EXPECT_EQ(value.ListValue()[1] + .ListValue()[2] + .MapValue()[EncodableValue("a")] + .StringValue(), + "b"); +} + +} // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/encodable_value.h b/shell/platform/common/cpp/client_wrapper/include/flutter/encodable_value.h new file mode 100644 index 00000000000..03d9803fe14 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/encodable_value.h @@ -0,0 +1,569 @@ +// 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_ENCODABLE_VALUE_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENCODABLE_VALUE_H_ + +#include +#include +#include +#include +#include +#include + +namespace flutter { + +static_assert(sizeof(double) == 8, "EncodableValue requires a 64-bit double"); + +class EncodableValue; +// Convenience type aliases for list and map EncodableValue types. +using EncodableList = std::vector; +using EncodableMap = std::map; + +// An object that can contain any value or collection type supported by +// Flutter's standard method codec. +// +// For details, see: +// https://api.flutter.dev/flutter/services/StandardMessageCodec-class.html +// +// As an example, the following Dart structure: +// { +// 'flag': true, +// 'name': 'Thing', +// 'values': [1, 2.0, 4], +// } +// would correspond to: +// EncodableValue(EncodableMap{ +// {EncodableValue("flag"), EncodableValue(true)}, +// {EncodableValue("name"), EncodableValue("Thing")}, +// {EncodableValue("values"), EncodableValue(EncodableList{ +// EncodableValue(1), +// EncodableValue(2.0), +// EncodableValue(4), +// })}, +// }) +class EncodableValue { + public: + // Possible types for an EncodableValue to reperesent. + enum class Type { + kNull, // A null value. + kBool, // A boolean value. + kInt, // A 32-bit integer. + kLong, // A 64-bit integer. + kDouble, // A 64-bit floating point number. + kString, // A string. + kByteList, // A list of bytes. + kIntList, // A list of 32-bit integers. + kLongList, // A list of 64-bit integers. + kDoubleList, // A list of 64-bit floating point numbers. + kList, // A list of EncodableValues. + kMap, // A mapping from EncodableValues to EncodableValues. + }; + + // Creates an instance representing a null value. + EncodableValue() {} + + // Creates an instance representing a bool value. + explicit EncodableValue(bool value) : bool_(value), type_(Type::kBool) {} + + // Creates an instance representing a 32-bit integer value. + explicit EncodableValue(int32_t value) : int_(value), type_(Type::kInt) {} + + // Creates an instance representing a 64-bit integer value. + explicit EncodableValue(int64_t value) : long_(value), type_(Type::kLong) {} + + // Creates an instance representing a 64-bit floating point value. + explicit EncodableValue(double value) + : double_(value), type_(Type::kDouble) {} + + // Creates an instance representing a string value. + explicit EncodableValue(const char* value) + : string_(new std::string(value)), type_(Type::kString) {} + + // Creates an instance representing a string value. + explicit EncodableValue(const std::string& value) + : string_(new std::string(value)), type_(Type::kString) {} + + // Creates an instance representing a list of bytes. + explicit EncodableValue(std::vector list) + : byte_list_(new std::vector(std::move(list))), + type_(Type::kByteList) {} + + // Creates an instance representing a list of 32-bit integers. + explicit EncodableValue(std::vector list) + : int_list_(new std::vector(std::move(list))), + type_(Type::kIntList) {} + + // Creates an instance representing a list of 64-bit integers. + explicit EncodableValue(std::vector list) + : long_list_(new std::vector(std::move(list))), + type_(Type::kLongList) {} + + // Creates an instance representing a list of 64-bit floating point values. + explicit EncodableValue(std::vector list) + : double_list_(new std::vector(std::move(list))), + type_(Type::kDoubleList) {} + + // Creates an instance representing a list of EncodableValues. + explicit EncodableValue(EncodableList list) + : list_(new EncodableList(std::move(list))), type_(Type::kList) {} + + // Creates an instance representing a map from EncodableValues to + // EncodableValues. + explicit EncodableValue(EncodableMap map) + : map_(new EncodableMap(std::move(map))), type_(Type::kMap) {} + + // Convience constructor for creating default value of the given type. + // + // Collections types will be empty, numeric types will be 0, strings will be + // empty, and booleans will be false. For non-collection types, prefer using + // the value-based constructor with an explicit value for clarity. + explicit EncodableValue(Type type) : type_(type) { + switch (type_) { + case Type::kNull: + break; + case Type::kBool: + bool_ = false; + break; + case Type::kInt: + int_ = 0; + break; + case Type::kLong: + long_ = 0; + break; + case Type::kDouble: + double_ = 0.0; + break; + case Type::kString: + string_ = new std::string(); + break; + case Type::kByteList: + byte_list_ = new std::vector(); + break; + case Type::kIntList: + int_list_ = new std::vector(); + break; + case Type::kLongList: + long_list_ = new std::vector(); + break; + case Type::kDoubleList: + double_list_ = new std::vector(); + break; + case Type::kList: + list_ = new std::vector(); + break; + case Type::kMap: + map_ = new std::map(); + break; + } + } + + ~EncodableValue() { DestroyValue(); } + + EncodableValue(const EncodableValue& other) { + DestroyValue(); + + type_ = other.type_; + switch (type_) { + case Type::kNull: + break; + case Type::kBool: + bool_ = other.bool_; + break; + case Type::kInt: + int_ = other.int_; + break; + case Type::kLong: + long_ = other.long_; + break; + case Type::kDouble: + double_ = other.double_; + break; + case Type::kString: + string_ = new std::string(*other.string_); + break; + case Type::kByteList: + byte_list_ = new std::vector(*other.byte_list_); + break; + case Type::kIntList: + int_list_ = new std::vector(*other.int_list_); + break; + case Type::kLongList: + long_list_ = new std::vector(*other.long_list_); + break; + case Type::kDoubleList: + double_list_ = new std::vector(*other.double_list_); + break; + case Type::kList: + list_ = new std::vector(*other.list_); + break; + case Type::kMap: + map_ = new std::map(*other.map_); + break; + } + } + + EncodableValue(EncodableValue&& other) { *this = std::move(other); } + + EncodableValue& operator=(const EncodableValue& other) { + if (&other == this) { + return *this; + } + using std::swap; + EncodableValue temp(other); + swap(*this, temp); + return *this; + } + + EncodableValue& operator=(EncodableValue&& other) { + if (&other == this) { + return *this; + } + DestroyValue(); + + type_ = other.type_; + switch (type_) { + case Type::kNull: + break; + case Type::kBool: + bool_ = other.bool_; + break; + case Type::kInt: + int_ = other.int_; + break; + case Type::kLong: + long_ = other.long_; + break; + case Type::kDouble: + double_ = other.double_; + break; + case Type::kString: + string_ = other.string_; + break; + case Type::kByteList: + byte_list_ = other.byte_list_; + break; + case Type::kIntList: + int_list_ = other.int_list_; + break; + case Type::kLongList: + long_list_ = other.long_list_; + break; + case Type::kDoubleList: + double_list_ = other.double_list_; + break; + case Type::kList: + list_ = other.list_; + break; + case Type::kMap: + map_ = other.map_; + break; + } + // Ensure that destruction doesn't run on the source of the move. + other.type_ = Type::kNull; + return *this; + } + + // Allow assigning any value type that can be used for a constructor. + template + EncodableValue& operator=(const T& value) { + *this = EncodableValue(value); + return *this; + } + + // This operator exists only to provide a stable ordering for use as a + // std::map key. It does not attempt to provide useful ordering semantics. + // Notably: + // - Numeric values are not guaranteed any ordering across numeric types. + // E.g., 1 as a Long may sort after 100 as an Int. + // - Collection types use pointer equality, rather than value. This means that + // multiple collections with the same values will end up as separate keys + // in a map (consistent with default Dart Map behavior). + bool operator<(const EncodableValue& other) const { + if (type_ != other.type_) { + return type_ < other.type_; + } + switch (type_) { + case Type::kNull: + return false; + case Type::kBool: + return bool_ < other.bool_; + case Type::kInt: + return int_ < other.int_; + case Type::kLong: + return long_ < other.long_; + case Type::kDouble: + return double_ < other.double_; + case Type::kString: + return *string_ < *other.string_; + case Type::kByteList: + case Type::kIntList: + case Type::kLongList: + case Type::kDoubleList: + case Type::kList: + case Type::kMap: + return this < &other; + } + assert(false); + return false; + } + + // Returns the bool value this object represents. + // + // It is a programming error to call this unless IsBool() is true. + bool BoolValue() const { + assert(IsBool()); + return bool_; + } + + // Returns the 32-bit integer value this object represents. + // + // It is a programming error to call this unless IsInt() is true. + int32_t IntValue() const { + assert(IsInt()); + return int_; + } + + // Returns the 64-bit integer value this object represents. + // + // It is a programming error to call this unless IsLong() or IsInt() is true. + // + // Note that calling this function on an Int value is the only case where + // a *Value() function can be called without the corresponding Is*() being + // true. This is to simplify handling objects received from Flutter where the + // values may be larger than 32-bit, since they have the same type on the Dart + // side, but will be either 32-bit or 64-bit here depending on the value. + int64_t LongValue() const { + assert(IsLong() || IsInt()); + if (IsLong()) { + return long_; + } + return int_; + } + + // Returns the double value this object represents. + // + // It is a programming error to call this unless IsDouble() is true. + double DoubleValue() const { + assert(IsDouble()); + return double_; + } + + // Returns the string value this object represents. + // + // It is a programming error to call this unless IsString() is true. + const std::string& StringValue() const { + assert(IsString()); + return *string_; + } + + // Returns the byte list this object represents. + // + // It is a programming error to call this unless IsByteList() is true. + const std::vector& ByteListValue() const { + assert(IsByteList()); + return *byte_list_; + } + + // Returns the byte list this object represents. + // + // It is a programming error to call this unless IsByteList() is true. + std::vector& ByteListValue() { + assert(IsByteList()); + return *byte_list_; + } + + // Returns the 32-bit integer list this object represents. + // + // It is a programming error to call this unless IsIntList() is true. + const std::vector& IntListValue() const { + assert(IsIntList()); + return *int_list_; + } + + // Returns the 32-bit integer list this object represents. + // + // It is a programming error to call this unless IsIntList() is true. + std::vector& IntListValue() { + assert(IsIntList()); + return *int_list_; + } + + // Returns the 64-bit integer list this object represents. + // + // It is a programming error to call this unless IsLongList() is true. + const std::vector& LongListValue() const { + assert(IsLongList()); + return *long_list_; + } + + // Returns the 64-bit integer list this object represents. + // + // It is a programming error to call this unless IsLongList() is true. + std::vector& LongListValue() { + assert(IsLongList()); + return *long_list_; + } + + // Returns the double list this object represents. + // + // It is a programming error to call this unless IsDoubleList() is true. + const std::vector& DoubleListValue() const { + assert(IsDoubleList()); + return *double_list_; + } + + // Returns the double list this object represents. + // + // It is a programming error to call this unless IsDoubleList() is true. + std::vector& DoubleListValue() { + assert(IsDoubleList()); + return *double_list_; + } + + // Returns the list of EncodableValues this object represents. + // + // It is a programming error to call this unless IsList() is true. + const EncodableList& ListValue() const { + assert(IsList()); + return *list_; + } + + // Returns the list of EncodableValues this object represents. + // + // It is a programming error to call this unless IsList() is true. + EncodableList& ListValue() { + assert(IsList()); + return *list_; + } + + // Returns the map of EncodableValue : EncodableValue pairs this object + // represent. + // + // It is a programming error to call this unless IsMap() is true. + const EncodableMap& MapValue() const { + assert(IsMap()); + return *map_; + } + + // Returns the map of EncodableValue : EncodableValue pairs this object + // represent. + // + // It is a programming error to call this unless IsMap() is true. + EncodableMap& MapValue() { + assert(IsMap()); + return *map_; + } + + // Returns true if this represents a null value. + bool IsNull() const { return type_ == Type::kNull; } + + // Returns true if this represents a bool value. + bool IsBool() const { return type_ == Type::kBool; } + + // Returns true if this represents a 32-bit integer value. + bool IsInt() const { return type_ == Type::kInt; } + + // Returns true if this represents a 64-bit integer value. + bool IsLong() const { return type_ == Type::kLong; } + + // Returns true if this represents a double value. + bool IsDouble() const { return type_ == Type::kDouble; } + + // Returns true if this represents a string value. + bool IsString() const { return type_ == Type::kString; } + + // Returns true if this represents a list of bytes. + bool IsByteList() const { return type_ == Type::kByteList; } + + // Returns true if this represents a list of 32-bit integers. + bool IsIntList() const { return type_ == Type::kIntList; } + + // Returns true if this represents a list of 64-bit integers. + bool IsLongList() const { return type_ == Type::kLongList; } + + // Returns true if this represents a list of doubles. + bool IsDoubleList() const { return type_ == Type::kDoubleList; } + + // Returns true if this represents a list of EncodableValues. + bool IsList() const { return type_ == Type::kList; } + + // Returns true if this represents a map of EncodableValue : EncodableValue + // pairs. + bool IsMap() const { return type_ == Type::kMap; } + + // Returns the type this value represents. + // + // This is primarily intended for use with switch(); for individual checks, + // prefer an Is*() call. + Type type() const { return type_; } + + private: + // Performs any cleanup necessary for the active union value. This must be + // called before assigning a new value, and on object destruction. + // + // After calling this, type_ will alway be kNull. + void DestroyValue() { + switch (type_) { + case Type::kNull: + case Type::kBool: + case Type::kInt: + case Type::kLong: + case Type::kDouble: + break; + case Type::kString: + delete string_; + break; + case Type::kByteList: + delete byte_list_; + break; + case Type::kIntList: + delete int_list_; + break; + case Type::kLongList: + delete long_list_; + break; + case Type::kDoubleList: + delete double_list_; + break; + case Type::kList: + delete list_; + break; + case Type::kMap: + delete map_; + break; + } + + type_ = Type::kNull; + } + + // The anonymous union that stores the represented value. Accessing any of + // these entries other than the one that corresponds to the current value of + // |type_| has undefined behavior. + // + // Pointers are used for the non-POD types to avoid making the overall size + // of the union unnecessarily large. + // + // TODO: Replace this with std::variant once c++17 is available. + union { + bool bool_; + int32_t int_; + int64_t long_; + double double_; + std::string* string_; + std::vector* byte_list_; + std::vector* int_list_; + std::vector* long_list_; + std::vector* double_list_; + std::vector* list_; + std::map* map_; + }; + + // The currently active union entry. + Type type_ = Type::kNull; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENCODABLE_VALUE_H_