/* * Copyright (C) 2018, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "aidl_to_cpp_common.h" #include #include #include "ast_cpp.h" #include "logging.h" #include "os.h" using ::android::base::Join; namespace android { namespace aidl { namespace cpp { string ClassName(const AidlDefinedType& defined_type, ClassNames type) { string base_name = defined_type.GetName(); if (base_name.length() >= 2 && base_name[0] == 'I' && isupper(base_name[1])) { base_name = base_name.substr(1); } switch (type) { case ClassNames::CLIENT: return "Bp" + base_name; case ClassNames::SERVER: return "Bn" + base_name; case ClassNames::INTERFACE: return "I" + base_name; case ClassNames::DEFAULT_IMPL: return "I" + base_name + "Default"; case ClassNames::BASE: return base_name; case ClassNames::RAW: [[fallthrough]]; default: return defined_type.GetName(); } } std::string HeaderFile(const AidlDefinedType& defined_type, ClassNames class_type, bool use_os_sep) { std::string file_path = defined_type.GetPackage(); for (char& c : file_path) { if (c == '.') { c = (use_os_sep) ? OS_PATH_SEPARATOR : '/'; } } if (!file_path.empty()) { file_path += (use_os_sep) ? OS_PATH_SEPARATOR : '/'; } file_path += ClassName(defined_type, class_type); file_path += ".h"; return file_path; } void EnterNamespace(CodeWriter& out, const AidlDefinedType& defined_type) { const std::vector packages = defined_type.GetSplitPackage(); for (const std::string& package : packages) { out << "namespace " << package << " {\n"; } } void LeaveNamespace(CodeWriter& out, const AidlDefinedType& defined_type) { const std::vector packages = defined_type.GetSplitPackage(); for (auto it = packages.rbegin(); it != packages.rend(); ++it) { out << "} // namespace " << *it << "\n"; } } string BuildVarName(const AidlArgument& a) { string prefix = "out_"; if (a.GetDirection() & AidlArgument::IN_DIR) { prefix = "in_"; } return prefix + a.GetName(); } 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 toJsonValueExpr; }; const static std::unordered_map kTypeInfoMap = { {"void", {"void", nullptr}}, {"boolean", { "bool", [](CodeWriter& c, const string& var_name, bool) { c << "Json::Value(" << var_name << "? \"true\" : \"false\")"; }, }}, {"byte", { "int8_t", [](CodeWriter& c, const string& var_name, bool) { c << "Json::Value(" << var_name << ")"; }, }}, {"char", { "char16_t", [](CodeWriter& c, 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 string& var_name, bool) { c << "Json::Value(" << var_name << ")"; }, }}, {"long", { "int64_t", [](CodeWriter& c, const string& var_name, bool) { c << "Json::Value(static_cast(" << var_name << "))"; }, }}, {"float", { "float", [](CodeWriter& c, const string& var_name, bool) { c << "Json::Value(" << var_name << ")"; }, }}, {"double", { "double", [](CodeWriter& c, const string& var_name, bool) { c << "Json::Value(" << var_name << ")"; }, }}, {"String", { "std::string", [](CodeWriter& c, const string& var_name, bool) { c << "Json::Value(" << var_name << ")"; }, }} // missing List, Map, ParcelFileDescriptor, IBinder }; TypeInfo GetTypeInfo(const AidlTypeSpecifier& aidl) { CHECK(aidl.IsResolved()) << aidl.ToString(); const string& aidl_name = aidl.GetName(); TypeInfo info; if (AidlTypenames::IsBuiltinTypename(aidl_name)) { auto it = kTypeInfoMap.find(aidl_name); if (it != kTypeInfoMap.end()) { info = it->second; } } // Missing interface and parcelable type return info; } 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()) { writer << log << " = Json::Value(Json::arrayValue);\n"; writer << "for (const auto& v: " << var_object_expr << ") " << log << ".append("; info.toJsonValueExpr(writer, "v", isNdk); writer << ");"; } else { writer << log << " = "; info.toJsonValueExpr(writer, 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(); } 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) << "if (" << className << "::logFunc != nullptr) {\n"; (*writer).Indent(); for (const auto& a : method.GetArguments()) { if (a->IsIn()) { WriteLogForArguments(writer, *a, isServer, "_log_input_args", isNdk); } } (*writer).Dedent(); (*writer) << "}\n"; (*writer) << "auto _log_start = std::chrono::steady_clock::now();\n"; writer->Close(); return code; } const string GenLogAfterExecute(const string className, const AidlInterface& interface, const AidlMethod& method, const string& statusVarName, const string& returnVarName, bool isServer, bool isNdk) { string code; CodeWriterPtr writer = CodeWriter::ForString(&code); (*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, // }, // } (*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(_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) << "_log_transaction[\"" << (isServer ? "stub_address" : "proxy_address") << "\"] = "; (*writer) << "Json::Value(" << "(std::ostringstream() << " << (isNdk && isServer ? "_aidl_impl" : "static_cast(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"; } 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) << "_log_transaction[\"binder_status\"] = _log_status;\n"; for (const auto& a : method.GetOutArguments()) { WriteLogForArguments(writer, *a, isServer, "_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); } // call the user-provided function with the Json object for the entire // transaction (*writer) << className << "::logFunc(_log_transaction);\n"; (*writer).Dedent(); (*writer) << "}\n"; writer->Close(); return code; } std::string GenerateEnumValues(const AidlEnumDeclaration& enum_decl, const std::vector& enclosing_namespaces_of_enum_decl) { const auto fq_name = Join(Append(enclosing_namespaces_of_enum_decl, enum_decl.GetSplitPackage()), "::") + "::" + enum_decl.GetName(); const auto size = enum_decl.GetEnumerators().size(); std::ostringstream code; code << "#pragma clang diagnostic push\n"; code << "#pragma clang diagnostic ignored \"-Wc++17-extensions\"\n"; code << "template <>\n"; code << "constexpr inline std::array<" << fq_name << ", " << size << "> enum_values<" << fq_name << "> = {\n"; for (const auto& enumerator : enum_decl.GetEnumerators()) { code << " " << fq_name << "::" << enumerator->GetName() << ",\n"; } code << "};\n"; code << "#pragma clang diagnostic pop\n"; return code.str(); } } // namespace cpp } // namespace aidl } // namespace android