| 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::(const Method &method) { |
| 44 | std::string ; |
| 45 | llvm::raw_string_ostream (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::(const Method &method) { |
| 58 | std::string ; |
| 59 | llvm::raw_string_ostream (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::() { |
| 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(ParamType: method.ThisType, ParamName: "this_ptr" , Policy: method.Policy, |
| 88 | /* IsFollowedByLen = */ false); |
| 89 | for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { |
| 90 | EmitStorageForOneParameter(ParamType: Iter->Type, ParamName: Iter->Name, Policy: method.Policy, |
| 91 | IsFollowedByLen: 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(OS&: 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(ParamType: method.ThisType, ParamName: "this_ptr" , Policy: method.Policy); |
| 154 | for (auto Iter = method.Params.begin(); Iter != method.Params.end(); Iter++) { |
| 155 | EmitDecodeForOneParameter(ParamType: Iter->Type, ParamName: Iter->Name, Policy: 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(OS&: 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(x: 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(x: LengthArg); |
| 253 | Iter++; |
| 254 | } |
| 255 | } |
| 256 | MethodCallStream << "(" << llvm::join(R&: Args, Separator: ", " ) << ")" ; |
| 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(Value: 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(x: 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(Value: 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 ; |
| 459 | llvm::raw_string_ostream (Header); |
| 460 | HeaderStream << "static " << CallbackReturnTypeName << " " << CallbackName |
| 461 | << "(" << llvm::join(R&: CallbackParamsAsStrings, Separator: ", " ) << ") {" ; |
| 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 | |