Files
TheRock/compiler/amd-llvm/lldb/tools/lldb-rpc-gen/server/RPCServerSourceEmitter.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

585 lines
23 KiB
C++

//===-- RPCServerSourceEmitter.cpp ----------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "RPCServerSourceEmitter.h"
#include "RPCCommon.h"
#include "clang/AST/AST.h"
#include "clang/Frontend/CompilerInstance.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/raw_ostream.h"
#include <map>
using namespace clang;
using namespace lldb_rpc_gen;
// For methods with pointer return types, it's important that we know how big
// the type of the pointee is. We must correctly size a buffer (in the form of a
// Bytes object) before we can actually use it.
static const std::map<llvm::StringRef, size_t> MethodsWithPointerReturnTypes = {
{"_ZN4lldb12SBModuleSpec12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16
{"_ZNK4lldb8SBModule12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16
};
void RPCServerSourceEmitter::EmitMethod(const Method &method) {
if (method.ContainsFunctionPointerParameter)
EmitCallbackFunction(method);
EmitCommentHeader(method);
EmitFunctionHeader(method);
EmitFunctionBody(method);
EmitFunctionFooter();
}
void RPCServerSourceEmitter::EmitCommentHeader(const Method &method) {
std::string CommentLine;
llvm::raw_string_ostream CommentStream(CommentLine);
CommentStream << "// " << method.QualifiedName << "("
<< method.CreateParamListAsString(eServer) << ")";
if (method.IsConst)
CommentStream << " const";
EmitLine("//------------------------------------------------------------");
EmitLine(CommentLine);
EmitLine("//------------------------------------------------------------");
}
void RPCServerSourceEmitter::EmitFunctionHeader(const Method &method) {
std::string FunctionHeader;
llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader);
FunctionHeaderStream
<< "bool rpc_server::" << method.MangledName
<< "::HandleRPCCall(rpc_common::Connection &connection, RPCStream "
"&send, RPCStream &response) {";
EmitLine(FunctionHeader);
IndentLevel++;
}
void RPCServerSourceEmitter::EmitFunctionBody(const Method &method) {
EmitLine("// 1) Make local storage for incoming function arguments");
EmitStorageForParameters(method);
EmitLine("// 2) Decode all function arguments");
EmitDecodeForParameters(method);
EmitLine("// 3) Call the method and encode the return value");
EmitMethodCallAndEncode(method);
}
void RPCServerSourceEmitter::EmitFunctionFooter() {
EmitLine("return true;");
IndentLevel--;
EmitLine("}");
}
void RPCServerSourceEmitter::EmitStorageForParameters(const Method &method) {
// If we have an instance method and it isn't a constructor, we'll need to
// emit a "this" pointer.
if (method.IsInstance && !method.IsCtor)
EmitStorageForOneParameter(method.ThisType, "this_ptr", method.Policy,
/* IsFollowedByLen = */ false);
for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) {
EmitStorageForOneParameter(Iter->Type, Iter->Name, method.Policy,
Iter->IsFollowedByLen);
// Skip over the length parameter, we don't emit it.
if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) &&
Iter->IsFollowedByLen)
Iter++;
}
}
void RPCServerSourceEmitter::EmitStorageForOneParameter(
QualType ParamType, const std::string &ParamName,
const PrintingPolicy &Policy, bool IsFollowedByLen) {
// First, we consider `const char *`, `const char **`. They have special
// server-side types.
if (TypeIsConstCharPtr(ParamType)) {
EmitLine("rpc_common::ConstCharPointer " + ParamName + ";");
return;
} else if (TypeIsConstCharPtrPtr(ParamType)) {
EmitLine("rpc_common::StringList " + ParamName + ";");
return;
}
QualType UnderlyingType =
lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType);
const bool IsSBClass = lldb_rpc_gen::TypeIsSBClass(UnderlyingType);
if (ParamType->isPointerType() && !IsSBClass) {
// Void pointer with no length is usually a baton for a callback. We're
// going to hold onto the pointer value so we can send it back to the
// client-side when we implement callbacks.
if (ParamType->isVoidPointerType() && !IsFollowedByLen) {
EmitLine("void * " + ParamName + " = nullptr;");
return;
}
if (!ParamType->isFunctionPointerType()) {
EmitLine("Bytes " + ParamName + ";");
return;
}
assert(ParamType->isFunctionPointerType() && "Unhandled pointer type");
EmitLine("rpc_common::function_ptr_t " + ParamName + " = nullptr;");
return;
}
std::string StorageDeclaration;
llvm::raw_string_ostream StorageDeclarationStream(StorageDeclaration);
UnderlyingType.print(StorageDeclarationStream, Policy);
StorageDeclarationStream << " ";
if (IsSBClass)
StorageDeclarationStream << "*";
StorageDeclarationStream << ParamName;
if (IsSBClass)
StorageDeclarationStream << " = nullptr";
else
StorageDeclarationStream << " = {}";
StorageDeclarationStream << ";";
EmitLine(StorageDeclaration);
}
void RPCServerSourceEmitter::EmitDecodeForParameters(const Method &method) {
if (method.IsInstance && !method.IsCtor)
EmitDecodeForOneParameter(method.ThisType, "this_ptr", method.Policy);
for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) {
EmitDecodeForOneParameter(Iter->Type, Iter->Name, method.Policy);
if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) &&
Iter->IsFollowedByLen)
Iter++;
}
}
void RPCServerSourceEmitter::EmitDecodeForOneParameter(
QualType ParamType, const std::string &ParamName,
const PrintingPolicy &Policy) {
QualType UnderlyingType =
lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType);
if (TypeIsSBClass(UnderlyingType)) {
std::string DecodeLine;
llvm::raw_string_ostream DecodeLineStream(DecodeLine);
DecodeLineStream << ParamName << " = "
<< "RPCServerObjectDecoder<";
UnderlyingType.print(DecodeLineStream, Policy);
DecodeLineStream << ">(send, rpc_common::RPCPacket::ValueType::Argument);";
EmitLine(DecodeLine);
EmitLine("if (!" + ParamName + ")");
IndentLevel++;
EmitLine("return false;");
IndentLevel--;
} else {
EmitLine("if (!RPCValueDecoder(send, "
"rpc_common::RPCPacket::ValueType::Argument, " +
ParamName + "))");
IndentLevel++;
EmitLine("return false;");
IndentLevel--;
}
}
std::string RPCServerSourceEmitter::CreateMethodCall(const Method &method) {
std::string MethodCall;
llvm::raw_string_ostream MethodCallStream(MethodCall);
if (method.IsInstance) {
if (!method.IsCtor)
MethodCallStream << "this_ptr->";
MethodCallStream << method.BaseName;
} else
MethodCallStream << method.QualifiedName;
std::vector<std::string> Args;
std::string FunctionPointerName;
for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) {
std::string Arg;
// We must check for `const char *` and `const char **` first.
if (TypeIsConstCharPtr(Iter->Type)) {
// `const char *` is stored server-side as rpc_common::ConstCharPointer
Arg = Iter->Name + ".c_str()";
} else if (TypeIsConstCharPtrPtr(Iter->Type)) {
// `const char **` is stored server-side as rpc_common::StringList
Arg = Iter->Name + ".argv()";
} else if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) {
Arg = Iter->Name;
if (!Iter->Type->isPointerType())
Arg = "*" + Iter->Name;
} else if (Iter->Type->isPointerType() &&
!Iter->Type->isFunctionPointerType() &&
(!Iter->Type->isVoidPointerType() || Iter->IsFollowedByLen)) {
// We move pointers between the server and client as 'Bytes' objects.
// Pointers with length arguments will have their length filled in below.
// Pointers with no length arguments are assumed to behave like an array
// with length of 1, except for void pointers which are handled
// differently.
Arg = "(" + Iter->Type.getAsString(method.Policy) + ")" + Iter->Name +
".GetData()";
} else if (Iter->Type->isFunctionPointerType()) {
// If we have a function pointer, we only want to pass something along if
// we got a real pointer.
Arg = Iter->Name + " ? " + method.MangledName + "_callback : nullptr";
FunctionPointerName = Iter->Name;
} else if (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen &&
method.ContainsFunctionPointerParameter) {
// Assumptions:
// - This is assumed to be the baton for the function pointer.
// - This is assumed to come after the function pointer parameter.
// We always produce this regardless of the value of the baton argument.
Arg = "new CallbackInfo(" + FunctionPointerName + ", " + Iter->Name +
", connection.GetConnectionID())";
} else
Arg = Iter->Name;
if (Iter->Type->isRValueReferenceType())
Arg = "std::move(" + Arg + ")";
Args.push_back(Arg);
if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) &&
Iter->IsFollowedByLen) {
std::string LengthArg = Iter->Name + ".GetSize()";
if (!Iter->Type->isVoidPointerType()) {
QualType UUT = lldb_rpc_gen::GetUnqualifiedUnderlyingType(Iter->Type);
LengthArg += " / sizeof(" + UUT.getAsString(method.Policy) + ")";
}
Args.push_back(LengthArg);
Iter++;
}
}
MethodCallStream << "(" << llvm::join(Args, ", ") << ")";
return MethodCall;
}
std::string RPCServerSourceEmitter::CreateEncodeLine(const std::string &Value,
bool IsEncodingSBClass) {
std::string EncodeLine;
llvm::raw_string_ostream EncodeLineStream(EncodeLine);
if (IsEncodingSBClass)
EncodeLineStream << "RPCServerObjectEncoder(";
else
EncodeLineStream << "RPCValueEncoder(";
EncodeLineStream
<< "response, rpc_common::RPCPacket::ValueType::ReturnValue, ";
EncodeLineStream << Value;
EncodeLineStream << ");";
return EncodeLine;
}
// There are 4 cases to consider:
// - const SBClass &: No need to do anything.
// - const foo &: No need to do anything.
// - SBClass &: The server and the client hold on to IDs to refer to specific
// instances, so there's no need to send any information back to the client.
// - foo &: The client is sending us a value over the wire, but because the type
// is mutable, we must send the changed value back in case the method call
// mutated it.
//
// Updating a mutable reference is done as a return value from the RPC
// perspective. These return values need to be emitted after the method's return
// value, and they are emitted in the order in which they occur in the
// declaration.
void RPCServerSourceEmitter::EmitEncodesForMutableParameters(
const std::vector<Param> &Params) {
for (auto Iter = Params.begin(); Iter != Params.end(); Iter++) {
// No need to manually update an SBClass
if (lldb_rpc_gen::TypeIsSBClass(Iter->Type))
continue;
if (!Iter->Type->isReferenceType() && !Iter->Type->isPointerType())
continue;
// If we have a void pointer with no length, there's nothing to update. This
// is likely a baton for a callback. The same goes for function pointers.
if (Iter->Type->isFunctionPointerType() ||
(Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen))
continue;
// No need to update pointers and references to const-qualified data.
QualType UnderlyingType = lldb_rpc_gen::GetUnderlyingType(Iter->Type);
if (UnderlyingType.isConstQualified())
continue;
const std::string EncodeLine =
CreateEncodeLine(Iter->Name, /* IsEncodingSBClass = */ false);
EmitLine(EncodeLine);
}
}
// There are 3 possible scenarios that this method can encounter:
// 1. The method has no return value and is not a constructor.
// Only the method call itself is emitted.
// 2. The method is a constructor.
// The call to the constructor is emitted in the encode line.
// 3. The method has a return value.
// The method call is emitted and the return value is captured in a variable.
// After that, an encode call is emitted with the variable that captured the
// return value.
void RPCServerSourceEmitter::EmitMethodCallAndEncode(const Method &method) {
const std::string MethodCall = CreateMethodCall(method);
// If this function returns nothing, we just emit the call and update any
// mutable references. Note that constructors have return type `void` so we
// must explicitly check for that here.
if (!method.IsCtor && method.ReturnType->isVoidType()) {
EmitLine(MethodCall + ";");
EmitEncodesForMutableParameters(method.Params);
return;
}
static constexpr llvm::StringLiteral ReturnVariableName("__result");
// If this isn't a constructor, we'll need to store the result of the method
// call in a result variable.
if (!method.IsCtor) {
// We need to determine what the appropriate return type is. Here is the
// strategy:
// 1.) `SBFoo` -> `SBFoo &&`
// 2.) If the type is a pointer other than `const char *` or `const char **`
// or `void *`, the return type will be `Bytes` (e.g. `const uint8_t *`
// -> `Bytes`).
// 3.) Otherwise, emit the exact same return type.
std::string ReturnTypeName;
std::string AssignLine;
llvm::raw_string_ostream AssignLineStream(AssignLine);
if (method.ReturnType->isPointerType() &&
!lldb_rpc_gen::TypeIsConstCharPtr(method.ReturnType) &&
!lldb_rpc_gen::TypeIsConstCharPtrPtr(method.ReturnType) &&
!method.ReturnType->isVoidPointerType()) {
llvm::StringRef MangledNameRef(method.MangledName);
auto Pos = MethodsWithPointerReturnTypes.find(MangledNameRef);
assert(Pos != MethodsWithPointerReturnTypes.end() &&
"Unable to determine the size of the return buffer");
if (Pos == MethodsWithPointerReturnTypes.end()) {
EmitLine(
"// Intentionally inserting a compiler error. lldb-rpc-gen "
"was unable to determine how large the return buffer should be.");
EmitLine("#error: \"unable to determine size of return buffer\"");
return;
}
AssignLineStream << "Bytes " << ReturnVariableName << "(" << MethodCall
<< ", " << Pos->second << ");";
} else {
if (lldb_rpc_gen::TypeIsSBClass(method.ReturnType)) {
// We want to preserve constness, so we don't strip qualifications from
// the underlying type
QualType UnderlyingReturnType =
lldb_rpc_gen::GetUnderlyingType(method.ReturnType);
ReturnTypeName =
UnderlyingReturnType.getAsString(method.Policy) + " &&";
} else
ReturnTypeName = method.ReturnType.getAsString(method.Policy);
AssignLineStream << ReturnTypeName << " " << ReturnVariableName << " = "
<< MethodCall << ";";
}
EmitLine(AssignLine);
}
const bool IsEncodingSBClass =
lldb_rpc_gen::TypeIsSBClass(method.ReturnType) || method.IsCtor;
std::string ValueToEncode;
if (IsEncodingSBClass) {
if (method.IsCtor)
ValueToEncode = MethodCall;
else
ValueToEncode = "std::move(" + ReturnVariableName.str() + ")";
} else
ValueToEncode = ReturnVariableName.str();
const std::string ReturnValueEncodeLine =
CreateEncodeLine(ValueToEncode, IsEncodingSBClass);
EmitLine(ReturnValueEncodeLine);
EmitEncodesForMutableParameters(method.Params);
}
// NOTE: This contains most of the same knowledge as RPCLibrarySourceEmitter. I
// have chosen not to re-use code here because the needs are different enough
// that it would be more work to re-use than just reimplement portions of it.
// Specifically:
// - Callbacks do not neatly fit into a `Method` object, which currently
// assumes that you have a CXXMethodDecl (We have a FunctionDecl at most).
// - We only generate callbacks that have a `void *` baton parameter. We hijack
// those baton parameters and treat them differently.
// - Callbacks need to do something special for moving SB class references back
// to the client-side.
void RPCServerSourceEmitter::EmitCallbackFunction(const Method &method) {
// Check invariants and locate necessary resources
Param FuncPointerParam;
Param BatonParam;
for (const auto &Param : method.Params)
if (Param.Type->isFunctionPointerType())
FuncPointerParam = Param;
else if (Param.Type->isVoidPointerType())
BatonParam = Param;
assert(FuncPointerParam.Type->isFunctionPointerType() &&
"Emitting callback function with no function pointer");
assert(BatonParam.Type->isVoidPointerType() &&
"Emitting callback function with no baton");
QualType FuncType = FuncPointerParam.Type->getPointeeType();
const auto *FuncProtoType = FuncType->getAs<FunctionProtoType>();
assert(FuncProtoType && "Emitting callback with no parameter information");
if (!FuncProtoType)
return; // If asserts are off, we'll just fail to compile.
std::vector<Param> CallbackParams;
std::vector<std::string> CallbackParamsAsStrings;
uint8_t ArgIdx = 0;
for (QualType ParamType : FuncProtoType->param_types()) {
Param CallbackParam;
CallbackParam.IsFollowedByLen = false;
CallbackParam.Type = ParamType;
if (ParamType->isVoidPointerType())
CallbackParam.Name = "baton";
else
CallbackParam.Name = "arg" + std::to_string(ArgIdx++);
CallbackParams.push_back(CallbackParam);
CallbackParamsAsStrings.push_back(ParamType.getAsString(method.Policy) +
" " + CallbackParam.Name);
}
const std::string CallbackReturnTypeName =
FuncProtoType->getReturnType().getAsString(method.Policy);
const std::string CallbackName = method.MangledName + "_callback";
// Emit Function Header
std::string Header;
llvm::raw_string_ostream HeaderStream(Header);
HeaderStream << "static " << CallbackReturnTypeName << " " << CallbackName
<< "(" << llvm::join(CallbackParamsAsStrings, ", ") << ") {";
EmitLine(Header);
IndentLevel++;
// Emit Function Body
EmitLine("// RPC connection setup and sanity checking");
EmitLine("CallbackInfo *callback_info = (CallbackInfo *)baton;");
EmitLine("rpc_common::ConnectionSP connection_sp = "
"rpc_common::Connection::GetConnectionFromID(callback_info->"
"connection_id);");
EmitLine("if (!connection_sp)");
IndentLevel++;
if (FuncProtoType->getReturnType()->isVoidType())
EmitLine("return;");
else
EmitLine("return {};");
IndentLevel--;
EmitLine("// Preparing to make the call");
EmitLine("static RPCFunctionInfo g_func(\"" + CallbackName + "\");");
EmitLine("RPCStream send;");
EmitLine("RPCStream response;");
EmitLine("g_func.Encode(send);");
EmitLine("// The first thing we encode is the callback address so that the "
"client-side can know where the callback is");
EmitLine("RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, "
"callback_info->callback);");
EmitLine("// Encode all the arguments");
for (const Param &CallbackParam : CallbackParams) {
if (lldb_rpc_gen::TypeIsSBClass(CallbackParam.Type)) {
// FIXME: SB class server references are stored as non-const references so
// that we can actually change them as needed. If a parameter is marked
// const, we will fail to compile because we cannot make an
// SBFooServerReference from a `const SBFoo &`.
// To work around this issue, we'll apply a `const_cast` if needed so we
// can continue to generate callbacks for now, but we really should
// rethink the way we store object IDs server-side to support
// const-qualified parameters.
QualType UnderlyingSBClass =
lldb_rpc_gen::GetUnderlyingType(CallbackParam.Type);
QualType UnqualifiedUnderlyingSBClass =
UnderlyingSBClass.getUnqualifiedType();
std::string SBClassName = GetSBClassNameFromType(UnderlyingSBClass);
llvm::StringRef SBClassNameRef(SBClassName);
SBClassNameRef.consume_front("lldb::");
std::string ServerReferenceLine;
llvm::raw_string_ostream ServerReferenceLineStream(ServerReferenceLine);
ServerReferenceLineStream << "rpc_server::" << SBClassNameRef
<< "ServerReference " << CallbackParam.Name
<< "_ref(";
if (UnderlyingSBClass.isConstQualified()) {
QualType NonConstSBType =
method.Context.getLValueReferenceType(UnqualifiedUnderlyingSBClass);
ServerReferenceLineStream << "const_cast<" << NonConstSBType << ">(";
}
ServerReferenceLineStream << CallbackParam.Name;
if (UnderlyingSBClass.isConstQualified())
ServerReferenceLineStream << ")";
ServerReferenceLineStream << ");";
EmitLine(ServerReferenceLine);
EmitLine(
CallbackParam.Name +
"_ref.Encode(send, rpc_common::RPCPacket::ValueType::Argument);");
} else {
std::string ParamName;
if (CallbackParam.Type->isVoidPointerType())
ParamName = "callback_info->baton";
else
ParamName = CallbackParam.Name;
EmitLine(
"RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, " +
ParamName + ");");
}
}
if (!FuncProtoType->getReturnType()->isVoidType()) {
EmitLine("// Storage for return value");
const bool ReturnsSBClass =
lldb_rpc_gen::TypeIsSBClass(FuncProtoType->getReturnType());
std::string ReturnValueLine = CallbackReturnTypeName;
llvm::raw_string_ostream ReturnValueLineStream(ReturnValueLine);
if (ReturnsSBClass)
ReturnValueLineStream << " *";
ReturnValueLineStream << " __result = ";
if (ReturnsSBClass)
ReturnValueLineStream << "nullptr";
else
ReturnValueLineStream << "{}";
ReturnValueLineStream << ";";
EmitLine(ReturnValueLine);
}
EmitLine(
"if (connection_sp->SendRPCCallAndWaitForResponse(send, response)) {");
IndentLevel++;
if (!FuncProtoType->getReturnType()->isVoidType()) {
if (lldb_rpc_gen::TypeIsSBClass(FuncProtoType->getReturnType())) {
EmitLine("__result = rpc_server::RPCServerObjectDecoder<" +
CallbackReturnTypeName +
">(response, rpc_common::RPCPacket::ValueType::ReturnValue);");
} else
EmitLine("RPCValueDecoder(response, "
"rpc_common::RPCPacket::ValueType::ReturnValue, __result);");
}
IndentLevel--;
EmitLine("}");
if (!FuncProtoType->getReturnType()->isVoidType()) {
if (lldb_rpc_gen::TypeIsSBClass(FuncProtoType->getReturnType()))
EmitLine("return *__result;");
else
EmitLine("return __result;");
}
// Emit Function Footer;
IndentLevel--;
EmitLine("};");
}