tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

BindingOperations.cpp (23088B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "BindingOperations.h"
      7 
      8 #include <clang/AST/Attr.h>
      9 #include <clang/AST/Expr.h>
     10 #include <clang/AST/RecursiveASTVisitor.h>
     11 #include <clang/Basic/Version.h>
     12 
     13 #include <algorithm>
     14 #include <array>
     15 #include <cuchar>
     16 #include <set>
     17 #include <string>
     18 #include <unordered_map>
     19 #include <vector>
     20 
     21 #ifdef __cpp_lib_optional
     22 #include <optional>
     23 template <typename T> using optional = std::optional<T>;
     24 #else
     25 #include <llvm/ADT/Optional.h>
     26 template <typename T> using optional = clang::Optional<T>;
     27 #endif
     28 
     29 using namespace clang;
     30 
     31 namespace {
     32 
     33 template <typename InputIt>
     34 bool hasReverseQualifiedName(InputIt first, InputIt last,
     35                             const NamedDecl &tag) {
     36  const NamedDecl *currentDecl = &tag;
     37  InputIt currentName;
     38  for (currentName = first; currentName != last; currentName++) {
     39    if (!currentDecl || !currentDecl->getIdentifier() ||
     40        currentDecl->getName() != *currentName)
     41      return false;
     42 
     43    currentDecl = dyn_cast<NamedDecl>(currentDecl->getDeclContext());
     44  }
     45  if (currentName != last)
     46    return false;
     47 
     48  if (currentDecl != nullptr)
     49    return false;
     50 
     51  return true;
     52 }
     53 
     54 bool isMozillaJniObjectBase(const CXXRecordDecl &klass) {
     55  const auto qualifiedName =
     56      std::array<StringRef, 3>{"mozilla", "jni", "ObjectBase"};
     57  return hasReverseQualifiedName(qualifiedName.crbegin(), qualifiedName.crend(),
     58                                 klass);
     59 }
     60 
     61 bool isMozillaJniNativeImpl(const CXXRecordDecl &klass) {
     62  const auto qualifiedName =
     63      std::array<StringRef, 3>{"mozilla", "jni", "NativeImpl"};
     64  return hasReverseQualifiedName(qualifiedName.crbegin(), qualifiedName.crend(),
     65                                 klass);
     66 }
     67 
     68 const NamedDecl *fieldNamed(StringRef name, const RecordDecl &strukt) {
     69  for (const auto *decl : strukt.decls()) {
     70    const auto *namedDecl = dyn_cast<VarDecl>(decl);
     71    if (!namedDecl)
     72      continue;
     73 
     74    if (!namedDecl->getIdentifier() || namedDecl->getName() != name)
     75      continue;
     76 
     77    return namedDecl;
     78  }
     79 
     80  return {};
     81 }
     82 
     83 optional<StringRef> nameFieldValue(const RecordDecl &strukt) {
     84  const auto *nameField = dyn_cast_or_null<VarDecl>(fieldNamed("name", strukt));
     85  if (!nameField)
     86    return {};
     87 
     88  const auto *def = nameField->getDefinition();
     89  if (!def)
     90    return {};
     91 
     92  const auto *name = dyn_cast_or_null<StringLiteral>(def->getInit());
     93  if (!name)
     94    return {};
     95 
     96  return name->getString();
     97 }
     98 
     99 struct AbstractBinding {
    100  // Subset of tools/analysis/BindingSlotLang
    101  enum class Lang {
    102    Cpp,
    103    Jvm,
    104  };
    105  static constexpr size_t LangLength = 2;
    106  static constexpr std::array<StringRef, LangLength> langNames = {
    107      "cpp",
    108      "jvm",
    109  };
    110 
    111  static optional<Lang> langFromString(StringRef langName) {
    112    const auto it = std::find(langNames.begin(), langNames.end(), langName);
    113    if (it == langNames.end())
    114      return {};
    115 
    116    return Lang(it - langNames.begin());
    117  }
    118  static StringRef stringFromLang(Lang lang) { return langNames[size_t(lang)]; }
    119 
    120  // Subset of tools/analysis/BindingSlotKind
    121  enum class Kind {
    122    Class,
    123    Method,
    124    Getter,
    125    Setter,
    126    Const,
    127  };
    128  static constexpr size_t KindLength = 5;
    129  static constexpr std::array<StringRef, KindLength> kindNames = {
    130      "class", "method", "getter", "setter", "const",
    131  };
    132 
    133  static optional<Kind> kindFromString(StringRef kindName) {
    134    const auto it = std::find(kindNames.begin(), kindNames.end(), kindName);
    135    if (it == kindNames.end())
    136      return {};
    137 
    138    return Kind(it - kindNames.begin());
    139  }
    140  static StringRef stringFromKind(Kind kind) { return kindNames[size_t(kind)]; }
    141 
    142  Lang lang;
    143  Kind kind;
    144  StringRef symbol;
    145 };
    146 constexpr size_t AbstractBinding::KindLength;
    147 constexpr std::array<StringRef, AbstractBinding::KindLength>
    148    AbstractBinding::kindNames;
    149 constexpr size_t AbstractBinding::LangLength;
    150 constexpr std::array<StringRef, AbstractBinding::LangLength>
    151    AbstractBinding::langNames;
    152 
    153 struct BindingTo : public AbstractBinding {
    154  BindingTo(AbstractBinding b) : AbstractBinding(std::move(b)) {}
    155  static constexpr StringRef ANNOTATION = "binding_to";
    156 };
    157 constexpr StringRef BindingTo::ANNOTATION;
    158 
    159 struct BoundAs : public AbstractBinding {
    160  BoundAs(AbstractBinding b) : AbstractBinding(std::move(b)) {}
    161  static constexpr StringRef ANNOTATION = "bound_as";
    162 };
    163 constexpr StringRef BoundAs::ANNOTATION;
    164 
    165 template <typename B>
    166 void setBindingAttr(ASTContext &C, Decl &decl, B binding) {
    167 #if CLANG_VERSION_MAJOR >= 18
    168  auto utf8 = StringLiteralKind::UTF8;
    169 #else
    170  auto utf8 = StringLiteral::UTF8;
    171 #endif
    172  // recent LLVM: CreateImplicit then setDelayedArgs
    173  Expr *langExpr = StringLiteral::Create(
    174      C, AbstractBinding::stringFromLang(binding.lang), utf8, false, {}, {});
    175  Expr *kindExpr = StringLiteral::Create(
    176      C, AbstractBinding::stringFromKind(binding.kind), utf8, false, {}, {});
    177  Expr *symbolExpr =
    178      StringLiteral::Create(C, binding.symbol, utf8, false, {}, {});
    179  auto **args = new (C, 16) Expr *[3]{langExpr, kindExpr, symbolExpr};
    180  auto *attr = AnnotateAttr::CreateImplicit(C, B::ANNOTATION, args, 3);
    181  decl.addAttr(attr);
    182 }
    183 
    184 optional<AbstractBinding> readBinding(const AnnotateAttr &attr) {
    185  if (attr.args_size() != 3)
    186    return {};
    187 
    188  const auto *langExpr = attr.args().begin()[0];
    189  const auto *kindExpr = attr.args().begin()[1];
    190  const auto *symbolExpr = attr.args().begin()[2];
    191  if (!langExpr || !kindExpr || !symbolExpr)
    192    return {};
    193 
    194  const auto *langName =
    195      dyn_cast<StringLiteral>(langExpr->IgnoreUnlessSpelledInSource());
    196  const auto *kindName =
    197      dyn_cast<StringLiteral>(kindExpr->IgnoreUnlessSpelledInSource());
    198  const auto *symbol =
    199      dyn_cast<StringLiteral>(symbolExpr->IgnoreUnlessSpelledInSource());
    200  if (!langName || !kindName || !symbol)
    201    return {};
    202 
    203  const auto lang = AbstractBinding::langFromString(langName->getString());
    204  const auto kind = AbstractBinding::kindFromString(kindName->getString());
    205 
    206  if (!lang || !kind)
    207    return {};
    208 
    209  return AbstractBinding{
    210      .lang = *lang,
    211      .kind = *kind,
    212      .symbol = symbol->getString(),
    213  };
    214 }
    215 
    216 optional<BindingTo> getBindingTo(const Decl &decl) {
    217  for (const auto *attr : decl.specific_attrs<AnnotateAttr>()) {
    218    if (attr->getAnnotation() != BindingTo::ANNOTATION)
    219      continue;
    220 
    221    const auto binding = readBinding(*attr);
    222    if (!binding)
    223      continue;
    224 
    225    return BindingTo{*binding};
    226  }
    227  return {};
    228 }
    229 
    230 // C++23: turn into generator
    231 std::vector<BoundAs> getBoundAs(const Decl &decl) {
    232  std::vector<BoundAs> found;
    233 
    234  for (const auto *attr : decl.specific_attrs<AnnotateAttr>()) {
    235    if (attr->getAnnotation() != BoundAs::ANNOTATION)
    236      continue;
    237 
    238    const auto binding = readBinding(*attr);
    239    if (!binding)
    240      continue;
    241 
    242    found.push_back(BoundAs{*binding});
    243  }
    244 
    245  return found;
    246 }
    247 
    248 class FindCallCall : private RecursiveASTVisitor<FindCallCall> {
    249 public:
    250  struct Result {
    251    using Kind = AbstractBinding::Kind;
    252 
    253    Kind kind;
    254    StringRef name;
    255  };
    256 
    257  static optional<Result> search(Stmt *statement) {
    258    FindCallCall finder;
    259    finder.TraverseStmt(statement);
    260    return finder.result;
    261  }
    262 
    263 private:
    264  optional<Result> result;
    265 
    266  friend RecursiveASTVisitor<FindCallCall>;
    267 
    268  optional<Result> tryParseCallCall(CallExpr *callExpr) {
    269    const auto *callee =
    270        dyn_cast_or_null<CXXMethodDecl>(callExpr->getDirectCallee());
    271    if (!callee)
    272      return {};
    273 
    274    if (!callee->getIdentifier())
    275      return {};
    276 
    277    const auto action = callee->getIdentifier()->getName();
    278 
    279    if (action != "Call" && action != "Get" && action != "Set")
    280      return {};
    281 
    282    const auto *parentClass =
    283        dyn_cast_or_null<ClassTemplateSpecializationDecl>(callee->getParent());
    284 
    285    if (!parentClass)
    286      return {};
    287 
    288    const auto *parentTemplate = parentClass->getTemplateInstantiationPattern();
    289 
    290    if (!parentTemplate || !parentTemplate->getIdentifier())
    291      return {};
    292 
    293    const auto parentName = parentTemplate->getIdentifier()->getName();
    294 
    295    AbstractBinding::Kind kind;
    296    if (action == "Call") {
    297      if (parentName == "Constructor" || parentName == "Method") {
    298        kind = AbstractBinding::Kind::Method;
    299      } else {
    300        return {};
    301      }
    302    } else if (parentName == "Field") {
    303      if (action == "Get") {
    304        kind = AbstractBinding::Kind::Getter;
    305      } else if (action == "Set") {
    306        kind = AbstractBinding::Kind::Setter;
    307      } else {
    308        return {};
    309      }
    310    } else {
    311      return {};
    312    }
    313 
    314    const auto *templateArg =
    315        parentClass->getTemplateArgs().get(0).getAsType()->getAsRecordDecl();
    316 
    317    if (!templateArg)
    318      return {};
    319 
    320    const auto name = nameFieldValue(*templateArg);
    321    if (!name)
    322      return {};
    323 
    324    return Result{
    325        .kind = kind,
    326        .name = *name,
    327    };
    328 
    329    return {};
    330  }
    331  bool VisitCallExpr(CallExpr *callExpr) {
    332    return !(result = tryParseCallCall(callExpr));
    333  }
    334 };
    335 
    336 constexpr StringRef JVM_SCIP_SYMBOL_PREFIX = "S_jvm_";
    337 
    338 std::string javaScipSymbol(StringRef prefix, StringRef name,
    339                           AbstractBinding::Kind kind) {
    340  auto symbol = (prefix + name).str();
    341 
    342  switch (kind) {
    343  case AbstractBinding::Kind::Class:
    344    std::replace(symbol.begin(), symbol.end(), '$', '#');
    345    symbol += "#";
    346    break;
    347  case AbstractBinding::Kind::Method:
    348    symbol += "().";
    349    break;
    350  case AbstractBinding::Kind::Const:
    351  case AbstractBinding::Kind::Getter:
    352  case AbstractBinding::Kind::Setter:
    353    symbol += ".";
    354    break;
    355  }
    356 
    357  return symbol;
    358 }
    359 
    360 void addSlotOwnerAttribute(llvm::json::OStream &J, const Decl &decl) {
    361  if (const auto bindingTo = getBindingTo(decl)) {
    362    J.attributeBegin("slotOwner");
    363    J.objectBegin();
    364    J.attribute("slotKind", AbstractBinding::stringFromKind(bindingTo->kind));
    365    J.attribute("slotLang", "cpp");
    366    J.attribute("ownerLang", AbstractBinding::stringFromLang(bindingTo->lang));
    367    J.attribute("sym", bindingTo->symbol);
    368    J.objectEnd();
    369    J.attributeEnd();
    370  }
    371 }
    372 void addBindingSlotsAttribute(llvm::json::OStream &J, const Decl &decl) {
    373  const auto allBoundAs = getBoundAs(decl);
    374  if (!allBoundAs.empty()) {
    375    J.attributeBegin("bindingSlots");
    376    J.arrayBegin();
    377    for (const auto boundAs : allBoundAs) {
    378      J.objectBegin();
    379      J.attribute("slotKind", AbstractBinding::stringFromKind(boundAs.kind));
    380      J.attribute("slotLang", AbstractBinding::stringFromLang(boundAs.lang));
    381      J.attribute("ownerLang", "cpp");
    382      J.attribute("sym", boundAs.symbol);
    383      J.objectEnd();
    384    }
    385    J.arrayEnd();
    386    J.attributeEnd();
    387  }
    388 }
    389 
    390 // The mangling scheme is documented at
    391 // https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html
    392 // The main takeaways are:
    393 // - _0xxxx is the utf16 code unit xxxx
    394 // - _1 is _
    395 // - _2 is ;
    396 // - _3 is [
    397 // - __ is the separator between function name and overload specification
    398 // - _ is otherwise the separator between packages/classes/methods
    399 //
    400 // This method takes a StringRef & and mutates it and can be called twice on a
    401 // Jnicall function name to get
    402 //  first the demangled name
    403 //  second the demangled overload specification
    404 // But we don't use the later for now because we have no way to map that to how
    405 // SCIP resolves overloads.
    406 optional<std::string> demangleJnicallPart(StringRef &remainder) {
    407  std::string demangled;
    408 
    409  std::mbstate_t ps = {};
    410 
    411  while (!remainder.empty()) {
    412    switch (remainder[0]) {
    413    case '0': {
    414      remainder = remainder.drop_front(1);
    415 
    416      uint16_t codeUnit;
    417      const auto ok = remainder.substr(1, 4).getAsInteger(16, codeUnit);
    418      remainder = remainder.drop_front(4);
    419 
    420      if (!ok) // failed reading xxxx as hexadecimal from _0xxxx
    421        return {};
    422 
    423      std::array<char, MB_LEN_MAX> codePoint;
    424      const auto mbLen = std::c16rtomb(codePoint.data(), codeUnit, &ps);
    425 
    426      if (mbLen == -1) // failed converting utf16 to utf8
    427        return {};
    428 
    429      demangled += StringRef(codePoint.begin(), mbLen);
    430      break;
    431    }
    432    case '1':
    433      remainder = remainder.drop_front(1);
    434      ps = {};
    435      demangled += '_';
    436      break;
    437    case '2':
    438      remainder = remainder.drop_front(1);
    439      ps = {};
    440      demangled += ';';
    441      break;
    442    case '3':
    443      remainder = remainder.drop_front(1);
    444      ps = {};
    445      demangled += '[';
    446      break;
    447    case '_':
    448      remainder = remainder.drop_front(1);
    449      ps = {};
    450      if (remainder.empty()) // the string ends with _
    451        return {};
    452 
    453      switch (remainder[0]) {
    454      case '0':
    455      case '1':
    456      case '2':
    457      case '3':
    458        demangled += '.';
    459        break;
    460      default:
    461        // either:
    462        // * the string began with _[^0-3], which is not supposed to happen; or
    463        // * we reached __[^0-3] meaning we finished the first part of the name
    464        // and remainder holds the overload specification
    465        return demangled;
    466      }
    467    default:
    468      ps = {};
    469      demangled += '.';
    470      break;
    471    }
    472    StringRef token;
    473    std::tie(token, remainder) = remainder.split('_');
    474    demangled += token;
    475  }
    476 
    477  return demangled;
    478 }
    479 
    480 optional<std::string>
    481 scipSymbolFromJnicallFunctionName(StringRef functionName) {
    482  if (!functionName.consume_front("Java_"))
    483    return {};
    484 
    485  const auto demangledName = demangleJnicallPart(functionName);
    486 
    487  if (!demangledName || demangledName->empty())
    488    return {};
    489 
    490  // demangleJavaName returns something like
    491  // .some.package.Class$InnerClass.method
    492  // - prepend S_jvm_
    493  // - remove the leading dot
    494  // - replace the last dot with a #
    495  // - replace the other dots with /
    496  // - replace $ with #
    497  // - add the ([+overloadNumber]). suffix
    498  auto symbol = JVM_SCIP_SYMBOL_PREFIX.str();
    499  symbol += demangledName->substr(1);
    500  const auto lastDot = symbol.rfind('.');
    501  if (lastDot != std::string::npos)
    502    symbol[lastDot] = '#';
    503  std::replace(symbol.begin(), symbol.end(), '.', '/');
    504  std::replace(symbol.begin(), symbol.end(), '$', '#');
    505 
    506  // Keep track of how many times we have seen this method, to build the
    507  // ([+overloadNumber]). suffix. This assumes this function is called on C
    508  // function definitions in the same order the matching overloads are declared
    509  // in Java.
    510  static std::unordered_map<std::string, uint> jnicallFunctions;
    511  auto &overloadNumber = jnicallFunctions[symbol];
    512 
    513  symbol += '(';
    514  if (overloadNumber) {
    515    symbol += '+';
    516    symbol += overloadNumber;
    517    overloadNumber++;
    518  }
    519  symbol += ").";
    520 
    521  return symbol;
    522 };
    523 
    524 } // anonymous namespace
    525 
    526 // class [wrapper] : public mozilla::jni::ObjectBase<[wrapper]>
    527 // {
    528 //   static constexpr char name[] = "[nameFieldValue]";
    529 // }
    530 void findBindingToJavaClass(ASTContext &C, CXXRecordDecl &klass) {
    531  for (const auto &baseSpecifier : klass.bases()) {
    532    const auto *base = baseSpecifier.getType()->getAsCXXRecordDecl();
    533    if (!base)
    534      continue;
    535 
    536    if (!isMozillaJniObjectBase(*base))
    537      continue;
    538 
    539    const auto name = nameFieldValue(klass);
    540    if (!name)
    541      continue;
    542 
    543    const auto symbol =
    544        javaScipSymbol(JVM_SCIP_SYMBOL_PREFIX, *name, BindingTo::Kind::Class);
    545    const auto binding = BindingTo{{
    546        .lang = BindingTo::Lang::Jvm,
    547        .kind = BindingTo::Kind::Class,
    548        .symbol = symbol,
    549    }};
    550 
    551    setBindingAttr(C, klass, binding);
    552    return;
    553  }
    554 }
    555 
    556 // When a Java method is marked as native, the JRE looks by default for a
    557 // function named Java_<mangled method name>[__<mangled overload
    558 // specification>].
    559 void findBindingToJavaFunction(ASTContext &C, FunctionDecl &function) {
    560  const auto *identifier = function.getIdentifier();
    561  if (!identifier)
    562    return;
    563 
    564  const auto name = identifier->getName();
    565  const auto symbol = scipSymbolFromJnicallFunctionName(name);
    566  if (!symbol)
    567    return;
    568 
    569  const auto binding = BoundAs{{
    570      .lang = BindingTo::Lang::Jvm,
    571      .kind = BindingTo::Kind::Method,
    572      .symbol = *symbol,
    573  }};
    574 
    575  setBindingAttr(C, function, binding);
    576 }
    577 
    578 // class [parent]
    579 // {
    580 //   struct [methodStruct] {
    581 //     static constexpr char name[] = "[methodNameFieldValue]";
    582 //   }
    583 //   [method]
    584 //   {
    585 //     ...
    586 //     mozilla::jni::{Method,Constructor,Field}<[methodStruct]>::{Call,Get,Set}(...)
    587 //     ...
    588 //   }
    589 // }
    590 void findBindingToJavaMember(ASTContext &C, CXXMethodDecl &method) {
    591  const auto *parent = method.getParent();
    592  if (!parent)
    593    return;
    594  const auto classBinding = getBindingTo(*parent);
    595  if (!classBinding)
    596    return;
    597 
    598  auto *body = method.getBody();
    599  if (!body)
    600    return;
    601 
    602  const auto found = FindCallCall::search(body);
    603  if (!found)
    604    return;
    605 
    606  const auto symbol =
    607      javaScipSymbol(classBinding->symbol, found->name, found->kind);
    608  const auto binding = BindingTo{{
    609      .lang = BindingTo::Lang::Jvm,
    610      .kind = found->kind,
    611      .symbol = symbol,
    612  }};
    613 
    614  setBindingAttr(C, method, binding);
    615 }
    616 
    617 // class [parent]
    618 // {
    619 //   struct [methodStruct] {
    620 //     static constexpr char name[] = "[methodNameFieldValue]";
    621 //   }
    622 //   [method]
    623 //   {
    624 //     ...
    625 //     mozilla::jni::{Method,Constructor,Field}<[methodStruct]>::{Call,Get,Set}(...)
    626 //     ...
    627 //   }
    628 // }
    629 void findBindingToJavaConstant(ASTContext &C, VarDecl &field) {
    630  const auto *parent = dyn_cast_or_null<CXXRecordDecl>(field.getDeclContext());
    631  if (!parent)
    632    return;
    633 
    634  const auto classBinding = getBindingTo(*parent);
    635  if (!classBinding)
    636    return;
    637 
    638  const auto symbol = javaScipSymbol(classBinding->symbol, field.getName(),
    639                                     BindingTo::Kind::Const);
    640  const auto binding = BindingTo{{
    641      .lang = BindingTo::Lang::Jvm,
    642      .kind = BindingTo::Kind::Const,
    643      .symbol = symbol,
    644  }};
    645 
    646  setBindingAttr(C, field, binding);
    647 }
    648 
    649 // class [klass] : public [wrapper]::Natives<[klass]> {...}
    650 // class [wrapper] : public mozilla::jni::ObjectBase<[wrapper]>
    651 // {
    652 //   static constexpr char name[] = "[nameFieldValue]";
    653 //
    654 //   struct [methodStruct] {
    655 //     static constexpr char name[] = "[methodNameFieldValue]";
    656 //   }
    657 //
    658 //   template<typename T>
    659 //   class [wrapper]::Natives : public mozilla::jni::NativeImpl<[wrapper], T> {
    660 //     static const JNINativeMethod methods[] = {
    661 //       mozilla::jni::MakeNativeMethod<[wrapper]::[methodStruct]>(
    662 //               mozilla::jni::NativeStub<[wrapper]::[methodStruct], Impl>
    663 //               ::template Wrap<&Impl::[method]>),
    664 //     }
    665 //   }
    666 // }
    667 void findBoundAsJavaClasses(ASTContext &C, CXXRecordDecl &klass) {
    668  for (const auto &baseSpecifier : klass.bases()) {
    669    const auto *base = baseSpecifier.getType()->getAsCXXRecordDecl();
    670    if (!base)
    671      continue;
    672 
    673    for (const auto &baseBaseSpecifier : base->bases()) {
    674      const auto *baseBase = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
    675          baseBaseSpecifier.getType()->getAsCXXRecordDecl());
    676      if (!baseBase)
    677        continue;
    678 
    679      if (!isMozillaJniNativeImpl(*baseBase))
    680        continue;
    681 
    682      const auto *wrapper =
    683          baseBase->getTemplateArgs().get(0).getAsType()->getAsCXXRecordDecl();
    684 
    685      if (!wrapper)
    686        continue;
    687 
    688      const auto name = nameFieldValue(*wrapper);
    689      if (!name)
    690        continue;
    691 
    692      const auto javaClassSymbol =
    693          javaScipSymbol(JVM_SCIP_SYMBOL_PREFIX, *name, BoundAs::Kind::Class);
    694      const auto classBinding = BoundAs{{
    695          .lang = BoundAs::Lang::Jvm,
    696          .kind = BoundAs::Kind::Class,
    697          .symbol = javaClassSymbol,
    698      }};
    699      setBindingAttr(C, klass, classBinding);
    700 
    701      const auto *methodsDecl =
    702          dyn_cast_or_null<VarDecl>(fieldNamed("methods", *base));
    703      if (!methodsDecl)
    704        continue;
    705 
    706      const auto *methodsDef = methodsDecl->getDefinition();
    707      if (!methodsDef)
    708        continue;
    709 
    710      const auto *inits = dyn_cast_or_null<InitListExpr>(methodsDef->getInit());
    711      if (!inits)
    712        continue;
    713 
    714      std::set<const CXXMethodDecl *> alreadyBound;
    715 
    716      for (const auto *init : inits->inits()) {
    717        const auto *call =
    718            dyn_cast<CallExpr>(init->IgnoreUnlessSpelledInSource());
    719        if (!call)
    720          continue;
    721 
    722        const auto *funcDecl = call->getDirectCallee();
    723        if (!funcDecl)
    724          continue;
    725 
    726        const auto *templateArgs = funcDecl->getTemplateSpecializationArgs();
    727        if (!templateArgs)
    728          continue;
    729 
    730        const auto *strukt = dyn_cast_or_null<RecordDecl>(
    731            templateArgs->get(0).getAsType()->getAsRecordDecl());
    732        if (!strukt)
    733          continue;
    734 
    735        const auto *wrapperRef = dyn_cast_or_null<DeclRefExpr>(
    736            call->getArg(0)->IgnoreUnlessSpelledInSource());
    737        if (!wrapperRef)
    738          continue;
    739 
    740        const auto *boundRef = dyn_cast_or_null<UnaryOperator>(
    741            wrapperRef->template_arguments().front().getArgument().getAsExpr());
    742        if (!boundRef)
    743          continue;
    744 
    745        auto addToBound = [&](CXXMethodDecl &boundDecl, uint overloadNum) {
    746          const auto methodName = nameFieldValue(*strukt);
    747          if (!methodName)
    748            return;
    749 
    750          auto javaMethodSymbol = javaClassSymbol;
    751          javaMethodSymbol += *methodName;
    752          javaMethodSymbol += '(';
    753          if (overloadNum > 0) {
    754            javaMethodSymbol += '+';
    755            javaMethodSymbol += std::to_string(overloadNum);
    756          }
    757          javaMethodSymbol += ").";
    758 
    759          const auto binding = BoundAs{{
    760              .lang = BoundAs::Lang::Jvm,
    761              .kind = BoundAs::Kind::Method,
    762              .symbol = javaMethodSymbol,
    763          }};
    764          setBindingAttr(C, boundDecl, binding);
    765        };
    766 
    767        if (auto *bound =
    768                dyn_cast_or_null<DeclRefExpr>(boundRef->getSubExpr())) {
    769          auto *method = dyn_cast_or_null<CXXMethodDecl>(bound->getDecl());
    770          if (!method)
    771            continue;
    772          addToBound(*method, 0);
    773        } else if (const auto *bound = dyn_cast_or_null<UnresolvedLookupExpr>(
    774                       boundRef->getSubExpr())) {
    775          // XXX This is hackish
    776          // In case of overloads it's not obvious which one we should use
    777          // this expects the declaration order between C++ and Java to match
    778          auto declarations =
    779              std::vector<Decl *>(bound->decls_begin(), bound->decls_end());
    780          auto byLocation = [](Decl *a, Decl *b) {
    781            return a->getLocation() < b->getLocation();
    782          };
    783          std::sort(declarations.begin(), declarations.end(), byLocation);
    784 
    785          uint i = 0;
    786          for (auto *decl : declarations) {
    787            auto *method = dyn_cast<CXXMethodDecl>(decl);
    788            if (!method)
    789              continue;
    790            if (alreadyBound.find(method) == alreadyBound.end()) {
    791              addToBound(*method, i);
    792              alreadyBound.insert(method);
    793              break;
    794            }
    795            i++;
    796          }
    797        }
    798      }
    799    }
    800  }
    801 }
    802 
    803 void emitBindingAttributes(llvm::json::OStream &J, const Decl &decl) {
    804  addSlotOwnerAttribute(J, decl);
    805  addBindingSlotsAttribute(J, decl);
    806 }