summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Stewart <pstew@chromium.org>2014-09-03 19:01:54 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-09-05 04:10:43 +0000
commit0e3301aa8024df8ff895d9700f97e809cad2cf5e (patch)
tree01ec286b1b77c1b8427056703a3876e0b551a754
parent154cad5f50f22e473237bb2ead8b5789180cfe2f (diff)
downloaddbus-binding-generator-0e3301aa8024df8ff895d9700f97e809cad2cf5e.tar.gz
chromeos-dbus-bindings: Add D-Bus signature parser
Add a utility for converting a D-Bus signature into the C++ typename equivalent. BUG=chromium:404505 TEST=New unit test Change-Id: I05b51014741f001089dfcbbab6eb1a0b6f96d888 Reviewed-on: https://chromium-review.googlesource.com/216491 Reviewed-by: Paul Stewart <pstew@chromium.org> Commit-Queue: Paul Stewart <pstew@chromium.org> Tested-by: Paul Stewart <pstew@chromium.org>
-rw-r--r--chromeos-dbus-bindings/chromeos-dbus-bindings.gyp7
-rw-r--r--chromeos-dbus-bindings/dbus_signature.cc190
-rw-r--r--chromeos-dbus-bindings/dbus_signature.h78
-rw-r--r--chromeos-dbus-bindings/dbus_signature_unittest.cc148
4 files changed, 422 insertions, 1 deletions
diff --git a/chromeos-dbus-bindings/chromeos-dbus-bindings.gyp b/chromeos-dbus-bindings/chromeos-dbus-bindings.gyp
index 2e1ddc2..cdcf525 100644
--- a/chromeos-dbus-bindings/chromeos-dbus-bindings.gyp
+++ b/chromeos-dbus-bindings/chromeos-dbus-bindings.gyp
@@ -23,6 +23,7 @@
'target_name': 'libchromeos-dbus-bindings',
'type': 'static_library',
'sources': [
+ 'dbus_signature.cc',
'method_name_generator.cc',
'xml_interface_parser.cc',
],
@@ -30,7 +31,10 @@
'exported_deps': [
'expat',
],
- 'deps': ['<@(exported_deps)'],
+ 'deps': [
+ 'dbus-1',
+ '<@(exported_deps)',
+ ],
},
'all_dependent_settings': {
'variables': {
@@ -66,6 +70,7 @@
'includes': ['../../platform2/common-mk/common_test.gypi'],
'sources': [
'testrunner.cc',
+ 'dbus_signature_unittest.cc',
'method_name_generator_unittest.cc',
'xml_interface_parser_unittest.cc',
],
diff --git a/chromeos-dbus-bindings/dbus_signature.cc b/chromeos-dbus-bindings/dbus_signature.cc
new file mode 100644
index 0000000..f3f62d8
--- /dev/null
+++ b/chromeos-dbus-bindings/dbus_signature.cc
@@ -0,0 +1,190 @@
+// Copyright 2014 The Chromium OS 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 "chromeos-dbus-bindings/dbus_signature.h"
+
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+#include <dbus/dbus-protocol.h>
+
+using base::StringPrintf;
+using std::string;
+using std::vector;
+
+namespace chromeos_dbus_bindings {
+
+// static
+const char DbusSignature::kArrayTypename[] = "std::vector";
+const char DbusSignature::kBooleanTypename[] = "bool";
+const char DbusSignature::kByteTypename[] = "uint8_t";
+const char DbusSignature::kDefaultObjectPathTypename[] = "std::string";
+const char DbusSignature::kDictTypename[] = "std::map";
+const char DbusSignature::kDoubleTypename[] = "double";
+const char DbusSignature::kSigned16Typename[] = "int16_t";
+const char DbusSignature::kSigned32Typename[] = "int32_t";
+const char DbusSignature::kSigned64Typename[] = "int64_t";
+const char DbusSignature::kStringTypename[] = "std::string";
+const char DbusSignature::kUnixFdTypename[] = "int";
+const char DbusSignature::kUnsigned16Typename[] = "uint16_t";
+const char DbusSignature::kUnsigned32Typename[] = "uint32_t";
+const char DbusSignature::kUnsigned64Typename[] = "uint64_t";
+const char DbusSignature::kVariantTypename[] = "chromeos::Any";
+
+DbusSignature::DbusSignature()
+ : object_path_typename_(kDefaultObjectPathTypename) {}
+
+bool DbusSignature::Parse(const string& signature, string* output) {
+ string::const_iterator end;
+ if (!GetTypenameForSignature(
+ signature.begin(), signature.end(), &end, output)) {
+ LOG(ERROR) << "Parse failed for signature " << signature;
+ return false;
+ }
+ if (end != signature.end()) {
+ LOG(WARNING) << "A portion of signature " << signature
+ << " is left unparsed: " << string(end, signature.end());
+ }
+ return true;
+}
+
+bool DbusSignature::GetTypenameForSignature(
+ string::const_iterator signature,
+ string::const_iterator end,
+ string::const_iterator* next,
+ string* output) {
+ if (signature == end) {
+ LOG(ERROR) << "Signature is empty";
+ return false;
+ }
+
+ string::const_iterator cur = signature;
+ int signature_value = *cur++;
+ switch (signature_value) {
+ case DBUS_TYPE_ARRAY:
+ if (!GetArrayTypenameForSignature(cur, end, &cur, output)) {
+ return false;
+ }
+ break;
+
+ case DBUS_TYPE_BOOLEAN:
+ *output = kBooleanTypename;
+ break;
+
+ case DBUS_TYPE_BYTE:
+ *output = kByteTypename;
+ break;
+
+ case DBUS_TYPE_DOUBLE:
+ *output = kDoubleTypename;
+ break;
+
+ case DBUS_TYPE_OBJECT_PATH:
+ *output = object_path_typename_;
+ break;
+
+ case DBUS_TYPE_INT16:
+ *output = kSigned16Typename;
+ break;
+
+ case DBUS_TYPE_INT32:
+ *output = kSigned32Typename;
+ break;
+
+ case DBUS_TYPE_INT64:
+ *output = kSigned64Typename;
+ break;
+
+ case DBUS_TYPE_STRING:
+ *output = kStringTypename;
+ break;
+
+ case DBUS_TYPE_UNIX_FD:
+ *output = kUnixFdTypename;
+ break;
+
+ case DBUS_TYPE_UINT16:
+ *output = kUnsigned16Typename;
+ break;
+
+ case DBUS_TYPE_UINT32:
+ *output = kUnsigned32Typename;
+ break;
+
+ case DBUS_TYPE_UINT64:
+ *output = kUnsigned64Typename;
+ break;
+
+ case DBUS_TYPE_VARIANT:
+ *output = kVariantTypename;
+ break;
+
+ default:
+ LOG(ERROR) << "Unexpected token " << *signature;
+ return false;
+ }
+
+ if (next) {
+ *next = cur;
+ }
+
+ return true;
+}
+
+bool DbusSignature::GetArrayTypenameForSignature(
+ string::const_iterator signature,
+ string::const_iterator end,
+ string::const_iterator* next,
+ string* output) {
+ string::const_iterator cur = signature;
+ if (cur == end) {
+ LOG(ERROR) << "At end of string while reading array parameter";
+ return false;
+ }
+
+ if (*cur == DBUS_DICT_ENTRY_BEGIN_CHAR) {
+ vector<string> children;
+ ++cur;
+ while (cur != end && *cur != DBUS_DICT_ENTRY_END_CHAR) {
+ children.emplace_back();
+ if (!GetTypenameForSignature(cur, end, &cur, &children.back())) {
+ LOG(ERROR) << "Unable to decode child elements starting at "
+ << string(cur, end);
+ return false;
+ }
+ }
+ if (cur == end) {
+ LOG(ERROR) << "At end of string while processing dict "
+ << "starting at " << string(signature, end);
+ return false;
+ }
+
+ DCHECK_EQ(DBUS_DICT_ENTRY_END_CHAR, *cur);
+ ++cur;
+
+ if (children.size() != 2) {
+ LOG(ERROR) << "Dict entry contains " << children.size()
+ << " members starting at " << string(signature, end)
+ << " where only 2 children is valid.";
+ return false;
+ }
+ *output = StringPrintf("%s<%s,%s>", kDictTypename,
+ children[0].c_str(), children[1].c_str());
+ } else {
+ string child;
+ if (!GetTypenameForSignature(cur, end, &cur, &child)) {
+ LOG(ERROR) << "Unable to decode child element starting at "
+ << string(cur, end);
+ return false;
+ }
+ *output = StringPrintf("%s<%s>", kArrayTypename, child.c_str());
+ }
+
+ if (next) {
+ *next = cur;
+ }
+
+ return true;
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/dbus_signature.h b/chromeos-dbus-bindings/dbus_signature.h
new file mode 100644
index 0000000..707d3a6
--- /dev/null
+++ b/chromeos-dbus-bindings/dbus_signature.h
@@ -0,0 +1,78 @@
+// Copyright 2014 The Chromium OS 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 CHROMEOS_DBUS_BINDINGS_DBUS_SIGNATURE_H_
+#define CHROMEOS_DBUS_BINDINGS_DBUS_SIGNATURE_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+namespace chromeos_dbus_bindings {
+
+class DbusSignature {
+ public:
+ DbusSignature();
+ virtual ~DbusSignature() = default;
+
+ // Returns a C++ typename in |output| for a D-Bus signature in |signature|
+ // and returns true on success. Returns false otherwise.
+ bool Parse(const std::string& signature, std::string* output);
+
+ void set_object_path_typename(const std::string& object_path_typename) {
+ object_path_typename_ = object_path_typename;
+ }
+
+ private:
+ friend class DbusSignatureTest;
+ FRIEND_TEST(DbusSignatureTest, DefaultObjectPathTypename);
+ FRIEND_TEST(DbusSignatureTest, ParseSuccesses);
+
+ // Typenames are C++ syntax types.
+ static const char kArrayTypename[];
+ static const char kBooleanTypename[];
+ static const char kByteTypename[];
+ static const char kDefaultObjectPathTypename[];
+ static const char kDictTypename[];
+ static const char kDoubleTypename[];
+ static const char kSigned16Typename[];
+ static const char kSigned32Typename[];
+ static const char kSigned64Typename[];
+ static const char kStringTypename[];
+ static const char kUnixFdTypename[];
+ static const char kUnsigned16Typename[];
+ static const char kUnsigned32Typename[];
+ static const char kUnsigned64Typename[];
+ static const char kVariantTypename[];
+
+ // Returns the C++ type name for the next D-Bus signature in the string at
+ // |signature| in |output|, as well as the next position within the string
+ // that parsing should continue |next|. It is not an error to pass a
+ // pointer to |signature| or nullptr as |next|. Returns true on success.
+ bool GetTypenameForSignature(std::string::const_iterator signature,
+ std::string::const_iterator end,
+ std::string::const_iterator* next,
+ std::string* output);
+
+ // Utility task for GetTypenameForSignature() which handles array objects
+ // and decodes them into a map or vector depending on the encoded sub-elements
+ // in the array. The arguments and return values are the same
+ // as GetTypenameForSignature().
+ bool GetArrayTypenameForSignature(std::string::const_iterator signature,
+ std::string::const_iterator end,
+ std::string::const_iterator* next,
+ std::string* output);
+
+
+ // The C++ typename to be used for D-Bus object pathnames.
+ std::string object_path_typename_;
+
+ DISALLOW_COPY_AND_ASSIGN(DbusSignature);
+};
+
+} // namespace chromeos_dbus_bindings
+
+#endif // CHROMEOS_DBUS_BINDINGS_DBUS_SIGNATURE_H_
diff --git a/chromeos-dbus-bindings/dbus_signature_unittest.cc b/chromeos-dbus-bindings/dbus_signature_unittest.cc
new file mode 100644
index 0000000..f8a6d46
--- /dev/null
+++ b/chromeos-dbus-bindings/dbus_signature_unittest.cc
@@ -0,0 +1,148 @@
+// Copyright 2014 The Chromium OS 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 "chromeos-dbus-bindings/dbus_signature.h"
+
+#include <map>
+#include <string>
+
+#include <dbus/dbus-protocol.h>
+#include <gtest/gtest.h>
+
+using std::map;
+using std::string;
+using testing::Test;
+
+namespace chromeos_dbus_bindings {
+
+namespace {
+
+// Failing signatures.
+const char kEmptySignature[] = "";
+const char kEmptyDictSignature[] = "a{}";
+const char kMissingArraryParameterSignature[] = "a";
+const char kMissingArraryParameterInnerSignature[] = "a{sa}i";
+const char kOrphanDictSignature[] = "a{s{i}}";
+const char kTooFewDictMembersSignature[] = "a{s}";
+const char kTooManyDictMembersSignature[] = "a{sa{i}u}";
+const char kUnclosedDictOuterSignature[] = "a{s";
+const char kUnclosedDictInnerSignature[] = "a{a{u}";
+const char kUnexpectedCloseSignature[] = "a}i{";
+const char kUnknownSignature[] = "al";
+
+// Succeeding signatures.
+const char kBoolArraySignature[] = "ab";
+const char kByteArraySignature[] = "ay";
+const char kByteArrayArraySignature[] = "aay";
+const char kObjectArraySignature[] = "ao";
+const char kObjectDictBlobSignature[] = "a{oa{sa{sv}}}";
+const char kObjectNameDictSignature[] = "a{os}";
+const char kStringArraySignature[] = "as";
+const char kStringStringDictSignature[] = "a{ss}";
+const char kStringStringStringDictSignature[] = "a{sa{ss}}";
+const char kStringStringVariantDictSignature[] = "a{sa{sv}}";
+const char kStringVariantDictSignature[] = "a{sv}";
+const char kStringVariantDictWithTrailingSignature[] = "a{sv}NoneOfThisParses";
+const char kUnsigned64ArraySignature[] = "at";
+
+// Corresponding typenames for signatures above.
+const char kBoolArrayTypename[] = "std::vector<bool>";
+const char kByteArrayTypename[] = "std::vector<uint8_t>";
+const char kByteArrayArrayTypename[] = "std::vector<std::vector<uint8_t>>";
+const char kObjectArrayTypename[] = "std::vector<ObjectPathType>";
+const char kObjectDictBlobTypename[] =
+ "std::map<ObjectPathType,std::map<std::string,std::map"
+ "<std::string,chromeos::Any>>>";
+const char kObjectNameDictTypename[] = "std::map<ObjectPathType,std::string>";
+const char kStringArrayTypename[] = "std::vector<std::string>";
+const char kStringStringDictTypename[] = "std::map<std::string,std::string>";
+const char kStringStringStringDictTypename[] =
+ "std::map<std::string,std::map<std::string,std::string>>";
+const char kStringStringVariantDictTypename[] =
+ "std::map<std::string,std::map<std::string,chromeos::Any>>";
+const char kStringVariantDictTypename[] = "std::map<std::string,chromeos::Any>";
+const char kUnsigned64ArrayTypename[] = "std::vector<uint64_t>";
+
+// Define an object type name to disambiguate the typenames above.
+const char kObjectPathTypename[] = "ObjectPathType";
+
+} // namespace
+
+class DbusSignatureTest : public Test {
+ protected:
+ DbusSignature signature_;
+};
+
+TEST_F(DbusSignatureTest, ParseFailures) {
+ for (const auto& failing_string : { kEmptySignature,
+ kEmptyDictSignature,
+ kMissingArraryParameterSignature,
+ kMissingArraryParameterInnerSignature,
+ kOrphanDictSignature,
+ kTooFewDictMembersSignature,
+ kTooManyDictMembersSignature,
+ kUnclosedDictOuterSignature,
+ kUnclosedDictInnerSignature,
+ kUnexpectedCloseSignature,
+ kUnknownSignature }) {
+ string unused_output;
+ EXPECT_FALSE(signature_.Parse(failing_string, &unused_output))
+ << "Expected signature " << failing_string
+ << " to fail but it succeeded";
+ }
+}
+
+TEST_F(DbusSignatureTest, DefaultObjectPathTypename) {
+ // The ParseSuccesses test below overrides the default object typename, so
+ // test the default behavior separately.
+ string output;
+ EXPECT_TRUE(signature_.Parse(DBUS_TYPE_OBJECT_PATH_AS_STRING, &output));
+ EXPECT_EQ(DbusSignature::kDefaultObjectPathTypename, output);
+}
+
+TEST_F(DbusSignatureTest, ParseSuccesses) {
+ const map<string, string> parse_values {
+ // Simple types.
+ { DBUS_TYPE_BOOLEAN_AS_STRING, DbusSignature::kBooleanTypename },
+ { DBUS_TYPE_BYTE_AS_STRING, DbusSignature::kByteTypename },
+ { DBUS_TYPE_DOUBLE_AS_STRING, DbusSignature::kDoubleTypename },
+ { DBUS_TYPE_OBJECT_PATH_AS_STRING, kObjectPathTypename },
+ { DBUS_TYPE_INT16_AS_STRING, DbusSignature::kSigned16Typename },
+ { DBUS_TYPE_INT32_AS_STRING, DbusSignature::kSigned32Typename },
+ { DBUS_TYPE_INT64_AS_STRING, DbusSignature::kSigned64Typename },
+ { DBUS_TYPE_STRING_AS_STRING, DbusSignature::kStringTypename },
+ { DBUS_TYPE_UNIX_FD_AS_STRING, DbusSignature::kUnixFdTypename },
+ { DBUS_TYPE_UINT16_AS_STRING, DbusSignature::kUnsigned16Typename },
+ { DBUS_TYPE_UINT32_AS_STRING, DbusSignature::kUnsigned32Typename },
+ { DBUS_TYPE_UINT64_AS_STRING, DbusSignature::kUnsigned64Typename },
+ { DBUS_TYPE_VARIANT_AS_STRING, DbusSignature::kVariantTypename },
+
+ // Complex types.
+ { kBoolArraySignature, kBoolArrayTypename },
+ { kByteArraySignature, kByteArrayTypename },
+ { kByteArrayArraySignature, kByteArrayArrayTypename },
+ { kObjectArraySignature, kObjectArrayTypename },
+ { kObjectDictBlobSignature, kObjectDictBlobTypename },
+ { kObjectNameDictSignature, kObjectNameDictTypename },
+ { kStringArraySignature, kStringArrayTypename },
+ { kStringStringDictSignature, kStringStringDictTypename },
+ { kStringStringStringDictSignature, kStringStringStringDictTypename },
+ { kStringStringVariantDictSignature, kStringStringVariantDictTypename },
+ { kStringVariantDictSignature, kStringVariantDictTypename },
+ { kStringVariantDictWithTrailingSignature, kStringVariantDictTypename },
+ { kUnsigned64ArraySignature, kUnsigned64ArrayTypename },
+ };
+ signature_.set_object_path_typename(kObjectPathTypename);
+ for (const auto& parse_test : parse_values) {
+ string output;
+ EXPECT_TRUE(signature_.Parse(parse_test.first, &output))
+ << "Expected signature " << parse_test.first
+ << " to succeed but it failed.";
+ EXPECT_EQ(parse_test.second, output)
+ << "Expected typename for " << parse_test.first
+ << " to be " << parse_test.second << " but instead it was " << output;
+ }
+}
+
+} // namespace chromeos_dbus_bindings