================ @@ -174,6 +174,233 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( if (!valobj_sp) return optional_info; + // std::function has many variants, try to disambiguate + ValueObjectSP func_as_base_ptr; + { + ValueObjectSP outer_f = valobj_sp->GetChildMemberWithName("__f_"); + + if (!outer_f) + return optional_info; // Unrecognized implementation + + if (outer_f->IsPointerType()) { + // git: 3e519524c118651123eecf60c2bbc5d65ad9bac3 + // + // class function<_Rp()> { + // aligned_storage<3*sizeof(void*)>::type __buf_; + // __base<_Rp>* __f_; + // } + + func_as_base_ptr = std::move(outer_f); + } else if (auto inner_f = outer_f->GetChildMemberWithName("__f_")) { + // git: 050b064f15ee56ee0b42c9b957a3dd0f32532394 + // + // class function<_Rp(_ArgTypes...)> { + // __value_func<_Rp(_ArgTypes...)> __f_; + // } + // + // class __value_func<_Rp(_ArgTypes...)> { + // aligned_storage<3 * sizeof(void*)>::type __buf_; + // __base<_Rp(_ArgTypes...)>* __f_; + // } + + func_as_base_ptr = std::move(inner_f); + } else + return optional_info; // Unrecognized implementation + } + + // __base<...> is a pure virtual class with an interface to create/copy/destroy/invoke + // the underlying value. This interface is implemented by partial specializations of the + // __func<_Fp, _Alloc, ...> template where _Fp is the wrapped functor object + Status status; + ValueObjectSP func_as_base = func_as_base_ptr->Dereference(status); + if (status.Fail()) + return optional_info; + + // First we'll try to extract the __func<...> template instantiation's type by looking up + // the declarations of the member function pointers in it's vtable + CompilerType func_type; + Address func_method_addr; + { + ValueObjectSP vtable = func_as_base->GetVTable(); + + llvm::Expected<uint32_t> num_entries = vtable->GetNumChildren(); + if (num_entries.takeError()) + return optional_info; + + // __base is pure virtual, __func is final. All member function pointers are equally + // good candidates to find the enclosing class. + // + // In practice the first two vtable entries point to artificial destructors which the + // type system refuses to elaborate as their artificial specifications are not added + // to the enclosing class' declaration context. This causes various warnings, and dont + // get us any closer to the concrete type thus we skip them. + for (uint32_t idx = 2; idx < *num_entries; idx++) { + ValueObjectSP entry = vtable->GetChildAtIndex(idx); + + // Points to a potentially interesting member function + addr_t mfunc_load_addr = entry->GetValueAsUnsigned(0); + if (!mfunc_load_addr) + continue; + + if (!valobj_sp->GetTargetSP()->ResolveLoadAddress(mfunc_load_addr, func_method_addr)) + continue; + + Function* func = func_method_addr.CalculateSymbolContextFunction(); + if (!func) + continue; + + CompilerDeclContext mfunc_decl_ctx = func->GetDeclContext(); + if (!mfunc_decl_ctx.IsClassMethod()) + continue; + + // Member functions are contained in their enclosing class' decl context + CompilerDeclContext mfunc_parent = mfunc_decl_ctx.GetDecl().GetDeclContext(); + if (!mfunc_parent.IsValid()) + continue; + + func_type = mfunc_parent.GetDecl().GetType(); + break; + } + } + + CompilerType callable_type = func_type.GetTypeTemplateArgument(0); + if (!callable_type) + return optional_info; + + // Now that the __func is a known type we can dig for the wrapped callable + ValueObjectSP callable; + { + // class __func<_Fp, _Alloc, _Rp(_ArgTypes...)> : __base<_Rp(_ArgTypes...)> { + // __alloc_func<_Fp, _Alloc, _Rp(_ArgTypes...)> __f_; + // } + // + // class __alloc_func<_Fp, _Ap, _Rp(_ArgTypes...)> { + // __compressed_pair<_Fp, _Ap> __f_; + // } + // + // class __compressed_pair : __compressed_pair_elem<_T1, 0>, + // __compressed_pair_elem<_T2, 1> { + // } + // + // struct __compressed_pair_elem { + // _Tp __value_; + // } + ValueObjectSP alloc_func = func_as_base->Cast(func_type); + if (!alloc_func) + return optional_info; + + ValueObjectSP pair = alloc_func->GetChildAtNamePath({"__f_", "__f_"}); + if (!pair) + return optional_info; + + if (callable_type.IsRecordType() && callable_type.GetNumFields() == 0) { + // callable_type is an empty class, and has been optimized away! Serve a dummy + callable = valobj_sp->CreateValueObjectFromAddress("__value_", + pair->GetLoadAddress(), + pair->GetExecutionContextRef(), + callable_type); + } else { + ValueObjectSP elem0 = pair->GetChildAtIndex(0); + if (!elem0) + return optional_info; + + callable = elem0->GetChildMemberWithName("__value_"); + if (!callable) + return optional_info; + } + } + + if (callable_type.IsFunctionPointerType()) { + addr_t target_load_addr = callable->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + + ModuleSP mod = func_method_addr.CalculateSymbolContextModule(); + + Address callable_addr; + if (!mod->ResolveFileAddress(target_load_addr, callable_addr)) + return optional_info; + + SymbolContext sc; + mod->ResolveSymbolContextForAddress(callable_addr, eSymbolContextSymbol | eSymbolContextLineEntry, sc); + + if (!sc.symbol) + return optional_info; + + return LibCppStdFunctionCallableInfo { + .callable_symbol = *sc.symbol, + .callable_address = sc.symbol->GetAddress(), + .callable_line_entry = sc.line_entry, + .callable_case = LibCppStdFunctionCallableCase::FreeOrMemberFunction + }; + } else if (callable_type.IsMemberFunctionPointerType()) { + // TODO: Member function's unsigned value comes back as invalid! I am guessing + // the ValueObject wants to let me know that this is not necessarily as simple + // as that.. I remember reading something in the Itanium ABI about member + // pointers taking up two pointers of space. Perhaps that's why it is not legal + // to read their underlying value raw? + + printf("Curious!"); ---------------- Michael137 wrote:
FYI, yes, it's represented as a pointer and possible offset: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#member-function-pointers https://github.com/llvm/llvm-project/pull/111892 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits