gen_log: pass a struct to a logging callback

gen_log now uses a struct instead of Json object.
- no json deps
  - simplify the build system
- smaller artifacts
  - netd_aidl_interface-V5-cpp.so: 634692 -> 483484

Bug: 161439795
Bug: 172188290
Test: aidl_integration_test
Change-Id: I7de3e10b1221bf83ce46e6636c5aad1457515f21
diff --git a/aidl_to_cpp_common.cpp b/aidl_to_cpp_common.cpp
index 2d69a4a..6ac1d30 100644
--- a/aidl_to_cpp_common.cpp
+++ b/aidl_to_cpp_common.cpp
@@ -32,6 +32,22 @@
 namespace aidl {
 namespace cpp {
 
+char kTransactionLogStruct[] = R"(struct TransactionLog {
+  double duration_ms;
+  std::string interface_name;
+  std::string method_name;
+  const void* proxy_address;
+  const void* stub_address;
+  std::vector<std::pair<std::string, std::string>> input_args;
+  std::vector<std::pair<std::string, std::string>> output_args;
+  std::string result;
+  std::string exception_message;
+  int32_t exception_code;
+  int32_t transaction_error;
+  int32_t service_specific_error_code;
+};
+)";
+
 char kToStringHelper[] = R"(template <typename _T> class _has_toString {
   template <typename _U> static std::true_type __has_toString(decltype(&_U::toString));
   template <typename _U> static std::false_type __has_toString(...);
@@ -195,174 +211,26 @@
   return ToStringRaw(type, expr);
 }
 
