Build toString + operator== in client libraries.

This is Phase I of a three phase plan to remove toString + operator==
from sources since the VNDK is currently not in place.

Phase I: functions in client and source libraries
Phase II: rebuild prebuilts with the functions in the clients
  across all targets and vendors.
Phase III: remove functions from the source libraries

Test: hidl_test
Bug: 65200821
Change-Id: I4f2b10c088b1c1663a7a10d479e2607311dc462c
diff --git a/CompoundType.cpp b/CompoundType.cpp
index d70bc78..91bf0cf 100644
--- a/CompoundType.cpp
+++ b/CompoundType.cpp
@@ -459,19 +459,64 @@
 status_t CompoundType::emitGlobalTypeDeclarations(Formatter &out) const {
     Scope::emitGlobalTypeDeclarations(out);
 
+    // TODO(b/65200821): remove these ifdefs
+    out << "#ifdef REALLY_IS_HIDL_INTERNAL_LIB" << gCurrentCompileName << "\n";
     out << "std::string toString("
         << getCppArgumentType()
         << ");\n\n";
-
     if (canCheckEquality()) {
         out << "bool operator==("
             << getCppArgumentType() << ", " << getCppArgumentType() << ");\n\n";
 
         out << "bool operator!=("
             << getCppArgumentType() << ", " << getCppArgumentType() << ");\n\n";
+    }
+    out << "#else\n";
+    out << "static inline std::string toString("
+        << getCppArgumentType()
+        << (mFields->empty() ? "" : " o")
+        << ") ";
+
+    out.block([&] {
+        // include toString for scalar types
+        out << "using ::android::hardware::toString;\n"
+            << "std::string os;\n";
+        out << "os += \"{\";\n";
+
+        for (const NamedReference<Type>* 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();
+
+    if (canCheckEquality()) {
+        out << "static inline bool operator==("
+            << getCppArgumentType() << " " << (mFields->empty() ? "/* lhs */" : "lhs") << ", "
+            << getCppArgumentType() << " " << (mFields->empty() ? "/* rhs */" : "rhs") << ") ";
+        out.block([&] {
+            for (const auto &field : *mFields) {
+                out.sIf("lhs." + field->name() + " != rhs." + field->name(), [&] {
+                    out << "return false;\n";
+                }).endl();
+            }
+            out << "return true;\n";
+        }).endl().endl();
+
+        out << "static inline bool operator!=("
+            << getCppArgumentType() << " lhs," << getCppArgumentType() << " rhs)";
+        out.block([&] {
+            out << "return !(lhs == rhs);\n";
+        }).endl().endl();
     } else {
         out << "// operator== and operator!= are not generated for " << localName() << "\n\n";
     }
+    out << "#endif  // REALLY_IS_HIDL_INTERNAL_LIB\n";
 
     return OK;
 }
@@ -537,6 +582,8 @@
         emitResolveReferenceDef(out, prefix, false /* isReader */);
     }
 
+    // TODO(b/65200821): remove toString + operator== from .cpp once all prebuilts are rebuilt
+
     out << "std::string toString("
         << getCppArgumentType()
         << (mFields->empty() ? "" : " o")
diff --git a/EnumType.cpp b/EnumType.cpp
index 02caf62..151ca26 100644
--- a/EnumType.cpp
+++ b/EnumType.cpp
@@ -359,25 +359,83 @@
     emitBitFieldBitwiseAssignmentOperator(out, "|");
     emitBitFieldBitwiseAssignmentOperator(out, "&");
 
-    // toString for bitfields, equivalent to dumpBitfield in Java
+    // TODO(b/65200821): remove these ifndefs
+    out << "#ifdef REALLY_IS_HIDL_INTERNAL_LIB" << gCurrentCompileName << "\n";
+        // toString for bitfields, equivalent to dumpBitfield in Java
+        out << "template<typename>\n"
+            << "std::string toString("
+            << resolveToScalarType()->getCppArgumentType()
+            << " o);\n";
+        out << "template<>\n"
+            << "std::string toString<" << getCppStackType() << ">("
+            << resolveToScalarType()->getCppArgumentType()
+            << " o);\n\n";
+
+        // toString for enum itself
+        out << "std::string toString("
+            << getCppArgumentType()
+            << " o);\n\n";
+    out << "#else\n";
+    const ScalarType *scalarType = mStorageType->resolveToScalarType();
+    CHECK(scalarType != NULL);
+
     out << "template<typename>\n"
-        << "std::string toString("
-        << resolveToScalarType()->getCppArgumentType()
+        << "static inline std::string toString(" << resolveToScalarType()->getCppArgumentType()
         << " o);\n";
     out << "template<>\n"
-        << "std::string toString<" << getCppStackType() << ">("
-        << resolveToScalarType()->getCppArgumentType()
-        << " o);\n\n";
+        << "inline std::string toString<" << getCppStackType() << ">("
+        << scalarType->getCppArgumentType() << " o) ";
+    out.block([&] {
+        // include toHexString for scalar types
+        out << "using ::android::hardware::details::toHexString;\n"
+            << "std::string os;\n"
+            << getBitfieldCppType(StorageMode_Stack) << " flipped = 0;\n"
+            << "bool first = true;\n";
+        for (EnumValue *value : values()) {
+            std::string valueName = fullName() + "::" + value->name();
+            out.sIf("(o & " + valueName + ")" +
+                    " == static_cast<" + scalarType->getCppStackType() +
+                    ">(" + valueName + ")", [&] {
+                out << "os += (first ? \"\" : \" | \");\n"
+                    << "os += \"" << value->name() << "\";\n"
+                    << "first = false;\n"
+                    << "flipped |= " << valueName << ";\n";
+            }).endl();
+        }
+        // put remaining bits
+        out.sIf("o != flipped", [&] {
+            out << "os += (first ? \"\" : \" | \");\n";
+            scalarType->emitHexDump(out, "os", "o & (~flipped)");
+        });
+        out << "os += \" (\";\n";
+        scalarType->emitHexDump(out, "os", "o");
+        out << "os += \")\";\n";
 
-    // toString for enum itself
-    out << "std::string toString("
-        << getCppArgumentType()
-        << " o);\n\n";
+        out << "return os;\n";
+    }).endl().endl();
+
+    out << "static inline std::string toString(" << getCppArgumentType() << " o) ";
+
+    out.block([&] {
+        out << "using ::android::hardware::details::toHexString;\n";
+        for (EnumValue *value : values()) {
+            out.sIf("o == " + fullName() + "::" + value->name(), [&] {
+                out << "return \"" << value->name() << "\";\n";
+            }).endl();
+        }
+        out << "std::string os;\n";
+        scalarType->emitHexDump(out, "os",
+            "static_cast<" + scalarType->getCppStackType() + ">(o)");
+        out << "return os;\n";
+    }).endl().endl();
+    out << "#endif  // REALLY_IS_HIDL_INTERNAL_LIB\n";
 
     return OK;
 }
 
 status_t EnumType::emitTypeDefinitions(Formatter& out, const std::string& /* prefix */) const {
+    // TODO(b/65200821): remove toString from .cpp once all prebuilts are rebuilt
+
     const ScalarType *scalarType = mStorageType->resolveToScalarType();
     CHECK(scalarType != NULL);
 
diff --git a/Interface.cpp b/Interface.cpp
index e9e1c8f..3d9786f 100644
--- a/Interface.cpp
+++ b/Interface.cpp
@@ -822,9 +822,24 @@
     if (status != OK) {
         return status;
     }
+
+    // TODO(b/65200821): remove these ifndefs
+    out << "#ifdef REALLY_IS_HIDL_INTERNAL_LIB" << gCurrentCompileName << "\n";
     out << "std::string toString("
         << getCppArgumentType()
         << ");\n";
+    out << "#else\n";
+    out << "static inline std::string toString(" << getCppArgumentType() << " o) ";
+
+    out.block([&] {
+        out << "std::string os = \"[class or subclass of \";\n"
+            << "os += " << fullName() << "::descriptor;\n"
+            << "os += \"]\";\n"
+            << "os += o->isRemote() ? \"@remote\" : \"@local\";\n"
+            << "return os;\n";
+    }).endl().endl();
+    out << "#endif  // REALLY_IS_HIDL_INTERNAL_LIB\n";
+
     return OK;
 }
 
@@ -835,6 +850,7 @@
         return err;
     }
 
