Emit toString functions for all types.

* toString() is mainly for debugging purposes only.

* For HIDL internal types (hidl_string, hidl_vec, etc.)
  toString() is found in ::android::hardware::details.

* For a user defined type
  android.hardware.foo@1.0::IFoo.Type,
  toString() is found in ::android::hardware::foo::V1_0.

* For bitfield<::anroid::hardware::foo::V1_0::T>
  that gets translated to the underlying
  numeric type of T, it doesn't make sense to override toString().
  A templated toString() function for each user-defined HIDL enum \
  is introduced into the same namespace; call it with
    using namespace ::android::hardware::foo::V1_0;
    toString<IFoo::MyEnumType>(value);

Test: hidl_test and look at the output of logcat

Bug: 33459772

Change-Id: I70eee018e31d700bf1376334276dbd343af5615f
diff --git a/ArrayType.cpp b/ArrayType.cpp
index 306cb9d..aaeca60 100644
--- a/ArrayType.cpp
+++ b/ArrayType.cpp
@@ -318,7 +318,6 @@
     out << "}\n\n";
 }
 
-
 bool ArrayType::needsEmbeddedReadWrite() const {
     return mElementType->needsEmbeddedReadWrite();
 }
diff --git a/CompoundType.cpp b/CompoundType.cpp
index 64e588a..0b2a579 100644
--- a/CompoundType.cpp
+++ b/CompoundType.cpp
@@ -419,6 +419,10 @@
 status_t CompoundType::emitGlobalTypeDeclarations(Formatter &out) const {
     Scope::emitGlobalTypeDeclarations(out);
 
+    out << "std::string toString("
+        << getCppArgumentType()
+        << ");\n";
+
     if (!canCheckEquality()) {
         return OK;
     }
@@ -506,6 +510,29 @@
         emitResolveReferenceDef(out, prefix, false /* isReader */);
     }
 
+    out << "std::string toString("
+        << getCppArgumentType()
+        << (mFields->empty() ? "" : " o")
+        << ") ";
+
+    out.block([&] {
+        // include toString for scalar types
+        out << "using ::android::hardware::details::toString;\n"
+            << "std::string os;\n";
+        out << "os += \"{\";\n";
+
+        for (const CompoundField *field : *mFields) {
+            out << "os += \"";
+            if (field != *(mFields->begin())) {
+                out << ", ";
+            }
+            out << "." << field->name() << " = \";\n";
+            field->type().emitDump(out, "os", "o." + field->name());
+        }
+
+        out << "os += \"}\"; return os;\n";
+    }).endl().endl();
+
     return OK;
 }
 
diff --git a/EnumType.cpp b/EnumType.cpp
index 6ce8010..bc4ad7c 100644
--- a/EnumType.cpp
+++ b/EnumType.cpp
@@ -267,6 +267,55 @@
     emitBitFieldBitwiseAssignmentOperator(out, "|");
     emitBitFieldBitwiseAssignmentOperator(out, "&");
 
+    out << "template<typename>\n"
+        << "std::string toString("
+        << resolveToScalarType()->getCppArgumentType()
+        << " o);\n";
+    out << "template<>\n"
+        << "std::string toString<" << getCppStackType() << ">("
+        << resolveToScalarType()->getCppArgumentType()
+        << " o);\n\n";
+
+    out << "inline std::string toString("
+        << getCppArgumentType()
+        << " o) ";
+
+    out.block([&] {
+        out << "return toString<" << getCppStackType() << ">("
+            << "static_cast<" << resolveToScalarType()->getCppStackType() << ">(o));\n";
+    }).endl().endl();
+
+    return OK;
+}
+
+status_t EnumType::emitTypeDefinitions(Formatter &out, const std::string /* prefix */) const {
+
+    const ScalarType *scalarType = mStorageType->resolveToScalarType();
+    CHECK(scalarType != NULL);
+
+    out << "template<>\n"
+        << "std::string toString<" << getCppStackType() << ">("
+        << resolveToScalarType()->getCppArgumentType()
+        << " o) ";
+    out.block([&] {
+        // include toHexString for scalar types
+        out << "using ::android::hardware::details::toHexString;\n"
+            << "std::string os;\n";
+        out << "bool first = true;\n";
+        for (EnumValue *value : values()) {
+            out.sIf("o & " + fullName() + "::" + value->name(), [&] {
+                out << "os += (first ? \"\" : \" | \"); "
+                    << "first = false;\n"
+                    << "os += \"" << value->name() << "\";\n";
+            }).endl();
+        }
+        out << "os += \" (\";\n";
+        scalarType->emitHexDump(out, "os", "o");
+        out << "os += \")\";\n";
+
+        out << "return os;\n";
+    }).endl().endl();
+
     return OK;
 }
 
@@ -680,6 +729,20 @@
             true /* needsCast */);
 }
 
