generate C++ interface header, proxy and stub headers and sources.
diff --git a/generateCpp.cpp b/generateCpp.cpp
new file mode 100644
index 0000000..f9b1189
--- /dev/null
+++ b/generateCpp.cpp
@@ -0,0 +1,889 @@
+#include "AST.h"
+
+#include "Coordinator.h"
+#include "Formatter.h"
+#include "Interface.h"
+#include "Method.h"
+#include "Scope.h"
+
+#include <android-base/logging.h>
+#include <sys/stat.h>
+#include <vector>
+
+namespace android {
+
+static bool MakeParentHierarchy(const std::string &path) {
+ static const mode_t kMode = 0755;
+
+ size_t start = 1; // Ignore leading '/'
+ size_t slashPos;
+ while ((slashPos = path.find("/", start)) != std::string::npos) {
+ std::string partial = path.substr(0, slashPos);
+
+ struct stat st;
+ if (stat(partial.c_str(), &st) < 0) {
+ if (errno != ENOENT) {
+ return false;
+ }
+
+ int res = mkdir(partial.c_str(), kMode);
+ if (res < 0) {
+ return false;
+ }
+ } else if (!S_ISDIR(st.st_mode)) {
+ return false;
+ }
+
+ start = slashPos + 1;
+ }
+
+ return true;
+}
+
+static void SplitString(
+ const std::string &s, char c, std::vector<std::string> *components) {
+ components->clear();
+
+ size_t startPos = 0;
+ size_t matchPos;
+ while ((matchPos = s.find(c, startPos)) != std::string::npos) {
+ components->push_back(s.substr(startPos, matchPos - startPos));
+ startPos = matchPos + 1;
+ }
+
+ if (startPos + 1 < s.length()) {
+ components->push_back(s.substr(startPos));
+ }
+}
+
+static std::string upcase(const std::string in) {
+ std::string out{in};
+
+ for (auto &ch : out) {
+ ch = toupper(ch);
+ }
+
+ return out;
+}
+
+status_t AST::generateCpp() const {
+ status_t err = generateInterfaceHeader();
+
+ if (err == OK) {
+ err = generateStubHeader();
+ }
+
+ if (err == OK) {
+ err = generateProxyHeader();
+ }
+
+ if (err == OK) {
+ err = generateAllSource();
+ }
+
+ return err;
+}
+
+void AST::getPackageComponents(
+ std::vector<std::string> *components) const {
+ SplitString(mPackage.package(), '.', components);
+}
+
+void AST::getPackageAndVersionComponents(
+ std::vector<std::string> *components, bool cpp_compatible) const {
+ getPackageComponents(components);
+
+ const std::string packageVersion = mPackage.version();
+ CHECK(packageVersion[0] == '@');
+
+ if (!cpp_compatible) {
+ components->push_back(packageVersion.substr(1));
+ return;
+ }
+
+ const size_t dotPos = packageVersion.find('.');
+
+ // Form "Vmajor_minor".
+ std::string versionString = "V";
+ versionString.append(packageVersion.substr(1, dotPos - 1));
+ versionString.append("_");
+ versionString.append(packageVersion.substr(dotPos + 1));
+
+ components->push_back(versionString);
+}
+
+std::string AST::makeHeaderGuard(const std::string &baseName) const {
+ std::vector<std::string> packageComponents;
+ getPackageAndVersionComponents(
+ &packageComponents, true /* cpp_compatible */);
+
+ std::string guard = "HIDL_GENERATED";
+ for (const auto &component : packageComponents) {
+ guard += "_";
+ guard += component;
+ }
+
+ guard += "_";
+ guard += baseName;
+ guard += "_H_";
+
+ return guard;
+}
+
+void AST::enterLeaveNamespace(Formatter &out, bool enter) const {
+ std::vector<std::string> packageComponents;
+ getPackageAndVersionComponents(
+ &packageComponents, true /* cpp_compatible */);
+
+ if (enter) {
+ for (const auto &component : packageComponents) {
+ out << "namespace " << component << " {\n";
+ }
+ } else {
+ for (auto it = packageComponents.rbegin();
+ it != packageComponents.rend();
+ ++it) {
+ out << "} // namespace " << *it << "\n";
+ }
+ }
+}
+
+status_t AST::generateInterfaceHeader() const {
+ const std::string packagePath =
+ mCoordinator->getPackagePath(mPackage, true /* relative */);
+
+ std::string path = "/tmp/android/hardware/";
+ path.append(packagePath);
+
+ std::string ifaceName;
+ bool isInterface = true;
+ if (!AST::isInterface(&ifaceName)) {
+ ifaceName = "types";
+ isInterface = false;
+ }
+ path.append(ifaceName);
+ path.append(".h");
+
+ CHECK(MakeParentHierarchy(path));
+ FILE *file = fopen(path.c_str(), "w");
+
+ if (file == NULL) {
+ return -errno;
+ }
+
+ Formatter out(file);
+
+ const std::string guard = makeHeaderGuard(ifaceName);
+
+ out << "#ifndef " << guard << "\n";
+ out << "#define " << guard << "\n\n";
+
+ out << "#include <hwbinder/HidlSupport.h>\n";
+
+ if (isInterface) {
+ out << "#include <hwbinder/IBinder.h>\n";
+ out << "#include <hwbinder/IInterface.h>\n";
+ out << "#include <hwbinder/Status.h>\n";
+ }
+
+ out << "#include <utils/NativeHandle.h>\n\n";
+
+ enterLeaveNamespace(out, true /* enter */);
+ out << "\n";
+
+ if (isInterface) {
+ out << "struct "
+ << ifaceName
+ << " : public ::android::hidl::IInterface {\n";
+
+ out.indent();
+
+ out << "DECLARE_HWBINDER_META_INTERFACE(" << ifaceName << ");\n\n";
+ }
+
+ status_t err = emitTypeDeclarations(out);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (isInterface) {
+ const Interface *iface = mRootScope->getInterface();
+
+ out << "enum Call {\n";
+ out.indent();
+
+ bool first = true;
+ for (const auto &method : iface->methods()) {
+ out << upcase(method->name());
+
+ if (first) {
+ out << " = ::android::hidl::IBinder::FIRST_CALL_TRANSACTION";
+ first = false;
+ }
+
+ out << ",\n";
+ }
+
+ out.unindent();
+ out << "};\n\n";
+
+ bool haveCallbacks = false;
+ for (const auto &method : iface->methods()) {
+ const bool returnsValue = !method->results().empty();
+
+ if (!returnsValue) {
+ continue;
+ }
+
+ haveCallbacks = true;
+
+ out << "using "
+ << method->name()
+ << "_cb = std::function<void("
+ << Method::GetSignature(method->results())
+ << ")>;\n";
+ }
+
+ if (haveCallbacks) {
+ out << "\n";
+ }
+
+ for (const auto &method : iface->methods()) {
+ const bool returnsValue = !method->results().empty();
+
+ out << "virtual ::android::hidl::binder::Status "
+ << method->name()
+ << "("
+ << Method::GetSignature(method->args());
+
+ if (returnsValue) {
+ if (!method->args().empty()) {
+ out << ", ";
+ }
+
+ out << method->name() << "_cb _aidl_cb = nullptr";
+ }
+
+ out << ") = 0;\n";
+ }
+ }
+
+ if (isInterface) {
+ out.unindent();
+
+ out << "};\n";
+ }
+
+ out << "\n";
+ enterLeaveNamespace(out, false /* enter */);
+
+ out << "\n#endif // " << guard << "\n";
+
+ return OK;
+}
+
+status_t AST::emitTypeDeclarations(Formatter &out) const {
+ return mRootScope->emitTypeDeclarations(out);
+}
+
+status_t AST::generateStubHeader() const {
+ std::string ifaceName;
+ if (!AST::isInterface(&ifaceName)) {
+ // types.hal does not get a stub header.
+ return OK;
+ }
+
+ const std::string packagePath =
+ mCoordinator->getPackagePath(mPackage, true /* relative */);
+
+ // cut off the leading 'I'.
+ const std::string baseName = ifaceName.substr(1);
+
+ std::string path = "/tmp/android/hardware/";
+ path.append(packagePath);
+ path.append("Bn");
+ path.append(baseName);
+ path.append(".h");
+
+ CHECK(MakeParentHierarchy(path));
+ FILE *file = fopen(path.c_str(), "w");
+
+ if (file == NULL) {
+ return -errno;
+ }
+
+ Formatter out(file);
+
+ const std::string guard = makeHeaderGuard("Bn" + baseName);
+
+ out << "#ifndef " << guard << "\n";
+ out << "#define " << guard << "\n\n";
+
+ std::vector<std::string> packageComponents;
+ getPackageAndVersionComponents(
+ &packageComponents, false /* cpp_compatible */);
+
+ out << "#include <";
+ for (const auto &component : packageComponents) {
+ out << component << "/";
+ }
+ out << ifaceName << ".h>\n\n";
+
+ enterLeaveNamespace(out, true /* enter */);
+ out << "\n";
+
+ out << "struct "
+ << "Bn"
+ << baseName
+ << " : public ::android::hidl::BnInterface<"
+ << ifaceName
+ << "> {\n";
+
+ out.indent();
+
+ out << "::android::status_t onTransact(\n";
+ out.indent();
+ out.indent();
+ out << "uint32_t _aidl_code,\n";
+ out << "const ::android::hidl::Parcel &_aidl_data,\n";
+ out << "::android::hidl::Parcel *_aidl_reply,\n";
+ out << "uint32_t _aidl_flags = 0,\n";
+ out << "TransactCallback _aidl_cb = nullptr) override;\n";
+ out.unindent();
+ out.unindent();
+
+ out.unindent();
+
+ out << "};\n\n";
+
+ enterLeaveNamespace(out, false /* enter */);
+
+ out << "\n#endif // " << guard << "\n";
+
+ return OK;
+}
+
+status_t AST::generateProxyHeader() const {
+ std::string ifaceName;
+ if (!AST::isInterface(&ifaceName)) {
+ // types.hal does not get a proxy header.
+ return OK;
+ }
+
+ const std::string packagePath =
+ mCoordinator->getPackagePath(mPackage, true /* relative */);
+
+ // cut off the leading 'I'.
+ const std::string baseName = ifaceName.substr(1);
+
+ std::string path = "/tmp/android/hardware/";
+ path.append(packagePath);
+ path.append("Bp");
+ path.append(baseName);
+ path.append(".h");
+
+ CHECK(MakeParentHierarchy(path));
+ FILE *file = fopen(path.c_str(), "w");
+
+ if (file == NULL) {
+ return -errno;
+ }
+
+ Formatter out(file);
+
+ const std::string guard = makeHeaderGuard("Bp" + baseName);
+
+ out << "#ifndef " << guard << "\n";
+ out << "#define " << guard << "\n\n";
+
+ std::vector<std::string> packageComponents;
+ getPackageAndVersionComponents(
+ &packageComponents, false /* cpp_compatible */);
+
+ out << "#include <";
+ for (const auto &component : packageComponents) {
+ out << component << "/";
+ }
+ out << ifaceName << ".h>\n\n";
+
+ enterLeaveNamespace(out, true /* enter */);
+ out << "\n";
+
+ out << "struct "
+ << "Bp"
+ << baseName
+ << " : public ::android::hidl::BpInterface<"
+ << ifaceName
+ << "> {\n";
+
+ out.indent();
+
+ out << "explicit Bp"
+ << baseName
+ << "(const ::android::sp<::android::hidl::IBinder> &_aidl_impl);"
+ << "\n\n";
+
+ const Interface *iface = mRootScope->getInterface();
+
+ for (const auto &method : iface->methods()) {
+ const bool returnsValue = !method->results().empty();
+
+ out << "::android::hidl::binder::Status "
+ << method->name()
+ << "("
+ << Method::GetSignature(method->args());
+
+ if (returnsValue) {
+ if (!method->args().empty()) {
+ out << ", ";
+ }
+
+ out << method->name() << "_cb _aidl_cb";
+ }
+
+ out << ") override;\n";
+ }
+
+ out.unindent();
+
+ out << "};\n\n";
+
+ enterLeaveNamespace(out, false /* enter */);
+
+ out << "\n#endif // " << guard << "\n";
+
+ return OK;
+}
+
+status_t AST::generateAllSource() const {
+ const std::string packagePath =
+ mCoordinator->getPackagePath(mPackage, true /* relative */);
+
+ std::string path = "/tmp/android/hardware/";
+ path.append(packagePath);
+
+ std::string ifaceName;
+ std::string baseName;
+
+ bool isInterface = true;
+ if (!AST::isInterface(&ifaceName)) {
+ baseName = "types";
+ isInterface = false;
+ } else {
+ baseName = ifaceName.substr(1); // cut off the leading 'I'.
+ }
+
+ path.append(baseName);
+
+ if (baseName != "types") {
+ path.append("All");
+ }
+
+ path.append(".cpp");
+
+ CHECK(MakeParentHierarchy(path));
+ FILE *file = fopen(path.c_str(), "w");
+
+ if (file == NULL) {
+ return -errno;
+ }
+
+ Formatter out(file);
+
+ std::vector<std::string> packageComponents;
+ getPackageAndVersionComponents(
+ &packageComponents, false /* cpp_compatible */);
+
+ std::string prefix;
+ for (const auto &component : packageComponents) {
+ prefix += component;
+ prefix += "/";
+ }
+
+ if (isInterface) {
+ out << "#include <" << prefix << "/Bp" << baseName << ".h>\n";
+ out << "#include <" << prefix << "/Bn" << baseName << ".h>\n";
+ } else {
+ out << "#include <" << prefix << "types.h>\n";
+ }
+
+ out << "\n";
+
+ enterLeaveNamespace(out, true /* enter */);
+ out << "\n";
+
+ status_t err = generateTypeSource(out, ifaceName);
+
+ if (err == OK && isInterface) {
+ err = generateProxySource(out, baseName);
+ }
+
+ if (err == OK && isInterface) {
+ err = generateStubSource(out, baseName);
+ }
+
+ enterLeaveNamespace(out, false /* enter */);
+
+ return err;
+}
+
+status_t AST::generateTypeSource(
+ Formatter &out, const std::string &ifaceName) const {
+ return mRootScope->emitTypeDefinitions(out, ifaceName);
+}
+
+void AST::emitCppReaderWriter(
+ Formatter &out,
+ const std::string &parcelObj,
+ bool parcelObjIsPointer,
+ const TypedVar *arg,
+ bool isReader,
+ Type::ErrorMode mode) const {
+ const Type &type = arg->type();
+
+ if (isReader) {
+ std::string extra;
+ out << type.getCppResultType(&extra)
+ << " "
+ << arg->name()
+ << extra
+ << ";\n";
+ }
+
+ type.emitReaderWriter(
+ out,
+ arg->name(),
+ parcelObj,
+ parcelObjIsPointer,
+ isReader,
+ mode);
+}
+
+status_t AST::generateProxySource(
+ Formatter &out, const std::string &baseName) const {
+ const std::string klassName = "Bp" + baseName;
+
+ out << klassName
+ << "::"
+ << klassName
+ << "(const ::android::sp<::android::hidl::IBinder> &_aidl_impl)\n";
+
+ out.indent();
+ out.indent();
+
+ out << ": BpInterface"
+ << "<I"
+ << baseName
+ << ">(_aidl_impl) {\n";
+
+ out.unindent();
+ out.unindent();
+ out << "}\n\n";
+
+ const Interface *iface = mRootScope->getInterface();
+
+ for (const auto &method : iface->methods()) {
+ const bool returnsValue = !method->results().empty();
+
+ out << "::android::hidl::binder::Status "
+ << klassName
+ << "::"
+ << method->name()
+ << "("
+ << Method::GetSignature(method->args());
+
+ if (returnsValue) {
+ if (!method->args().empty()) {
+ out << ", ";
+ }
+
+ out << method->name() << "_cb _aidl_cb";
+ }
+
+ out << ") {\n";
+
+ out.indent();
+
+ out << "::android::hidl::Parcel _aidl_data;\n";
+ out << "::android::hidl::Parcel _aidl_reply;\n";
+ out << "::android::status_t _aidl_err;\n\n";
+ out << "::android::hidl::binder::Status _aidl_status;\n";
+
+ out << "_aidl_err = _aidl_data.writeInterfaceToken("
+ "getInterfaceDescriptor());\n";
+
+ out << "if (_aidl_err != ::android::OK) { goto _aidl_error; }\n\n";
+
+ for (const auto &arg : method->args()) {
+ emitCppReaderWriter(
+ out,
+ "_aidl_data",
+ false /* parcelObjIsPointer */,
+ arg,
+ false /* reader */,
+ Type::ErrorMode_Goto);
+ }
+
+ out << "_aidl_err = remote()->transact(I"
+ << baseName
+ << "::"
+ << upcase(method->name())
+ << ", _aidl_data, &_aidl_reply);\n";
+
+ out << "if (_aidl_err != ::android::OK) { goto _aidl_error; }\n\n";
+
+ out << "_aidl_err = _aidl_status.readFromParcel(_aidl_reply);\n";
+ out << "if (_aidl_err != ::android::OK) { goto _aidl_error; }\n\n";
+
+ out << "if (!_aidl_status.isOk()) { return _aidl_status; }\n\n";
+
+ for (const auto &arg : method->results()) {
+ emitCppReaderWriter(
+ out,
+ "_aidl_reply",
+ false /* parcelObjIsPointer */,
+ arg,
+ true /* reader */,
+ Type::ErrorMode_Goto);
+ }
+
+ if (returnsValue) {
+ out << "if (_aidl_cb != nullptr) {\n";
+ out.indent();
+ out << "_aidl_cb(";
+
+ bool first = true;
+ for (const auto &arg : method->results()) {
+ if (!first) {
+ out << ", ";
+ }
+
+ if (arg->type().resultNeedsDeref()) {
+ out << "*";
+ }
+ out << arg->name();
+
+ first = false;
+ }
+
+ out << ");\n";
+ out.unindent();
+ out << "}\n\n";
+ }
+
+ out.unindent();
+ out << "_aidl_error:\n";
+ out.indent();
+ out << "_aidl_status.setFromStatusT(_aidl_err);\n"
+ << "return _aidl_status;\n";
+
+ out.unindent();
+ out << "}\n\n";
+ }
+
+ return OK;
+}
+
+status_t AST::generateStubSource(
+ Formatter &out, const std::string &baseName) const {
+ out << "IMPLEMENT_HWBINDER_META_INTERFACE("
+ << baseName
+ << ", \""
+ << mPackage.string()
+ << "::I"
+ << baseName
+ << "\");\n\n";
+
+ const std::string klassName = "Bn" + baseName;
+
+ out << "::android::status_t " << klassName << "::onTransact(\n";
+
+ out.indent();
+ out.indent();
+
+ out << "uint32_t _aidl_code,\n"
+ << "const ::android::hidl::Parcel &_aidl_data,\n"
+ << "::android::hidl::Parcel *_aidl_reply,\n"
+ << "uint32_t _aidl_flags,\n"
+ << "TransactCallback _aidl_cb) {\n";
+
+ out.unindent();
+
+ out << "::android::status_t _aidl_err = ::android::OK;\n\n";
+
+ out << "switch (_aidl_code) {\n";
+ out.indent();
+
+ const Interface *iface = mRootScope->getInterface();
+
+ for (const auto &method : iface->methods()) {
+ out << "case Call::" << upcase(method->name()) << ":\n{\n";
+ out.indent();
+
+ status_t err = generateStubSourceForMethod(out, method);
+
+ if (err != OK) {
+ return err;
+ }
+
+ out.unindent();
+ out << "}\n\n";
+ }
+
+ out << "default:\n{\n";
+ out.indent();
+
+ out << "return ::android::hidl::BnInterface<I"
+ << baseName
+ << ">::onTransact(\n";
+
+ out.indent();
+ out.indent();
+
+ out << "_aidl_code, _aidl_data, _aidl_reply, "
+ << "_aidl_flags, _aidl_cb);\n";
+
+ out.unindent();
+ out.unindent();
+
+ out.unindent();
+ out << "}\n";
+
+ out.unindent();
+ out << "}\n\n";
+
+ out << "if (_aidl_err == ::android::UNEXPECTED_NULL) {\n";
+ out.indent();
+ out << "_aidl_err = ::android::hidl::binder::Status::fromExceptionCode(\n";
+ out.indent();
+ out.indent();
+ out << "::android::hidl::binder::Status::EX_NULL_POINTER)\n";
+ out.indent();
+ out.indent();
+ out << ".writeToParcel(_aidl_reply);\n";
+ out.unindent();
+ out.unindent();
+ out.unindent();
+ out.unindent();
+
+ out.unindent();
+ out << "}\n\n";
+
+ out << "return _aidl_err;\n";
+
+ out.unindent();
+ out << "}\n\n";
+
+ return OK;
+}
+
+status_t AST::generateStubSourceForMethod(
+ Formatter &out, const Method *method) const {
+ out << "if (!_aidl_data.checkInterface(this)) {\n";
+ out.indent();
+ out << "_aidl_err = ::android::BAD_TYPE;\n";
+ out << "break;\n";
+ out.unindent();
+ out << "}\n\n";
+
+ for (const auto &arg : method->args()) {
+ emitCppReaderWriter(
+ out,
+ "_aidl_data",
+ false /* parcelObjIsPointer */,
+ arg,
+ true /* reader */,
+ Type::ErrorMode_Break);
+ }
+
+ const bool returnsValue = !method->results().empty();
+
+ if (returnsValue) {
+ out << "bool _aidl_callbackCalled = false;\n\n";
+ }
+
+ out << "::android::hidl::binder::Status _aidl_status(\n";
+ out.indent();
+ out.indent();
+ out << method->name() << "(";
+
+ bool first = true;
+ for (const auto &arg : method->args()) {
+ if (!first) {
+ out << ", ";
+ }
+
+ if (arg->type().resultNeedsDeref()) {
+ out << "*";
+ }
+
+ out << arg->name();
+
+ first = false;
+ }
+
+ if (returnsValue) {
+ if (!first) {
+ out << ", ";
+ }
+
+ out << "[&](";
+
+ first = true;
+ for (const auto &arg : method->results()) {
+ if (!first) {
+ out << ", ";
+ }
+
+ out << "const auto &" << arg->name();
+
+ first = false;
+ }
+
+ out << ") {\n";
+ out.indent();
+ out << "_aidl_callbackCalled = true;\n\n";
+
+ out << "::android::hidl::binder::Status::ok()"
+ << ".writeToParcel(_aidl_reply);\n\n";
+
+ for (const auto &arg : method->results()) {
+ emitCppReaderWriter(
+ out,
+ "_aidl_reply",
+ true /* parcelObjIsPointer */,
+ arg,
+ false /* reader */,
+ Type::ErrorMode_Ignore);
+ }
+
+ out << "_aidl_cb(*_aidl_reply);\n";
+
+ out.unindent();
+ out << "}\n";
+ }
+
+ out.unindent();
+ out.unindent();
+ out << "));\n\n";
+
+ if (returnsValue) {
+ out << "if (!_aidl_callbackCalled) {\n";
+ out.indent();
+ }
+
+ out << "_aidl_err = _aidl_status.writeToParcel(_aidl_reply);\n";
+
+ if (returnsValue) {
+ out.unindent();
+ out << "}\n\n";
+ }
+
+ out << "break;\n";
+
+ return OK;
+}
+
+} // namespace android
+