+    // TODO(b/65200821): remove toString from .cpp once all prebuilts are rebuilt
     out << "std::string toString("
         << getCppArgumentType()
         << " o) ";
diff --git a/Type.cpp b/Type.cpp
index c26d68c..930f9b9 100644
--- a/Type.cpp
+++ b/Type.cpp
@@ -28,6 +28,9 @@
 
 namespace android {
 
+// TODO(b/65200821): remove
+std::string gCurrentCompileName;
+
 Type::Type(Scope* parent) : mParent(parent) {}
 
 Type::~Type() {}
diff --git a/Type.h b/Type.h
index cf6478a..d45e066 100644
--- a/Type.h
+++ b/Type.h
@@ -30,6 +30,10 @@
 
 namespace android {
 
+// TODO(b/65200821): remove
+// HACK because no no type can depend or see AST
+extern std::string gCurrentCompileName;
+
 struct ConstantExpression;
 struct Formatter;
 struct FQName;
diff --git a/generateCpp.cpp b/generateCpp.cpp
index 6822090..ba66b1b 100644
--- a/generateCpp.cpp
+++ b/generateCpp.cpp
@@ -893,6 +893,9 @@
         << mPackage.string() << "::" << baseName
         << "\"\n\n";
 
+    // TODO(b/65200821): remove define
+    out << "#define REALLY_IS_HIDL_INTERNAL_LIB" << gCurrentCompileName << "\n";
+
     out << "#include <android/log.h>\n";
     out << "#include <cutils/trace.h>\n";
     out << "#include <hidl/HidlTransportSupport.h>\n\n";
diff --git a/main.cpp b/main.cpp
index 5dc93ce..c83c296 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1479,6 +1479,9 @@
     for (int i = 0; i < argc; ++i) {
         FQName fqName(argv[i]);
 
+        // TODO(b/65200821): remove
+        gCurrentCompileName = "_" + StringHelper::Uppercase(fqName.tokenName());
+
         if (!fqName.isValid()) {
             fprintf(stderr,
                     "ERROR: Invalid fully-qualified name.\n");