[fidl] Add support for vector to simple C bindings
Vectors are supported for primtives and handles when bounded.
Test: spaceship_tests.c
Change-Id: Id2400e9141b95e5a58a6945c1baaf1e2fa9dd677
diff --git a/system/host/fidl/include/fidl/c_generator.h b/system/host/fidl/include/fidl/c_generator.h
index 39f1e3e..04b688b 100644
--- a/system/host/fidl/include/fidl/c_generator.h
+++ b/system/host/fidl/include/fidl/c_generator.h
@@ -39,6 +39,7 @@
flat::Type::Kind kind;
std::string type;
std::string name;
+ std::string element_type;
std::vector<uint32_t> array_counts;
};
diff --git a/system/host/fidl/lib/c_generator.cpp b/system/host/fidl/lib/c_generator.cpp
index 5a472e2..494d528 100644
--- a/system/host/fidl/lib/c_generator.cpp
+++ b/system/host/fidl/lib/c_generator.cpp
@@ -18,7 +18,7 @@
constexpr const char* kIndent = " ";
CGenerator::Member MessageHeader() {
- return {flat::Type::Kind::kIdentifier, "fidl_message_header_t", "hdr", {}};
+ return {flat::Type::Kind::kIdentifier, "fidl_message_header_t", "hdr", {}, {}};
}
// Functions named "Emit..." are called to actually emit to an std::ostream
@@ -69,6 +69,10 @@
*file << "[" << array_count << "]";
}
break;
+ case flat::Type::Kind::kVector:
+ *file << "const " << member.element_type << "* " << member.name << "_data, "
+ << "size_t " << member.name << "_count";
+ break;
case flat::Type::Kind::kString:
*file << "const char* " << member.name << "_data, "
<< "size_t " << member.name << "_size";
@@ -81,9 +85,6 @@
case flat::Type::Kind::kIdentifier:
*file << "const " << member.type << "* " << member.name;
break;
- default:
- assert(false && "bad type simple interface");
- break;
}
}
@@ -94,6 +95,11 @@
for (uint32_t array_count : member.array_counts) {
*file << "[" << array_count << "]";
}
+ case flat::Type::Kind::kVector:
+ *file << member.element_type << "* " << member.name << "_data, "
+ << "size_t " << member.name << "_capacity, "
+ << "size_t* out_" << member.name << "_count";
+ break;
case flat::Type::Kind::kString:
*file << "char* " << member.name << "_data, "
<< "size_t " << member.name << "_capacity, "
@@ -105,9 +111,6 @@
case flat::Type::Kind::kIdentifier:
*file << member.type << "* " << member.name;
break;
- default:
- assert(false && "bad type simple interface");
- break;
}
}
@@ -158,28 +161,35 @@
void EmitMeasureParams(std::ostream* file,
const std::vector<CGenerator::Member>& params,
- StringView suffix) {
+ StringView vector_suffix,
+ StringView string_suffix) {
for (const auto& member : params) {
- if (member.kind != flat::Type::Kind::kString)
- continue;
- *file << " + FIDL_ALIGN(" << member.name << suffix << ")";
+ if (member.kind == flat::Type::Kind::kVector)
+ *file << " + FIDL_ALIGN(sizeof(*" << member.name << "_data) * " << member.name << vector_suffix << ")";
+ else if (member.kind == flat::Type::Kind::kString)
+ *file << " + FIDL_ALIGN(" << member.name << string_suffix << ")";
}
}
-bool HasSecondaryObjects(const std::vector<CGenerator::Member>& params) {
+// This function assumes the |params| are part of a [Layout="Simple"] interface.
+// In particular, simple interfaces don't have nullable structs or nested
+// vectors. The only secondary objects they contain are top-level vectors and
+// strings.
+size_t CountSecondaryObjects(const std::vector<CGenerator::Member>& params) {
+ size_t count = 0u;
for (const auto& member : params) {
- if (member.kind == flat::Type::Kind::kString ||
- member.kind == flat::Type::Kind::kVector)
- return true;
+ if (member.kind == flat::Type::Kind::kVector ||
+ member.kind == flat::Type::Kind::kString)
+ ++count;
}
- return false;
+ return count;
}
void EmitLinearizeMessage(std::ostream* file,
StringView receiver,
StringView bytes,
const std::vector<CGenerator::Member>& request) {
- if (HasSecondaryObjects(request))
+ if (CountSecondaryObjects(request) > 0)
*file << kIndent << "uint32_t _next = sizeof(*" << receiver << ");\n";
for (const auto& member : request) {
const auto& name = member.name;
@@ -188,6 +198,12 @@
*file << kIndent << "memcpy(" << receiver << "->" << name << ", "
<< name << ", sizeof(" << receiver << "->" << name << "));\n";
break;
+ case flat::Type::Kind::kVector:
+ *file << kIndent << receiver << "->" << name << ".data = &" << bytes << "[_next];\n";
+ *file << kIndent << receiver << "->" << name << ".count = " << name << "_count;\n";
+ *file << kIndent << "memcpy(" << receiver << "->" << name << ".data, " << name << "_data, sizeof(*" << name << "_data) * " << name << "_count);\n";
+ *file << kIndent << "_next += FIDL_ALIGN(sizeof(*" << name << "_data) * " << name << "_count);\n";
+ break;
case flat::Type::Kind::kString:
*file << kIndent << receiver << "->" << name << ".data = &" << bytes << "[_next];\n";
*file << kIndent << receiver << "->" << name << ".size = " << name << "_size;\n";
@@ -202,9 +218,6 @@
case flat::Type::Kind::kIdentifier:
*file << kIndent << receiver << "->" << name << " = *" << name << ";\n";
break;
- default:
- assert(false && "bad type simple interface");
- break;
}
}
}
@@ -327,7 +340,18 @@
const flat::Type* type = decl.type.get();
auto type_name = NameFlatCType(library, type);
std::vector<uint32_t> array_counts = ArrayCounts(library, type);
- return CGenerator::Member{type->kind, type_name, name, std::move(array_counts)};
+ std::string element_type;
+ if (type->kind == flat::Type::Kind::kVector) {
+ auto vector_type = static_cast<const flat::VectorType*>(type);
+ element_type = NameFlatCType(library, vector_type->element_type.get());
+ }
+ return CGenerator::Member{
+ type->kind,
+ std::move(type_name),
+ std::move(name),
+ std::move(element_type),
+ std::move(array_counts),
+ };
}
std::vector<CGenerator::Member>
@@ -455,7 +479,7 @@
NamedInterface named_interface;
named_interface.c_name = NameInterface(*interface_info);
if (interface_info->HasAttribute("Discoverable")) {
- named_interface.discoverable_name = NameDiscoverable(*interface_info);
+ named_interface.discoverable_name = NameDiscoverable(*interface_info);
}
for (const auto& method : interface_info->methods) {
NamedMethod named_method;
@@ -534,7 +558,7 @@
void CGenerator::ProduceInterfaceForwardDeclaration(const NamedInterface& named_interface) {
if (!named_interface.discoverable_name.empty()) {
- file_ << "#define " << named_interface.c_name << "_Name \"" << named_interface.discoverable_name << "\"\n";
+ file_ << "#define " << named_interface.c_name << "_Name \"" << named_interface.discoverable_name << "\"\n";
}
for (const auto& method_info : named_interface.methods) {
file_ << "#define " << method_info.ordinal_name << " ((uint32_t)"
@@ -647,7 +671,7 @@
EmitClientMethodDecl(&file_, method_info.c_name, request, response);
file_ << " {\n";
file_ << kIndent << "uint32_t _wr_num_bytes = sizeof(" << method_info.request->c_name << ")";
- EmitMeasureParams(&file_, request, "_size");
+ EmitMeasureParams(&file_, request, "_count", "_size");
file_ << ";\n";
file_ << kIndent << "FIDL_ALIGNDECL char _wr_bytes[_wr_num_bytes];\n";
file_ << kIndent << method_info.request->c_name << "* _request = (" << method_info.request->c_name << "*)_wr_bytes;\n";
@@ -665,7 +689,7 @@
file_ << kIndent << "return zx_channel_write(_channel, 0u, _wr_bytes, _wr_num_bytes, _handles, _wr_num_handles);\n";
} else {
file_ << kIndent << "uint32_t _rd_num_bytes = sizeof(" << method_info.response->c_name << ")";
- EmitMeasureParams(&file_, response, "_capacity");
+ EmitMeasureParams(&file_, response, "_capacity", "_capacity");
file_ << ";\n";
file_ << kIndent << "FIDL_ALIGNDECL char _rd_bytes[_rd_num_bytes];\n";
if (!response.empty())
@@ -691,10 +715,26 @@
// before decoding the message so that we can close the handles
// using |_handles| rather than trying to find them in the decoded
// message.
- for (const auto& member : response) {
- if (member.kind != flat::Type::Kind::kString)
- continue;
- file_ << kIndent << "if (_response->" << member.name << ".size > " << member.name << "_capacity) {\n";
+ size_t count = CountSecondaryObjects(response);
+ if (count > 0u) {
+ file_ << kIndent << "if ";
+ if (count > 1u)
+ file_ << "(";
+ size_t i = 0;
+ for (const auto& member : response) {
+ if (member.kind == flat::Type::Kind::kVector) {
+ if (i++ > 0u)
+ file_ << " || ";
+ file_ << "(_response->" << member.name << ".count > " << member.name << "_capacity)";
+ } else if (member.kind == flat::Type::Kind::kString) {
+ if (i++ > 0u)
+ file_ << " || ";
+ file_ << "(_response->" << member.name << ".size > " << member.name << "_capacity)";
+ }
+ }
+ if (count > 1u)
+ file_ << ")";
+ file_ << " {\n";
file_ << kIndent << kIndent << "zx_handle_close_many(_handles, _actual_num_handles);\n";
file_ << kIndent << kIndent << "return ZX_ERR_BUFFER_TOO_SMALL;\n";
file_ << kIndent << "}\n";
@@ -712,6 +752,10 @@
case flat::Type::Kind::kArray:
file_ << kIndent << "memcpy(out_" << name << ", _response->" << name << ", sizeof(out_" << name << "));\n";
break;
+ case flat::Type::Kind::kVector:
+ file_ << kIndent << "memcpy(" << name << "_data, _response->" << name << ".data, sizeof(*" << name << "_data) * _response->" << name << ".count);\n";
+ file_ << kIndent << "*out_" << name << "_count = _response->" << name << ".count;\n";
+ break;
case flat::Type::Kind::kString:
file_ << kIndent << "memcpy(" << name << "_data, _response->" << name << ".data, _response->" << name << ".size);\n";
file_ << kIndent << "*out_" << name << "_size = _response->" << name << ".size;\n";
@@ -722,9 +766,6 @@
case flat::Type::Kind::kIdentifier:
file_ << kIndent << "*" << name << " = _response->" << name << ";\n";
break;
- default:
- assert(false && "bad type simple interface");
- break;
}
}
@@ -792,6 +833,10 @@
case flat::Type::Kind::kPrimitive:
file_ << ", request->" << member.name;
break;
+ case flat::Type::Kind::kVector:
+ file_ << ", (" << member.element_type << "*)request->" << member.name << ".data"
+ << ", request->" << member.name << ".count";
+ break;
case flat::Type::Kind::kString:
file_ << ", request->" << member.name << ".data"
<< ", request->" << member.name << ".size";
@@ -799,9 +844,6 @@
case flat::Type::Kind::kIdentifier:
file_ << ", &(request->" << member.name << ")";
break;
- default:
- assert(false && "bad type simple interface");
- break;
}
}
if (method_info.response != nullptr)
@@ -827,7 +869,7 @@
EmitServerReplyDecl(&file_, method_info.c_name, response);
file_ << " {\n";
file_ << kIndent << "uint32_t _wr_num_bytes = sizeof(" << method_info.response->c_name << ")";
- EmitMeasureParams(&file_, response, "_size");
+ EmitMeasureParams(&file_, response, "_count", "_size");
file_ << ";\n";
file_ << kIndent << "char _wr_bytes[_wr_num_bytes];\n";
file_ << kIndent << method_info.response->c_name << "* _response = (" << method_info.response->c_name << "*)_wr_bytes;\n";
diff --git a/system/host/fidl/lib/flat_ast.cpp b/system/host/fidl/lib/flat_ast.cpp
index 6a22aa0..3ec2049 100644
--- a/system/host/fidl/lib/flat_ast.cpp
+++ b/system/host/fidl/lib/flat_ast.cpp
@@ -188,6 +188,21 @@
if (type->kind == Type::Kind::kString) {
auto string_type = static_cast<StringType*>(type.get());
return string_type->max_size.Value() < Size::Max().Value();
+ } else if (type->kind == Type::Kind::kVector) {
+ auto vector_type = static_cast<VectorType*>(type.get());
+ if (vector_type->element_count.Value() == Size::Max().Value())
+ return false;
+ switch (vector_type->element_type->kind) {
+ case Type::Kind::kHandle:
+ case Type::Kind::kRequestHandle:
+ case Type::Kind::kPrimitive:
+ return true;
+ case Type::Kind::kArray:
+ case Type::Kind::kVector:
+ case Type::Kind::kString:
+ case Type::Kind::kIdentifier:
+ return false;
+ }
}
return fieldshape.Depth() == 0u;
}
diff --git a/system/utest/fidl-simple/ldsvc_tests.c b/system/utest/fidl-simple/ldsvc_tests.c
index 286e1d7..c5a5c56 100644
--- a/system/utest/fidl-simple/ldsvc_tests.c
+++ b/system/utest/fidl-simple/ldsvc_tests.c
@@ -206,6 +206,7 @@
}
ASSERT_EQ(ZX_OK, fuchsia_ldsvc_LoaderDone(client), "");
+ ASSERT_EQ(ZX_OK, zx_handle_close(client), "");
int result = 0;
rv = thrd_join(thread, &result);
diff --git a/system/utest/fidl-simple/rules.mk b/system/utest/fidl-simple/rules.mk
index e160f58..290fdb9 100644
--- a/system/utest/fidl-simple/rules.mk
+++ b/system/utest/fidl-simple/rules.mk
@@ -4,6 +4,24 @@
LOCAL_DIR := $(GET_LOCAL_DIR)
+#
+# fidl.test.spaceship
+#
+
+MODULE := $(LOCAL_DIR).spaceship
+
+MODULE_TYPE := fidl
+
+MODULE_FIDL_LIBRARY := fidl.test.spaceship
+
+MODULE_SRCS += $(LOCAL_DIR)/spaceship.fidl
+
+include make/module.mk
+
+#
+# fidl-simple-test
+#
+
MODULE := $(LOCAL_DIR)
MODULE_TYPE := usertest
@@ -13,12 +31,14 @@
$(LOCAL_DIR)/ldsvc_tests.c \
$(LOCAL_DIR)/main.c \
$(LOCAL_DIR)/server_tests.c \
+ $(LOCAL_DIR)/spaceship_tests.c \
MODULE_NAME := fidl-simple-test
MODULE_FIDL_LIBS := \
system/fidl/fuchsia-crash \
system/fidl/fuchsia-ldsvc \
+ system/utest/fidl-simple.spaceship \
MODULE_STATIC_LIBS := \
system/ulib/fidl \
diff --git a/system/utest/fidl-simple/spaceship.fidl b/system/utest/fidl-simple/spaceship.fidl
new file mode 100644
index 0000000..e3dc07a
--- /dev/null
+++ b/system/utest/fidl-simple/spaceship.fidl
@@ -0,0 +1,11 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library fidl.test.spaceship;
+
+[Layout="Simple"]
+interface SpaceShip {
+ 1: AdjustHeading(vector<uint32>:64 stars) -> (int8 result);
+ 2: ScanForLifeforms() -> (vector<uint32>:64 lifesigns);
+};
diff --git a/system/utest/fidl-simple/spaceship_tests.c b/system/utest/fidl-simple/spaceship_tests.c
new file mode 100644
index 0000000..dcd5820
--- /dev/null
+++ b/system/utest/fidl-simple/spaceship_tests.c
@@ -0,0 +1,129 @@
+// Copyright 2018 The Fuchsia 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 <fidl/test/spaceship/c/fidl.h>
+#include <string.h>
+#include <threads.h>
+#include <zircon/fidl.h>
+#include <zircon/syscalls.h>
+
+#include <unittest/unittest.h>
+
+static zx_status_t SpaceShip_AdjustHeading(void* ctx, const uint32_t* stars_data, size_t stars_count, fidl_txn_t* txn) {
+ EXPECT_EQ(3u, stars_count, "");
+ EXPECT_EQ(11u, stars_data[0], "");
+ EXPECT_EQ(0u, stars_data[1], "");
+ EXPECT_EQ(UINT32_MAX, stars_data[2], "");
+ return fidl_test_spaceship_SpaceShipAdjustHeading_reply(txn, -12);
+}
+
+static zx_status_t SpaceShip_ScanForLifeforms(void* ctx, fidl_txn_t* txn) {
+ const uint32_t lifesigns[5] = { 42u, 43u, UINT32_MAX, 0u, 9u };
+ return fidl_test_spaceship_SpaceShipScanForLifeforms_reply(txn, lifesigns, 5);
+}
+
+static const fidl_test_spaceship_SpaceShip_ops_t kOps = {
+ .AdjustHeading = SpaceShip_AdjustHeading,
+ .ScanForLifeforms = SpaceShip_ScanForLifeforms,
+};
+
+typedef struct spaceship_connection {
+ fidl_txn_t txn;
+ zx_handle_t channel;
+ zx_txid_t txid;
+} spaceship_connection_t;
+
+static zx_status_t spaceship_reply(fidl_txn_t* txn, const fidl_msg_t* msg) {
+ spaceship_connection_t* conn = (spaceship_connection_t*)txn;
+ if (msg->num_bytes < sizeof(fidl_message_header_t))
+ return ZX_ERR_INVALID_ARGS;
+ fidl_message_header_t* hdr = (fidl_message_header_t*)msg->bytes;
+ hdr->txid = conn->txid;
+ conn->txid = 0;
+ return zx_channel_write(conn->channel, 0, msg->bytes, msg->num_bytes,
+ msg->handles, msg->num_handles);
+}
+
+static int spaceship_server(void* ctx) {
+ spaceship_connection_t conn = {
+ .txn.reply = spaceship_reply,
+ .channel = *(zx_handle_t*)ctx,
+ };
+ zx_status_t status = ZX_OK;
+ while (status == ZX_OK) {
+ zx_signals_t observed;
+ status = zx_object_wait_one(
+ conn.channel, ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
+ ZX_TIME_INFINITE, &observed);
+ if ((observed & ZX_CHANNEL_READABLE) != 0) {
+ ASSERT_EQ(ZX_OK, status, "");
+ char bytes[ZX_CHANNEL_MAX_MSG_BYTES];
+ zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES];
+ fidl_msg_t msg = {
+ .bytes = bytes,
+ .handles = handles,
+ .num_bytes = 0u,
+ .num_handles = 0u,
+ };
+ status = zx_channel_read(conn.channel, 0, bytes, handles,
+ ZX_CHANNEL_MAX_MSG_BYTES,
+ ZX_CHANNEL_MAX_MSG_HANDLES,
+ &msg.num_bytes, &msg.num_handles);
+ ASSERT_EQ(ZX_OK, status, "");
+ ASSERT_GE(msg.num_bytes, sizeof(fidl_message_header_t), "");
+ fidl_message_header_t* hdr = (fidl_message_header_t*)msg.bytes;
+ conn.txid = hdr->txid;
+ status = fidl_test_spaceship_SpaceShip_dispatch(NULL, &conn.txn, &msg, &kOps);
+ ASSERT_EQ(ZX_OK, status, "");
+ } else {
+ break;
+ }
+ }
+
+ zx_handle_close(conn.channel);
+ return 0;
+}
+
+static bool spaceship_test(void) {
+ BEGIN_TEST;
+
+ zx_handle_t client, server;
+ zx_status_t status = zx_channel_create(0, &client, &server);
+ ASSERT_EQ(ZX_OK, status, "");
+
+ thrd_t thread;
+ int rv = thrd_create(&thread, spaceship_server, &server);
+ ASSERT_EQ(thrd_success, rv, "");
+
+ {
+ const uint32_t stars[3] = { 11u, 0u, UINT32_MAX };
+ int8_t result = 0;
+ ASSERT_EQ(ZX_OK, fidl_test_spaceship_SpaceShipAdjustHeading(client, stars, 3, &result), "");
+ ASSERT_EQ(-12, result, "");
+ }
+
+ {
+ uint32_t lifesigns[64];
+ size_t actual = 0;
+ ASSERT_EQ(ZX_OK, fidl_test_spaceship_SpaceShipScanForLifeforms(client, lifesigns, 64, &actual), "");
+ ASSERT_EQ(5u, actual, "");
+ ASSERT_EQ(42u, lifesigns[0], "");
+ ASSERT_EQ(43u, lifesigns[1], "");
+ ASSERT_EQ(UINT32_MAX, lifesigns[2], "");
+ ASSERT_EQ(0u, lifesigns[3], "");
+ ASSERT_EQ(9u, lifesigns[4], "");
+ }
+
+ ASSERT_EQ(ZX_OK, zx_handle_close(client), "");
+
+ int result = 0;
+ rv = thrd_join(thread, &result);
+ ASSERT_EQ(thrd_success, rv, "");
+
+ END_TEST;
+}
+
+BEGIN_TEST_CASE(spaceship_tests)
+RUN_NAMED_TEST("fidl.test.spaceship.SpaceShip test", spaceship_test)
+END_TEST_CASE(spaceship_tests);