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 }