-struct TypeInfo {
-  // name of the type in C++ output
-  std::string cpp_name;
-
-  // function that writes an expression to convert a variable to a Json::Value
-  // object
-  std::function<void(CodeWriter& w, const AidlTypeSpecifier& type, const string& var_name,
-                     bool isNdk)>
-      toJsonValueExpr;
-};
-
-const static std::unordered_map<std::string, TypeInfo> kTypeInfoMap = {
-    {"void", {"void", nullptr}},
-    {"boolean",
-     {
-         "bool",
-         [](CodeWriter& c, const AidlTypeSpecifier&, const string& var_name, bool) {
-           c << "Json::Value(" << var_name << "? \"true\" : \"false\")";
-         },
-     }},
-    {"byte",
-     {
-         "int8_t",
-         [](CodeWriter& c, const AidlTypeSpecifier&, const string& var_name, bool) {
-           c << "Json::Value(" << var_name << ")";
-         },
-     }},
-    {"char",
-     {
-         "char16_t",
-         [](CodeWriter& c, const AidlTypeSpecifier&, const string& var_name, bool isNdk) {
-           if (isNdk) {
-             c << "Json::Value(" << var_name << ")";
-           } else {
-             c << "Json::Value(std::string(android::String8(&" << var_name << ", 1)))";
-           }
-         },
-     }},
-    {"int",
-     {
-         "int32_t",
-         [](CodeWriter& c, const AidlTypeSpecifier&, const string& var_name, bool) {
-           c << "Json::Value(" << var_name << ")";
-         },
-     }},
-    {"long",
-     {
-         "int64_t",
-         [](CodeWriter& c, const AidlTypeSpecifier&, const string& var_name, bool) {
-           c << "Json::Value(static_cast<Json::Int64>(" << var_name << "))";
-         },
-     }},
-    {"float",
-     {
-         "float",
-         [](CodeWriter& c, const AidlTypeSpecifier&, const string& var_name, bool) {
-           c << "Json::Value(" << var_name << ")";
-         },
-     }},
-    {"double",
-     {
-         "double",
-         [](CodeWriter& c, const AidlTypeSpecifier&, const string& var_name, bool) {
-           c << "Json::Value(" << var_name << ")";
-         },
-     }},
-    {"String",
-     {
-         "std::string",
-         [](CodeWriter& c, const AidlTypeSpecifier&, const string& var_name, bool) {
-           c << "Json::Value(" << var_name << ")";
-         },
-     }}
-    // missing List, Map, ParcelFileDescriptor, IBinder
-};
-
-const static TypeInfo kTypeInfoForDefinedType{
-    "<<parcelable>>",  // pseudo-name for parcelable types
-    [](CodeWriter& c, const AidlTypeSpecifier& type, const string& var_name, bool) {
-      c << ToString(type, var_name);
-    }};
-
-TypeInfo GetTypeInfo(const AidlTypeSpecifier& aidl) {
-  AIDL_FATAL_IF(!aidl.IsResolved(), aidl) << aidl.ToString();
-  const string& aidl_name = aidl.GetName();
-
-  if (AidlTypenames::IsBuiltinTypename(aidl_name)) {
-    auto it = kTypeInfoMap.find(aidl_name);
-    if (it != kTypeInfoMap.end()) {
-      return it->second;
-    }
-    return {};
-  }
-
-  const AidlDefinedType* defined_type = aidl.GetDefinedType();
-  AIDL_FATAL_IF(defined_type == NULL, aidl) << aidl.ToString();
-  if (defined_type->AsStructuredParcelable() || defined_type->AsEnumDeclaration() ||
-      defined_type->AsUnionDeclaration()) {
-    return kTypeInfoForDefinedType;
-  }
-
-  // skip interface types
-  return {};
-}
-
-inline bool CanWriteLog(const TypeInfo& t) {
-  return t.cpp_name != "";
-}
-
-bool CanWriteLog(const AidlTypeSpecifier& aidl) {
-  return CanWriteLog(GetTypeInfo(aidl));
-}
-
-void WriteLogFor(CodeWriter& writer, const AidlTypeSpecifier& type, const std::string& name,
-                 bool isPointer, const std::string& log, bool isNdk) {
-  const TypeInfo info = GetTypeInfo(type);
-  if (!CanWriteLog(info)) {
-    return;
-  }
-
-  const string var_object_expr = ((isPointer ? "*" : "")) + name;
-  if (type.IsArray()) {
-    const AidlTypeSpecifier& base_type = type.ArrayBase();
-    writer << log << " = Json::Value(Json::arrayValue);\n";
-    writer << "for (const auto& v: " << var_object_expr << ") " << log << ".append(";
-    info.toJsonValueExpr(writer, base_type, "v", isNdk);
-    writer << ");";
-  } else {
-    writer << log << " = ";
-    info.toJsonValueExpr(writer, type, var_object_expr, isNdk);
-    writer << ";";
-  }
-  writer << "\n";
-}
-
-void WriteLogForArguments(CodeWriterPtr& writer, const AidlArgument& a, bool isServer,
-                          string logVarName, bool isNdk) {
-  if (!CanWriteLog(a.GetType())) {
-    return;
-  }
-  string logElementVarName = "_log_arg_element";
-  (*writer) << "{\n";
-  (*writer).Indent();
-  (*writer) << "Json::Value " << logElementVarName << "(Json::objectValue);\n";
-  string varName = isServer || isNdk ? BuildVarName(a) : a.GetName();
-  (*writer) << logElementVarName << "[\"name\"] = \"" << varName << "\";\n";
-
-  bool isPointer = a.IsOut() && !isServer;
-  WriteLogFor(*(writer.get()), a.GetType(), varName, isPointer, logElementVarName + "[\"value\"]",
-              isNdk);
-  (*writer) << logVarName << ".append(" << logElementVarName << ");\n";
-  (*writer) << "}\n";
-  (*writer).Dedent();
+void WriteLogForArgument(CodeWriter& w, const AidlArgument& a, bool is_server,
+                         const string& log_var, bool is_ndk) {
+  const string var_name = is_server || is_ndk ? BuildVarName(a) : a.GetName();
+  const bool is_pointer = a.IsOut() && !is_server;
+  const string value_expr = (is_pointer ? "*" : "") + var_name;
+  w << log_var
+    << ".emplace_back(\"" + var_name + "\", " + ToString(a.GetType(), value_expr) + ");\n";
 }
 
 const string GenLogBeforeExecute(const string className, const AidlMethod& method, bool isServer,
                                  bool isNdk) {
   string code;
   CodeWriterPtr writer = CodeWriter::ForString(&code);
-  (*writer) << "Json::Value _log_input_args(Json::arrayValue);\n";
+  (*writer) << className << "::TransactionLog _transaction_log;\n";
 
   (*writer) << "if (" << className << "::logFunc != nullptr) {\n";
   (*writer).Indent();
 
-  for (const auto& a : method.GetArguments()) {
-    if (a->IsIn()) {
-      WriteLogForArguments(writer, *a, isServer, "_log_input_args", isNdk);
-    }
+  for (const auto& a : method.GetInArguments()) {
+    WriteLogForArgument(*writer, *a, isServer, "_transaction_log.input_args", isNdk);
   }
 
   (*writer).Dedent();
@@ -381,95 +249,45 @@
 
   (*writer) << "if (" << className << "::logFunc != nullptr) {\n";
   (*writer).Indent();
-
-  // Write the log as a Json object. For example,
-  //
-  // Json log object for following interface description
-  //
-  // package foo.bar;
-  // interface IFoo {
-  //   String TestMethod(int arg1, inout String[] arg2, out double arg3);
-  // }
-  //
-  // would be:
-  //
-  // {
-  //   duration_ms: 100.42,
-  //   interface_name: "foo.bar.IFoo",
-  //   method_name: "TestMethod",
-  //   (proxy|stub)_address: "0x12345678",
-  //   input_args: [
-  //     {name: "arg1", value: 30,},
-  //     {name: "arg2", value: ["apple", "grape"],},
-  //   ],
-  //   output_args: [
-  //     {name: "arg2", value: ["mango", "banana"],},
-  //     {name: "arg3", value: "10.5",},
-  //   ],
-  //   _aidl_return: "ok",
-  //   binder_status: {
-  //     exception_code: -8,
-  //     exception_message: "Something wrong",
-  //     transaction_error: 0,
-  //     service_specific_error_code: -42,
-  //   },
-  // }
+  const auto address = (isNdk && isServer) ? "_aidl_impl.get()" : "static_cast<const void*>(this)";
   (*writer) << "auto _log_end = std::chrono::steady_clock::now();\n";
-  (*writer) << "Json::Value _log_transaction(Json::objectValue);\n";
-  (*writer) << "_log_transaction[\"duration_ms\"] = "
-            << "std::chrono::duration<double, std::milli>(_log_end - "
-               "_log_start).count();\n";
-  (*writer) << "_log_transaction[\"interface_name\"] = "
-            << "Json::Value(\"" << interface.GetCanonicalName() << "\");\n";
-  (*writer) << "_log_transaction[\"method_name\"] = "
-            << "Json::Value(\"" << method.GetName() << "\");\n";
+  (*writer) << "_transaction_log.duration_ms = std::chrono::duration<double, std::milli>(_log_end "
+               "- _log_start).count();\n";
+  (*writer) << "_transaction_log.interface_name = \"" << interface.GetCanonicalName() << "\";\n";
+  (*writer) << "_transaction_log.method_name = \"" << method.GetName() << "\";\n";
+  (*writer) << "_transaction_log.stub_address = " << (isServer ? address : "nullptr") << ";\n";
+  (*writer) << "_transaction_log.proxy_address = " << (isServer ? "nullptr" : address) << ";\n";
 
-  (*writer) << "_log_transaction[\"" << (isServer ? "stub_address" : "proxy_address") << "\"] = ";
-  (*writer) << "Json::Value("
-            << "(std::ostringstream() << "
-            << (isNdk && isServer ? "_aidl_impl" : "static_cast<const void*>(this)") << ").str()"
-            << ");\n";
-  (*writer) << "_log_transaction[\"input_args\"] = _log_input_args;\n";
-  (*writer) << "Json::Value _log_output_args(Json::arrayValue);\n";
-
-  (*writer) << "Json::Value _log_status(Json::objectValue);\n";
   if (isNdk) {
-    (*writer) << "_log_status[\"exception_code\"] = Json::Value(AStatus_getExceptionCode("
-              << statusVarName << ".get()));\n";
-    (*writer) << "_log_status[\"exception_message\"] = Json::Value(AStatus_getMessage("
-              << statusVarName << ".get()));\n";
-    (*writer) << "_log_status[\"transaction_error\"] = Json::Value(AStatus_getStatus("
-              << statusVarName << ".get()));\n";
-    (*writer) << "_log_status[\"service_specific_error_code\"] = "
-                 "Json::Value(AStatus_getServiceSpecificError("
-              << statusVarName << ".get()));\n";
+    (*writer) << "_transaction_log.exception_code = AStatus_getExceptionCode(" << statusVarName
+              << ".get());\n";
+    (*writer) << "_transaction_log.exception_message = AStatus_getMessage(" << statusVarName
+              << ".get());\n";
+    (*writer) << "_transaction_log.transaction_error = AStatus_getStatus(" << statusVarName
+              << ".get());\n";
+    (*writer) << "_transaction_log.service_specific_error_code = AStatus_getServiceSpecificError("
+              << statusVarName << ".get());\n";
   } else {
-    (*writer) << "_log_status[\"exception_code\"] = Json::Value(" << statusVarName
-              << ".exceptionCode());\n";
-    (*writer) << "_log_status[\"exception_message\"] = Json::Value(" << statusVarName
-              << ".exceptionMessage());\n";
-    (*writer) << "_log_status[\"transaction_error\"] = Json::Value(" << statusVarName
-              << ".transactionError());\n";
-    (*writer) << "_log_status[\"service_specific_error_code\"] = Json::Value(" << statusVarName
-              << ".serviceSpecificErrorCode());\n";
+    (*writer) << "_transaction_log.exception_code = " << statusVarName << ".exceptionCode();\n";
+    (*writer) << "_transaction_log.exception_message = " << statusVarName
+              << ".exceptionMessage();\n";
+    (*writer) << "_transaction_log.transaction_error = " << statusVarName
+              << ".transactionError();\n";
+    (*writer) << "_transaction_log.service_specific_error_code = " << statusVarName
+              << ".serviceSpecificErrorCode();\n";
   }
 
-  (*writer) << "_log_transaction[\"binder_status\"] = _log_status;\n";
-
   for (const auto& a : method.GetOutArguments()) {
-    WriteLogForArguments(writer, *a, isServer, "_log_output_args", isNdk);
+    WriteLogForArgument(*writer, *a, isServer, "_transaction_log.output_args", isNdk);
   }
 
-  (*writer) << "_log_transaction[\"output_args\"] = _log_output_args;\n";
-
   if (method.GetType().GetName() != "void") {
-    WriteLogFor(*(writer.get()), method.GetType(), returnVarName, !isServer,
-                "_log_transaction[\"" + returnVarName + "\"]", isNdk);
+    const string expr = (isServer ? "" : "*") + returnVarName;
+    (*writer) << "_transaction_log.result = " << ToString(method.GetType(), expr) << ";\n";
   }
 
-  // call the user-provided function with the Json object for the entire
-  // transaction
-  (*writer) << className << "::logFunc(_log_transaction);\n";
+  // call the user-provided function with the transaction log object
+  (*writer) << className << "::logFunc(_transaction_log);\n";
 
   (*writer).Dedent();
   (*writer) << "}\n";