+// a bitfield maps to the underlying scalar type in C++, so operator<< is
+// already defined. We can still emit useful information if the bitfield is
+// in a struct / union by overriding emitDump as below.
+void BitFieldType::emitDump(
+        Formatter &out,
+        const std::string &streamName,
+        const std::string &name) const {
+    CHECK(mElementType->isEnum());
+    const EnumType *enumType = static_cast<EnumType *>(mElementType);
+    out << streamName << " += "<< enumType->fqName().cppNamespace()
+        << "::toString<" << enumType->getCppStackType()
+        << ">(" << name << ");\n";
+}
+
 void BitFieldType::emitJavaFieldReaderWriter(
         Formatter &out,
         size_t depth,
diff --git a/EnumType.h b/EnumType.h
index 0ea97c1..a9a3d19 100644
--- a/EnumType.h
+++ b/EnumType.h
@@ -75,6 +75,7 @@
 
     status_t emitTypeDeclarations(Formatter &out) const override;
     status_t emitGlobalTypeDeclarations(Formatter &out) const override;
+    status_t emitTypeDefinitions(Formatter &out, const std::string prefix) const override;
 
     status_t emitJavaTypeDeclarations(
             Formatter &out, bool atTopLevel) const override;
@@ -170,6 +171,11 @@
         bool isReader,
         ErrorMode mode) const override;
 
+    void emitDump(
+            Formatter &out,
+            const std::string &streamName,
+            const std::string &name) const override;
+
     void emitJavaFieldReaderWriter(
         Formatter &out,
         size_t depth,
diff --git a/Interface.cpp b/Interface.cpp
index 9fd8954..9d7cc89 100644
--- a/Interface.cpp
+++ b/Interface.cpp
@@ -500,6 +500,53 @@
     }
 }
 
+status_t Interface::emitGlobalTypeDeclarations(Formatter &out) const {
+    status_t status = Scope::emitGlobalTypeDeclarations(out);
+    if (status != OK) {
+        return status;
+    }
+    out << "std::string toString("
+        << getCppArgumentType()
+        << ");\n";
+    return OK;
+}
+
+
+status_t Interface::emitTypeDefinitions(
+        Formatter &out, const std::string prefix) const {
+    std::string space = prefix.empty() ? "" : (prefix + "::");
+    status_t err = Scope::emitTypeDefinitions(out, space + localName());
+    if (err != OK) {
+        return err;
+    }
+
+    out << "std::string toString("
+        << getCppArgumentType()
+        << " o) ";
+
+    out.block([&] {
+        out << "std::string os;\nbool ok = false;\n";
+        // TODO b/34136228 use interfaceDescriptor instead
+        out << "auto ret = o->interfaceChain([&os, &ok] (const auto &chain) ";
+        out.block([&] {
+            out.sIf("chain.size() >= 1", [&] {
+                out << "os += chain[0].c_str();\n"
+                    << "ok = true;\n";
+            }).endl();
+        });
+        out << ");\n";
+        out.sIf("!ret.isOk() || !ok", [&] {
+            out << "os += \"[class or subclass of \";\n"
+                << "os += " << fullName() << "::descriptor;\n"
+                << "os += \"]\";\n";
+        }).endl();
+        out << "os += o->isRemote() ? \"@remote\" : \"@local\";\n"
+            << "return os;\n";
+    }).endl().endl();
+
+    return OK;
+}
+
 void Interface::emitJavaReaderWriter(
         Formatter &out,
         const std::string &parcelObj,
diff --git a/Interface.h b/Interface.h
index 0182818..87a50bc 100644
--- a/Interface.h
+++ b/Interface.h
@@ -89,6 +89,10 @@
             bool isReader,
             ErrorMode mode) const override;
 
+    status_t emitGlobalTypeDeclarations(Formatter &out) const override;
+    status_t emitTypeDefinitions(
+            Formatter &out, const std::string prefix) const override;
+
     void emitJavaReaderWriter(
             Formatter &out,
             const std::string &parcelObj,
diff --git a/NamedType.cpp b/NamedType.cpp
index 939b366..cb6556c 100644
--- a/NamedType.cpp
+++ b/NamedType.cpp
@@ -58,5 +58,12 @@
     return mLocation;
 }
 
+void NamedType::emitDump(
+        Formatter &out,
+        const std::string &streamName,
+        const std::string &name) const {
+    emitDumpWithMethod(out, streamName, fqName().cppNamespace() + "::toString", name);
+}
+
 }  // namespace android
 
diff --git a/NamedType.h b/NamedType.h
index 93940e8..6791b47 100644
--- a/NamedType.h
+++ b/NamedType.h
@@ -49,6 +49,11 @@
     // returns null if no location is set for this type.
     const Location &location() const;
 
+    void emitDump(
+            Formatter &out,
+            const std::string &streamName,
+            const std::string &name) const override;
+
 private:
     std::string mLocalName;
     FQName mFullName;
