Generate server side .cpp file
Bug: 24505488
Test: Unit tests, wrote some more
Change-Id: I89d18f0bb612f3a3ffadac41c6ff32b67d6552b7
diff --git a/aidl_language.cpp b/aidl_language.cpp
index 7f00635..6458d8a 100644
--- a/aidl_language.cpp
+++ b/aidl_language.cpp
@@ -92,6 +92,10 @@
id_(id) {
has_id_ = true;
delete args;
+ for (const unique_ptr<AidlArgument>& a : arguments_) {
+ if (a->IsIn()) { in_arguments_.push_back(a.get()); }
+ if (a->IsOut()) { out_arguments_.push_back(a.get()); }
+ }
}
AidlMethod::AidlMethod(bool oneway, AidlType* type, std::string name,
diff --git a/aidl_language.h b/aidl_language.h
index b6c119c..d076e94 100644
--- a/aidl_language.h
+++ b/aidl_language.h
@@ -80,7 +80,10 @@
virtual ~AidlArgument() = default;
Direction GetDirection() const { return direction_; }
+ bool IsOut() const { return direction_ & OUT_DIR; }
+ bool IsIn() const { return direction_ & IN_DIR; }
bool DirectionWasSpecified() const { return direction_specified_; }
+
std::string GetName() const { return name_; }
int GetLine() const { return line_; }
const AidlType& GetType() const { return *type_; }
@@ -117,7 +120,16 @@
void SetId(unsigned id) { id_ = id; }
const std::vector<std::unique_ptr<AidlArgument>>& GetArguments() const {
- return arguments_;
+ return arguments_;
+ }
+ // An inout parameter will appear in both GetInArguments()
+ // and GetOutArguments(). AidlMethod retains ownership of the argument
+ // pointers returned in this way.
+ const std::vector<const AidlArgument*>& GetInArguments() const {
+ return in_arguments_;
+ }
+ const std::vector<const AidlArgument*>& GetOutArguments() const {
+ return out_arguments_;
}
private:
@@ -126,7 +138,9 @@
std::unique_ptr<AidlType> type_;
std::string name_;
unsigned line_;
- std::vector<std::unique_ptr<AidlArgument>> arguments_;
+ const std::vector<std::unique_ptr<AidlArgument>> arguments_;
+ std::vector<const AidlArgument*> in_arguments_;
+ std::vector<const AidlArgument*> out_arguments_;
bool has_id_;
int id_;
diff --git a/generate_cpp.cpp b/generate_cpp.cpp
index 4f58c50..128e8a8 100644
--- a/generate_cpp.cpp
+++ b/generate_cpp.cpp
@@ -18,11 +18,13 @@
#include "parse_helpers.h"
#include <cctype>
+#include <cstring>
#include <memory>
#include <random>
#include <string>
#include <base/stringprintf.h>
+#include <base/strings.h>
#include "aidl_language.h"
#include "ast_cpp.h"
@@ -30,6 +32,7 @@
#include "logging.h"
using android::base::StringPrintf;
+using android::base::Join;
using std::string;
using std::unique_ptr;
using std::vector;
@@ -39,10 +42,12 @@
namespace cpp {
namespace internals {
namespace {
-
+const char kStatusOkOrBreakCheck[] = "if (status != android::OK) { break; }";
+const char kReturnVarName[] = "_aidl_return";
const char kAndroidStatusLiteral[] = "android::status_t";
const char kIBinderHeader[] = "binder/IBinder.h";
const char kIInterfaceHeader[] = "binder/IInterface.h";
+const char kParcelHeader[] = "binder/Parcel.h";
string UpperCase(const std::string& s) {
string result = s;
@@ -51,33 +56,53 @@
return result;
}
-string GetCPPVarDec(const TypeNamespace& types, const AidlType& type,
- const string& var_name, bool use_pointer) {
- const Type* cpp_type = types.Find(type.GetName());
- if (cpp_type == nullptr) {
- // We should have caught this in type resolution.
- LOG(FATAL) << "internal error";
+string BuildVarName(const AidlArgument& a) {
+ string prefix = "out_";
+ if (a.GetDirection() & AidlArgument::IN_DIR) {
+ prefix = "in_";
}
- return StringPrintf("%s%s %s%s",
- cpp_type->CppType().c_str(),
- (use_pointer) ? "*" : "",
- var_name.c_str(),
- type.IsArray() ? "[]" : "");
+ return prefix + a.GetName();
+}
+
+vector<string> BuildArgList(const TypeNamespace& types,
+ const AidlMethod& method,
+ bool for_declaration) {
+ // Build up the argument list for the server method call.
+ vector<string> method_arguments;
+ for (const unique_ptr<AidlArgument>& a : method.GetArguments()) {
+ string literal;
+ if (for_declaration) {
+ // Method declarations need types, pointers to out params, and variable
+ // names that match the .aidl specification.
+ const Type* type = types.Find(a->GetType().GetName());
+ literal = StringPrintf(
+ "%s%s %s", type->CppType().c_str(),
+ (a->IsOut()) ? "*" : "",
+ a->GetName().c_str());
+ } else {
+ if (a->IsOut()) { literal = "&"; }
+ literal += BuildVarName(*a);
+ }
+ method_arguments.push_back(literal);
+ }
+
+ const Type* return_type = types.Find(method.GetType().GetName());
+ if (return_type != types.VoidType()) {
+ if (for_declaration) {
+ method_arguments.push_back(
+ StringPrintf("%s* %s", return_type->CppType().c_str(),
+ kReturnVarName));
+ } else {
+ method_arguments.push_back(string{"&"} + kReturnVarName);
+ }
+ }
+
+ return method_arguments;
}
unique_ptr<Declaration> BuildMethodDecl(const AidlMethod& method,
const TypeNamespace& types,
bool for_interface) {
- vector<string> args;
- for (const unique_ptr<AidlArgument>& arg : method.GetArguments()) {
- args.push_back(GetCPPVarDec(
- types, arg->GetType(), arg->GetName(),
- AidlArgument::OUT_DIR & arg->GetDirection()));
- }
-
- string return_arg = GetCPPVarDec(types, method.GetType(), "_aidl_return", true);
- args.push_back(return_arg);
-
uint32_t modifiers = 0;
if (for_interface) {
modifiers |= MethodDecl::IS_VIRTUAL;
@@ -89,7 +114,7 @@
return unique_ptr<Declaration>{
new MethodDecl{kAndroidStatusLiteral,
method.GetName(),
- args,
+ BuildArgList(types, method, true /* for method decl */),
modifiers}};
}
@@ -99,6 +124,15 @@
return NPtr{new N{"android", NPtr{new N{"generated", std::move(decl)}}}};
}
+bool DeclareLocalVariable(const TypeNamespace& types, const AidlArgument& a,
+ StatementBlock* b) {
+ const Type* cpp_type = types.Find(a.GetType().GetName());
+ if (!cpp_type) { return false; }
+
+ b->AddLiteral(cpp_type->CppType() + " " + BuildVarName(a));
+ return true;
+}
+
} // namespace
enum class ClassNames { BASE, CLIENT, SERVER, INTERFACE };
@@ -131,10 +165,103 @@
return unique_ptr<Document>{new CppSource{ {}, std::move(ns)}};
}
+namespace {
+
+bool HandleServerTransaction(const TypeNamespace& types,
+ const AidlMethod& method,
+ StatementBlock* b) {
+ // Declare all the parameters now. In the common case, we expect no errors
+ // in serialization.
+ for (const unique_ptr<AidlArgument>& a : method.GetArguments()) {
+ if (!DeclareLocalVariable(types, *a, b)) { return false; }
+ }
+
+ // Declare a variable to hold the return value.
+ const Type* return_type = types.Find(method.GetType().GetName());
+ if (return_type != types.VoidType()) {
+ b->AddLiteral(StringPrintf(
+ "%s %s", return_type->CppType().c_str(), kReturnVarName));
+ }
+
+ // Declare the status variable
+ b->AddLiteral(StringPrintf("%s status", kAndroidStatusLiteral));
+
+ // Deserialize each "in" parameter to the transaction.
+ for (const AidlArgument* a : method.GetInArguments()) {
+ // Deserialization looks roughly like:
+ // status = data.ReadInt32(&in_param_name);
+ // if (status != android::OK) { break; }
+ const Type* type = types.Find(a->GetType().GetName());
+ b->AddStatement(new Assignment{
+ "status",
+ new MethodCall{"data." + type->ReadFromParcelMethod(),
+ "&" + BuildVarName(*a)}});
+ b->AddLiteral(kStatusOkOrBreakCheck, false /* no semicolon */);
+ }
+
+ // Call the actual method. This is implemented by the subclass.
+ b->AddStatement(new Assignment{
+ "status", new MethodCall{
+ method.GetName(),
+ new ArgList{BuildArgList(types, method,
+ false /* not for method decl */)}}});
+ b->AddLiteral(kStatusOkOrBreakCheck, false /* no semicolon */);
+
+ // Write each out parameter to the reply parcel.
+ for (const AidlArgument* a : method.GetOutArguments()) {
+ // Serialization looks roughly like:
+ // status = data.WriteInt32(out_param_name);
+ // if (status != android::OK) { break; }
+ const Type* type = types.Find(a->GetType().GetName());
+ b->AddStatement(new Assignment{
+ "status",
+ new MethodCall{"reply->" + type->WriteToParcelMethod(),
+ BuildVarName(*a)}});
+ b->AddLiteral(kStatusOkOrBreakCheck, false /* no semicolon */);
+ }
+
+ return true;
+}
+
+} // namespace
+
unique_ptr<Document> BuildServerSource(const TypeNamespace& types,
const AidlInterface& parsed_doc) {
- unique_ptr<CppNamespace> ns{new CppNamespace{"android"}};
- return unique_ptr<Document>{new CppSource{ {}, std::move(ns)}};
+ const string bn_name = ClassName(parsed_doc, ClassNames::SERVER);
+ vector<string> include_list{bn_name + ".h", kParcelHeader};
+ unique_ptr<MethodImpl> on_transact{new MethodImpl{
+ kAndroidStatusLiteral, bn_name, "onTransact",
+ {"uint32_t code",
+ "const android::Parcel& data",
+ "android::Parcel* reply",
+ "uint32_t flags"}
+ }};
+
+ // Add the all important switch statement, but retain a pointer to it.
+ SwitchStatement* s = new SwitchStatement{"code"};
+ on_transact->AddStatement(unique_ptr<AstNode>{s});
+
+ // The switch statement has a case statement for each transaction code.
+ for (const auto& method : parsed_doc.GetMethods()) {
+ StatementBlock* b = s->AddCase("Call::" + UpperCase(method->GetName()));
+ if (!b) { return nullptr; }
+
+ if (!HandleServerTransaction(types, *method, b)) { return nullptr; }
+ }
+
+ // The switch statement has a default case which defers to the super class.
+ // The superclass handles a few pre-defined transactions.
+ StatementBlock* b = s->AddCase("");
+ b->AddLiteral(
+ "status = android::BBinder::onTransact(code, data, reply, flags)");
+
+ // Finally, the server's onTransact method just returns a status code.
+ on_transact->AddStatement(unique_ptr<AstNode>{
+ new LiteralStatement{"return status"}});
+
+ return unique_ptr<Document>{new CppSource{
+ include_list,
+ NestInNamespaces(std::move(on_transact))}};
}
unique_ptr<Document> BuildInterfaceSource(const TypeNamespace& types,
diff --git a/generate_cpp.h b/generate_cpp.h
index 8dc152b..d6a8177 100644
--- a/generate_cpp.h
+++ b/generate_cpp.h
@@ -18,6 +18,7 @@
#define AIDL_GENERATE_CPP_H_
#include "aidl_language.h"
+#include "ast_cpp.h"
#include "options.h"
#include "type_cpp.h"
@@ -29,6 +30,18 @@
const cpp::TypeNamespace& types,
const AidlInterface& parsed_doc);
+namespace internals {
+std::unique_ptr<Document> BuildClientSource(const TypeNamespace& types,
+ const AidlInterface& parsed_doc);
+std::unique_ptr<Document> BuildServerSource(const TypeNamespace& types,
+ const AidlInterface& parsed_doc);
+std::unique_ptr<Document> BuildInterfaceSource(const TypeNamespace& types,
+ const AidlInterface& parsed_doc);
+std::unique_ptr<Document> BuildClientHeader(const TypeNamespace& types,
+ const AidlInterface& parsed_doc);
+std::unique_ptr<Document> BuildInterfaceHeader(const TypeNamespace& types,
+ const AidlInterface& parsed_doc);
+}
} // namespace cpp
} // namespace aidl
} // namespace android
diff --git a/generate_cpp_unittest.cpp b/generate_cpp_unittest.cpp
index f6bb7c1..9801d00 100644
--- a/generate_cpp_unittest.cpp
+++ b/generate_cpp_unittest.cpp
@@ -22,6 +22,7 @@
#include "aidl_language.h"
#include "ast_cpp.h"
#include "code_writer.h"
+#include "generate_cpp.h"
#include "tests/fake_io_delegate.h"
#include "type_cpp.h"
@@ -32,15 +33,6 @@
namespace android {
namespace aidl {
namespace cpp {
-namespace internals {
-unique_ptr<Document> BuildInterfaceSource(const TypeNamespace& types,
- const AidlInterface& parsed_doc);
-unique_ptr<Document> BuildClientHeader(const TypeNamespace& types,
- const AidlInterface& parsed_doc);
-unique_ptr<Document> BuildInterfaceHeader(const TypeNamespace& types,
- const AidlInterface& parsed_doc);
-}
-
namespace {
const char kTrivialInterfaceAIDL[] =
@@ -48,6 +40,41 @@
int Ping(int token);
})";
+const char kExpectedTrivialServerSourceOutput[] =
+R"(#include <BnPingResponder.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+namespace generated {
+
+android::status_t BnPingResponder::onTransact(uint32_t code, const android::Parcel& data, android::Parcel* reply, uint32_t flags) {
+switch (code) {
+case Call::PING:
+{
+int32_t in_token;
+int32_t _aidl_return;
+android::status_t status;
+status = data.readInt32(&in_token);
+if (status != android::OK) { break; }
+status = Ping(in_token, &_aidl_return);
+if (status != android::OK) { break; }
+}
+break;
+default:
+{
+status = android::BBinder::onTransact(code, data, reply, flags);
+}
+break;
+}
+return status;
+}
+
+} // namespace generated
+
+} // namespace android
+)";
+
const char kExpectedTrivialClientHeaderOutput[] =
R"(#ifndef BpPingResponder_H
#define BpPingResponder_H
@@ -156,6 +183,14 @@
Compare(doc.get(), kExpectedTrivialClientHeaderOutput);
}
+TEST_F(TrivialInterfaceASTTest, GeneratesServerSource) {
+ AidlInterface* interface = Parse();
+ ASSERT_NE(interface, nullptr);
+ TypeNamespace types;
+ unique_ptr<Document> doc = internals::BuildServerSource(types, *interface);
+ Compare(doc.get(), kExpectedTrivialServerSourceOutput);
+}
+
TEST_F(TrivialInterfaceASTTest, GeneratesInterfaceHeader) {
AidlInterface* interface = Parse();
ASSERT_NE(interface, nullptr);
diff --git a/type_cpp.cpp b/type_cpp.cpp
index 6f80845..8ab26c3 100644
--- a/type_cpp.cpp
+++ b/type_cpp.cpp
@@ -24,6 +24,18 @@
namespace android {
namespace aidl {
namespace cpp {
+namespace {
+
+class VoidType : public Type {
+ public:
+ VoidType() : Type("void", "void", "XXX", "XXX") {}
+ virtual ~VoidType() = default;
+ bool CanBeArray() const override { return false; }
+ bool CanBeOutParameter() const override { return false; }
+ bool CanWriteToParcel() const override { return false; }
+}; // class VoidType
+
+} // namespace
Type::Type(const string& aidl_type,
const string& cpp_type,
@@ -59,6 +71,9 @@
new Type("float", "float", "readFloat", "writeFloat"));
types_.emplace_back(
new Type("double", "double", "readDouble", "writeDouble"));
+
+ void_type_ = new class VoidType();
+ types_.emplace_back(void_type_);
}
bool TypeNamespace::AddParcelableType(const AidlParcelable* p,
diff --git a/type_cpp.h b/type_cpp.h
index 26f288d..779a0a7 100644
--- a/type_cpp.h
+++ b/type_cpp.h
@@ -72,6 +72,8 @@
const Type* Find(const std::string& type_name) const;
+ const Type* VoidType() const { return void_type_; }
+
protected:
const ValidatableType* GetValidatableType(
const std::string& type_name) const override;
@@ -79,6 +81,8 @@
private:
std::vector<std::unique_ptr<Type>> types_;
+ Type* void_type_ = nullptr;
+
DISALLOW_COPY_AND_ASSIGN(TypeNamespace);
}; // class TypeNamespace