1 //===-- RPCServerSourceEmitter.cpp ----------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "RPCServerSourceEmitter.h" 10 #include "RPCCommon.h" 11 12 #include "clang/AST/AST.h" 13 #include "clang/Frontend/CompilerInstance.h" 14 15 #include "llvm/ADT/StringExtras.h" 16 #include "llvm/ADT/StringRef.h" 17 #include "llvm/Support/ToolOutputFile.h" 18 #include "llvm/Support/raw_ostream.h" 19 20 #include <map> 21 22 using namespace clang; 23 using namespace lldb_rpc_gen; 24 25 // For methods with pointer return types, it's important that we know how big 26 // the type of the pointee is. We must correctly size a buffer (in the form of a 27 // Bytes object) before we can actually use it. 28 static const std::map<llvm::StringRef, size_t> MethodsWithPointerReturnTypes = { 29 {"_ZN4lldb12SBModuleSpec12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 30 {"_ZNK4lldb8SBModule12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 16 31 }; 32 33 void RPCServerSourceEmitter::EmitMethod(const Method &method) { 34 if (method.ContainsFunctionPointerParameter) 35 EmitCallbackFunction(method); 36 37 EmitCommentHeader(method); 38 EmitFunctionHeader(method); 39 EmitFunctionBody(method); 40 EmitFunctionFooter(); 41 } 42 43 void RPCServerSourceEmitter::EmitCommentHeader(const Method &method) { 44 std::string CommentLine; 45 llvm::raw_string_ostream CommentStream(CommentLine); 46 47 CommentStream << "// " << method.QualifiedName << "(" 48 << method.CreateParamListAsString(eServer) << ")"; 49 if (method.IsConst) 50 CommentStream << " const"; 51 52 EmitLine("//------------------------------------------------------------"); 53 EmitLine(CommentLine); 54 EmitLine("//------------------------------------------------------------"); 55 } 56 57 void RPCServerSourceEmitter::EmitFunctionHeader(const Method &method) { 58 std::string FunctionHeader; 59 llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader); 60 FunctionHeaderStream 61 << "bool rpc_server::" << method.MangledName 62 << "::HandleRPCCall(rpc_common::Connection &connection, RPCStream " 63 "&send, RPCStream &response) {"; 64 EmitLine(FunctionHeader); 65 IndentLevel++; 66 } 67 68 void RPCServerSourceEmitter::EmitFunctionBody(const Method &method) { 69 EmitLine("// 1) Make local storage for incoming function arguments"); 70 EmitStorageForParameters(method); 71 EmitLine("// 2) Decode all function arguments"); 72 EmitDecodeForParameters(method); 73 EmitLine("// 3) Call the method and encode the return value"); 74 EmitMethodCallAndEncode(method); 75 } 76 77 void RPCServerSourceEmitter::EmitFunctionFooter() { 78 EmitLine("return true;"); 79 IndentLevel--; 80 EmitLine("}"); 81 } 82 83 void RPCServerSourceEmitter::EmitStorageForParameters(const Method &method) { 84 // If we have an instance method and it isn't a constructor, we'll need to 85 // emit a "this" pointer. 86 if (method.IsInstance && !method.IsCtor) 87 EmitStorageForOneParameter(method.ThisType, "this_ptr", method.Policy, 88 /* IsFollowedByLen = */ false); 89 for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { 90 EmitStorageForOneParameter(Iter->Type, Iter->Name, method.Policy, 91 Iter->IsFollowedByLen); 92 // Skip over the length parameter, we don't emit it. 93 if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && 94 Iter->IsFollowedByLen) 95 Iter++; 96 } 97 } 98 99 void RPCServerSourceEmitter::EmitStorageForOneParameter( 100 QualType ParamType, const std::string &ParamName, 101 const PrintingPolicy &Policy, bool IsFollowedByLen) { 102 // First, we consider `const char *`, `const char **`. They have special 103 // server-side types. 104 if (TypeIsConstCharPtr(ParamType)) { 105 EmitLine("rpc_common::ConstCharPointer " + ParamName + ";"); 106 return; 107 } else if (TypeIsConstCharPtrPtr(ParamType)) { 108 EmitLine("rpc_common::StringList " + ParamName + ";"); 109 return; 110 } 111 112 QualType UnderlyingType = 113 lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); 114 const bool IsSBClass = lldb_rpc_gen::TypeIsSBClass(UnderlyingType); 115 116 if (ParamType->isPointerType() && !IsSBClass) { 117 // Void pointer with no length is usually a baton for a callback. We're 118 // going to hold onto the pointer value so we can send it back to the 119 // client-side when we implement callbacks. 120 if (ParamType->isVoidPointerType() && !IsFollowedByLen) { 121 EmitLine("void * " + ParamName + " = nullptr;"); 122 return; 123 } 124 125 if (!ParamType->isFunctionPointerType()) { 126 EmitLine("Bytes " + ParamName + ";"); 127 return; 128 } 129 130 assert(ParamType->isFunctionPointerType() && "Unhandled pointer type"); 131 EmitLine("rpc_common::function_ptr_t " + ParamName + " = nullptr;"); 132 return; 133 } 134 135 std::string StorageDeclaration; 136 llvm::raw_string_ostream StorageDeclarationStream(StorageDeclaration); 137 138 UnderlyingType.print(StorageDeclarationStream, Policy); 139 StorageDeclarationStream << " "; 140 if (IsSBClass) 141 StorageDeclarationStream << "*"; 142 StorageDeclarationStream << ParamName; 143 if (IsSBClass) 144 StorageDeclarationStream << " = nullptr"; 145 else 146 StorageDeclarationStream << " = {}"; 147 StorageDeclarationStream << ";"; 148 EmitLine(StorageDeclaration); 149 } 150 151 void RPCServerSourceEmitter::EmitDecodeForParameters(const Method &method) { 152 if (method.IsInstance && !method.IsCtor) 153 EmitDecodeForOneParameter(method.ThisType, "this_ptr", method.Policy); 154 for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { 155 EmitDecodeForOneParameter(Iter->Type, Iter->Name, method.Policy); 156 if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && 157 Iter->IsFollowedByLen) 158 Iter++; 159 } 160 } 161 162 void RPCServerSourceEmitter::EmitDecodeForOneParameter( 163 QualType ParamType, const std::string &ParamName, 164 const PrintingPolicy &Policy) { 165 QualType UnderlyingType = 166 lldb_rpc_gen::GetUnqualifiedUnderlyingType(ParamType); 167 168 if (TypeIsSBClass(UnderlyingType)) { 169 std::string DecodeLine; 170 llvm::raw_string_ostream DecodeLineStream(DecodeLine); 171 DecodeLineStream << ParamName << " = " 172 << "RPCServerObjectDecoder<"; 173 UnderlyingType.print(DecodeLineStream, Policy); 174 DecodeLineStream << ">(send, rpc_common::RPCPacket::ValueType::Argument);"; 175 EmitLine(DecodeLine); 176 EmitLine("if (!" + ParamName + ")"); 177 IndentLevel++; 178 EmitLine("return false;"); 179 IndentLevel--; 180 } else { 181 EmitLine("if (!RPCValueDecoder(send, " 182 "rpc_common::RPCPacket::ValueType::Argument, " + 183 ParamName + "))"); 184 IndentLevel++; 185 EmitLine("return false;"); 186 IndentLevel--; 187 } 188 } 189 190 std::string RPCServerSourceEmitter::CreateMethodCall(const Method &method) { 191 std::string MethodCall; 192 llvm::raw_string_ostream MethodCallStream(MethodCall); 193 if (method.IsInstance) { 194 if (!method.IsCtor) 195 MethodCallStream << "this_ptr->"; 196 MethodCallStream << method.BaseName; 197 } else 198 MethodCallStream << method.QualifiedName; 199 200 std::vector<std::string> Args; 201 std::string FunctionPointerName; 202 for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { 203 std::string Arg; 204 // We must check for `const char *` and `const char **` first. 205 if (TypeIsConstCharPtr(Iter->Type)) { 206 // `const char *` is stored server-side as rpc_common::ConstCharPointer 207 Arg = Iter->Name + ".c_str()"; 208 } else if (TypeIsConstCharPtrPtr(Iter->Type)) { 209 // `const char **` is stored server-side as rpc_common::StringList 210 Arg = Iter->Name + ".argv()"; 211 } else if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) { 212 Arg = Iter->Name; 213 if (!Iter->Type->isPointerType()) 214 Arg = "*" + Iter->Name; 215 } else if (Iter->Type->isPointerType() && 216 !Iter->Type->isFunctionPointerType() && 217 (!Iter->Type->isVoidPointerType() || Iter->IsFollowedByLen)) { 218 // We move pointers between the server and client as 'Bytes' objects. 219 // Pointers with length arguments will have their length filled in below. 220 // Pointers with no length arguments are assumed to behave like an array 221 // with length of 1, except for void pointers which are handled 222 // differently. 223 Arg = "(" + Iter->Type.getAsString(method.Policy) + ")" + Iter->Name + 224 ".GetData()"; 225 } else if (Iter->Type->isFunctionPointerType()) { 226 // If we have a function pointer, we only want to pass something along if 227 // we got a real pointer. 228 Arg = Iter->Name + " ? " + method.MangledName + "_callback : nullptr"; 229 FunctionPointerName = Iter->Name; 230 } else if (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen && 231 method.ContainsFunctionPointerParameter) { 232 // Assumptions: 233 // - This is assumed to be the baton for the function pointer. 234 // - This is assumed to come after the function pointer parameter. 235 // We always produce this regardless of the value of the baton argument. 236 Arg = "new CallbackInfo(" + FunctionPointerName + ", " + Iter->Name + 237 ", connection.GetConnectionID())"; 238 } else 239 Arg = Iter->Name; 240 241 if (Iter->Type->isRValueReferenceType()) 242 Arg = "std::move(" + Arg + ")"; 243 Args.push_back(Arg); 244 245 if (!lldb_rpc_gen::TypeIsConstCharPtrPtr(Iter->Type) && 246 Iter->IsFollowedByLen) { 247 std::string LengthArg = Iter->Name + ".GetSize()"; 248 if (!Iter->Type->isVoidPointerType()) { 249 QualType UUT = lldb_rpc_gen::GetUnqualifiedUnderlyingType(Iter->Type); 250 LengthArg += " / sizeof(" + UUT.getAsString(method.Policy) + ")"; 251 } 252 Args.push_back(LengthArg); 253 Iter++; 254 } 255 } 256 MethodCallStream << "(" << llvm::join(Args, ", ") << ")"; 257 258 return MethodCall; 259 } 260 261 std::string RPCServerSourceEmitter::CreateEncodeLine(const std::string &Value, 262 bool IsEncodingSBClass) { 263 std::string EncodeLine; 264 llvm::raw_string_ostream EncodeLineStream(EncodeLine); 265 266 if (IsEncodingSBClass) 267 EncodeLineStream << "RPCServerObjectEncoder("; 268 else 269 EncodeLineStream << "RPCValueEncoder("; 270 271 EncodeLineStream 272 << "response, rpc_common::RPCPacket::ValueType::ReturnValue, "; 273 EncodeLineStream << Value; 274 EncodeLineStream << ");"; 275 return EncodeLine; 276 } 277 278 // There are 4 cases to consider: 279 // - const SBClass &: No need to do anything. 280 // - const foo &: No need to do anything. 281 // - SBClass &: The server and the client hold on to IDs to refer to specific 282 // instances, so there's no need to send any information back to the client. 283 // - foo &: The client is sending us a value over the wire, but because the type 284 // is mutable, we must send the changed value back in case the method call 285 // mutated it. 286 // 287 // Updating a mutable reference is done as a return value from the RPC 288 // perspective. These return values need to be emitted after the method's return 289 // value, and they are emitted in the order in which they occur in the 290 // declaration. 291 void RPCServerSourceEmitter::EmitEncodesForMutableParameters( 292 const std::vector<Param> &Params) { 293 for (auto Iter = Params.begin(); Iter != Params.end(); Iter++) { 294 // No need to manually update an SBClass 295 if (lldb_rpc_gen::TypeIsSBClass(Iter->Type)) 296 continue; 297 298 if (!Iter->Type->isReferenceType() && !Iter->Type->isPointerType()) 299 continue; 300 301 // If we have a void pointer with no length, there's nothing to update. This 302 // is likely a baton for a callback. The same goes for function pointers. 303 if (Iter->Type->isFunctionPointerType() || 304 (Iter->Type->isVoidPointerType() && !Iter->IsFollowedByLen)) 305 continue; 306 307 // No need to update pointers and references to const-qualified data. 308 QualType UnderlyingType = lldb_rpc_gen::GetUnderlyingType(Iter->Type); 309 if (UnderlyingType.isConstQualified()) 310 continue; 311 312 const std::string EncodeLine = 313 CreateEncodeLine(Iter->Name, /* IsEncodingSBClass = */ false); 314 EmitLine(EncodeLine); 315 } 316 } 317 318 // There are 3 possible scenarios that this method can encounter: 319 // 1. The method has no return value and is not a constructor. 320 // Only the method call itself is emitted. 321 // 2. The method is a constructor. 322 // The call to the constructor is emitted in the encode line. 323 // 3. The method has a return value. 324 // The method call is emitted and the return value is captured in a variable. 325 // After that, an encode call is emitted with the variable that captured the 326 // return value. 327 void RPCServerSourceEmitter::EmitMethodCallAndEncode(const Method &method) { 328 const std::string MethodCall = CreateMethodCall(method); 329 330 // If this function returns nothing, we just emit the call and update any 331 // mutable references. Note that constructors have return type `void` so we 332 // must explicitly check for that here. 333 if (!method.IsCtor && method.ReturnType->isVoidType()) { 334 EmitLine(MethodCall + ";"); 335 EmitEncodesForMutableParameters(method.Params); 336 return; 337 } 338 339 static constexpr llvm::StringLiteral ReturnVariableName("__result"); 340 341 // If this isn't a constructor, we'll need to store the result of the method 342 // call in a result variable. 343 if (!method.IsCtor) { 344 // We need to determine what the appropriate return type is. Here is the 345 // strategy: 346 // 1.) `SBFoo` -> `SBFoo &&` 347 // 2.) If the type is a pointer other than `const char *` or `const char **` 348 // or `void *`, the return type will be `Bytes` (e.g. `const uint8_t *` 349 // -> `Bytes`). 350 // 3.) Otherwise, emit the exact same return type. 351 std::string ReturnTypeName; 352 std::string AssignLine; 353 llvm::raw_string_ostream AssignLineStream(AssignLine); 354 if (method.ReturnType->isPointerType() && 355 !lldb_rpc_gen::TypeIsConstCharPtr(method.ReturnType) && 356 !lldb_rpc_gen::TypeIsConstCharPtrPtr(method.ReturnType) && 357 !method.ReturnType->isVoidPointerType()) { 358 llvm::StringRef MangledNameRef(method.MangledName); 359 auto Pos = MethodsWithPointerReturnTypes.find(MangledNameRef); 360 assert(Pos != MethodsWithPointerReturnTypes.end() && 361 "Unable to determine the size of the return buffer"); 362 if (Pos == MethodsWithPointerReturnTypes.end()) { 363 EmitLine( 364 "// Intentionally inserting a compiler error. lldb-rpc-gen " 365 "was unable to determine how large the return buffer should be."); 366 EmitLine("#error: \"unable to determine size of return buffer\""); 367 return; 368 } 369 AssignLineStream << "Bytes " << ReturnVariableName << "(" << MethodCall 370 << ", " << Pos->second << ");"; 371 } else { 372 if (lldb_rpc_gen::TypeIsSBClass(method.ReturnType)) { 373 // We want to preserve constness, so we don't strip qualifications from 374 // the underlying type 375 QualType UnderlyingReturnType = 376 lldb_rpc_gen::GetUnderlyingType(method.ReturnType); 377 ReturnTypeName = 378 UnderlyingReturnType.getAsString(method.Policy) + " &&"; 379 } else 380 ReturnTypeName = method.ReturnType.getAsString(method.Policy); 381 382 AssignLineStream << ReturnTypeName << " " << ReturnVariableName << " = " 383 << MethodCall << ";"; 384 } 385 EmitLine(AssignLine); 386 } 387 388 const bool IsEncodingSBClass = 389 lldb_rpc_gen::TypeIsSBClass(method.ReturnType) || method.IsCtor; 390 391 std::string ValueToEncode; 392 if (IsEncodingSBClass) { 393 if (method.IsCtor) 394 ValueToEncode = MethodCall; 395 else 396 ValueToEncode = "std::move(" + ReturnVariableName.str() + ")"; 397 } else 398 ValueToEncode = ReturnVariableName.str(); 399 400 const std::string ReturnValueEncodeLine = 401 CreateEncodeLine(ValueToEncode, IsEncodingSBClass); 402 EmitLine(ReturnValueEncodeLine); 403 EmitEncodesForMutableParameters(method.Params); 404 } 405 406 // NOTE: This contains most of the same knowledge as RPCLibrarySourceEmitter. I 407 // have chosen not to re-use code here because the needs are different enough 408 // that it would be more work to re-use than just reimplement portions of it. 409 // Specifically: 410 // - Callbacks do not neatly fit into a `Method` object, which currently 411 // assumes that you have a CXXMethodDecl (We have a FunctionDecl at most). 412 // - We only generate callbacks that have a `void *` baton parameter. We hijack 413 // those baton parameters and treat them differently. 414 // - Callbacks need to do something special for moving SB class references back 415 // to the client-side. 416 void RPCServerSourceEmitter::EmitCallbackFunction(const Method &method) { 417 // Check invariants and locate necessary resources 418 Param FuncPointerParam; 419 Param BatonParam; 420 for (const auto &Param : method.Params) 421 if (Param.Type->isFunctionPointerType()) 422 FuncPointerParam = Param; 423 else if (Param.Type->isVoidPointerType()) 424 BatonParam = Param; 425 426 assert(FuncPointerParam.Type->isFunctionPointerType() && 427 "Emitting callback function with no function pointer"); 428 assert(BatonParam.Type->isVoidPointerType() && 429 "Emitting callback function with no baton"); 430 431 QualType FuncType = FuncPointerParam.Type->getPointeeType(); 432 const auto *FuncProtoType = FuncType->getAs<FunctionProtoType>(); 433 assert(FuncProtoType && "Emitting callback with no parameter information"); 434 if (!FuncProtoType) 435 return; // If asserts are off, we'll just fail to compile. 436 437 std::vector<Param> CallbackParams; 438 std::vector<std::string> CallbackParamsAsStrings; 439 uint8_t ArgIdx = 0; 440 for (QualType ParamType : FuncProtoType->param_types()) { 441 Param CallbackParam; 442 CallbackParam.IsFollowedByLen = false; 443 CallbackParam.Type = ParamType; 444 if (ParamType->isVoidPointerType()) 445 CallbackParam.Name = "baton"; 446 else 447 CallbackParam.Name = "arg" + std::to_string(ArgIdx++); 448 449 CallbackParams.push_back(CallbackParam); 450 CallbackParamsAsStrings.push_back(ParamType.getAsString(method.Policy) + 451 " " + CallbackParam.Name); 452 } 453 const std::string CallbackReturnTypeName = 454 FuncProtoType->getReturnType().getAsString(method.Policy); 455 const std::string CallbackName = method.MangledName + "_callback"; 456 457 // Emit Function Header 458 std::string Header; 459 llvm::raw_string_ostream HeaderStream(Header); 460 HeaderStream << "static " << CallbackReturnTypeName << " " << CallbackName 461 << "(" << llvm::join(CallbackParamsAsStrings, ", ") << ") {"; 462 EmitLine(Header); 463 IndentLevel++; 464 465 // Emit Function Body 466 EmitLine("// RPC connection setup and sanity checking"); 467 EmitLine("CallbackInfo *callback_info = (CallbackInfo *)baton;"); 468 EmitLine("rpc_common::ConnectionSP connection_sp = " 469 "rpc_common::Connection::GetConnectionFromID(callback_info->" 470 "connection_id);"); 471 EmitLine("if (!connection_sp)"); 472 IndentLevel++; 473 if (FuncProtoType->getReturnType()->isVoidType()) 474 EmitLine("return;"); 475 else 476 EmitLine("return {};"); 477 IndentLevel--; 478 479 EmitLine("// Preparing to make the call"); 480 EmitLine("static RPCFunctionInfo g_func(\"" + CallbackName + "\");"); 481 EmitLine("RPCStream send;"); 482 EmitLine("RPCStream response;"); 483 EmitLine("g_func.Encode(send);"); 484 485 EmitLine("// The first thing we encode is the callback address so that the " 486 "client-side can know where the callback is"); 487 EmitLine("RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, " 488 "callback_info->callback);"); 489 EmitLine("// Encode all the arguments"); 490 for (const Param &CallbackParam : CallbackParams) { 491 if (lldb_rpc_gen::TypeIsSBClass(CallbackParam.Type)) { 492 493 // FIXME: SB class server references are stored as non-const references so 494 // that we can actually change them as needed. If a parameter is marked 495 // const, we will fail to compile because we cannot make an 496 // SBFooServerReference from a `const SBFoo &`. 497 // To work around this issue, we'll apply a `const_cast` if needed so we 498 // can continue to generate callbacks for now, but we really should 499 // rethink the way we store object IDs server-side to support 500 // const-qualified parameters. 501 QualType UnderlyingSBClass = 502 lldb_rpc_gen::GetUnderlyingType(CallbackParam.Type); 503 QualType UnqualifiedUnderlyingSBClass = 504 UnderlyingSBClass.getUnqualifiedType(); 505 506 std::string SBClassName = GetSBClassNameFromType(UnderlyingSBClass); 507 llvm::StringRef SBClassNameRef(SBClassName); 508 SBClassNameRef.consume_front("lldb::"); 509 510 std::string ServerReferenceLine; 511 llvm::raw_string_ostream ServerReferenceLineStream(ServerReferenceLine); 512 ServerReferenceLineStream << "rpc_server::" << SBClassNameRef 513 << "ServerReference " << CallbackParam.Name 514 << "_ref("; 515 516 if (UnderlyingSBClass.isConstQualified()) { 517 QualType NonConstSBType = 518 method.Context.getLValueReferenceType(UnqualifiedUnderlyingSBClass); 519 ServerReferenceLineStream << "const_cast<" << NonConstSBType << ">("; 520 } 521 ServerReferenceLineStream << CallbackParam.Name; 522 if (UnderlyingSBClass.isConstQualified()) 523 ServerReferenceLineStream << ")"; 524 525 ServerReferenceLineStream << ");"; 526 EmitLine(ServerReferenceLine); 527 EmitLine( 528 CallbackParam.Name + 529 "_ref.Encode(send, rpc_common::RPCPacket::ValueType::Argument);"); 530 } else { 531 std::string ParamName; 532 if (CallbackParam.Type->isVoidPointerType()) 533 ParamName = "callback_info->baton"; 534 else 535 ParamName = CallbackParam.Name; 536 EmitLine( 537 "RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, " + 538 ParamName + ");"); 539 } 540 } 541 542 if (!FuncProtoType->getReturnType()->isVoidType()) { 543 EmitLine("// Storage for return value"); 544 const bool ReturnsSBClass = 545 lldb_rpc_gen::TypeIsSBClass(FuncProtoType->getReturnType()); 546 std::string ReturnValueLine = CallbackReturnTypeName; 547 llvm::raw_string_ostream ReturnValueLineStream(ReturnValueLine); 548 549 if (ReturnsSBClass) 550 ReturnValueLineStream << " *"; 551 ReturnValueLineStream << " __result = "; 552 if (ReturnsSBClass) 553 ReturnValueLineStream << "nullptr"; 554 else 555 ReturnValueLineStream << "{}"; 556 ReturnValueLineStream << ";"; 557 EmitLine(ReturnValueLine); 558 } 559 560 EmitLine( 561 "if (connection_sp->SendRPCCallAndWaitForResponse(send, response)) {"); 562 IndentLevel++; 563 if (!FuncProtoType->getReturnType()->isVoidType()) { 564 if (lldb_rpc_gen::TypeIsSBClass(FuncProtoType->getReturnType())) { 565 EmitLine("__result = rpc_server::RPCServerObjectDecoder<" + 566 CallbackReturnTypeName + 567 ">(response, rpc_common::RPCPacket::ValueType::ReturnValue);"); 568 } else 569 EmitLine("RPCValueDecoder(response, " 570 "rpc_common::RPCPacket::ValueType::ReturnValue, __result);"); 571 } 572 IndentLevel--; 573 EmitLine("}"); 574 if (!FuncProtoType->getReturnType()->isVoidType()) { 575 if (lldb_rpc_gen::TypeIsSBClass(FuncProtoType->getReturnType())) 576 EmitLine("return *__result;"); 577 else 578 EmitLine("return __result;"); 579 } 580 581 // Emit Function Footer; 582 IndentLevel--; 583 EmitLine("};"); 584 } 585