brintos

brintos / llvm-project-archived public Read only

0
0
Text · 22.7 KiB · a6d2528 Raw
585 lines · cpp
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-exception6//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 22using namespace clang;23using namespace lldb_rpc_gen;24 25// For methods with pointer return types, it's important that we know how big26// the type of the pointee is. We must correctly size a buffer (in the form of a27// Bytes object) before we can actually use it.28static const std::map<llvm::StringRef, size_t> MethodsWithPointerReturnTypes = {29    {"_ZN4lldb12SBModuleSpec12GetUUIDBytesEv", 16}, // sizeof(uuid_t) -> 1630    {"_ZNK4lldb8SBModule12GetUUIDBytesEv", 16},     // sizeof(uuid_t) -> 1631};32 33void 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 43void 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 57void RPCServerSourceEmitter::EmitFunctionHeader(const Method &method) {58  std::string FunctionHeader;59  llvm::raw_string_ostream FunctionHeaderStream(FunctionHeader);60  FunctionHeaderStream61      << "bool rpc_server::" << method.MangledName62      << "::HandleRPCCall(rpc_common::Connection &connection, RPCStream "63         "&send, RPCStream &response) {";64  EmitLine(FunctionHeader);65  IndentLevel++;66}67 68void 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 77void RPCServerSourceEmitter::EmitFunctionFooter() {78  EmitLine("return true;");79  IndentLevel--;80  EmitLine("}");81}82 83void RPCServerSourceEmitter::EmitStorageForParameters(const Method &method) {84  // If we have an instance method and it isn't a constructor, we'll need to85  // 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 99void 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 special103  // 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're118    // going to hold onto the pointer value so we can send it back to the119    // 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  else146    StorageDeclarationStream << " = {}";147  StorageDeclarationStream << ";";148  EmitLine(StorageDeclaration);149}150 151void 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 162void 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 190std::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  } else198    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::ConstCharPointer207      Arg = Iter->Name + ".c_str()";208    } else if (TypeIsConstCharPtrPtr(Iter->Type)) {209      // `const char **` is stored server-side as rpc_common::StringList210      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 array221      // with length of 1, except for void pointers which are handled222      // 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 if227      // 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    } else239      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 261std::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  else269    EncodeLineStream << "RPCValueEncoder(";270 271  EncodeLineStream272      << "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 specific282//   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 type284//   is mutable, we must send the changed value back in case the method call285//   mutated it.286//287// Updating a mutable reference is done as a return value from the RPC288// perspective. These return values need to be emitted after the method's return289// value, and they are emitted in the order in which they occur in the290// declaration.291void 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 SBClass295    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. This302    // 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 the326//    return value.327void 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 any331  // mutable references. Note that constructors have return type `void` so we332  // 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 method342  // call in a result variable.343  if (!method.IsCtor) {344    // We need to determine what the appropriate return type is. Here is the345    // 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 << "(" << MethodCall370                       << ", " << Pos->second << ");";371    } else {372      if (lldb_rpc_gen::TypeIsSBClass(method.ReturnType)) {373        // We want to preserve constness, so we don't strip qualifications from374        // the underlying type375        QualType UnderlyingReturnType =376            lldb_rpc_gen::GetUnderlyingType(method.ReturnType);377        ReturnTypeName =378            UnderlyingReturnType.getAsString(method.Policy) + " &&";379      } else380        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    else396      ValueToEncode = "std::move(" + ReturnVariableName.str() + ")";397  } else398    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. I407// have chosen not to re-use code here because the needs are different enough408// 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 currently411//    assumes that you have a CXXMethodDecl (We have a FunctionDecl at most).412//  - We only generate callbacks that have a `void *` baton parameter. We hijack413//    those baton parameters and treat them differently.414//  - Callbacks need to do something special for moving SB class references back415//    to the client-side.416void RPCServerSourceEmitter::EmitCallbackFunction(const Method &method) {417  // Check invariants and locate necessary resources418  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    else447      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 Header458  std::string Header;459  llvm::raw_string_ostream HeaderStream(Header);460  HeaderStream << "static " << CallbackReturnTypeName << " " << CallbackName461               << "(" << llvm::join(CallbackParamsAsStrings, ", ") << ") {";462  EmitLine(Header);463  IndentLevel++;464 465  // Emit Function Body466  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  else476    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 so494      // that we can actually change them as needed. If a parameter is marked495      // const, we will fail to compile because we cannot make an496      // SBFooServerReference from a `const SBFoo &`.497      // To work around this issue, we'll apply a `const_cast` if needed so we498      // can continue to generate callbacks for now, but we really should499      // rethink the way we store object IDs server-side to support500      // 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::" << SBClassNameRef513                                << "ServerReference " << CallbackParam.Name514                                << "_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      else535        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    else555      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    } else569      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    else578      EmitLine("return __result;");579  }580 581  // Emit Function Footer;582  IndentLevel--;583  EmitLine("};");584}585