diff --git a/tools/fidlcat/lib/library_loader.cc b/tools/fidlcat/lib/library_loader.cc index 77d66c7bf7f6324fb1ad9c9b8bccc801a22503ff..62de4252c32b03b019b3f9b5f585c2996fab5f3b 100644 --- a/tools/fidlcat/lib/library_loader.cc +++ b/tools/fidlcat/lib/library_loader.cc @@ -113,16 +113,17 @@ InterfaceMethod::InterfaceMethod(const Interface& interface, const rapidjson::Value& value) : enclosing_interface_(interface), ordinal_(std::strtoll(value["ordinal"].GetString(), nullptr, 10)), - name_(value["name"].GetString()), value_(value) { + name_(value["name"].GetString()), + value_(value) { if (value_["has_request"].GetBool()) { request_ = std::make_unique<Struct>(interface.enclosing_library(), value, "maybe_request_size", "maybe_request"); } if (value_["has_response"].GetBool()) { - response_ = std::make_unique<Struct>(interface.enclosing_library(), value, - "maybe_response_size", - "maybe_response"); + response_ = + std::make_unique<Struct>(interface.enclosing_library(), value, + "maybe_response_size", "maybe_response"); } } @@ -173,6 +174,11 @@ Library::Library(const LibraryLoader& enclosing, rapidjson::Document& document) std::forward_as_tuple(uni["name"].GetString()), std::forward_as_tuple(*this, uni)); } + for (auto& xuni : backing_document_["xunion_declarations"].GetArray()) { + xunions_.emplace(std::piecewise_construct, + std::forward_as_tuple(xuni["name"].GetString()), + std::forward_as_tuple(*this, xuni)); + } } std::unique_ptr<Type> Library::TypeFromIdentifier( @@ -202,7 +208,11 @@ std::unique_ptr<Type> Library::TypeFromIdentifier( } return type; } - // And probably for tables. + auto xuni = xunions_.find(identifier); + if (xuni != xunions_.end()) { + // Note: XUnion and nullable XUnion are encoded in the same way + return std::make_unique<XUnionType>(std::ref(xuni->second), is_nullable); + } return Type::get_illegal(); } diff --git a/tools/fidlcat/lib/library_loader.h b/tools/fidlcat/lib/library_loader.h index 3e415c02db4f6f4ea6eed40ebeaa64d9bdec636c..0b8a0d553dc18ae50f167cf0978cd215f3103bdb 100644 --- a/tools/fidlcat/lib/library_loader.h +++ b/tools/fidlcat/lib/library_loader.h @@ -59,6 +59,7 @@ class Enum; class Struct; class Table; class Union; +class XUnion; class Enum { public: @@ -88,7 +89,7 @@ class Enum { const rapidjson::Value& value_; }; -// TODO: Consider whether this is duplicative of Struct / Table / XUnion member. +// TODO: Consider whether this is duplicative of Struct / Table member. class UnionMember { public: UnionMember(const Union& enclosing_union, const rapidjson::Value& value) @@ -110,6 +111,16 @@ class UnionMember { virtual const Union& enclosing_union() const { return enclosing_union_; } + // Only valid for xunions. TODO: clean this up when unions go away - at that + // point, there is no value in this method's being optional. + std::optional<Ordinal> ordinal() const { + if (value_.HasMember("ordinal")) { + return std::optional<Ordinal>( + std::strtoll(value_["ordinal"].GetString(), nullptr, 10)); + } + return {}; + } + virtual ~UnionMember() {} private: @@ -148,15 +159,28 @@ class Union { return std::strtoll(value_["size"].GetString(), nullptr, 10); } + protected: + const Library& enclosing_library_; + const rapidjson::Value& value_; + private: const UnionMember& get_illegal_member() const; - const Library& enclosing_library_; - const rapidjson::Value& value_; std::vector<UnionMember> members_; mutable std::unique_ptr<UnionMember> illegal_; }; +class XUnion : public Union { + friend class Library; + + public: + XUnion(const Library& enclosing_library, const rapidjson::Value& value) + : Union(enclosing_library, value) {} + + XUnion(const XUnion& other) + : XUnion(other.enclosing_library_, other.value_) {} +}; + class StructMember { public: StructMember(const Struct& enclosing_struct, const rapidjson::Value& value) @@ -187,7 +211,8 @@ class Struct { public: Struct(const Library& enclosing_library, const rapidjson::Value& value, std::string size_name, std::string member_name) - : enclosing_library_(enclosing_library), value_(value), + : enclosing_library_(enclosing_library), + value_(value), size_(std::strtoll(value_[size_name].GetString(), nullptr, 10)) { auto member_arr = value[member_name].GetArray(); members_.reserve(member_arr.Size()); @@ -309,7 +334,8 @@ class InterfaceMethod { class Interface { public: Interface(const Library& library, const rapidjson::Value& value) - : value_(value), enclosing_library_(library), + : value_(value), + enclosing_library_(library), name_(value_["name"].GetString()) { for (auto& method : value["methods"].GetArray()) { interface_methods_.emplace_back(*this, method); @@ -388,6 +414,7 @@ class Library { std::map<std::string, Struct> structs_; std::map<std::string, Table> tables_; std::map<std::string, Union> unions_; + std::map<std::string, XUnion> xunions_; }; // An indexed collection of libraries. diff --git a/tools/fidlcat/lib/testdata/types.test.fidl b/tools/fidlcat/lib/testdata/types.test.fidl index 5027ef43700d23ea471f72846dd0553709272ecb..cea23a5efff5c875509bf7ff70849bf43fb21219 100644 --- a/tools/fidlcat/lib/testdata/types.test.fidl +++ b/tools/fidlcat/lib/testdata/types.test.fidl @@ -42,6 +42,11 @@ protocol this_is_an_interface { NullableUnionIntFirst(int32 i, int_struct_union? isu); ShortUnion(u8_u16_union u, int32 i); + XUnion(int_struct_xunion isu, int32 i); + NullableXUnion(int_struct_xunion? isu, int32 i); + NullableXUnionIntFirst(int32 i, int_struct_xunion? isu); + ShortXUnion(u8_u16_xunion u, int32 i); + Table(value_table table, int32 i); }; @@ -92,6 +97,16 @@ union u8_u16_union { uint16 variant_u16; }; +xunion int_struct_xunion { + int32 variant_i; + two_string_struct variant_tss; +}; + +xunion u8_u16_xunion { + uint8 variant_u8; + uint16 variant_u16; +}; + table value_table { 1: int16 first_int16; 2: two_string_struct second_struct; diff --git a/tools/fidlcat/lib/wire_parser_test.cc b/tools/fidlcat/lib/wire_parser_test.cc index bcf2523795bc1c0e2ca0b688917489d3c4440eeb..48f3d9033b8a3e44e9390e0b9a319f7017181488 100644 --- a/tools/fidlcat/lib/wire_parser_test.cc +++ b/tools/fidlcat/lib/wire_parser_test.cc @@ -454,37 +454,39 @@ TEST_WIRE_TO_JSON(NullableStructAndInt, NullableStructAndInt, // struct{uint8 f1; uint32 f2;} // struct{struct{uint8 f1; uint32 f2;} inner; uint8 f3;} -// Union tests +// Union and XUnion tests namespace { -test::fidlcat::examples::int_struct_union GetIntUnion(int32_t i) { - test::fidlcat::examples::int_struct_union u; +using isu = test::fidlcat::examples::int_struct_union; +using xisu = test::fidlcat::examples::int_struct_xunion; + +template <class T> +T GetIntUnion(int32_t i) { + T u; u.set_variant_i(i); return u; } -test::fidlcat::examples::int_struct_union GetStructUnion(std::string v1, - std::string v2) { - test::fidlcat::examples::int_struct_union u; +template <class T> +T GetStructUnion(std::string v1, std::string v2) { + T u; test::fidlcat::examples::two_string_struct tss = TwoStringStructFromVals(v1, v2); u.set_variant_tss(tss); return u; } -std::unique_ptr<test::fidlcat::examples::int_struct_union> GetIntUnionPtr( - int32_t i) { - std::unique_ptr<test::fidlcat::examples::int_struct_union> ptr( - new test::fidlcat::examples::int_struct_union()); +template <class T> +std::unique_ptr<T> GetIntUnionPtr(int32_t i) { + std::unique_ptr<T> ptr(new T()); ptr->set_variant_i(i); return ptr; } -std::unique_ptr<test::fidlcat::examples::int_struct_union> GetStructUnionPtr( - std::string v1, std::string v2) { - std::unique_ptr<test::fidlcat::examples::int_struct_union> ptr( - new test::fidlcat::examples::int_struct_union()); +template <class T> +std::unique_ptr<T> GetStructUnionPtr(std::string v1, std::string v2) { + std::unique_ptr<T> ptr(new T()); test::fidlcat::examples::two_string_struct tss = TwoStringStructFromVals(v1, v2); ptr->set_variant_tss(tss); @@ -494,38 +496,72 @@ std::unique_ptr<test::fidlcat::examples::int_struct_union> GetStructUnionPtr( } // namespace TEST_WIRE_TO_JSON(UnionInt, Union, R"({"isu":{"variant_i":"42"}, "i" : "1"})", - GetIntUnion(42), 1); + GetIntUnion<isu>(42), 1); + TEST_WIRE_TO_JSON( UnionStruct, Union, R"({"isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}, "i":"1"})", - GetStructUnion("harpo", "chico"), 1); + GetStructUnion<isu>("harpo", "chico"), 1); TEST_WIRE_TO_JSON(NullableUnionInt, NullableUnion, R"({"isu":{"variant_i":"42"}, "i" : "1"})", - GetIntUnionPtr(42), 1); + GetIntUnionPtr<isu>(42), 1); + TEST_WIRE_TO_JSON( NullableUnionStruct, NullableUnion, R"({"isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}, "i":"1"})", - GetStructUnionPtr("harpo", "chico"), 1); + GetStructUnionPtr<isu>("harpo", "chico"), 1); TEST_WIRE_TO_JSON(NullableUnionIntFirstInt, NullableUnionIntFirst, R"({"i" : "1", "isu":{"variant_i":"42"}})", 1, - GetIntUnionPtr(42)); + GetIntUnionPtr<isu>(42)); + TEST_WIRE_TO_JSON( NullableUnionIntFirstStruct, NullableUnionIntFirst, R"({"i": "1", "isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}})", - 1, GetStructUnionPtr("harpo", "chico")); + 1, GetStructUnionPtr<isu>("harpo", "chico")); + +TEST_WIRE_TO_JSON(XUnionInt, XUnion, R"({"isu":{"variant_i":"42"}, "i" : "1"})", + GetIntUnion<xisu>(42), 1); + +TEST_WIRE_TO_JSON( + XUnionStruct, XUnion, + R"({"isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}, "i":"1"})", + GetStructUnion<xisu>("harpo", "chico"), 1); + +TEST_WIRE_TO_JSON(NullableXUnionInt, NullableXUnion, + R"({"isu":{"variant_i":"42"}, "i" : "1"})", + GetIntUnionPtr<xisu>(42), 1); + +TEST_WIRE_TO_JSON( + NullableXUnionStruct, NullableXUnion, + R"({"isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}, "i":"1"})", + GetStructUnionPtr<xisu>("harpo", "chico"), 1); + +TEST_WIRE_TO_JSON(NullableXUnionIntFirstInt, NullableXUnionIntFirst, + R"({"i" : "1", "isu":{"variant_i":"42"}})", 1, + GetIntUnionPtr<xisu>(42)); + +TEST_WIRE_TO_JSON( + NullableXUnionIntFirstStruct, NullableXUnionIntFirst, + R"({"i": "1", "isu":{"variant_tss":{"value1":"harpo","value2":"chico"}}})", + 1, GetStructUnionPtr<xisu>("harpo", "chico")); namespace { -test::fidlcat::examples::u8_u16_union GetUInt8Union(uint8_t i) { - test::fidlcat::examples::u8_u16_union u; +using uuu = test::fidlcat::examples::u8_u16_union; +using uux = test::fidlcat::examples::u8_u16_xunion; + +template <class T> +T GetUInt8Union(uint8_t i) { + T u; u.set_variant_u8(i); return u; } -test::fidlcat::examples::u8_u16_union GetUInt16Union(uint16_t i) { - test::fidlcat::examples::u8_u16_union u; +template <class T> +T GetUInt16Union(uint16_t i) { + T u; u.set_variant_u16(i); return u; } @@ -533,12 +569,20 @@ test::fidlcat::examples::u8_u16_union GetUInt16Union(uint16_t i) { } // namespace TEST_WIRE_TO_JSON(ShortUnion8, ShortUnion, - R"({"u":{"variant_u8":"16"}, "i":"1"})", GetUInt8Union(16), - 1); + R"({"u":{"variant_u8":"16"}, "i":"1"})", + GetUInt8Union<uuu>(16), 1); TEST_WIRE_TO_JSON(ShortUnion16, ShortUnion, R"({"u":{"variant_u16":"1024"}, "i":"1"})", - GetUInt16Union(1024), 1); + GetUInt16Union<uuu>(1024), 1); + +TEST_WIRE_TO_JSON(ShortXUnion8, ShortXUnion, + R"({"u":{"variant_u8":"16"}, "i":"1"})", + GetUInt8Union<uux>(16), 1); + +TEST_WIRE_TO_JSON(ShortXUnion16, ShortXUnion, + R"({"u":{"variant_u16":"1024"}, "i":"1"})", + GetUInt16Union<uux>(1024), 1); // Enum Tests diff --git a/tools/fidlcat/lib/wire_types.cc b/tools/fidlcat/lib/wire_types.cc index 948260182cd329fd41ba28e9a688c53009ad8f18..56a45587c9e1bf7d26fd69a041c0e1ab8ccee896 100644 --- a/tools/fidlcat/lib/wire_types.cc +++ b/tools/fidlcat/lib/wire_types.cc @@ -278,6 +278,9 @@ class Envelope { return oss.str(); } + static constexpr size_t INLINE_SIZE = + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint64_t); + private: const uint8_t* ptr_; }; @@ -326,7 +329,7 @@ class EnvelopeType : public Type { } virtual size_t InlineSize() const override { - return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint64_t); + return Envelope::INLINE_SIZE; } private: @@ -444,6 +447,73 @@ Marker UnionType::GetValueCallback(Marker marker, size_t length, size_t UnionType::InlineSize() const { return union_.size(); } +XUnionType::XUnionType(const XUnion& uni, bool is_nullable) + : xunion_(uni), is_nullable_(is_nullable) {} + +Marker XUnionType::GetValueCallback(Marker marker, size_t length, + ObjectTracker* tracker, + ValueGeneratingCallback& callback) const { + const uint8_t* bytes = marker.byte_pos(); + // Advance by the size of the ordinal + padding. + marker.AdvanceBytesBy(sizeof(uint64_t)); + if (!marker.is_valid()) { + return marker; + } + + uint32_t ordinal = internal::MemoryFrom<uint32_t>(bytes); + if (ordinal == 0) { + if (!is_nullable_) { + FXL_LOG(WARNING) << "Encoding error: found null value in non-nullable " + "xunion. This is likely a bug in the FIDL binding, " + "so contact the FIDL binding authors."; + } + Envelope envelope(marker.byte_pos()); + FXL_CHECK(envelope.num_bytes() == 0 && envelope.num_handles() == 0 && + envelope.pointer() == 0) + << "Undefined ordinal in xunion without empty envelope."; + callback = NullCallback(true); + marker.AdvanceBytesBy(Envelope::INLINE_SIZE); + return marker; + } + std::unique_ptr<Type> target_type; + std::string member_name; + for (auto& member : xunion_.members()) { + std::optional<Ordinal> maybe_target_ordinal = member.ordinal(); + if (!maybe_target_ordinal) { + continue; + } + Ordinal target_ordinal = *maybe_target_ordinal; + if (target_ordinal == ordinal) { + target_type = member.GetType(); + member_name = member.name(); + break; + } + } + if (target_type == nullptr) { + // Need to figure out what to do with this... + member_name = "unknown$"; + member_name.append(std::to_string(ordinal)); + } + + EnvelopeType type(target_type.release()); + ValueGeneratingCallback value_callback; + marker = + type.GetValueCallback(marker, type.InlineSize(), tracker, value_callback); + callback = [member_name, value_callback = std::move(value_callback)]( + ObjectTracker* tracker, Marker& marker, + rapidjson::Value& value, + rapidjson::Document::AllocatorType& allocator) mutable { + value.SetObject(); + tracker->ObjectEnqueue(member_name, std::move(value_callback), value, + allocator); + return true; + }; + + return marker; +} + +size_t XUnionType::InlineSize() const { return xunion_.size(); } + PointerType::PointerType(Type* target_type, bool keep_null) : target_type_(target_type), keep_null_(keep_null) {} diff --git a/tools/fidlcat/lib/wire_types.h b/tools/fidlcat/lib/wire_types.h index 3f235fef98a9f97452518edf4a066bdef6288c32..6c376bcaf085fac2339627c92550919db8c5d3af 100644 --- a/tools/fidlcat/lib/wire_types.h +++ b/tools/fidlcat/lib/wire_types.h @@ -112,9 +112,9 @@ class ObjectTracker { // constructor) + the given offset. bool RunCallbacksFrom(Marker& marker); - void MessageEnqueue( - ValueGeneratingCallback&& callback, rapidjson::Value& target_object, - rapidjson::Document::AllocatorType& allocator); + void MessageEnqueue(ValueGeneratingCallback&& callback, + rapidjson::Value& target_object, + rapidjson::Document::AllocatorType& allocator); // Enqueues a callback to be executed when running RunCallbacksFrom. // |key| is the JSON key it will construct. @@ -377,6 +377,21 @@ class UnionType : public Type { const Union& union_; }; +class XUnionType : public Type { + public: + XUnionType(const XUnion& uni, bool is_nullable); + + virtual Marker GetValueCallback( + Marker marker, size_t length, ObjectTracker* tracker, + ValueGeneratingCallback& callback) const override; + + virtual size_t InlineSize() const override; + + private: + const XUnion& xunion_; + const bool is_nullable_; +}; + // A type that can be used to express that this is a pointer to an instance of // another type. class PointerType : public Type {