diff --git a/ScalarType.cpp b/ScalarType.cpp
index fbd127c..9b6c0c3 100644
--- a/ScalarType.cpp
+++ b/ScalarType.cpp
@@ -211,6 +211,13 @@
     handleError(out, mode);
 }
 
+void ScalarType::emitHexDump(
+        Formatter &out,
+        const std::string &streamName,
+        const std::string &name) const {
+    out << streamName << " += toHexString(" << name << ");\n";
+}
+
 void ScalarType::emitJavaFieldReaderWriter(
         Formatter &out,
         size_t /* depth */,
diff --git a/ScalarType.h b/ScalarType.h
index 80f8159..8b70d72 100644
--- a/ScalarType.h
+++ b/ScalarType.h
@@ -80,6 +80,11 @@
             ErrorMode mode,
             bool needsCast) const;
 
+    void emitHexDump(
+            Formatter &out,
+            const std::string &streamName,
+            const std::string &name) const;
+
     void emitJavaFieldReaderWriter(
             Formatter &out,
             size_t depth,
diff --git a/Type.cpp b/Type.cpp
index 7e67702..f155816 100644
--- a/Type.cpp
+++ b/Type.cpp
@@ -189,6 +189,26 @@
     CHECK(!"Should not be here");
 }
 
+void Type::emitDump(
+        Formatter &out,
+        const std::string &streamName,
+        const std::string &name) const {
+    emitDumpWithMethod(out, streamName, "::android::hardware::details::toString", name);
+}
+
+void Type::emitDumpWithMethod(
+        Formatter &out,
+        const std::string &streamName,
+        const std::string &methodName,
+        const std::string &name) const {
+    out << streamName
+        << " += "
+        << methodName
+        << "("
+        << name
+        << ");\n";
+}
+
 bool Type::useParentInEmitResolveReferencesEmbedded() const {
     return needsResolveReferences();
 }
diff --git a/Type.h b/Type.h
index 007b114..79a44bf 100644
--- a/Type.h
+++ b/Type.h
@@ -152,6 +152,11 @@
             const std::string &parentName,
             const std::string &offsetText) const;
 
+    virtual void emitDump(
+            Formatter &out,
+            const std::string &streamName,
+            const std::string &name) const;
+
     virtual bool useParentInEmitResolveReferencesEmbedded() const;
 
     virtual bool useNameInEmitReaderWriterEmbedded(bool isReader) const;
@@ -240,6 +245,12 @@
             const std::string &suffix,
             const std::string &extra) const;
 
+    void emitDumpWithMethod(
+            Formatter &out,
+            const std::string &streamName,
+            const std::string &methodName,
+            const std::string &name) const;
+
 private:
     std::vector<Annotation *> *mAnnotations;
 
diff --git a/test/main.cpp b/test/main.cpp
index 356d61e..cbf3d6f 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -491,6 +491,34 @@
     }
 };
 
+TEST_F(HidlTest, ToStringTest) {
+    using namespace android::hardware;
+
+    LOG(INFO) << toString(IFoo::Everything{});
+
+    auto handle = native_handle_create(0, 1);
+    handle->data[0] = 5;
+    IFoo::Everything e {
+        .u = {.p = reinterpret_cast<void *>(0x5)},
+        .number = 10,
+        .h = handle,
+        .descSync = {std::vector<GrantorDescriptor>(), handle, 5},
+        .descUnsync = {std::vector<GrantorDescriptor>(), handle, 6},
+        .mem = hidl_memory("mymem", handle, 5),
+        .p = reinterpret_cast<void *>(0x6),
+        .vs = {"hello", "world"},
+        .multidimArray = hidl_vec<hidl_string>{"hello", "great", "awesome", "nice"}.data(),
+        .sArray = hidl_vec<hidl_string>{"awesome", "thanks", "you're welcome"}.data(),
+        .anotherStruct = {.first = "first", .last = "last"},
+        .bf = IFoo::BitField::V0 | IFoo::BitField::V2
+    };
+    LOG(INFO) << toString(e);
+    LOG(INFO) << toString(foo);
+    // toString is for debugging purposes only; no good EXPECT
+    // statement can be written here.
+    native_handle_delete(handle);
+}
+
 TEST_F(HidlTest, ServiceListTest) {
     static const std::set<std::string> binderizedSet = {
         "android.hardware.tests.pointer@1.0::IPointer/pointer",
diff --git a/utils/Formatter.cpp b/utils/Formatter.cpp
index 6709785..df3a5a4 100644
--- a/utils/Formatter.cpp
+++ b/utils/Formatter.cpp
@@ -86,7 +86,6 @@
     return this->block(block);
 }
 
-
 Formatter &Formatter::operator<<(const std::string &out) {
     const size_t len = out.length();
     size_t start = 0;