| /* |
| * Copyright (C) 2017 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 "AST.h" |
| |
| #include "Coordinator.h" |
| #include "EnumType.h" |
| #include "HidlTypeAssertion.h" |
| #include "Interface.h" |
| #include "Method.h" |
| #include "Reference.h" |
| #include "ScalarType.h" |
| #include "Scope.h" |
| |
| #include <android-base/logging.h> |
| #include <hidl-util/Formatter.h> |
| #include <hidl-util/StringHelper.h> |
| #include <algorithm> |
| #include <string> |
| #include <vector> |
| |
| namespace android { |
| |
| status_t AST::generateCppAdapter(const std::string& outputPath) const { |
| status_t err = OK; |
| |
| err = generateCppAdapterHeader(outputPath); |
| if (err != OK) return err; |
| err = generateCppAdapterSource(outputPath); |
| |
| return err; |
| } |
| |
| status_t AST::generateCppAdapterHeader(const std::string& outputPath) const { |
| const std::string klassName = AST::isInterface() ? getInterface()->getAdapterName() : "Atypes"; |
| |
| Formatter out = mCoordinator->getFormatter(outputPath, mPackage, |
| Coordinator::Location::GEN_OUTPUT, klassName + ".h"); |
| |
| if (!out.isValid()) { |
| return UNKNOWN_ERROR; |
| } |
| |
| const std::string guard = makeHeaderGuard(klassName, true /* indicateGenerated */); |
| |
| out << "#ifndef " << guard << "\n"; |
| out << "#define " << guard << "\n\n"; |
| |
| if (AST::isInterface()) { |
| generateCppPackageInclude(out, mPackage, getInterface()->localName()); |
| |
| enterLeaveNamespace(out, true /* enter */); |
| out.endl(); |
| |
| const std::string mockName = getInterface()->fqName().cppName(); |
| |
| out << "class " << klassName << " : public " << mockName << " "; |
| out.block([&] { |
| out << "public:\n"; |
| out << "typedef " << mockName << " Pure;\n"; |
| |
| out << klassName << "(::android::sp<" << mockName << "> impl);\n"; |
| |
| generateMethods(out, [&](const Method* method, const Interface* /* interface */) { |
| if (method->isHidlReserved()) { |
| return OK; |
| } |
| |
| out << "virtual "; |
| method->generateCppSignature(out); |
| out << " override;\n"; |
| |
| return OK; |
| }); |
| out << "private:\n"; |
| out << "::android::sp<" << mockName << "> mImpl;\n"; |
| |
| }) << ";\n\n"; |
| |
| enterLeaveNamespace(out, false /* enter */); |
| } else { |
| out << "// no adapters for types.hal\n"; |
| } |
| |
| out << "#endif // " << guard << "\n"; |
| |
| return OK; |
| } |
| |
| status_t AST::generateCppAdapterSource(const std::string& outputPath) const { |
| const std::string klassName = AST::isInterface() ? getInterface()->getAdapterName() : "Atypes"; |
| |
| Formatter out = mCoordinator->getFormatter( |
| outputPath, mPackage, Coordinator::Location::GEN_OUTPUT, klassName + ".cpp"); |
| |
| if (!out.isValid()) { |
| return UNKNOWN_ERROR; |
| } |
| |
| generateCppPackageInclude(out, mPackage, klassName); |
| |
| if (AST::isInterface()) { |
| out << "#include <hidladapter/HidlBinderAdapter.h>\n"; |
| generateCppPackageInclude(out, mPackage, getInterface()->localName()); |
| |
| std::set<FQName> allImportedNames; |
| getAllImportedNames(&allImportedNames); |
| for (const auto& item : allImportedNames) { |
| if (item.name() == "types") { |
| continue; |
| } |
| generateCppPackageInclude(out, item, item.getInterfaceAdapterName()); |
| } |
| |
| out.endl(); |
| |
| enterLeaveNamespace(out, true /* enter */); |
| out.endl(); |
| |
| const std::string mockName = getInterface()->fqName().cppName(); |
| |
| out << klassName << "::" << klassName << "(::android::sp<" << mockName |
| << "> impl) : mImpl(impl) {}"; |
| |
| generateMethods(out, [&](const Method* method, const Interface* /* interface */) { |
| generateAdapterMethod(out, method); |
| return OK; |
| }); |
| |
| enterLeaveNamespace(out, false /* enter */); |
| out.endl(); |
| } else { |
| out << "// no adapters for types.hal\n"; |
| } |
| |
| return OK; |
| } |
| |
| void AST::generateAdapterMethod(Formatter& out, const Method* method) const { |
| if (method->isHidlReserved()) { |
| return; |
| } |
| |
| const auto adapt = [](Formatter& out, const std::string& var, const Type* type) { |
| if (!type->isInterface()) { |
| out << var; |
| return; |
| } |
| |
| // TODO(b/66900959): if we are creating the adapter for a 1.1 IFoo |
| // and we are using a method that takes/returns a 1.0 Callback, but |
| // there exists a 1.1 Callback (or other subclass that is depended |
| // on by this module), then wrap with the adapter subclass adapter |
| // IFF that callback is a subclass. However, if the callback |
| // is 1.0 ICallback, then wrap with a 1.0 adapter. |
| |
| const Interface* interface = static_cast<const Interface*>(type); |
| out << "static_cast<::android::sp<" << interface->fqName().cppName() << ">>(" |
| << interface->fqName().cppName() << "::castFrom(" |
| << "::android::hardware::details::adaptWithDefault(" |
| << "static_cast<::android::sp<" << interface->fqName().cppName() << ">>(" << var |
| << "), [&] { return new " << interface->fqName().getInterfaceAdapterFqName().cppName() |
| << "(" << var << "); })))"; |
| }; |
| |
| const std::string klassName = getInterface()->getAdapterName(); |
| |
| method->generateCppSignature(out, klassName); |
| out.block([&] { |
| bool hasCallback = !method->canElideCallback() && !method->results().empty(); |
| |
| if (hasCallback) { |
| out << method->name() << "_cb _hidl_cb_wrapped = [&]("; |
| method->emitCppResultSignature(out); |
| out << ") "; |
| out.block([&] { |
| out << "return _hidl_cb(\n"; |
| out.indent([&]() { |
| out.join(method->results().begin(), method->results().end(), ",\n", |
| [&](auto arg) { adapt(out, arg->name(), arg->get()); }); |
| }); |
| out << ");\n"; |
| }); |
| out << ";\n"; |
| } |
| |
| out << "auto _hidl_out = mImpl->" << method->name() << "(\n"; |
| out.indent([&]() { |
| out.join(method->args().begin(), method->args().end(), ",\n", |
| [&](auto arg) { adapt(out, arg->name(), arg->get()); }); |
| if (hasCallback) { |
| if (!method->args().empty()) { |
| out << ",\n"; |
| } |
| out << "_hidl_cb_wrapped"; |
| } |
| }); |
| out << ");\n"; |
| |
| const auto elidedCallback = method->canElideCallback(); |
| if (elidedCallback) { |
| out.sIf("!_hidl_out.isOkUnchecked()", [&] { out << "return _hidl_out;\n"; }); |
| out << "return "; |
| adapt(out, "_hidl_out", elidedCallback->get()); |
| out << ";\n"; |
| } else { |
| out << "return _hidl_out;\n"; |
| } |
| }).endl(); |
| } |
| |
| } // namespace android |