diff options
author | Paul Stewart <pstew@chromium.org> | 2014-09-03 19:01:54 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-09-05 04:10:43 +0000 |
commit | 0e3301aa8024df8ff895d9700f97e809cad2cf5e (patch) | |
tree | 01ec286b1b77c1b8427056703a3876e0b551a754 | |
parent | 154cad5f50f22e473237bb2ead8b5789180cfe2f (diff) | |
download | dbus-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.gyp | 7 | ||||
-rw-r--r-- | chromeos-dbus-bindings/dbus_signature.cc | 190 | ||||
-rw-r--r-- | chromeos-dbus-bindings/dbus_signature.h | 78 | ||||
-rw-r--r-- | chromeos-dbus-bindings/dbus_signature_unittest.cc | 148 |
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 |