From 8385f86a8c2e369207b763b93dd54aa3e9197caa Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Wed, 7 Oct 2026 13:26:46 +0200 Subject: [PATCH 1/2] [cppinterop] Update vendored CppInterOp to ROOT-cppjit-2026-10-07 --- interpreter/CppInterOp/cppinterop-version.tag | 2 +- .../CppInterOp/lib/CppInterOp/CppInterOp.cpp | 154 ++++++++++++----- .../CppInterOp/lib/CppInterOp/CppInterOp.td | 17 +- .../lib/CppInterOp/InterpreterInfo.h | 9 +- .../CppInterOp/lib/CppInterOp/exports.ld | 33 ++++ .../scripts/browser_tests_safari.py | 2 +- .../unittests/CppInterOp/CAPITest.cpp | 3 + .../CppInterOp/FunctionReflectionTest.cpp | 85 +++++++++ .../unittests/CppInterOp/InterpreterTest.cpp | 16 +- .../CppInterOp/ScopeReflectionTest.cpp | 163 ++++++++++++++++++ .../CppInterOp/unittests/CppInterOp/Utils.h | 11 +- .../CppInterOp/VariableReflectionTest.cpp | 16 ++ 12 files changed, 460 insertions(+), 51 deletions(-) diff --git a/interpreter/CppInterOp/cppinterop-version.tag b/interpreter/CppInterOp/cppinterop-version.tag index 5d3a9941ae1e2..ee26a7af2c381 100644 --- a/interpreter/CppInterOp/cppinterop-version.tag +++ b/interpreter/CppInterOp/cppinterop-version.tag @@ -1 +1 @@ -b50e5ecbc511d9c48c33f9c446824a8566854ef3 +d23b04a52cf7f7aab7dc16d1c38b628085469c0a diff --git a/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp b/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp index ff9b0ba642010..30119c4282838 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp +++ b/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp @@ -80,6 +80,7 @@ #include "clang/Sema/TemplateDeduction.h" #include "llvm/ADT/APInt.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" @@ -720,6 +721,28 @@ static SourceLocation GetValidSLoc(Sema& semaRef) { return SM.getLocForStartOfFile(SM.getMainFileID()); } +namespace { +class clangSilent { +public: + explicit clangSilent(clang::DiagnosticsEngine& diag) + : fDiagEngine(&diag), + fOldDiagValue(fDiagEngine->getSuppressAllDiagnostics()) { + fDiagEngine->setSuppressAllDiagnostics(true); + } + + ~clangSilent() { fDiagEngine->setSuppressAllDiagnostics(fOldDiagValue); } + + clangSilent(const clangSilent&) = delete; + clangSilent& operator=(const clangSilent&) = delete; + clangSilent(clangSilent&&) = delete; + clangSilent& operator=(clangSilent&&) = delete; + +private: + clang::DiagnosticsEngine* fDiagEngine; + bool fOldDiagValue; +}; +} // namespace + // See TClingClassInfo::IsLoaded bool IsComplete(ConstDeclRef DRef) { INTEROP_TRACE(DRef); @@ -733,7 +756,21 @@ bool IsComplete(ConstDeclRef DRef) { clang::Sema& S = getSema(); SourceLocation fakeLoc = GetValidSLoc(S); compat::SynthesizingCodeRAII RAII(&getInterp()); - return INTEROP_RETURN(S.isCompleteType(fakeLoc, QT)); + // Query only: attempting the implicit instantiation of a specialization + // that can never be defined (its template is only declared) must neither + // print an error nor leave the diagnostics engine in an error state that + // poisons the next translation unit. Suppressed diagnostics still bump + // the engine's trap counters (DiagnosticsEngine::ProcessDiag increments + // TrapNumErrorsOccurred before the suppression check), and a prior + // operation may already have flagged an error, so carry a trap over the + // query and reset on any sign of diagnostics. + clangSilent Silence(S.getDiagnostics()); + bool HadError = S.getDiagnostics().hasErrorOccurred(); + clang::DiagnosticErrorTrap Trap(S.getDiagnostics()); + bool complete = S.isCompleteType(fakeLoc, QT); + if (HadError || Trap.hasErrorOccurred()) + S.getDiagnostics().Reset(/*soft=*/true); + return INTEROP_RETURN(complete); } if (const auto* CXXRD = dyn_cast(D)) @@ -1221,6 +1258,20 @@ DeclRef GetUnderlyingScope(ConstDeclRef DRef) { GetUnderlyingScopeImpl(unwrap(DRef)))); } +DeclRef GetTemplatedDecl(DeclRef DRef) { + INTEROP_TRACE(DRef); + auto* D = unwrap(DRef); + if (auto* CTSD = + llvm::dyn_cast_or_null(D)) + return INTEROP_RETURN( + CTSD->getSpecializedTemplate()->getTemplatedDecl()->getCanonicalDecl()); + if (auto* CTD = llvm::dyn_cast_or_null(D)) + return INTEROP_RETURN(CTD->getTemplatedDecl()->getCanonicalDecl()); + if (auto* FTD = llvm::dyn_cast_or_null(D)) + return INTEROP_RETURN(FTD->getTemplatedDecl()->getCanonicalDecl()); + return INTEROP_RETURN(DRef); +} + DeclRef GetScope(const std::string& name, ConstDeclRef parent) { INTEROP_TRACE(name, parent); // FIXME: GetScope should be replaced by a general purpose lookup @@ -1464,19 +1515,25 @@ DeclRef GetParentScope(ConstDeclRef DRef) { size_t GetNumBases(ConstDeclRef DRef) { INTEROP_TRACE(DRef); - const auto* D = unwrap(DRef); + // const_cast: completing the definition mutates the AST, but the base list + // is logically a read-only property of the class. Taking a mutable DeclRef + // would make unqualified calls in downstream wrappers ambiguous via ADL. + auto* D = const_cast(unwrap(DRef)); // hasDefinition() completes the redecl chain (dataPtr), which may // deserialize it; so does getNumBases() below. compat::SynthesizingCodeRAII RAII(&getInterp()); - if (const auto* CTSD = - llvm::dyn_cast_or_null(D)) + if (auto* CTSD = llvm::dyn_cast_or_null(D)) if (!CTSD->hasDefinition()) - compat::InstantiateClassTemplateSpecialization( - getInterp(), const_cast(CTSD)); - if (const auto* CXXRD = llvm::dyn_cast_or_null(D)) { - if (CXXRD->hasDefinition()) - return INTEROP_RETURN(CXXRD->getNumBases()); + compat::InstantiateClassTemplateSpecialization(getInterp(), CTSD); + if (llvm::isa_and_nonnull(D)) { + // A class known only as an autoload-annotated forward declaration + // (e.g., injected by a runtime ROOT dictionary) is not completed by + // hasDefinition(); resolve its definition through Sema first, or the + // base list is silently empty. + if (const auto* Def = llvm::dyn_cast_or_null( + unwrap(GetOrForceDefinition(D)))) + return INTEROP_RETURN(Def->getNumBases()); } return INTEROP_RETURN(0); @@ -1484,9 +1541,16 @@ size_t GetNumBases(ConstDeclRef DRef) { DeclRef GetBaseClass(ConstDeclRef DRef, size_t ibase) { INTEROP_TRACE(DRef, ibase); - const auto* D = unwrap(DRef); - const auto* CXXRD = llvm::dyn_cast_or_null(D); + // const_cast: see GetNumBases. + auto* D = const_cast(unwrap(DRef)); + if (!llvm::isa_and_nonnull(D)) + return INTEROP_RETURN(nullptr); + compat::SynthesizingCodeRAII RAII(&getInterp()); + // See GetNumBases: autoload-annotated forward declarations need Sema to + // resolve their definition before bases are visible. + const auto* CXXRD = llvm::dyn_cast_or_null( + unwrap(GetOrForceDefinition(D))); if (!CXXRD || CXXRD->getNumBases() <= ibase) return INTEROP_RETURN(nullptr); @@ -1849,6 +1913,14 @@ TypeRef GetFunctionReturnType(ConstFuncRef func) { needInstantiation = true; } + if (needInstantiation && FD->getTrailingRequiresClause()) { + compat::SynthesizingCodeRAII RAII(&getInterp()); + clang::ConstraintSatisfaction Satisfaction; + if (getSema().CheckFunctionConstraints(FD, Satisfaction) || + !Satisfaction.IsSatisfied) + needInstantiation = false; + } + if (needInstantiation) { // Lazy AST instantiation — logically const for the caller. InstantiateFunctionDefinition( @@ -3524,7 +3596,7 @@ TypeRef GetVariableType(ConstDeclRef var) { // address instead of re-evaluating and re-allocating on every call. static intptr_t MaterializeConstArrayValue(ASTContext& C, const VarDecl* VD, const APValue& Val, - std::map& Cache) { + llvm::DenseMap& Cache) { const clang::ArrayType* AT = Val.isArray() ? C.getAsArrayType(VD->getType()) : nullptr; const uint64_t EltBytes = @@ -3665,7 +3737,7 @@ intptr_t GetVariableOffset(compat::Interpreter& I, Decl* D, if (VD->hasInit() && (VD->isConstexpr() || VD->getType().isConstQualified())) { if (const APValue* val = VD->evaluateValue()) { - if (VD->getType()->isIntegralType(C)) + if (VD->getType()->isIntegralOrEnumerationType()) return (intptr_t)val->getInt().getRawData(); if (intptr_t ArrAddr = MaterializeConstArrayValue( C, VD, *val, getInterpInfo(&I).ConstArrayValueStore)) @@ -3693,7 +3765,7 @@ intptr_t GetVariableOffset(compat::Interpreter& I, Decl* D, !InitVD->getType().isVolatileQualified() && (InitVD->isConstexpr() || InitVD->getType().isConstQualified())) { if (const APValue* val = InitVD->evaluateValue()) { - if (InitVD->getType()->isIntegralType(C)) + if (InitVD->getType()->isIntegralOrEnumerationType()) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) return reinterpret_cast(val->getInt().getRawData()); if (intptr_t ArrAddr = MaterializeConstArrayValue( @@ -3725,7 +3797,7 @@ intptr_t GetVariableOffset(compat::Interpreter& I, Decl* D, if (VD->hasInit() && (VD->isConstexpr() || VD->getType().isConstQualified())) { if (const APValue* val = VD->evaluateValue()) { - if (VD->getType()->isIntegralType(C)) { + if (VD->getType()->isIntegralOrEnumerationType()) { return (intptr_t)val->getInt().getRawData(); } if (intptr_t ArrAddr = MaterializeConstArrayValue( @@ -4761,7 +4833,7 @@ void make_narg_ctor_with_return(const FunctionDecl* FD, const unsigned N, // public, and decltype/typeof sugar is spelled as its expression, whose // names may only resolve in the declaring scope. Builtins and compound types // keep the status quo. -static bool isWrapperSpellable(QualType QT) { +bool isWrapperSpellable(QualType QT) { for (const clang::Type* T = QT.getTypePtr();;) { if (isa(T)) return false; @@ -4785,6 +4857,23 @@ static bool isWrapperSpellable(QualType QT) { return true; } +// The type the wrapper spells for QT: the first reachable spelling when +// desugaring QT one step at a time, or QT unchanged when there is none. A +// typedef nested in a private helper class desugars to the type it names; +// a public typedef of a private class is reachable as written and stays, +// and its canonical type could not be spelled at all. +QualType getWrapperSpellableType(QualType QT) { + const ASTContext& C = getASTContext(); + for (QualType T = QT;;) { + if (isWrapperSpellable(T)) + return T; + QualType Next = T.getSingleStepDesugaredType(C); + if (Next == T) + return QT; + T = Next; + } +} + void make_narg_call_with_return(compat::Interpreter& I, const FunctionDecl* FD, const unsigned N, const std::string& class_name, std::ostringstream& buf, int indent_level) { @@ -4813,13 +4902,7 @@ void make_narg_call_with_return(compat::Interpreter& I, const FunctionDecl* FD, make_narg_ctor_with_return(FD, N, class_name, buf, indent_level); return; } - QualType QT = FD->getReturnType(); - // A return type spelled through a non-public path (e.g. a typedef nested - // in a private helper class) cannot be named in the wrapper; fall back to - // the canonical type when that one is spellable (the inverse also exists: - // a public typedef of a private class must keep its sugared name). - if (!isWrapperSpellable(QT) && isWrapperSpellable(QT.getCanonicalType())) - QT = QT.getCanonicalType(); + QualType QT = getWrapperSpellableType(FD->getReturnType()); if (QT->isVoidType()) { std::ostringstream typedefbuf; std::ostringstream callbuf; @@ -6065,22 +6148,6 @@ void GetIncludePaths(std::vector& IncludePaths, bool withSystem, return INTEROP_VOID_RETURN(); } -namespace { -class clangSilent { -public: - clangSilent(clang::DiagnosticsEngine& diag) : fDiagEngine(diag) { - fOldDiagValue = fDiagEngine.getSuppressAllDiagnostics(); - fDiagEngine.setSuppressAllDiagnostics(true); - } - - ~clangSilent() { fDiagEngine.setSuppressAllDiagnostics(fOldDiagValue); } - -protected: - clang::DiagnosticsEngine& fDiagEngine; - bool fOldDiagValue; -}; -} // namespace - static int Declare(compat::Interpreter& I, const char* code, bool silent) { // Trap diagnostics on both paths: I.declare's rc is 0 even when // Parse recovered from emitted errors, so callers need the trap to @@ -6107,7 +6174,14 @@ int Declare(const char* code, bool silent) { int Process(const char* code) { INTEROP_TRACE(code); - return INTEROP_RETURN(getInterp().process(code)); + // Trap diagnostics like Declare: process's rc is kSuccess even when the + // parse recovered from emitted errors or a wrapped expression failed at + // run time, so callers cannot rely on it alone. + clang::DiagnosticsEngine& Diag = getSema().getDiagnostics(); + clang::DiagnosticErrorTrap Trap(Diag); + if (getInterp().process(code) != compat::Interpreter::kSuccess) + return INTEROP_RETURN(1); + return INTEROP_RETURN(Trap.hasErrorOccurred() ? 1 : 0); } // Classify the QualType of a successfully-evaluated value into a diff --git a/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.td b/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.td index 9681c61ce9358..6aba48d089136 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.td +++ b/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.td @@ -451,6 +451,16 @@ underlying decl is not a class it returns the input unchanged.}]; ]; } +def GetTemplatedDecl : CppInterOpAPI { + let Doc = [{Returns the templated (pattern) declaration of the +template the given declaration belongs to.}]; + + let ReturnType = "DeclRef"; + let Args = [ + Arg<"DeclRef", "DRef"> + ]; +} + def InstantiateTemplate : CppInterOpAPI { let Doc = [{Builds a template instantiation for a given templated declaration. Offers a single interface for instantiation of class, function and @@ -703,7 +713,9 @@ def IsVirtualMethod : CppInterOpAPI { def GetNumBases : CppInterOpAPI { let Doc = [{Gets the number of Base Classes for the Derived Class that -is passed as a parameter.}]; +is passed as a parameter. Bases are a property of the class definition: if +only a declaration is loaded, the definition is first forced into the AST +(see GetOrForceDefinition).}]; let ReturnType = "size_t"; let Args = [ @@ -725,7 +737,8 @@ def GetBaseClass : CppInterOpAPI { let Doc = [{Gets a specific Base Class using its index. Typically GetNumBases() is used to get the number of Base Classes, and then that number can be used to iterate through the index value to get each specific -base class.}]; +base class. As for GetNumBases(), the definition is forced into the AST if +only a declaration is loaded.}]; let ReturnType = "DeclRef"; let Args = [ diff --git a/interpreter/CppInterOp/lib/CppInterOp/InterpreterInfo.h b/interpreter/CppInterOp/lib/CppInterOp/InterpreterInfo.h index 006521d24c4e3..b24c0b8bbcc2b 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/InterpreterInfo.h +++ b/interpreter/CppInterOp/lib/CppInterOp/InterpreterInfo.h @@ -19,13 +19,14 @@ #include "ErrorInternal.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" #include "clang/AST/Type.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" #include #include -#include #include #include #include @@ -39,13 +40,13 @@ struct InterpreterInfo { llvm::StringMap BuiltinMap; // Per-interpreter wrapper caches. Keyed on AST nodes that belong to this // interpreter, so the caches must be destroyed together with it. - std::map WrapperStore; - std::map DtorWrapperStore; + llvm::DenseMap WrapperStore; + llvm::DenseMap DtorWrapperStore; // Constant-array initializers materialized by GetVariableOffset, keyed on // the AST node so each variable is laid out once (stable address per // query, no bump-allocator growth). A 0 entry records a deterministic // materialization failure. - std::map ConstArrayValueStore; + llvm::DenseMap ConstArrayValueStore; // A deque keeps element addresses stable so DiagnosticRef::data // survives push_back. std::deque StoredDiags; diff --git a/interpreter/CppInterOp/lib/CppInterOp/exports.ld b/interpreter/CppInterOp/lib/CppInterOp/exports.ld index a799ee14705aa..e7241a77e19ca 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/exports.ld +++ b/interpreter/CppInterOp/lib/CppInterOp/exports.ld @@ -58,3 +58,36 @@ -Wl,--export=_ZN5clang11Interpreter6createENSt3__210unique_ptrINS_16CompilerInstanceENS1_14default_deleteIS3_EEEENS2_INS_26IncrementalExecutorBuilderENS4_IS7_EEEE -Wl,--export=_ZNK5clang7TagDecl13getDefinitionEv -Wl,--export=_ZNK5clang12FunctionDecl9isDefinedERPKS0_b +-Wl,--export=_ZN5clang10ASTContext17setExternalSourceEN4llvm18IntrusiveRefCntPtrINS_17ExternalASTSourceEEE +-Wl,--export=_ZN5clang17ExternalASTSource10PrintStatsEv +-Wl,--export=_ZN5clang17ExternalASTSource12CompleteTypeEPNS_17ObjCInterfaceDeclE +-Wl,--export=_ZN5clang17ExternalASTSource12ReadCommentsEv +-Wl,--export=_ZN5clang17ExternalASTSource15GetExternalDeclENS_12GlobalDeclIDE +-Wl,--export=_ZN5clang17ExternalASTSource16layoutRecordTypeEPKNS_10RecordDeclERyS4_RN4llvm8DenseMapIPKNS_9FieldDeclEyNS5_12DenseMapInfoIS9_vEENS5_6detail12DenseMapPairIS9_yEEEERNS6_IPKNS_13CXXRecordDeclENS_9CharUnitsENSA_ISJ_vEENSD_ISJ_SK_EEEESO_ +-Wl,--export=_ZN5clang17ExternalASTSource19CompleteRedeclChainEPKNS_4DeclE +-Wl,--export=_ZN5clang17ExternalASTSource19FindFileRegionDeclsENS_6FileIDEjjRN4llvm15SmallVectorImplIPNS_4DeclEEE +-Wl,--export=_ZN5clang17ExternalASTSource19GetExternalDeclStmtEy +-Wl,--export=_ZN5clang17ExternalASTSource19GetExternalSelectorEj +-Wl,--export=_ZN5clang17ExternalASTSource19getSourceDescriptorEj +-Wl,--export=_ZN5clang17ExternalASTSource20StartTranslationUnitEPNS_11ASTConsumerE +-Wl,--export=_ZN5clang17ExternalASTSource20StartedDeserializingEv +-Wl,--export=_ZN5clang17ExternalASTSource21FinishedDeserializingEv +-Wl,--export=_ZN5clang17ExternalASTSource22hasExternalDefinitionsEPKNS_4DeclE +-Wl,--export=_ZN5clang17ExternalASTSource23GetNumExternalSelectorsEv +-Wl,--export=_ZN5clang17ExternalASTSource23completeVisibleDeclsMapEPKNS_11DeclContextE +-Wl,--export=_ZN5clang17ExternalASTSource24FindExternalLexicalDeclsEPKNS_11DeclContextEN4llvm12function_refIFbNS_4Decl4KindEEEERNS4_15SmallVectorImplIPS6_EE +-Wl,--export=_ZN5clang17ExternalASTSource27LoadExternalSpecializationsEPKNS_4DeclEN4llvm8ArrayRefINS_16TemplateArgumentEEE +-Wl,--export=_ZN5clang17ExternalASTSource27LoadExternalSpecializationsEPKNS_4DeclEb +-Wl,--export=_ZN5clang17ExternalASTSource28GetExternalCXXBaseSpecifiersEy +-Wl,--export=_ZN5clang17ExternalASTSource29wasThisDeclarationADefinitionEPKNS_12FunctionDeclE +-Wl,--export=_ZN5clang17ExternalASTSource2IDE +-Wl,--export=_ZN5clang17ExternalASTSource30FindExternalVisibleDeclsByNameEPKNS_11DeclContextENS_15DeclarationNameES3_ +-Wl,--export=_ZN5clang17ExternalASTSource30GetExternalCXXCtorInitializersEy +-Wl,--export=_ZN5clang18ExternalSemaSource14ReadMethodPoolENS_8SelectorE +-Wl,--export=_ZN5clang18ExternalSemaSource19ReadKnownNamespacesERN4llvm15SmallVectorImplIPNS_13NamespaceDeclEEE +-Wl,--export=_ZN5clang18ExternalSemaSource20ReadUndefinedButUsedERN4llvm9MapVectorIPNS_9NamedDeclENS_14SourceLocationENS1_8DenseMapIS4_jNS1_12DenseMapInfoIS4_vEENS1_6detail12DenseMapPairIS4_jEEEENS1_11SmallVectorINSt3__24pairIS4_S5_EELj0EEEEE +-Wl,--export=_ZN5clang18ExternalSemaSource23updateOutOfDateSelectorENS_8SelectorE +-Wl,--export=_ZN5clang18ExternalSemaSource2IDE +-Wl,--export=_ZN5clang18ExternalSemaSource32ReadMismatchingDeleteExpressionsERN4llvm9MapVectorIPNS_9FieldDeclENS1_11SmallVectorINSt3__24pairINS_14SourceLocationEbEELj4EEENS1_8DenseMapIS4_jNS1_12DenseMapInfoIS4_vEENS1_6detail12DenseMapPairIS4_jEEEENS5_INS7_IS4_SA_EELj0EEEEE +-Wl,--export=_ZN5clang18ExternalSemaSourceD2Ev +-Wl,--export=_ZNK5clang17ExternalASTSource20getMemoryBufferSizesERNS0_17MemoryBufferSizesE diff --git a/interpreter/CppInterOp/scripts/browser_tests_safari.py b/interpreter/CppInterOp/scripts/browser_tests_safari.py index 4210c22a5636a..d459f6a8e971e 100644 --- a/interpreter/CppInterOp/scripts/browser_tests_safari.py +++ b/interpreter/CppInterOp/scripts/browser_tests_safari.py @@ -5,5 +5,5 @@ driver = webdriver.Safari() driver.get("http://localhost:6931/" + sys.argv[1]) -time.sleep(60) +time.sleep(15) driver.quit() diff --git a/interpreter/CppInterOp/unittests/CppInterOp/CAPITest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/CAPITest.cpp index aba59c7099828..97bc7e5229eda 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/CAPITest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/CAPITest.cpp @@ -183,6 +183,9 @@ TYPED_TEST(CppInterOpTest, CAPI_EnumFunctions) { // Exercises the hand-written cppinterop_Evaluate C bridge. The C++ overload // returning Cpp::Box has no C wrapper (NoCWrapper); C clients use this form. TYPED_TEST(CppInterOpTest, CAPI_Evaluate) { +#ifdef EMSCRIPTEN_STATIC_LIBRARY + GTEST_SKIP() << "Test fails for Emscipten static library build"; +#endif if (TypeParam::isOutOfProcess) GTEST_SKIP() << "Evaluate not supported in OOP JIT"; TestFixture::CreateInterpreter(); diff --git a/interpreter/CppInterOp/unittests/CppInterOp/FunctionReflectionTest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/FunctionReflectionTest.cpp index 38b35ae27c323..80fdec03f0f12 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -1,6 +1,7 @@ #include "Utils.h" #include "CppInterOp/CppInterOp.h" +#include "../../lib/CppInterOp/Unwrap.h" #include "clang/AST/ASTContext.h" #include "clang/AST/PrettyPrinter.h" @@ -882,6 +883,50 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_GetFunctionReturnType) { "double"); } +TYPED_TEST(CPPINTEROP_TEST_MODE, + FunctionReflection_GetFunctionReturnTypeUnsatisfiedConstraint) { + TestFixture::CreateInterpreter({"-std=c++20"}); + Cpp::Declare(R"( + template struct ConstrainedAuto { + T m_value; + auto size() const requires requires { m_value.size(); } { + return m_value.size(); + } + decltype(auto) key() const requires requires { m_value.key(); } { + return m_value.key(); + } + }; + struct WithKey { int key() const { return 1; } }; + )"); + + ASTContext& C = Interp->getCI()->getASTContext(); + std::vector int_arg = {C.IntTy.getAsOpaquePtr()}; + Cpp::DeclRef inst = + Cpp::InstantiateTemplate(Cpp::GetNamed("ConstrainedAuto"), int_arg); + ASSERT_TRUE(inst); + + testing::internal::CaptureStderr(); + Cpp::DeclRef size = Cpp::GetNamed("size", inst); + Cpp::DeclRef key = Cpp::GetNamed("key", inst); + ASSERT_TRUE(size); + ASSERT_TRUE(key); + Cpp::GetFunctionReturnType(Cpp::FuncRef{size.data}); + Cpp::GetFunctionReturnType(Cpp::FuncRef{key.data}); + std::string err = testing::internal::GetCapturedStderr(); + EXPECT_EQ(err, ""); + EXPECT_FALSE(Cpp::unwrap(size)->isInvalidDecl()); + EXPECT_FALSE(Cpp::unwrap(key)->isInvalidDecl()); + + std::vector with_key_arg = { + Cpp::GetTypeFromScope(Cpp::GetNamed("WithKey")).data}; + Cpp::DeclRef inst2 = + Cpp::InstantiateTemplate(Cpp::GetNamed("ConstrainedAuto"), with_key_arg); + ASSERT_TRUE(inst2); + EXPECT_EQ(Cpp::GetTypeAsString(Cpp::GetFunctionReturnType( + Cpp::FuncRef{Cpp::GetNamed("key", inst2).data})), + "int"); +} + TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsAllocator) { std::vector Decls; std::string code = R"( @@ -5537,6 +5582,46 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_PrivateTypedefReturn) { Cpp::JitCall::kGenericCall); } +// The private path desugars to a public typedef of a private class: that +// typedef is spellable, the canonical type is not. +TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_PrivateTypedefChainReturn) { +#ifdef EMSCRIPTEN + GTEST_SKIP() << "Test fails for Emscripten builds"; +#endif + if (TypeParam::isOutOfProcess) + GTEST_SKIP() << "Test fails for OOP JIT builds"; + + std::vector Decls; + std::string code = R"( + struct Res { + private: + struct PC { int v = 5; }; + public: + typedef PC PP; + private: + struct Helper { typedef PP Iterator_t; }; + public: + Helper::Iterator_t get() { return {}; } + }; + )"; + + GetAllTopLevelDecls(code, Decls, /*filter_implicitGenerated=*/false, + /*interpreter_args=*/{"-include", "new"}); + ASSERT_EQ(Decls.size(), 1); + auto Fns = Cpp::GetFunctionsUsingName(Decls[0], "get"); + ASSERT_EQ(Fns.size(), 1); + Cpp::JitCall JC = Cpp::MakeFunctionCallable(Fns[0]); + ASSERT_EQ(JC.getKind(), Cpp::JitCall::kGenericCall); + + Cpp::ObjectRef obj = Cpp::Construct(Decls[0]); + struct { + int v; + } result = {0}; + JC.Invoke(&result, {}, obj.data); + EXPECT_EQ(result.v, 5); + Cpp::Destruct(obj, Decls[0]); +} + // decltype sugar is spelled as its expression: a call in it keeps its // qualifier as written (detail::helper), which does not resolve at file scope; // the canonical type is spellable. A typedef of a class type underneath keeps diff --git a/interpreter/CppInterOp/unittests/CppInterOp/InterpreterTest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/InterpreterTest.cpp index 3c0ab07786377..8b756025732db 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/InterpreterTest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/InterpreterTest.cpp @@ -427,6 +427,12 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, Interpreter_Process) { EXPECT_FALSE(Cpp::Process("error_here;") == 0); // Linker/JIT error. EXPECT_FALSE(Cpp::Process("int f(); int res = f();") == 0); +#ifndef CPPINTEROP_USE_CLING + // Failed static initializer of a raw declaration. Cling reports it only + // with root-project/root#23610. + EXPECT_FALSE(Cpp::Process("extern \"C\" int unresolved_fn();" + "int unresolved_r = unresolved_fn();") == 0); +#endif } // libc_nonshared.a symbols are per-module and invisible to dlsym; jitted @@ -809,7 +815,15 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, Interpreter_DISABLED_DetectResourceDir) { GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif TestFixture::CreateInterpreter(); - EXPECT_STRNE(Cpp::DetectResourceDir().c_str(), Cpp::GetResourceDir()); + // The clang on PATH yields either nothing or a resource dir of this + // library's clang major version. That dir may be the interpreter's own + // (same clang, or CreateInterpreter fell back to this detection), so the + // two cannot be required to differ. + std::string Detected = Cpp::DetectResourceDir(); + if (!Detected.empty()) { + EXPECT_STREQ(llvm::sys::path::filename(Detected).str().c_str(), + CLANG_VERSION_MAJOR_STRING); + } llvm::SmallString<256> Clang(LLVM_BINARY_DIR); llvm::sys::path::append(Clang, "bin", "clang"); diff --git a/interpreter/CppInterOp/unittests/CppInterOp/ScopeReflectionTest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/ScopeReflectionTest.cpp index 09c3aacb875bc..e4ba31e13f61e 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/ScopeReflectionTest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/ScopeReflectionTest.cpp @@ -1,5 +1,7 @@ #include "Utils.h" +#include "../../lib/CppInterOp/Unwrap.h" + #include "CppInterOp/CppInterOp.h" #include "clang/AST/ASTContext.h" @@ -8,15 +10,21 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/GlobalDecl.h" #include "clang/AST/Type.h" +#include "clang/Basic/Diagnostic.h" #include "clang/Basic/Version.h" #include "clang/Frontend/CompilerInstance.h" +#include "clang/Sema/ExternalSemaSource.h" #include "clang/Sema/Sema.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" + #include "gtest/gtest.h" #include +#include #include #include +#include using namespace TestUtils; using namespace llvm; @@ -289,6 +297,51 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_IsComplete) { EXPECT_FALSE(Cpp::IsComplete(nullptr)); } +TYPED_TEST(CPPINTEROP_TEST_MODE, + ScopeReflection_IsCompleteSilentFailedInstantiation) { + // IsComplete() runs isCompleteType(), which attempts implicit + // instantiation. A failing attempt must neither print an error nor advance + // the engine's trap counters (suppressed diagnostics still increment + // TrapNumErrorsOccurred), and it must leave the interpreter usable. + TestFixture::CreateInterpreter(); + // The specializations are only pointed to, so they are declared but not + // instantiated yet: the instantiation attempt happens inside IsComplete(). + ASSERT_EQ(Interp->declare(R"( + template struct FwdOnly; + template struct Broken { typename T::missing_type m; }; + FwdOnly* g_pf; + Broken* g_pb; + )"), + 0); + + auto spec_of = [&](const char* var) { + Cpp::DeclRef V = Cpp::GetNamed(var); + if (!V) + return Cpp::DeclRef{}; + return Cpp::GetScopeFromType( + Cpp::GetPointeeType(Cpp::GetVariableType(V))); + }; + Cpp::DeclRef FwdSpec = spec_of("g_pf"); + Cpp::DeclRef BrokenSpec = spec_of("g_pb"); + ASSERT_TRUE(FwdSpec); + ASSERT_TRUE(BrokenSpec); + + auto& Diags = Interp->getCI()->getSema().getDiagnostics(); + + // Declaration-only template: no definition exists. + EXPECT_FALSE(Cpp::IsComplete(FwdSpec)); + + // A definition exists but its instantiation fails. IsComplete()'s result + // follows clang's semantics for invalid specializations; what IsComplete + // must guarantee is that the query leaves no trace. + clang::DiagnosticErrorTrap Trap(Diags); + Cpp::IsComplete(BrokenSpec); + EXPECT_FALSE(Trap.hasErrorOccurred()); + + // Nothing above left the interpreter in an error state. + EXPECT_EQ(Interp->declare("int valid_after_failed_instantiation = 1;"), 0); +} + TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_GetOrForceDefinition) { std::vector Decls; std::string code = R"( @@ -437,6 +490,33 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_IsTemplateSpecialization) { Cpp::GetScopeFromType(Cpp::GetVariableType(Decls[1])))); } +TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_GetTemplatedDecl) { + std::vector Decls; + std::string code = R"( + template struct MyTmpl { T value; }; + MyTmpl inst_int; + MyTmpl inst_double; + template T func_template(T t) { return t; } + )"; + + GetAllTopLevelDecls(code, Decls); + ASSERT_EQ(Decls.size(), 4U); + + auto Pattern = Cpp::GetTemplatedDecl(Decls[0]); + auto GetScopeOfVar = [](Decl* D) { + return Cpp::GetScopeFromType(Cpp::GetVariableType(D)); + }; + + EXPECT_EQ(Cpp::GetTemplatedDecl(GetScopeOfVar(Decls[1])), Pattern); + EXPECT_EQ(Cpp::GetTemplatedDecl(GetScopeOfVar(Decls[2])), Pattern); + + ASSERT_TRUE(Cpp::GetTemplatedDecl(Decls[3])); + ASSERT_TRUE(Cpp::IsFunction((Cpp::GetTemplatedDecl(Decls[3])))); + ASSERT_NE(Cpp::GetTemplatedDecl(Decls[3]), Decls[3]); + + EXPECT_FALSE(Cpp::GetTemplatedDecl(nullptr)); +} + TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_IsTypedefed) { std::vector Decls; std::string code = R"( @@ -1128,6 +1208,89 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_GetBaseClass) { EXPECT_EQ(Cpp::GetCompleteName(A_class), "A"); } +namespace { +// Mimics ROOT's runtime-dictionary autoloading: the interpreter knows a +// class only as a forward declaration (as injected by a non-ACLiC +// dictionary), and the definition is materialized lazily through the +// external source when Sema asks to complete the type. Reproduces +// https://github.com/cms-analysis/HiggsAnalysis-CombinedLimit/issues/1289, +// where cppjit built the Python proxy from the incomplete declaration and +// silently dropped the entire base hierarchy. +class LazyDefinitionSource : public clang::ExternalSemaSource { +public: + explicit LazyDefinitionSource(std::map Defs) + : Pending(std::move(Defs)) {} + + void CompleteType(clang::TagDecl* Tag) override { + auto It = Pending.find(Tag->getName().str()); + if (It == Pending.end()) + return; + // Erase before declaring: Declare re-enters Sema, which may ask again. + std::string Code = std::move(It->second); + Pending.erase(It); + Cpp::Declare(Code.c_str()); + } + +private: + std::map Pending; +}; + +TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_GetBasesOfLazilyDefinedClass) { + TestFixture::CreateInterpreter(); + Cpp::Declare("class LateBoundBase {};" + "class LateBoundDerived;" + "class LateBoundOther;"); + Cpp::DeclRef Derived = Cpp::GetNamed("LateBoundDerived"); + Cpp::DeclRef Other = Cpp::GetNamed("LateBoundOther"); + ASSERT_TRUE(Derived); + ASSERT_TRUE(Other); + ASSERT_FALSE(Cpp::IsComplete(Derived)); + ASSERT_FALSE(Cpp::IsComplete(Other)); + + // RequireCompleteTypeImpl consults the ASTContext's external source, not + // Sema's; the IntrusiveRefCntPtr hands ownership to the context, which + // releases the source at interpreter teardown. + ASTContext& Ctx = Interp->getCI()->getASTContext(); + if (Ctx.getExternalSource()) + GTEST_SKIP() << "Interpreter already has an external source installed"; + Ctx.setExternalSource(llvm::IntrusiveRefCntPtr( + new LazyDefinitionSource( + {{"LateBoundDerived", + "class LateBoundDerived : public LateBoundBase {};"}, + {"LateBoundOther", + "class LateBoundOther : public LateBoundBase {};"}}))); + + // Sema only routes a type to the external source when the declaration is + // marked as externally stored (as deserialized autoload stubs are). The + // AST drops that mark once the external source was asked for the lexical + // contents, which may happen while another class is materialized, so + // mark each class right before it is first queried. + auto MarkAsAutoloadStub = [](Cpp::DeclRef Fwd) { + for (auto* RD : Cpp::unwrap(Fwd)->redecls()) + RD->setHasExternalLexicalStorage(); + }; + + // Without resolving the definition through Sema, both queries operated + // on the forward declaration and saw an empty base list. Each class is + // first queried through a different entry point, so that each of them + // has to complete the type on its own. + MarkAsAutoloadStub(Derived); + EXPECT_EQ(Cpp::GetNumBases(Derived), 1U); + EXPECT_TRUE(Cpp::IsComplete(Derived)); + Cpp::DeclRef Base = Cpp::GetBaseClass(Derived, 0); + ASSERT_TRUE(Base); + EXPECT_EQ(Cpp::GetQualifiedName(Base), "LateBoundBase"); + + MarkAsAutoloadStub(Other); + Base = Cpp::GetBaseClass(Other, 0); + ASSERT_TRUE(Base); + EXPECT_EQ(Cpp::GetQualifiedName(Base), "LateBoundBase"); + EXPECT_TRUE(Cpp::IsComplete(Other)); + EXPECT_EQ(Cpp::GetNumBases(Other), 1U); + EXPECT_FALSE(Cpp::GetBaseClass(Other, 1)); +} +} // namespace + TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_IsSubclass) { std::vector Decls; std::string code = R"( diff --git a/interpreter/CppInterOp/unittests/CppInterOp/Utils.h b/interpreter/CppInterOp/unittests/CppInterOp/Utils.h index 8804bd0e6c178..72579d310a9ad 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/Utils.h +++ b/interpreter/CppInterOp/unittests/CppInterOp/Utils.h @@ -62,9 +62,9 @@ void GetAllSubDecls(clang::Decl* D, std::vector& SubDecls, bool IsTargetX86(); -// OOP-JIT is incompatible with two configurations and is excluded +// OOP-JIT is incompatible with three configurations and is excluded // from the typed-test matrix wholesale (rather than per-test) when -// either applies: +// any applies: // * Any sanitizer (ASan/MSan/TSan): upstream LLVM ORC trips // `Resolving symbol with incorrect flags` // (`llvm/lib/ExecutionEngine/Orc/Core.cpp`, the JITSymbolFlags @@ -74,6 +74,10 @@ bool IsTargetX86(); // mismatch. In-process JIT is unaffected. // * Emscripten: the OOP path requires fork/exec + a separate // executor binary, which the wasm runtime doesn't provide. +// * Cling: the backend has no out-of-process executor and does not +// know `--use-oop-jit`; the flag reaches cling as an unknown +// argument and the variant runs in-process, repeating every +// InProcessJIT test under the other name. #if defined(__has_feature) # if __has_feature(address_sanitizer) || \ __has_feature(memory_sanitizer) || \ @@ -87,6 +91,9 @@ bool IsTargetX86(); #if defined(__EMSCRIPTEN__) # define CPPINTEROP_OOP_DISABLED 1 #endif +#if defined(CPPINTEROP_USE_CLING) +# define CPPINTEROP_OOP_DISABLED 1 +#endif // Address-sanitized build (gcc spells it __SANITIZE_ADDRESS__, clang // __has_feature). diff --git a/interpreter/CppInterOp/unittests/CppInterOp/VariableReflectionTest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/VariableReflectionTest.cpp index 156b0f7f90698..74e5a4fd1e295 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/VariableReflectionTest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/VariableReflectionTest.cpp @@ -458,6 +458,22 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, } Cpp::GetVariableOffset(Cpp::GetNamed("kBit", klass)); // outcome varies + + Cpp::Declare(R"( + enum InClassEnum { kE0, kE1 = 5 }; + enum class InClassScopedEnum : long { kS0, kS1 = -3 }; + struct InClassConstEnumInit { + static const InClassEnum kEnum = kE1; + static const InClassScopedEnum kScoped = InClassScopedEnum::kS1; + }; + )"); + EXPECT_TRUE(Cpp::GetNamed("InClassConstEnumInit")); + + EXPECT_TRUE(Cpp::GetVariableOffset(Cpp::GetNamed("kEnum", Cpp::GetNamed("InClassConstEnumInit")))); + EXPECT_EQ(Cpp::GetEnumConstantValue(Cpp::GetNamed("kE1")), 5); + + EXPECT_TRUE(Cpp::GetVariableOffset(Cpp::GetNamed("kScoped", Cpp::GetNamed("InClassConstEnumInit")))); + EXPECT_EQ(Cpp::GetEnumConstantValue(Cpp::GetNamed("kS1", Cpp::GetNamed("InClassScopedEnum"))), -3); // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast, // performance-no-int-to-ptr) } From a1595455a1712a515114e1913184182087784527 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Tue, 6 Oct 2026 15:50:22 +0200 Subject: [PATCH 2/2] [cppjit] Update vendored cppjit to ROOT-2026-10-06 --- bindings/pyroot/cppjit/cppjit-version.tag | 2 +- bindings/pyroot/cppjit/docs/ReleaseNotes.md | 3 +- .../cppjit/python/cppjit/_cpython_cppjit.py | 5 +- bindings/pyroot/cppjit/python/cppjit/ll.py | 25 ++++ bindings/pyroot/cppjit/src/cpyrt/API.cxx | 4 +- .../cppjit/src/cpyrt/CPPClassMethod.cxx | 5 +- .../cppjit/src/cpyrt/CPPConstructor.cxx | 4 +- .../pyroot/cppjit/src/cpyrt/CPPDataMember.cxx | 14 +- bindings/pyroot/cppjit/src/cpyrt/CPPEnum.cxx | 32 ++-- .../cppjit/src/cpyrt/CPPExcInstance.cxx | 6 +- .../pyroot/cppjit/src/cpyrt/CPPFunction.cxx | 5 +- .../pyroot/cppjit/src/cpyrt/CPPInstance.cxx | 86 ++++++----- .../pyroot/cppjit/src/cpyrt/CPPMethod.cxx | 85 +++++------ bindings/pyroot/cppjit/src/cpyrt/CPPMethod.h | 2 + .../pyroot/cppjit/src/cpyrt/CPPOperator.cxx | 2 +- .../pyroot/cppjit/src/cpyrt/CPPOverload.cxx | 35 +++-- bindings/pyroot/cppjit/src/cpyrt/CPPScope.cxx | 57 ++++--- .../pyroot/cppjit/src/cpyrt/Converters.cxx | 111 +++++++------- .../pyroot/cppjit/src/cpyrt/CustomPyTypes.cxx | 9 +- .../pyroot/cppjit/src/cpyrt/Dispatcher.cxx | 4 +- .../pyroot/cppjit/src/cpyrt/Executors.cxx | 48 +++--- .../pyroot/cppjit/src/cpyrt/LowLevelViews.cxx | 112 +++++++------- .../cppjit/src/cpyrt/MemoryRegulator.cxx | 2 +- .../pyroot/cppjit/src/cpyrt/ProxyWrappers.cxx | 20 +-- .../pyroot/cppjit/src/cpyrt/PyException.cxx | 6 +- .../pyroot/cppjit/src/cpyrt/PyObjectDir27.inc | 44 +++--- .../pyroot/cppjit/src/cpyrt/PyStrings.cxx | 6 +- .../pyroot/cppjit/src/cpyrt/Pythonize.cxx | 63 ++++---- .../pyroot/cppjit/src/cpyrt/TemplateProxy.cxx | 41 +++-- .../cppjit/src/cpyrt/TupleOfInstances.cxx | 95 ++++++------ bindings/pyroot/cppjit/src/cpyrt/Utility.cxx | 109 +++++++------- bindings/pyroot/cppjit/src/cpyrt/cpyrt.h | 60 +------- .../pyroot/cppjit/src/cpyrt/cpyrtModule.cxx | 127 +++++++++++++--- .../cppjit/src/interop/interop_wrapper.cxx | 30 +++- bindings/pyroot/cppjit/test/test_lowlevel.py | 141 ++++++++++++++++++ .../pyroot/cppjit/test/test_regression.py | 17 +++ 36 files changed, 825 insertions(+), 592 deletions(-) diff --git a/bindings/pyroot/cppjit/cppjit-version.tag b/bindings/pyroot/cppjit/cppjit-version.tag index 7905b105a9b02..402a8ea0b7ba9 100644 --- a/bindings/pyroot/cppjit/cppjit-version.tag +++ b/bindings/pyroot/cppjit/cppjit-version.tag @@ -1 +1 @@ -e0945224e5b84336d570872ea47989457dae9d8e +0f6cc367f2f7f169c6a09ffaa97169230e52b299 diff --git a/bindings/pyroot/cppjit/docs/ReleaseNotes.md b/bindings/pyroot/cppjit/docs/ReleaseNotes.md index 569547556c29a..e83b33abb40a0 100644 --- a/bindings/pyroot/cppjit/docs/ReleaseNotes.md +++ b/bindings/pyroot/cppjit/docs/ReleaseNotes.md @@ -28,7 +28,8 @@ This release supports Python 3.12-3.14 and LLVM 21-22. ## Other changes -- ... +- `import cppjit` raises a `RuntimeError` when the C++ standard headers do + not parse, for example on a host without a C++ toolchain. ## Contributors diff --git a/bindings/pyroot/cppjit/python/cppjit/_cpython_cppjit.py b/bindings/pyroot/cppjit/python/cppjit/_cpython_cppjit.py index cb4e4ec52525f..f41de438c0367 100644 --- a/bindings/pyroot/cppjit/python/cppjit/_cpython_cppjit.py +++ b/bindings/pyroot/cppjit/python/cppjit/_cpython_cppjit.py @@ -41,7 +41,10 @@ def _preload_backend_library(): libdir = os.path.dirname(os.path.dirname(spec.origin)) lib = ctypes.CDLL(os.path.join(libdir, "libcpyrt.dll")) if not lib.LoadCppInterOp(): - raise RuntimeError("failed to load CppInterOp (LoadCppInterOp returned 0)") + raise RuntimeError( + "cppjit could not initialize its C++ interpreter, see the diagnostic " + "above (LoadCppInterOp failed)" + ) return lib diff --git a/bindings/pyroot/cppjit/python/cppjit/ll.py b/bindings/pyroot/cppjit/python/cppjit/ll.py index 9621e9f20ddc5..4a9895eb95528 100644 --- a/bindings/pyroot/cppjit/python/cppjit/ll.py +++ b/bindings/pyroot/cppjit/python/cppjit/ll.py @@ -25,6 +25,7 @@ "free", "array_new", "array_delete", + "bind_value", "signals_as_exception", "set_signals_as_exception", "FatalError", @@ -46,6 +47,30 @@ def argc(): return len(sys.argv) +def bind_value(type_name, address, dims=None): + """Read a value of the C++ type `type_name` from `address`. + + Hands back the object that cppjit's converters would produce reading the + same memory themselves: a bound proxy for a class type, a Python value + for a builtin. Meant for code that has a type name and an address in hand + but no C++ entity to read them from, such as a framework exposing its own + data description. + + `address` is an integer, as returned by `addressof`. If `dims` is given, + it is a non-empty sequence of non-negative integers describing the shape + of an array starting at `address`, and the type name must end in `[]` or + `*` (scalar converters ignore `dims`); a `LowLevelView` of that shape is + returned. + + v = ll.bind_value('double', addr) # a float + a = ll.bind_value('double[]', addr, (2, 3)) # a 2x3 view + + No lifetime or bounds checking is or can be done: the caller vouches for + the address, the type and the shape. + """ + return cppjit._backend.bind_value(type_name, address, dims) + + # import low-level python converters for _name in ["addressof", "as_cobject", "as_capsule", "as_ctypes", "as_memoryview"]: try: diff --git a/bindings/pyroot/cppjit/src/cpyrt/API.cxx b/bindings/pyroot/cppjit/src/cpyrt/API.cxx index b0da815ec12b4..b9724f87af8d0 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/API.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/API.cxx @@ -338,7 +338,7 @@ bool cpyrt::Import(const std::string& mod_name) { Py_INCREF(value); // collect classes - if (PyClass_Check(value) || PyObject_HasAttr(value, PyStrings::gBases)) { + if (PyType_Check(value) || PyObject_HasAttr(value, PyStrings::gBases)) { // get full class name (including module) PyObject* pyClName = PyObject_GetAttr(value, PyStrings::gName); if (PyErr_Occurred()) @@ -347,7 +347,7 @@ bool cpyrt::Import(const std::string& mod_name) { // build full, qualified name std::string fullname = mod_name; fullname += "."; - fullname += cpyrt_PyText_AsString(pyClName); + fullname += PyUnicode_AsUTF8(pyClName); Py_XDECREF(pyClName); } diff --git a/bindings/pyroot/cppjit/src/cpyrt/CPPClassMethod.cxx b/bindings/pyroot/cppjit/src/cpyrt/CPPClassMethod.cxx index d6893a8bd7bbf..bbdc10221e525 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/CPPClassMethod.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/CPPClassMethod.cxx @@ -53,8 +53,7 @@ PyObject* cpyrt::CPPClassMethod::Call(CPPInstance*& self, cpyrt_PyArgs_t args, //---------------------------------------------------------------------------- PyObject* cpyrt::CPPClassMethod::GetTypeName() { PyObject* cppname = - cpyrt_PyText_FromString((GetReturnTypeName() + " (*)").c_str()); - cpyrt_PyText_AppendAndDel(&cppname, - GetSignature(false /* show_formalargs */)); + PyUnicode_FromString((GetReturnTypeName() + " (*)").c_str()); + PyUnicode_AppendAndDel(&cppname, GetSignature(false /* show_formalargs */)); return cppname; } diff --git a/bindings/pyroot/cppjit/src/cpyrt/CPPConstructor.cxx b/bindings/pyroot/cppjit/src/cpyrt/CPPConstructor.cxx index 657f875557dd5..1f8179ae58198 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/CPPConstructor.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/CPPConstructor.cxx @@ -33,7 +33,7 @@ PyObject* cpyrt::CPPConstructor::GetDocString() { // GetMethod() may return an empty function if this is just a special case // place holder const std::string& clName = interop::GetFinalName(this->GetScope()); - return cpyrt_PyText_FromFormat( + return PyUnicode_FromFormat( "%s::%s%s", clName.c_str(), clName.c_str(), this->GetMethod() ? this->GetSignatureString().c_str() : "()"); } @@ -49,7 +49,7 @@ PyObject* cpyrt::CPPConstructor::Reflex(interop::Reflex::RequestId_t request, format == interop::Reflex::AS_TYPE) return CreateScopeProxy(this->GetScope()); else if (format == interop::Reflex::AS_STRING) - return cpyrt_PyText_FromString(fn.c_str()); + return PyUnicode_FromString(fn.c_str()); } return PyCallable::Reflex(request, format); diff --git a/bindings/pyroot/cppjit/src/cpyrt/CPPDataMember.cxx b/bindings/pyroot/cppjit/src/cpyrt/CPPDataMember.cxx index b7a5787f6d3df..582d1dddffa6d 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/CPPDataMember.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/CPPDataMember.cxx @@ -251,7 +251,7 @@ static PyObject* dm_reflex(CPPDataMember* dm, PyObject* args) { if (request == interop::Reflex::TYPE) { if (format == interop::Reflex::OPTIMAL || format == interop::Reflex::AS_STRING) - return cpyrt_PyText_FromString(dm->fFullType.c_str()); + return PyUnicode_FromString(dm->fFullType.c_str()); } else if (request == interop::Reflex::OFFSET) { if (format == interop::Reflex::OPTIMAL) return PyLong_FromLong(dm->fOffset); @@ -350,7 +350,7 @@ void cpyrt::CPPDataMember::Set(interop::TCppScope_t scope, if (fFullType.find("(anonymous)") == std::string::npos && fFullType.find("(unnamed)") == std::string::npos) { // repurpose fDescription for lazy lookup of the enum later - fDescription = cpyrt_PyText_FromString((fFullType + "::" + name).c_str()); + fDescription = PyUnicode_FromString((fFullType + "::" + name).c_str()); fFlags |= kIsEnumPrep; } type = interop::ResolveType(type); @@ -379,14 +379,14 @@ void cpyrt::CPPDataMember::Set(interop::TCppScope_t scope, fConverter = CreateConverter(type, {(dim_t)dims.size(), dims.data()}); if (!(fFlags & kIsEnumPrep)) - fDescription = cpyrt_PyText_FromString(name.c_str()); + fDescription = PyUnicode_FromString(name.c_str()); } //----------------------------------------------------------------------------- void cpyrt::CPPDataMember::Set(interop::TCppScope_t scope, const std::string& name, void* address) { fEnclosingScope = scope; - fDescription = cpyrt_PyText_FromString(name.c_str()); + fDescription = PyUnicode_FromString(name.c_str()); fOffset = (intptr_t)address; fFlags = kIsStaticData | kIsConstData; fConverter = CreateConverter("internal_enum_type_t"); @@ -435,16 +435,16 @@ std::string cpyrt::CPPDataMember::GetName() { if (fFlags & kIsEnumType) { PyObject* repr = PyObject_Repr(fDescription); if (repr) { - std::string res = cpyrt_PyText_AsString(repr); + std::string res = PyUnicode_AsUTF8(repr); Py_DECREF(repr); return res; } PyErr_Clear(); return ""; } else if (fFlags & kIsEnumPrep) { - std::string fullName = cpyrt_PyText_AsString(fDescription); + std::string fullName = PyUnicode_AsUTF8(fDescription); return fullName.substr(fullName.rfind("::") + 2, std::string::npos); } - return cpyrt_PyText_AsString(fDescription); + return PyUnicode_AsUTF8(fDescription); } diff --git a/bindings/pyroot/cppjit/src/cpyrt/CPPEnum.cxx b/bindings/pyroot/cppjit/src/cpyrt/CPPEnum.cxx index 65852655d1228..853615c8ab60c 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/CPPEnum.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/CPPEnum.cxx @@ -11,12 +11,12 @@ using namespace cppjit; //- private helpers ---------------------------------------------------------- static PyObject* pytype_from_enum_type(const std::string& enum_type) { if (enum_type == "char") - return (PyObject*)&cpyrt_PyText_Type; + return (PyObject*)&PyUnicode_Type; else if (enum_type == "bool") - return (PyObject*)&PyInt_Type; // can't use PyBool_Type as base + return (PyObject*)&PyLong_Type; // can't use PyBool_Type as base else if (strstr("long", enum_type.c_str())) return (PyObject*)&PyLong_Type; - return (PyObject*)&PyInt_Type; // covers most cases + return (PyObject*)&PyLong_Type; // covers most cases } //---------------------------------------------------------------------------- @@ -37,7 +37,7 @@ PyObject* cpyrt::pyval_from_enum(const std::string& enum_type, PyObject* pytype, char val = (char)llval; bval = PyUnicode_FromOrdinal((int)val); } else if (enum_type == "int" || enum_type == "unsigned int") - bval = PyInt_FromLong((long)llval); + bval = PyLong_FromLong((long)llval); else bval = PyLong_FromLongLong(llval); @@ -82,10 +82,10 @@ static PyObject* enum_repr(PyObject* self) { if (kls_cppname && obj_cppname && obj_str) { const std::string resolved = interop::ResolveEnum(PyLong_AsVoidPtr(kls_scope)); - repr = cpyrt_PyText_FromFormat( - "(%s::%s) : (%s) %s", cpyrt_PyText_AsString(kls_cppname), - cpyrt_PyText_AsString(obj_cppname), resolved.c_str(), - cpyrt_PyText_AsString(obj_str)); + repr = PyUnicode_FromFormat("(%s::%s) : (%s) %s", + PyUnicode_AsUTF8(kls_cppname), + PyUnicode_AsUTF8(obj_cppname), resolved.c_str(), + PyUnicode_AsUTF8(obj_str)); } Py_XDECREF(obj_cppname); Py_XDECREF(kls_cppname); @@ -144,7 +144,7 @@ static PyObject* enum_ctype(PyObject* cls, PyObject* args, PyObject* kwds) { if (!pyres) PyErr_Clear(); - std::string underlying = pyres ? cpyrt_PyText_AsString(pyres) : "int"; + std::string underlying = pyres ? PyUnicode_AsUTF8(pyres) : "int"; PyTypeObject* ct = GetCTypesType(underlying); if (!ct) return nullptr; @@ -185,12 +185,12 @@ cpyrt::CPPEnum* cpyrt::CPPEnum_New(const std::string& name, // create the __cpp_name__ for templates PyObject* dct = PyDict_New(); - PyObject* pycppname = cpyrt_PyText_FromString(ename.c_str()); + PyObject* pycppname = PyUnicode_FromString(ename.c_str()); PyObject* pycppscope = PyLong_FromVoidPtr(etype.data); PyDict_SetItem(dct, PyStrings::gCppName, pycppname); PyDict_SetItem(dct, PyStrings::gThisModule, pycppscope); Py_DECREF(pycppname); - PyObject* pyresolved = cpyrt_PyText_FromString(resolved.c_str()); + PyObject* pyresolved = PyUnicode_FromString(resolved.c_str()); PyDict_SetItem(dct, PyStrings::gUnderlying, pyresolved); Py_DECREF(pyresolved); @@ -200,7 +200,7 @@ cpyrt::CPPEnum* cpyrt::CPPEnum_New(const std::string& name, if (!modname.empty()) modname = "." + modname; PyObject* pymodname = - cpyrt_PyText_FromString(("cppjit.gbl" + modname).c_str()); + PyUnicode_FromString(("cppjit.gbl" + modname).c_str()); PyDict_SetItem(dct, PyStrings::gModule, pymodname); Py_DECREF(pymodname); @@ -228,10 +228,10 @@ cpyrt::CPPEnum* cpyrt::CPPEnum_New(const std::string& name, break; } const std::string& dname = interop::GetFinalName(econstant); - PyObject* pydname = cpyrt_PyText_FromString(dname.c_str()); + PyObject* pydname = PyUnicode_FromString(dname.c_str()); PyObject_SetAttr(pyenum, pydname, val); Py_DECREF(pydname); - PyObject* pydcppname = cpyrt_PyText_FromString( + PyObject* pydcppname = PyUnicode_FromString( (ename.empty() ? dname : (ename + "::" + dname)).c_str()); PyObject_SetAttr(val, PyStrings::gCppName, pydcppname); Py_DECREF(pydcppname); @@ -255,8 +255,8 @@ cpyrt::CPPEnum* cpyrt::CPPEnum_New(const std::string& name, } else { // presumably not a class enum; simply pretend int - Py_INCREF(&PyInt_Type); - pyenum = (PyObject*)&PyInt_Type; + Py_INCREF(&PyLong_Type); + pyenum = (PyObject*)&PyLong_Type; } return pyenum; diff --git a/bindings/pyroot/cppjit/src/cpyrt/CPPExcInstance.cxx b/bindings/pyroot/cppjit/src/cpyrt/CPPExcInstance.cxx index 6a668f0c29503..62857a43e2943 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/CPPExcInstance.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/CPPExcInstance.cxx @@ -36,7 +36,7 @@ static PyObject* ep_new(PyTypeObject* subtype, PyObject* args, PyObject* kwds) { PyErr_Clear(); if (PyTuple_GET_SIZE(args) == 1) { PyObject* msg = PyTuple_GET_ITEM(args, 0); - if (cpyrt_PyText_Check(msg)) { + if (PyUnicode_Check(msg)) { Py_INCREF(msg); excobj->fTopMessage = msg; } @@ -72,7 +72,7 @@ static PyObject* ep_str(CPPExcInstance* self) { if (self->fTopMessage) { Py_INCREF(self->fTopMessage); PyObject* top = self->fTopMessage; - cpyrt_PyText_Append(&top, what); + PyUnicode_Append(&top, what); Py_DECREF(what); what = top; } @@ -218,7 +218,7 @@ PyTypeObject CPPExcInstance_Type = { (setattrofunc)ep_setattro, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_BASE_EXC_SUBCLASS | - Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_CHECKTYPES, // tp_flags + Py_TPFLAGS_HAVE_GC, // tp_flags (char*)"cppjit exception object proxy (internal)", // tp_doc (traverseproc)ep_traverse, // tp_traverse (inquiry)ep_clear, // tp_clear diff --git a/bindings/pyroot/cppjit/src/cpyrt/CPPFunction.cxx b/bindings/pyroot/cppjit/src/cpyrt/CPPFunction.cxx index cc6e8522a6152..c68d27791be91 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/CPPFunction.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/CPPFunction.cxx @@ -89,9 +89,8 @@ PyObject* cpyrt::CPPFunction::Call(CPPInstance*& self, cpyrt_PyArgs_t args, //---------------------------------------------------------------------------- PyObject* cpyrt::CPPFunction::GetTypeName() { PyObject* cppname = - cpyrt_PyText_FromString((GetReturnTypeName() + " (*)").c_str()); - cpyrt_PyText_AppendAndDel(&cppname, - GetSignature(false /* show_formalargs */)); + PyUnicode_FromString((GetReturnTypeName() + " (*)").c_str()); + PyUnicode_AppendAndDel(&cppname, GetSignature(false /* show_formalargs */)); return cppname; } diff --git a/bindings/pyroot/cppjit/src/cpyrt/CPPInstance.cxx b/bindings/pyroot/cppjit/src/cpyrt/CPPInstance.cxx index 05d0bc987d913..13ccf3a7b02d5 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/CPPInstance.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/CPPInstance.cxx @@ -288,8 +288,7 @@ static PyObject* op_dispatch(PyObject* self, PyObject* args, // CPPOverload. PyObject *mname = nullptr, *sigarg = nullptr; if (!PyArg_ParseTuple(args, const_cast("O!O!:__dispatch__"), - &cpyrt_PyText_Type, &mname, &cpyrt_PyText_Type, - &sigarg)) + &PyUnicode_Type, &mname, &PyUnicode_Type, &sigarg)) return nullptr; // get the named overload @@ -734,14 +733,14 @@ static PyObject* op_repr(CPPInstance* self) { PyObject* repr = nullptr; if (self->IsSmart()) { std::string smartPtrName = interop::GetScopedFinalName(SMART_TYPE(self)); - repr = cpyrt_PyText_FromFormat( + repr = PyUnicode_FromFormat( const_cast("<%s.%s object at %p held by %s at %p>"), - cpyrt_PyText_AsString(modname), clName.c_str(), self->GetObject(), + PyUnicode_AsUTF8(modname), clName.c_str(), self->GetObject(), smartPtrName.c_str(), self->GetObjectRaw()); } else { - repr = cpyrt_PyText_FromFormat(const_cast("<%s.%s object at %p>"), - cpyrt_PyText_AsString(modname), - clName.c_str(), self->GetObject()); + repr = PyUnicode_FromFormat(const_cast("<%s.%s object at %p>"), + PyUnicode_AsUTF8(modname), clName.c_str(), + self->GetObject()); } Py_DECREF(modname); @@ -826,7 +825,7 @@ static PyObject* op_str_internal(PyObject* pyobj, PyObject* lshift, if (res) { Py_DECREF(res); - return cpyrt_PyText_FromString(s.str().c_str()); + return PyUnicode_FromString(s.str().c_str()); } return nullptr; @@ -887,7 +886,7 @@ static PyObject* op_str(CPPInstance* self) { const std::string& pretty = compat::ObjToString(self->ObjectIsA(), self->GetObject()); if (!pretty.empty()) - return cpyrt_PyText_FromString(pretty.c_str()); + return PyUnicode_FromString(pretty.c_str()); continue; } @@ -931,7 +930,7 @@ static PyObject* op_str(CPPInstance* self) { // explicit template lookup PyObject* clName = - cpyrt_PyText_FromString(Utility::ClassName((PyObject*)self).c_str()); + PyUnicode_FromString(Utility::ClassName((PyObject*)self).c_str()); PyObject* OL = PyObject_GetItem(printValue, clName); Py_DECREF(clName); @@ -944,8 +943,8 @@ static PyObject* op_str(CPPInstance* self) { if (pretty) { // the std::string executor may return a Python string std::string pv; - if (cpyrt_PyText_Check(pretty)) { - if (const char* s = cpyrt_PyText_AsString(pretty)) + if (PyUnicode_Check(pretty)) { + if (const char* s = PyUnicode_AsUTF8(pretty)) pv = s; else PyErr_Clear(); @@ -956,7 +955,7 @@ static PyObject* op_str(CPPInstance* self) { // an address or the generic fallback means no usable pretty printer if (!pv.empty() && pv.find("@0x") == std::string::npos && pv != "{not representable}") - result = cpyrt_PyText_FromString(pv.c_str()); + result = PyUnicode_FromString(pv.c_str()); Py_DECREF(pretty); if (result) return result; @@ -1151,37 +1150,36 @@ PyTypeObject CPPInstance_Type = { 0, // tp_getattro 0, // tp_setattro 0, // tp_as_buffer - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES | - Py_TPFLAGS_HAVE_GC, // tp_flags - (char*)"cppjit object proxy (internal)", // tp_doc - (traverseproc)op_traverse, // tp_traverse - (inquiry)op_clear, // tp_clear - (richcmpfunc)op_richcompare, // tp_richcompare - 0, // tp_weaklistoffset - 0, // tp_iter - 0, // tp_iternext - op_methods, // tp_methods - 0, // tp_members - op_getset, // tp_getset - 0, // tp_base - 0, // tp_dict - 0, // tp_descr_get - 0, // tp_descr_set - 0, // tp_dictoffset - 0, // tp_init - 0, // tp_alloc - (newfunc)op_new, // tp_new - 0, // tp_free - 0, // tp_is_gc - 0, // tp_bases - 0, // tp_mro - 0, // tp_cache - 0, // tp_subclasses - 0, // tp_weaklist - 0, // tp_del - 0, // tp_version_tag - 0, // tp_finalize - 0 // tp_vectorcall + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, // tp_flags + (char*)"cppjit object proxy (internal)", // tp_doc + (traverseproc)op_traverse, // tp_traverse + (inquiry)op_clear, // tp_clear + (richcmpfunc)op_richcompare, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + op_methods, // tp_methods + 0, // tp_members + op_getset, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + 0, // tp_alloc + (newfunc)op_new, // tp_new + 0, // tp_free + 0, // tp_is_gc + 0, // tp_bases + 0, // tp_mro + 0, // tp_cache + 0, // tp_subclasses + 0, // tp_weaklist + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall CPYRT_PYTYPE_TAIL}; } // namespace cppjit::cpyrt diff --git a/bindings/pyroot/cppjit/src/cpyrt/CPPMethod.cxx b/bindings/pyroot/cppjit/src/cpyrt/CPPMethod.cxx index 36d652dc7caf9..0221f836748ac 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/CPPMethod.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/CPPMethod.cxx @@ -64,11 +64,11 @@ inline bool cpyrt::CPPMethod::VerifyArgCount_(Py_ssize_t actual) { if (maxargs != actual) { if (actual < (Py_ssize_t)fArgsRequired) { - SetPyError_(cpyrt_PyText_FromFormat( + SetPyError_(PyUnicode_FromFormat( "takes at least %d arguments (%zd given)", fArgsRequired, actual)); return false; } else if (maxargs < actual) { - SetPyError_(cpyrt_PyText_FromFormat( + SetPyError_(PyUnicode_FromFormat( "takes at most %zd arguments (%zd given)", maxargs, actual)); return false; } @@ -81,6 +81,7 @@ inline void cpyrt::CPPMethod::Copy_(const CPPMethod& /* other */) { // fScope and fMethod handled separately // do not copy caches + fDeclaringScope = nullptr; fExecutor = nullptr; fArgIndices = nullptr; fArgsRequired = -1; @@ -289,17 +290,17 @@ void cpyrt::CPPMethod::SetPyError_(PyObject* msg) { // no traceback, extract its message and fall through PyObject* descr = PyObject_Str(evalue); if (descr) { - details = cpyrt_PyText_AsString(descr); + details = PyUnicode_AsUTF8(descr); Py_DECREF(descr); } } PyObject* doc = GetDocString(); - const char* cdoc = cpyrt_PyText_AsString(doc); - const char* cmsg = msg ? cpyrt_PyText_AsString(msg) : nullptr; + const char* cdoc = PyUnicode_AsUTF8(doc); + const char* cmsg = msg ? PyUnicode_AsUTF8(msg) : nullptr; PyObject* errtype = etype ? etype : PyExc_TypeError; PyObject* pyname = PyObject_GetAttr(errtype, PyStrings::gName); - const char* cname = pyname ? cpyrt_PyText_AsString(pyname) : "Exception"; + const char* cname = pyname ? PyUnicode_AsUTF8(pyname) : "Exception"; if (!isCppExc) { // this is the case where no Python error has occured yet, or an internal @@ -318,9 +319,9 @@ void cpyrt::CPPMethod::SetPyError_(PyObject* msg) { Py_XDECREF(topMessage); if (msg) { topMessage = - cpyrt_PyText_FromFormat("%s =>\n %s: %s | ", cdoc, cname, cmsg); + PyUnicode_FromFormat("%s =>\n %s: %s | ", cdoc, cname, cmsg); } else { - topMessage = cpyrt_PyText_FromFormat("%s =>\n %s: ", cdoc, cname); + topMessage = PyUnicode_FromFormat("%s =>\n %s: ", cdoc, cname); } // restore the updated error #if PY_VERSION_HEX >= 0x030c0000 @@ -341,8 +342,8 @@ extern std::unordered_map //- constructors and destructor ---------------------------------------------- cpyrt::CPPMethod::CPPMethod(interop::TCppScope_t scope, interop::TCppMethod_t method) - : fMethod(method), fScope(scope), fExecutor(nullptr), fArgIndices(nullptr), - fArgsRequired(-1) { + : fMethod(method), fScope(scope), fDeclaringScope(nullptr), + fExecutor(nullptr), fArgIndices(nullptr), fArgsRequired(-1) { interop::TCppType_t result = interop::ResolveType(interop::GetMethodReturnType(fMethod)); if (TypeReductionMap.find(result) != TypeReductionMap.end()) @@ -402,7 +403,7 @@ PyObject* cpyrt::CPPMethod::GetPrototype(bool fa) { // gives // a::b std::string finalscope = interop::GetScopedFinalName(fScope); - return cpyrt_PyText_FromFormat( + return PyUnicode_FromFormat( "%s%s %s%s", (interop::IsStaticMethod(fMethod) ? "static " : ""), interop::GetMethodReturnTypeAsString(fMethod).c_str(), interop::GetScopedFinalName(interop::TCppScope_t(fMethod.data)).c_str(), @@ -411,12 +412,11 @@ PyObject* cpyrt::CPPMethod::GetPrototype(bool fa) { //---------------------------------------------------------------------------- PyObject* cpyrt::CPPMethod::GetTypeName() { - PyObject* cppname = cpyrt_PyText_FromString( + PyObject* cppname = PyUnicode_FromString( (GetReturnTypeName() + " (" + (fScope ? interop::GetScopedFinalName(fScope) + "::*)" : "*)")) .c_str()); - cpyrt_PyText_AppendAndDel(&cppname, - GetSignature(false /* show_formalargs */)); + PyUnicode_AppendAndDel(&cppname, GetSignature(false /* show_formalargs */)); return cppname; } @@ -434,7 +434,7 @@ PyObject* cpyrt::CPPMethod::Reflex(interop::Reflex::RequestId_t request, if (format == interop::Reflex::AS_STRING || (format == interop::Reflex::OPTIMAL && !scope)) - return cpyrt_PyText_FromString(rtn.c_str()); + return PyUnicode_FromString(rtn.c_str()); else if (format == interop::Reflex::AS_TYPE || format == interop::Reflex::OPTIMAL) { if (scope) @@ -597,7 +597,7 @@ PyObject* cpyrt::CPPMethod::GetCoVarNames() { // TODO: static methods need no 'self' (but is harmless otherwise) PyObject* co_varnames = PyTuple_New(co_argcount + 1 /* self */); - PyTuple_SET_ITEM(co_varnames, 0, cpyrt_PyText_FromString("self")); + PyTuple_SET_ITEM(co_varnames, 0, PyUnicode_FromString("self")); for (int iarg = 0; iarg < co_argcount; ++iarg) { std::string argrep = interop::GetMethodArgTypeAsString(fMethod, iarg); const std::string& parname = interop::GetMethodArgName(fMethod, iarg); @@ -606,7 +606,7 @@ PyObject* cpyrt::CPPMethod::GetCoVarNames() { argrep += parname; } - PyObject* pyspec = cpyrt_PyText_FromString(argrep.c_str()); + PyObject* pyspec = PyUnicode_FromString(argrep.c_str()); PyTuple_SET_ITEM(co_varnames, iarg + 1, pyspec); } @@ -688,7 +688,7 @@ PyObject* cpyrt::CPPMethod::GetArgDefault(int iarg, bool silent) { if (!pyval && PyErr_Occurred() && silent) { PyErr_Clear(); - pyval = cpyrt_PyText_FromString( + pyval = PyUnicode_FromString( defvalue.c_str()); // allows continuation, but is likely to fail } @@ -729,12 +729,12 @@ int cpyrt::CPPMethod::GetArgMatchScore(PyObject* args_tuple) { size_t score = 0; for (int i = 0; i < n; i++) { PyObject* pItem = PyTuple_GetItem(args_tuple, i); - if (!cpyrt_PyText_Check(pItem)) { + if (!PyUnicode_Check(pItem)) { PyErr_SetString(PyExc_TypeError, "argument types should be in string format"); return INT_MAX; } - std::string req_type(cpyrt_PyText_AsString(pItem)); + std::string req_type(PyUnicode_AsUTF8(pItem)); size_t arg_score = interop::CompareMethodArgType(fMethod, i, req_type); @@ -772,8 +772,7 @@ bool cpyrt::CPPMethod::Initialize(CallContext* ctxt) { //---------------------------------------------------------------------------- bool cpyrt::CPPMethod::ProcessKwds(PyObject* self_in, PyCallArgs& cargs) { if (!PyTuple_CheckExact(cargs.fKwds)) { - SetPyError_( - cpyrt_PyText_FromString("received unknown keyword names object")); + SetPyError_(PyUnicode_FromString("received unknown keyword names object")); return false; } Py_ssize_t nKeys = PyTuple_GET_SIZE(cargs.fKwds); @@ -802,13 +801,13 @@ bool cpyrt::CPPMethod::ProcessKwds(PyObject* self_in, PyCallArgs& cargs) { for (Py_ssize_t ikey = 0; ikey < nKeys; ++ikey) { key = PyTuple_GET_ITEM(cargs.fKwds, ikey); value = cargs.fArgs[npos_args + ikey]; - const char* ckey = cpyrt_PyText_AsStringChecked(key); + const char* ckey = PyUnicode_AsUTF8(key); if (!ckey) return false; auto p = fArgIndices->find(ckey); if (p == fArgIndices->end()) { - SetPyError_(cpyrt_PyText_FromFormat( + SetPyError_(PyUnicode_FromFormat( "%s::%s got an unexpected keyword argument \'%s\'", interop::GetFinalName(fScope).c_str(), interop::GetName(interop::TCppScope_t(fMethod.data)).c_str(), ckey)); @@ -839,7 +838,7 @@ bool cpyrt::CPPMethod::ProcessKwds(PyObject* self_in, PyCallArgs& cargs) { for (Py_ssize_t i = start; i < nArgs; ++i) { if (vArgs[i]) { - SetPyError_(cpyrt_PyText_FromFormat( + SetPyError_(PyUnicode_FromFormat( "%s::%s got multiple values for argument %d", interop::GetFinalName(fScope).c_str(), interop::GetName(interop::TCppScope_t(fMethod.data)).c_str(), @@ -923,7 +922,7 @@ bool cpyrt::CPPMethod::ProcessArgs(PyCallArgs& cargs) { } // no self, set error and lament - SetPyError_(cpyrt_PyText_FromFormat( + SetPyError_(PyUnicode_FromFormat( "unbound method %s::%s must be called with a %s instance as first " "argument", interop::GetFinalName(fScope).c_str(), @@ -952,8 +951,8 @@ bool cpyrt::CPPMethod::ConvertAndSetArgs(cpyrt_PyArgs_t args, size_t nargsf, if (!fConverters[i]->SetArg(cpyrt_PyArgs_GET_ITEM(args, i), cppArgs[i], ctxt)) { SetPyError_( - cpyrt_PyText_FromFormat("could not convert argument %d: %s", i + 1, - fConverters[i]->GetFailureMsg().c_str())); + PyUnicode_FromFormat("could not convert argument %d: %s", i + 1, + fConverters[i]->GetFailureMsg().c_str())); isOK = false; break; } @@ -1017,15 +1016,17 @@ PyObject* cpyrt::CPPMethod::Call(CPPInstance*& self, cpyrt_PyArgs_t args, // brought into fScope through a using-declaration (e.g. `using Base::meth;`) // is still declared in the base, so 'this' has to be adjusted to that base's // subobject. Using fScope here would yield a zero offset and corrupt memory. - interop::TCppScope_t declaring = - interop::GetParentScope(interop::TCppScope_t(fMethod.data)); - if (!declaring) - declaring = fScope; + if (!fDeclaringScope) { + fDeclaringScope = + interop::GetParentScope(interop::TCppScope_t(fMethod.data)); + if (!fDeclaringScope) + fDeclaringScope = fScope; + } ptrdiff_t offset = 0; - if (derived && derived != declaring) - offset = - interop::GetBaseOffset(derived, declaring, object, 1 /* up-cast */); + if (derived && derived != fDeclaringScope) + offset = interop::GetBaseOffset(derived, fDeclaringScope, object, + 1 /* up-cast */); // actual call; recycle self instead of returning new object for same address // objects @@ -1043,7 +1044,7 @@ PyObject* cpyrt::CPPMethod::Call(CPPInstance*& self, cpyrt_PyArgs_t args, //- protected members -------------------------------------------------------- PyObject* cpyrt::CPPMethod::GetSignature(bool fa) { // construct python string from the method's signature - return cpyrt_PyText_FromString(GetSignatureString(fa).c_str()); + return PyUnicode_FromString(GetSignatureString(fa).c_str()); } /** @@ -1064,7 +1065,7 @@ PyObject* cpyrt::CPPMethod::GetSignatureNames() { for (int iarg = 0; iarg < argcount; ++iarg) { const std::string& argname_cpp = interop::GetMethodArgName(fMethod, iarg); - PyObject* argname_py = cpyrt_PyText_FromString(argname_cpp.c_str()); + PyObject* argname_py = PyUnicode_FromString(argname_cpp.c_str()); PyTuple_SET_ITEM(signature_names, iarg, argname_py); } @@ -1090,8 +1091,8 @@ PyObject* cpyrt::CPPMethod::GetSignatureTypes() { // Insert the return type first std::string return_type = GetReturnTypeName(); - PyObject* return_type_py = cpyrt_PyText_FromString(return_type.c_str()); - PyDict_SetItem(signature_types_dict, cpyrt_PyText_FromString("return_type"), + PyObject* return_type_py = PyUnicode_FromString(return_type.c_str()); + PyDict_SetItem(signature_types_dict, PyUnicode_FromString("return_type"), return_type_py); // Build a tuple of the argument types for this signature. @@ -1101,16 +1102,16 @@ PyObject* cpyrt::CPPMethod::GetSignatureTypes() { for (int iarg = 0; iarg < argcount; ++iarg) { const std::string& argtype_cpp = interop::GetMethodArgTypeAsString(fMethod, iarg); - PyObject* argtype_py = cpyrt_PyText_FromString(argtype_cpp.c_str()); + PyObject* argtype_py = PyUnicode_FromString(argtype_cpp.c_str()); PyTuple_SET_ITEM(parameter_types, iarg, argtype_py); } - PyDict_SetItem(signature_types_dict, cpyrt_PyText_FromString("input_types"), + PyDict_SetItem(signature_types_dict, PyUnicode_FromString("input_types"), parameter_types); // Const-qualification of the method itself (always false for free // functions and static methods). - PyDict_SetItem(signature_types_dict, cpyrt_PyText_FromString("is_const"), + PyDict_SetItem(signature_types_dict, PyUnicode_FromString("is_const"), PyBool_FromLong(IsConst())); return signature_types_dict; diff --git a/bindings/pyroot/cppjit/src/cpyrt/CPPMethod.h b/bindings/pyroot/cppjit/src/cpyrt/CPPMethod.h index 16dc2f7fdd5ab..41e9546b4c182 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/CPPMethod.h +++ b/bindings/pyroot/cppjit/src/cpyrt/CPPMethod.h @@ -116,6 +116,8 @@ class CPPMethod : public PyCallable { // representation interop::TCppMethod_t fMethod; interop::TCppScope_t fScope; + // class that declares fMethod, which may be a base of fScope (set by Call) + interop::TCppScope_t fDeclaringScope; Executor* fExecutor; // call dispatch buffers diff --git a/bindings/pyroot/cppjit/src/cpyrt/CPPOperator.cxx b/bindings/pyroot/cppjit/src/cpyrt/CPPOperator.cxx index b48a2436402db..3b221c36cc880 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/CPPOperator.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/CPPOperator.cxx @@ -15,7 +15,7 @@ cpyrt::CPPOperator::CPPOperator(interop::TCppScope_t scope, // order if (name == "__mul__") fStub = CPPInstance_Type.tp_as_number->nb_multiply; - else if (name == CPPJIT__div__) + else if (name == "__truediv__") fStub = CPPInstance_Type.tp_as_number->nb_true_divide; else if (name == "__add__") fStub = CPPInstance_Type.tp_as_number->nb_add; diff --git a/bindings/pyroot/cppjit/src/cpyrt/CPPOverload.cxx b/bindings/pyroot/cppjit/src/cpyrt/CPPOverload.cxx index 3ce11adbe69e7..9601620559866 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/CPPOverload.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/CPPOverload.cxx @@ -48,12 +48,12 @@ class TPythonCallback : public PyCallable { } PyObject* GetSignature(bool /*show_formalargs*/ = true) override { - return cpyrt_PyText_FromString("*args, **kwargs"); + return PyUnicode_FromString("*args, **kwargs"); } PyObject* GetSignatureNames() override { return PyTuple_New(0); } PyObject* GetSignatureTypes() override { return PyTuple_New(0); } PyObject* GetPrototype(bool /*show_formalargs*/ = true) override { - return cpyrt_PyText_FromString(""); + return PyUnicode_FromString(""); } PyObject* GetDocString() override { if (PyObject_HasAttrString(fCallable, "__doc__")) { @@ -212,7 +212,7 @@ static inline PyObject* HandleReturn(CPPOverload* pymeth, CPPInstance* im_self, //= cpyrt method proxy object behaviour =================================== static PyObject* mp_name(CPPOverload* pymeth, void*) { - return cpyrt_PyText_FromString(pymeth->GetName().c_str()); + return PyUnicode_FromString(pymeth->GetName().c_str()); } //---------------------------------------------------------------------------- @@ -242,10 +242,10 @@ static PyObject* mp_doc(CPPOverload* pymeth, void*) { return doc; // overloaded method - PyObject* separator = cpyrt_PyText_FromString("\n"); + PyObject* separator = PyUnicode_FromString("\n"); for (CPPOverload::Methods_t::size_type i = 1; i < nMethods; ++i) { - cpyrt_PyText_Append(&doc, separator); - cpyrt_PyText_AppendAndDel(&doc, methods[i]->GetDocString()); + PyUnicode_Append(&doc, separator); + PyUnicode_AppendAndDel(&doc, methods[i]->GetDocString()); } Py_DECREF(separator); @@ -436,7 +436,7 @@ static inline int set_flag(CPPOverload* pymeth, PyObject* value, //---------------------------------------------------------------------------- static PyObject* mp_getcreates(CPPOverload* pymeth, void*) { // Get '__creates__' boolean, which determines ownership of return values. - return PyInt_FromLong((long)IsCreator(pymeth->fMethodInfo->fFlags)); + return PyLong_FromLong((long)IsCreator(pymeth->fMethodInfo->fFlags)); } //---------------------------------------------------------------------------- @@ -486,16 +486,16 @@ CPPJIT_BOOLEAN_PROPERTY(sig2exc, CallContext::kProtected, "__sig2exc__") static PyObject* mp_getcppname(CPPOverload* pymeth, void*) { if ((void*)pymeth == (void*)&CPPOverload_Type) - return cpyrt_PyText_FromString("CPPOverload_Type"); + return PyUnicode_FromString("CPPOverload_Type"); auto& methods = pymeth->fMethodInfo->fMethods; if (methods.empty()) - return cpyrt_PyText_FromString("void (*)()"); // debatable + return PyUnicode_FromString("void (*)()"); // debatable if (methods.size() == 1) return methods[0]->GetTypeName(); - return cpyrt_PyText_FromString("void* (*)(...)"); // id. + return PyUnicode_FromString("void* (*)(...)"); // id. } //---------------------------------------------------------------------------- @@ -670,8 +670,7 @@ static PyObject* mp_vectorcall(CPPOverload* pymeth, PyObject* const* args, if (!PyErr_Occurred()) { // this should not happen; set an error to prevent core dump and report PyObject* sig = methods[i]->GetPrototype(); - PyErr_Format(PyExc_SystemError, "%s =>\n %s", - cpyrt_PyText_AsString(sig), + PyErr_Format(PyExc_SystemError, "%s =>\n %s", PyUnicode_AsUTF8(sig), (char*)"nullptr result without error in overload call"); Py_DECREF(sig); } @@ -697,7 +696,7 @@ static PyObject* mp_vectorcall(CPPOverload* pymeth, PyObject* const* args, } // first summarize, then add details - PyObject* topmsg = cpyrt_PyText_FromFormat( + PyObject* topmsg = PyUnicode_FromFormat( "none of the %d overloaded methods succeeded. Full details:", (int)nMethods); SetDetailedException(std::move(errors), topmsg /* steals */, @@ -713,7 +712,7 @@ static PyObject* mp_str(CPPOverload* cppinst) { std::ostringstream s; s << "fMethodInfo->fName << "\" at " << (void*)cppinst << ">"; - return cpyrt_PyText_FromString(s.str().c_str()); + return PyUnicode_FromString(s.str().c_str()); } //---------------------------------------------------------------------------- @@ -1025,7 +1024,7 @@ PyObject* cpyrt::CPPOverload::FindOverload(const std::string& signature, bool found = accept_any; if (!found) { PyObject* pysig2 = meth->GetSignature(false); - std::string sig2(cpyrt_PyText_AsString(pysig2)); + std::string sig2(PyUnicode_AsUTF8(pysig2)); sig2.erase(std::remove(sig2.begin(), sig2.end(), ' '), std::end(sig2)); Py_DECREF(pysig2); if (sig1 == sig2) @@ -1033,7 +1032,7 @@ PyObject* cpyrt::CPPOverload::FindOverload(const std::string& signature, if (!found) { pysig2 = meth->GetSignature(true); - std::string sig3(cpyrt_PyText_AsString(pysig2)); + std::string sig3(PyUnicode_AsUTF8(pysig2)); sig3.erase(std::remove(sig3.begin(), sig3.end(), ' '), std::end(sig3)); Py_DECREF(pysig2); if (sig1 == sig3) @@ -1109,12 +1108,12 @@ PyObject* cpyrt::CPPOverload::FindOverload(PyObject* args_tuple, for (int i = 0; i < n; i++) { PyObject* pItem = PyTuple_GetItem(args_tuple, i); - if (!cpyrt_PyText_Check(pItem)) { + if (!PyUnicode_Check(pItem)) { PyErr_Format(PyExc_LookupError, "argument types should be in string format"); return (PyObject*)nullptr; } - std::string arg_type(cpyrt_PyText_AsString(pItem)); + std::string arg_type(PyUnicode_AsUTF8(pItem)); sigargs += arg_type + ", "; } sigargs += ")"; diff --git a/bindings/pyroot/cppjit/src/cpyrt/CPPScope.cxx b/bindings/pyroot/cppjit/src/cpyrt/CPPScope.cxx index b58469d3845a8..9f695789b6683 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/CPPScope.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/CPPScope.cxx @@ -35,7 +35,7 @@ add_template(PyObject* pyclass, const std::string& name, // specific lookup must be the current overload, if already found. const std::string& ncl = TypeManip::clean_type(name); - PyObject* pyncl = cpyrt_PyText_FromString(ncl.c_str()); + PyObject* pyncl = PyUnicode_FromString(ncl.c_str()); TemplateProxy* pytmpl = (TemplateProxy*)PyType_Type.tp_getattro(pyclass, pyncl); if (!pytmpl) { @@ -95,23 +95,23 @@ static void meta_dealloc(CPPScope* scope) { //----------------------------------------------------------------------------- static PyObject* meta_getcppname(CPPScope* scope, void*) { if ((void*)scope == (void*)&CPPInstance_Type) - return cpyrt_PyText_FromString("CPPInstance_Type"); - return cpyrt_PyText_FromString( + return PyUnicode_FromString("CPPInstance_Type"); + return PyUnicode_FromString( interop::GetScopedFinalName(scope->fCppType).c_str()); } //----------------------------------------------------------------------------- static PyObject* meta_getmodule(CPPScope* scope, void*) { if ((void*)scope == (void*)&CPPInstance_Type) - return cpyrt_PyText_FromString("cppjit.gbl"); + return PyUnicode_FromString("cppjit.gbl"); if (scope->fModuleName) - return cpyrt_PyText_FromString(scope->fModuleName); + return PyUnicode_FromString(scope->fModuleName); // get C++ representation of outer scope interop::TCppScope_t parent_scope = interop::GetParentScope(scope->fCppType); if (parent_scope == interop::GetGlobalScope()) - return cpyrt_PyText_FromString(const_cast("cppjit.gbl")); + return PyUnicode_FromString(const_cast("cppjit.gbl")); // now peel scopes one by one, pulling in the python naming (which will // simply recurse if not overridden in python) @@ -124,8 +124,8 @@ static PyObject* meta_getmodule(CPPScope* scope, void*) { // append name of our module PyObject* pymodname = PyObject_GetAttr(pyscope, PyStrings::gName); if (pymodname) { - cpyrt_PyText_AppendAndDel(&pymodule, cpyrt_PyText_FromString(".")); - cpyrt_PyText_AppendAndDel(&pymodule, pymodname); + PyUnicode_AppendAndDel(&pymodule, PyUnicode_FromString(".")); + PyUnicode_AppendAndDel(&pymodule, pymodname); } } Py_DECREF(pyscope); @@ -138,7 +138,7 @@ static PyObject* meta_getmodule(CPPScope* scope, void*) { // lookup through python failed, so simply cook up a '::' -> '.' replacement std::string modname = interop::GetScopedFinalName(parent_scope); TypeManip::cppscope_to_pyscope(modname); - return cpyrt_PyText_FromString(("cppjit.gbl." + modname).c_str()); + return PyUnicode_FromString(("cppjit.gbl." + modname).c_str()); } //----------------------------------------------------------------------------- @@ -150,12 +150,12 @@ static int meta_setmodule(CPPScope* scope, PyObject* value, void*) { return -1; } - const char* newname = cpyrt_PyText_AsStringChecked(value); + const char* newname = PyUnicode_AsUTF8(value); if (!value) return -1; free(scope->fModuleName); - Py_ssize_t sz = cpyrt_PyText_GET_SIZE(value); + Py_ssize_t sz = PyUnicode_GET_LENGTH(value); scope->fModuleName = (char*)malloc(sz + 1); memcpy(scope->fModuleName, newname, sz + 1); @@ -167,7 +167,7 @@ static PyObject* meta_repr(CPPScope* scope) { // Specialized b/c type_repr expects __module__ to live in the dictionary, // whereas it is a property (to save memory). if ((void*)scope == (void*)&CPPInstance_Type) - return cpyrt_PyText_FromFormat( + return PyUnicode_FromFormat( const_cast(""), scope); if (scope->fFlags & (CPPScope::kIsMeta | CPPScope::kIsPython)) { @@ -185,9 +185,9 @@ static PyObject* meta_repr(CPPScope* scope) { const char* kind = (scope->fFlags & CPPScope::kIsNamespace) ? "namespace" : "class"; - PyObject* repr = cpyrt_PyText_FromFormat("<%s %s.%s at %p>", kind, - cpyrt_PyText_AsString(modname), - clName.c_str(), scope); + PyObject* repr = + PyUnicode_FromFormat("<%s %s.%s at %p>", kind, PyUnicode_AsUTF8(modname), + clName.c_str(), scope); Py_DECREF(modname); return repr; @@ -242,7 +242,7 @@ static PyObject* pt_new(PyTypeObject* subtype, PyObject* args, PyObject* kwds) { // there has been a user meta class override in a derived class, so do // the consistent thing, thus allowing user control over naming result->fCppType = - interop::GetScope(cpyrt_PyText_AsString(PyTuple_GET_ITEM(args, 0))); + interop::GetScope(PyUnicode_AsUTF8(PyTuple_GET_ITEM(args, 0))); } else { // coming here from cppjit or from sub-classing in python; take the // C++ type from the meta class to make sure that the latter category @@ -272,7 +272,7 @@ static PyObject* pt_new(PyTypeObject* subtype, PyObject* args, PyObject* kwds) { // the direct base can be useful for some templates, such as // shared_ptrs, so make it accessible (the __cpp_cross__ data member // also signals that this is a cross-inheritance class) - PyObject* bname = cpyrt_PyText_FromString( + PyObject* bname = PyUnicode_FromString( interop::GetBaseName(result->fCppType, 0).c_str()); if (!bname) PyErr_Clear(); @@ -359,11 +359,11 @@ static PyObject* meta_getattro(PyObject* pyclass, PyObject* pyname) { return attr; } - if (!cpyrt_PyText_CheckExact(pyname) || !CPPScope_Check(pyclass)) + if (!PyUnicode_CheckExact(pyname) || !CPPScope_Check(pyclass)) return possibly_shadowed; // filter for python specials - std::string name = cpyrt_PyText_AsString(pyname); + std::string name = PyUnicode_AsUTF8(pyname); if (name.size() >= 5 && name.compare(0, 2, "__") == 0 && name.compare(name.size() - 2, name.size(), "__") == 0) return possibly_shadowed; @@ -500,7 +500,7 @@ static PyObject* meta_getattro(PyObject* pyclass, PyObject* pyname) { klass->fImp.fUsing->push_back(PyWeakref_NewRef(pyuscope, nullptr)); // the namespace may not otherwise be held, so tie the lifetimes PyObject* llname = - cpyrt_PyText_FromString(("__lifeline_" + uname).c_str()); + PyUnicode_FromString(("__lifeline_" + uname).c_str()); PyType_Type.tp_setattro(pyclass, llname, pyuscope); Py_DECREF(llname); Py_DECREF(pyuscope); @@ -548,14 +548,13 @@ static PyObject* meta_getattro(PyObject* pyclass, PyObject* pyname) { PyObject* sklass = PyObject_Str(pyclass); PyErr_Restore(pytype, pyvalue, pytrace); if (sklass) { - topmsg = cpyrt_PyText_FromFormat( - "%s has no attribute \'%s\'. Full details:", - cpyrt_PyText_AsString(sklass), cpyrt_PyText_AsString(pyname)); + topmsg = PyUnicode_FromFormat( + "%s has no attribute \'%s\'. Full details:", PyUnicode_AsUTF8(sklass), + PyUnicode_AsUTF8(pyname)); Py_DECREF(sklass); } else { - topmsg = - cpyrt_PyText_FromFormat("no such attribute \'%s\'. Full details:", - cpyrt_PyText_AsString(pyname)); + topmsg = PyUnicode_FromFormat("no such attribute \'%s\'. Full details:", + PyUnicode_AsUTF8(pyname)); } SetDetailedException(std::move(errors), topmsg /* steals */, PyExc_AttributeError /* default error */); @@ -574,7 +573,7 @@ static int meta_setattro(PyObject* pyclass, PyObject* pyname, PyObject* pyval) { // skip if the given pyval is a descriptor already, or an unassignable class if (((CPPScope*)pyclass)->fFlags & CPPScope::kIsNamespace && !cpyrt::CPPDataMember_Check(pyval) && !cpyrt::CPPScope_Check(pyval)) { - std::string name = cpyrt_PyText_AsString(pyname); + std::string name = PyUnicode_AsUTF8(pyname); if (interop::GetNamed(name, ((CPPScope*)pyclass)->fCppType)) { PyObject* attr = meta_getattro(pyclass, pyname); // triggers creation if (!attr) @@ -656,7 +655,7 @@ static PyObject* meta_dir(CPPScope* klass) { // get rid of duplicates for (Py_ssize_t i = 0; i < PyList_GET_SIZE(dirlist); ++i) - dir_cppnames.insert(cpyrt_PyText_AsString(PyList_GET_ITEM(dirlist, i))); + dir_cppnames.insert(PyUnicode_AsUTF8(PyList_GET_ITEM(dirlist, i))); Py_DECREF(dirlist); dirlist = PyList_New(dir_cppnames.size()); @@ -664,7 +663,7 @@ static PyObject* meta_dir(CPPScope* klass) { // copy total onto python list Py_ssize_t i = 0; for (const auto& name : dir_cppnames) { - PyList_SET_ITEM(dirlist, i++, cpyrt_PyText_FromString(name.c_str())); + PyList_SET_ITEM(dirlist, i++, PyUnicode_FromString(name.c_str())); } return dirlist; } diff --git a/bindings/pyroot/cppjit/src/cpyrt/Converters.cxx b/bindings/pyroot/cppjit/src/cpyrt/Converters.cxx index 1cba38cd4afb1..c21d00ef182e9 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/Converters.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/Converters.cxx @@ -392,7 +392,7 @@ static inline bool cpyrt_PyLong_AsBool(PyObject* pyobject) { // conversions) #define CPPJIT_PYLONG_AS_TYPE(name, type, limit_low, limit_high) \ static inline type cpyrt_PyLong_As##name(PyObject* pyobject) { \ - if (!(PyLong_Check(pyobject) || PyInt_Check(pyobject))) { \ + if (!PyLong_Check(pyobject)) { \ if (pyobject == cpyrt::gDefaultObject) \ return (type)0; \ PyErr_SetString(PyExc_TypeError, \ @@ -422,7 +422,7 @@ static inline long cpyrt_PyLong_AsStrictLong(PyObject* pyobject) { // strict python integer to C++ long integer conversion // prevent float -> long (see cpyrt_PyLong_AsStrictInt) - if (!(PyLong_Check(pyobject) || PyInt_Check(pyobject))) { + if (!PyLong_Check(pyobject)) { if (pyobject == cpyrt::gDefaultObject) return (long)0; PyErr_SetString(PyExc_TypeError, @@ -437,7 +437,7 @@ static inline PY_LONG_LONG cpyrt_PyLong_AsStrictLongLong(PyObject* pyobject) { // strict python integer to C++ long long integer conversion // prevent float -> long (see cpyrt_PyLong_AsStrictInt) - if (!(PyLong_Check(pyobject) || PyInt_Check(pyobject))) { + if (!PyLong_Check(pyobject)) { if (pyobject == cpyrt::gDefaultObject) return (PY_LONG_LONG)0; PyErr_SetString(PyExc_TypeError, @@ -682,13 +682,13 @@ static inline int ExtractChar(PyObject* pyobject, const char* tname, int low, PyErr_Format(PyExc_ValueError, "%s expected, got bytes of size " PY_SSIZE_T_FORMAT, tname, PyBytes_GET_SIZE(pyobject)); - } else if (cpyrt_PyText_Check(pyobject)) { - if (cpyrt_PyText_GET_SIZE(pyobject) == 1) - lchar = (int)(cpyrt_PyText_AsString(pyobject)[0]); + } else if (PyUnicode_Check(pyobject)) { + if (PyUnicode_GET_LENGTH(pyobject) == 1) + lchar = (int)(PyUnicode_AsUTF8(pyobject)[0]); else PyErr_Format(PyExc_ValueError, "%s expected, got str of size " PY_SSIZE_T_FORMAT, tname, - cpyrt_PyText_GET_SIZE(pyobject)); + PyUnicode_GET_LENGTH(pyobject)); } else if (pyobject == cpyrt::gDefaultObject) { lchar = (int)'\0'; } else if (!PyFloat_Check(pyobject)) { // don't allow truncating conversion @@ -773,7 +773,7 @@ static inline int ExtractChar(PyObject* pyobject, const char* tname, int low, \ PyObject* cpyrt::name##Converter::FromMemory(void* address) { \ /* return char in "native" str type as that's more natural in use */ \ - return cpyrt_PyText_FromFormat("%c", *((type*)address)); \ + return PyUnicode_FromFormat("%c", *((type*)address)); \ } \ \ bool cpyrt::name##Converter::ToMemory(PyObject* value, void* address, \ @@ -942,25 +942,25 @@ CPPJIT_IMPL_BASIC_CHAR_CONVERTER(UChar, unsigned char, 0, UCHAR_MAX) PyObject* cpyrt::SCharAsIntConverter::FromMemory(void* address) { // special case to be used with arrays: return a Python int instead of str // (following the same convention as module array.array) - return PyInt_FromLong((long)*((signed char*)address)); + return PyLong_FromLong((long)*((signed char*)address)); } PyObject* cpyrt::UCharAsIntConverter::FromMemory(void* address) { // special case to be used with arrays: return a Python int instead of str // (following the same convention as module array.array) - return PyInt_FromLong((long)*((unsigned char*)address)); + return PyLong_FromLong((long)*((unsigned char*)address)); } //---------------------------------------------------------------------------- bool cpyrt::WCharConverter::SetArg(PyObject* pyobject, Parameter& para, CallContext* /* ctxt */) { // convert to C++ , set arg for call - if (!PyUnicode_Check(pyobject) || cpyrt_PyUnicode_GET_SIZE(pyobject) != 1) { + if (!PyUnicode_Check(pyobject) || PyUnicode_GET_LENGTH(pyobject) != 1) { PyErr_SetString(PyExc_ValueError, "single wchar_t character expected"); return false; } wchar_t val; - Py_ssize_t res = cpyrt_PyUnicode_AsWideChar(pyobject, &val, 1); + Py_ssize_t res = PyUnicode_AsWideChar(pyobject, &val, 1); if (res == -1) return false; para.fValue.fLong = (long)val; @@ -974,12 +974,12 @@ PyObject* cpyrt::WCharConverter::FromMemory(void* address) { bool cpyrt::WCharConverter::ToMemory(PyObject* value, void* address, PyObject* /* ctxt */) { - if (!PyUnicode_Check(value) || cpyrt_PyUnicode_GET_SIZE(value) != 1) { + if (!PyUnicode_Check(value) || PyUnicode_GET_LENGTH(value) != 1) { PyErr_SetString(PyExc_ValueError, "single wchar_t character expected"); return false; } wchar_t val; - Py_ssize_t res = cpyrt_PyUnicode_AsWideChar(value, &val, 1); + Py_ssize_t res = PyUnicode_AsWideChar(value, &val, 1); if (res == -1) return false; *((wchar_t*)address) = val; @@ -990,7 +990,7 @@ bool cpyrt::WCharConverter::ToMemory(PyObject* value, void* address, bool cpyrt::Char16Converter::SetArg(PyObject* pyobject, Parameter& para, CallContext* /* ctxt */) { // convert to C++ , set arg for call - if (!PyUnicode_Check(pyobject) || cpyrt_PyUnicode_GET_SIZE(pyobject) != 1) { + if (!PyUnicode_Check(pyobject) || PyUnicode_GET_LENGTH(pyobject) != 1) { PyErr_SetString(PyExc_ValueError, "single char16_t character expected"); return false; } @@ -1014,7 +1014,7 @@ PyObject* cpyrt::Char16Converter::FromMemory(void* address) { bool cpyrt::Char16Converter::ToMemory(PyObject* value, void* address, PyObject* /* ctxt */) { - if (!PyUnicode_Check(value) || cpyrt_PyUnicode_GET_SIZE(value) != 1) { + if (!PyUnicode_Check(value) || PyUnicode_GET_LENGTH(value) != 1) { PyErr_SetString(PyExc_ValueError, "single char16_t character expected"); return false; } @@ -1033,7 +1033,7 @@ bool cpyrt::Char16Converter::ToMemory(PyObject* value, void* address, bool cpyrt::Char32Converter::SetArg(PyObject* pyobject, Parameter& para, CallContext* /* ctxt */) { // convert to C++ , set arg for call - if (!PyUnicode_Check(pyobject) || 2 < cpyrt_PyUnicode_GET_SIZE(pyobject)) { + if (!PyUnicode_Check(pyobject) || 2 < PyUnicode_GET_LENGTH(pyobject)) { PyErr_SetString(PyExc_ValueError, "single char32_t character expected"); return false; } @@ -1057,7 +1057,7 @@ PyObject* cpyrt::Char32Converter::FromMemory(void* address) { bool cpyrt::Char32Converter::ToMemory(PyObject* value, void* address, PyObject* /* ctxt */) { - if (!PyUnicode_Check(value) || 2 < cpyrt_PyUnicode_GET_SIZE(value)) { + if (!PyUnicode_Check(value) || 2 < PyUnicode_GET_LENGTH(value)) { PyErr_SetString(PyExc_ValueError, "single char32_t character expected"); return false; } @@ -1073,24 +1073,24 @@ bool cpyrt::Char32Converter::ToMemory(PyObject* value, void* address, } //---------------------------------------------------------------------------- -CPPJIT_IMPL_BASIC_CONVERTER_IB(Int8, int8_t, long, c_int8, PyInt_FromLong, +CPPJIT_IMPL_BASIC_CONVERTER_IB(Int8, int8_t, long, c_int8, PyLong_FromLong, cpyrt_PyLong_AsInt8, 'l') -CPPJIT_IMPL_BASIC_CONVERTER_IB(UInt8, uint8_t, long, c_uint8, PyInt_FromLong, +CPPJIT_IMPL_BASIC_CONVERTER_IB(UInt8, uint8_t, long, c_uint8, PyLong_FromLong, cpyrt_PyLong_AsUInt8, 'l') -CPPJIT_IMPL_BASIC_CONVERTER_IB(Int16, int16_t, long, c_int16, PyInt_FromLong, +CPPJIT_IMPL_BASIC_CONVERTER_IB(Int16, int16_t, long, c_int16, PyLong_FromLong, cpyrt_PyLong_AsInt16, 'l') -CPPJIT_IMPL_BASIC_CONVERTER_IB(UInt16, uint16_t, long, c_uint16, PyInt_FromLong, - cpyrt_PyLong_AsUInt16, 'l') -CPPJIT_IMPL_BASIC_CONVERTER_IB(Int32, int32_t, long, c_int32, PyInt_FromLong, +CPPJIT_IMPL_BASIC_CONVERTER_IB(UInt16, uint16_t, long, c_uint16, + PyLong_FromLong, cpyrt_PyLong_AsUInt16, 'l') +CPPJIT_IMPL_BASIC_CONVERTER_IB(Int32, int32_t, long, c_int32, PyLong_FromLong, cpyrt_PyLong_AsInt32, 'l') CPPJIT_IMPL_BASIC_CONVERTER_IB(UInt32, uint32_t, unsigned long, c_uint32, PyLong_FromUnsignedLong, cpyrt_PyLong_AsUInt32, 'l') -CPPJIT_IMPL_BASIC_CONVERTER_IB(Short, short, long, c_short, PyInt_FromLong, +CPPJIT_IMPL_BASIC_CONVERTER_IB(Short, short, long, c_short, PyLong_FromLong, cpyrt_PyLong_AsShort, 'l') CPPJIT_IMPL_BASIC_CONVERTER_IB(UShort, unsigned short, long, c_ushort, - PyInt_FromLong, cpyrt_PyLong_AsUShort, 'l') -CPPJIT_IMPL_BASIC_CONVERTER_IB(Int, int, long, c_uint, PyInt_FromLong, + PyLong_FromLong, cpyrt_PyLong_AsUShort, 'l') +CPPJIT_IMPL_BASIC_CONVERTER_IB(Int, int, long, c_uint, PyLong_FromLong, cpyrt_PyLong_AsStrictInt, 'l') //---------------------------------------------------------------------------- @@ -1315,7 +1315,7 @@ bool cpyrt::EnumConverter::SetArg(PyObject* pyobject, Parameter& para, PyObject* pycppname = PyObject_GetAttr((PyObject*)Py_TYPE(pyobject), PyStrings::gCppName); if (pycppname) { - exact = (fEnumName == cpyrt_PyText_AsString(pycppname)); + exact = (fEnumName == PyUnicode_AsUTF8(pycppname)); Py_DECREF(pycppname); } else PyErr_Clear(); @@ -1391,16 +1391,15 @@ PyObject* cpyrt::CStringConverter::FromMemory(void* address) { if (address && *(void**)address) { if (fMaxSize != std::string::npos) // need to prevent reading beyond boundary - return cpyrt_PyText_FromStringAndSize(*(char**)address, - (Py_ssize_t)fMaxSize); + return PyUnicode_FromStringAndSize(*(char**)address, + (Py_ssize_t)fMaxSize); if (*(void**)address == (void*)fBuffer.data()) // if we're buffering, we know the size - return cpyrt_PyText_FromStringAndSize((char*)fBuffer.data(), - fBuffer.size()); + return PyUnicode_FromStringAndSize((char*)fBuffer.data(), fBuffer.size()); // no idea about lentgth: cut on \0 - return cpyrt_PyText_FromString(*(char**)address); + return PyUnicode_FromString(*(char**)address); } // empty string in case there's no address @@ -1458,12 +1457,12 @@ bool cpyrt::CStringConverter::ToMemory(PyObject* value, void* address, bool cpyrt::WCStringConverter::SetArg(PyObject* pyobject, Parameter& para, CallContext* /* ctxt */) { // construct a new string and copy it in new memory - Py_ssize_t len = PyUnicode_GetSize(pyobject); + Py_ssize_t len = PyUnicode_GetLength(pyobject); if (len == (Py_ssize_t)-1 && PyErr_Occurred()) return false; fBuffer = (wchar_t*)realloc(fBuffer, sizeof(wchar_t) * (len + 1)); - Py_ssize_t res = cpyrt_PyUnicode_AsWideChar(pyobject, fBuffer, len); + Py_ssize_t res = PyUnicode_AsWideChar(pyobject, fBuffer, len); if (res == -1) return false; // could free the buffer here @@ -1493,7 +1492,7 @@ PyObject* cpyrt::WCStringConverter::FromMemory(void* address) { bool cpyrt::WCStringConverter::ToMemory(PyObject* value, void* address, PyObject* /* ctxt */) { // convert to C++ wchar_t*, write it at
- Py_ssize_t len = PyUnicode_GetSize(value); + Py_ssize_t len = PyUnicode_GetLength(value); if (len == (Py_ssize_t)-1 && PyErr_Occurred()) return false; @@ -1506,11 +1505,11 @@ bool cpyrt::WCStringConverter::ToMemory(PyObject* value, void* address, Py_ssize_t res = -1; if (fMaxSize != std::wstring::npos) - res = cpyrt_PyUnicode_AsWideChar(value, *(wchar_t**)address, - (Py_ssize_t)fMaxSize); + res = + PyUnicode_AsWideChar(value, *(wchar_t**)address, (Py_ssize_t)fMaxSize); else // coverity[secure_coding] - can't help it, it's intentional. - res = cpyrt_PyUnicode_AsWideChar(value, *(wchar_t**)address, len); + res = PyUnicode_AsWideChar(value, *(wchar_t**)address, len); if (res == -1) return false; @@ -1610,8 +1609,7 @@ PyObject* cpyrt::NonConstCStringConverter::FromMemory(void* address) { // assume this is a buffer access if the size is known; otherwise assume // string if (fMaxSize != std::string::npos) - return cpyrt_PyText_FromStringAndSize(*(char**)address, - (Py_ssize_t)fMaxSize); + return PyUnicode_FromStringAndSize(*(char**)address, (Py_ssize_t)fMaxSize); return this->CStringConverter::FromMemory(address); } @@ -1625,7 +1623,7 @@ bool cpyrt::VoidArrayConverter::GetAddressSpecialCase(PyObject* pyobject, } // (2): allow integer zero to act as a null pointer (C NULL), no deriveds - if (PyInt_CheckExact(pyobject) || PyLong_CheckExact(pyobject)) { + if (PyLong_CheckExact(pyobject)) { intptr_t val = (intptr_t)PyLong_AsLongLong(pyobject); if (val == 0l) { address = (void*)val; @@ -1636,8 +1634,8 @@ bool cpyrt::VoidArrayConverter::GetAddressSpecialCase(PyObject* pyobject, } // (3): opaque PyCapsule (CObject in older pythons) from somewhere - if (cpyrt_PyCapsule_CheckExact(pyobject)) { - address = (void*)cpyrt_PyCapsule_GetPointer(pyobject, nullptr); + if (PyCapsule_CheckExact(pyobject)) { + address = (void*)PyCapsule_GetPointer(pyobject, nullptr); return true; } @@ -2016,7 +2014,7 @@ bool cpyrt::CStringArrayConverter::SetArg(PyObject* pyobject, Parameter& para, para.fTypeCode = 'V'; return true; - } else if (PySequence_Check(pyobject) && !cpyrt_PyText_Check(pyobject) && + } else if (PySequence_Check(pyobject) && !PyUnicode_Check(pyobject) && !PyBytes_Check(pyobject)) { // for (auto& p : fBuffer) free(p); fBuffer.clear(); @@ -2135,7 +2133,7 @@ static inline bool cpyrt_PyUnicodeAsBytes2Buffer(PyObject* pyobject, } \ \ PyErr_Clear(); \ - if (!(PyInt_Check(pyobject) || PyLong_Check(pyobject))) { \ + if (!PyLong_Check(pyobject)) { \ bool result = InstanceConverter::SetArg(pyobject, para, ctxt); \ para.fTypeCode = 'V'; \ return result; \ @@ -2167,15 +2165,15 @@ cpyrt::STLWStringConverter::STLWStringConverter(bool keepControl) bool cpyrt::STLWStringConverter::SetArg(PyObject* pyobject, Parameter& para, CallContext* ctxt) { if (PyUnicode_Check(pyobject)) { - Py_ssize_t len = cpyrt_PyUnicode_GET_SIZE(pyobject); + Py_ssize_t len = PyUnicode_GET_LENGTH(pyobject); fBuffer.resize(len); - cpyrt_PyUnicode_AsWideChar(pyobject, &fBuffer[0], len); + PyUnicode_AsWideChar(pyobject, &fBuffer[0], len); para.fValue.fVoidp = &fBuffer; para.fTypeCode = 'V'; return true; } - if (!(PyInt_Check(pyobject) || PyLong_Check(pyobject))) { + if (!PyLong_Check(pyobject)) { bool result = InstancePtrConverter::SetArg(pyobject, para, ctxt); para.fTypeCode = 'V'; return result; @@ -2195,9 +2193,9 @@ PyObject* cpyrt::STLWStringConverter::FromMemory(void* address) { bool cpyrt::STLWStringConverter::ToMemory(PyObject* value, void* address, PyObject* ctxt) { if (PyUnicode_Check(value)) { - Py_ssize_t len = cpyrt_PyUnicode_GET_SIZE(value); + Py_ssize_t len = PyUnicode_GET_LENGTH(value); wchar_t* buf = new wchar_t[len + 1]; - cpyrt_PyUnicode_AsWideChar(value, buf, len); + PyUnicode_AsWideChar(value, buf, len); *((std::wstring*)address) = std::wstring(buf, len); delete[] buf; return true; @@ -2212,7 +2210,7 @@ cpyrt::STLStringViewConverter::STLStringViewConverter(bool keepControl) bool cpyrt::STLStringViewConverter::SetArg(PyObject* pyobject, Parameter& para, CallContext* ctxt) { // normal instance convertion (eg. string_view object passed) - if (!PyInt_Check(pyobject) && !PyLong_Check(pyobject)) { + if (!PyLong_Check(pyobject)) { CallContextRAII noimp(ctxt); if (InstanceConverter::SetArg(pyobject, para, ctxt)) { para.fTypeCode = 'V'; @@ -2896,7 +2894,7 @@ static void* PyFunction_AsCPointer(PyObject* pyobject, TemplateProxy* pytmpl = (TemplateProxy*)pyobject; std::string fullname = pytmpl->fTI->fCppName; if (pytmpl->fTemplateArgs) - fullname += cpyrt_PyText_AsString(pytmpl->fTemplateArgs); + fullname += PyUnicode_AsUTF8(pytmpl->fTemplateArgs); interop::TCppScope_t scope = ((CPPClass*)pytmpl->fTI->fPyClass)->fCppType; std::string ret{}, sig{}; GetSignatureFromFnType(fn_type, ret, sig); @@ -3314,7 +3312,7 @@ bool cpyrt::InitializerListConverter::SetArg(PyObject* pyobject, // meant to be a syntactic thing, so only _python_ sequences are allowed; // bound C++ proxies (likely explicitly created std::initializer_list, go // through an instance converter - if (!PySequence_Check(pyobject) || cpyrt_PyText_Check(pyobject) || + if (!PySequence_Check(pyobject) || PyUnicode_Check(pyobject) || PyBytes_Check(pyobject)) return false; @@ -3729,6 +3727,11 @@ cppjit::cpyrt::CreateConverter(interop::TCppType_t type, cdims_t dims) { // resolved type is its underlying integer, which carries the conversion if (interop::IsEnumType(type) && fullType != "std::byte") { h = gConvFactories.find(resolvedTypeStr); + // the underlying integer may be spelled through a typedef (e.g. + // `enum E : std::int32_t`), so fall back to its canonical spelling + if (h == gConvFactories.end()) + h = gConvFactories.find( + interop::GetTypeAsString(interop::ResolveType(resolvedType))); if (h != gConvFactories.end()) return new EnumConverter( (h->second)(dims), diff --git a/bindings/pyroot/cppjit/src/cpyrt/CustomPyTypes.cxx b/bindings/pyroot/cppjit/src/cpyrt/CustomPyTypes.cxx index dcb64b8b619cf..a535be1dff77b 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/CustomPyTypes.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/CustomPyTypes.cxx @@ -34,7 +34,7 @@ static PyObject* tptc_call(typedefpointertoclassobject* self, PyObject* args, //----------------------------------------------------------------------------- static PyObject* tptc_getcppname(typedefpointertoclassobject* self, void*) { - return cpyrt_PyText_FromString( + return PyUnicode_FromString( (interop::GetScopedFinalName(self->fCppType) + "*").c_str()); } @@ -47,7 +47,7 @@ static PyObject* tptc_name(typedefpointertoclassobject* self, void*) { return pyname; } - return cpyrt_PyText_FromString("*"); + return PyUnicode_FromString("*"); } //----------------------------------------------------------------------------- @@ -205,7 +205,7 @@ static PyObject* im_call(PyObject* meth, PyObject* args, PyObject* kw) { // GIL) Py_INCREF(self); func->m_self = self; - PyObject* result = cpyrt_PyCFunction_Call((PyObject*)func, args, kw); + PyObject* result = PyObject_Call((PyObject*)func, args, kw); func->m_self = nullptr; Py_DECREF(self); Py_DECREF(args); @@ -250,8 +250,7 @@ PyTypeObject CustomInstanceMethod_Type = { 0, 0, 0, - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | - Py_TPFLAGS_BASETYPE, // tp_flags + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags (char*)"cpyrt custom instance method (internal)", // tp_doc 0, 0, diff --git a/bindings/pyroot/cppjit/src/cpyrt/Dispatcher.cxx b/bindings/pyroot/cppjit/src/cpyrt/Dispatcher.cxx index 9017ae626cb54..40b1dbdfa74b7 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/Dispatcher.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/Dispatcher.cxx @@ -339,7 +339,7 @@ bool cpyrt::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, std::string mtCppName = interop::GetName(interop::TCppScope_t(method.data)); - PyObject* key = cpyrt_PyText_FromString(mtCppName.c_str()); + PyObject* key = PyUnicode_FromString(mtCppName.c_str()); int contains = PyDict_Contains(dct, key); if (contains == -1) PyErr_Clear(); @@ -423,7 +423,7 @@ bool cpyrt::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, // TODO: should probably invert this looping; but that makes handling // overloads clunky PyObject* key = PyList_GET_ITEM(keys, i); - std::string mtCppName = cpyrt_PyText_AsString(key); + std::string mtCppName = PyUnicode_AsUTF8(key); const auto& methods = FindBaseMethod(tbase, mtCppName); for (auto method : methods) InjectMethod(method, mtCppName, code); diff --git a/bindings/pyroot/cppjit/src/cpyrt/Executors.cxx b/bindings/pyroot/cppjit/src/cpyrt/Executors.cxx index 02c59c3ca963d..6fc105e417f9f 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/Executors.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/Executors.cxx @@ -122,8 +122,8 @@ static inline PyObject* cpyrt_PyText_FromLong(long cl) { } int c = (int)cl; if (c < 0) - return cpyrt_PyText_FromFormat("%c", 256 - std::abs(c)); - return cpyrt_PyText_FromFormat("%c", c); + return PyUnicode_FromFormat("%c", 256 - std::abs(c)); + return PyUnicode_FromFormat("%c", c); } static inline PyObject* cpyrt_PyText_FromULong(unsigned long uc) { @@ -133,7 +133,7 @@ static inline PyObject* cpyrt_PyText_FromULong(unsigned long uc) { return nullptr; } int c = (int)uc; - return cpyrt_PyText_FromFormat("%c", c); + return PyUnicode_FromFormat("%c", c); } static inline PyObject* cpyrt_PyBool_FromLong(long b) { @@ -206,14 +206,14 @@ PyObject* cpyrt::UCharConstRefExecutor::Execute(interop::TCppMethod_t method, PyObject* cpyrt::Int8ConstRefExecutor::Execute(interop::TCppMethod_t method, interop::TCppObject_t self, CallContext* ctxt) { - return PyInt_FromLong(*((int8_t*)GILCallR(method, self, ctxt))); + return PyLong_FromLong(*((int8_t*)GILCallR(method, self, ctxt))); } //---------------------------------------------------------------------------- PyObject* cpyrt::UInt8ConstRefExecutor::Execute(interop::TCppMethod_t method, interop::TCppObject_t self, CallContext* ctxt) { - return PyInt_FromLong(*((uint8_t*)GILCallR(method, self, ctxt))); + return PyLong_FromLong(*((uint8_t*)GILCallR(method, self, ctxt))); } //---------------------------------------------------------------------------- @@ -254,7 +254,7 @@ PyObject* cpyrt::IntExecutor::Execute(interop::TCppMethod_t method, CallContext* ctxt) { // execute with argument , construct python int return // value - return PyInt_FromLong((int)GILCallI(method, self, ctxt)); + return PyLong_FromLong((int)GILCallI(method, self, ctxt)); } //---------------------------------------------------------------------------- @@ -263,7 +263,7 @@ PyObject* cpyrt::Int8Executor::Execute(interop::TCppMethod_t method, CallContext* ctxt) { // execute with argument , construct python int return // value - return PyInt_FromLong((int8_t)GILCallC(method, self, ctxt)); + return PyLong_FromLong((int8_t)GILCallC(method, self, ctxt)); } //---------------------------------------------------------------------------- @@ -272,7 +272,7 @@ PyObject* cpyrt::UInt8Executor::Execute(interop::TCppMethod_t method, CallContext* ctxt) { // execute with argument , construct python int return // value - return PyInt_FromLong((uint8_t)GILCallB(method, self, ctxt)); + return PyLong_FromLong((uint8_t)GILCallB(method, self, ctxt)); } //---------------------------------------------------------------------------- @@ -281,7 +281,7 @@ PyObject* cpyrt::ShortExecutor::Execute(interop::TCppMethod_t method, CallContext* ctxt) { // execute with argument , construct python int return // value - return PyInt_FromLong((short)GILCallH(method, self, ctxt)); + return PyLong_FromLong((short)GILCallH(method, self, ctxt)); } //---------------------------------------------------------------------------- @@ -390,11 +390,11 @@ bool cpyrt::RefExecutor::SetAssignable(PyObject* pyobject) { CPPJIT_IMPL_REFEXEC(Bool, bool, long, cpyrt_PyBool_FromLong, PyLong_AsLong) CPPJIT_IMPL_REFEXEC(Char, char, long, cpyrt_PyText_FromLong, PyLong_AsLong) CPPJIT_IMPL_REFEXEC(UChar, unsigned char, unsigned long, cpyrt_PyText_FromULong, PyLongOrInt_AsULong) -CPPJIT_IMPL_REFEXEC(Int8, int8_t, long, PyInt_FromLong, PyLong_AsLong) -CPPJIT_IMPL_REFEXEC(UInt8, uint8_t, unsigned long, PyInt_FromLong, PyLongOrInt_AsULong) -CPPJIT_IMPL_REFEXEC(Short, short, long, PyInt_FromLong, PyLong_AsLong) -CPPJIT_IMPL_REFEXEC(UShort, unsigned short, unsigned long, PyInt_FromLong, PyLongOrInt_AsULong) -CPPJIT_IMPL_REFEXEC(Int, int, long, PyInt_FromLong, PyLong_AsLong) +CPPJIT_IMPL_REFEXEC(Int8, int8_t, long, PyLong_FromLong, PyLong_AsLong) +CPPJIT_IMPL_REFEXEC(UInt8, uint8_t, unsigned long, PyLong_FromLong, PyLongOrInt_AsULong) +CPPJIT_IMPL_REFEXEC(Short, short, long, PyLong_FromLong, PyLong_AsLong) +CPPJIT_IMPL_REFEXEC(UShort, unsigned short, unsigned long, PyLong_FromLong, PyLongOrInt_AsULong) +CPPJIT_IMPL_REFEXEC(Int, int, long, PyLong_FromLong, PyLong_AsLong) CPPJIT_IMPL_REFEXEC(UInt, unsigned int, unsigned long, PyLong_FromUnsignedLong, PyLongOrInt_AsULong) CPPJIT_IMPL_REFEXEC(Long, long, long, PyLong_FromLong, PyLong_AsLong) CPPJIT_IMPL_REFEXEC(ULong, unsigned long, unsigned long, PyLong_FromUnsignedLong, PyLongOrInt_AsULong) @@ -427,16 +427,16 @@ PyObject* cpyrt::STLStringRefExecutor::Execute(interop::TCppMethod_t method, // value std::string* result = (std::string*)GILCallR(method, self, ctxt); if (!fAssignable) { - return cpyrt_PyText_FromStringAndSize(result->c_str(), result->size()); + return PyUnicode_FromStringAndSize(result->c_str(), result->size()); } - if (!cpyrt_PyText_Check(fAssignable)) { + if (!PyUnicode_Check(fAssignable)) { PyErr_Format(PyExc_TypeError, "wrong type in assignment (string expected)"); return nullptr; } - *result = std::string(cpyrt_PyText_AsString(fAssignable), - cpyrt_PyText_GET_SIZE(fAssignable)); + *result = std::string(PyUnicode_AsUTF8(fAssignable), + PyUnicode_GET_LENGTH(fAssignable)); Py_DECREF(fAssignable); fAssignable = nullptr; @@ -467,7 +467,7 @@ PyObject* cpyrt::CStringExecutor::Execute(interop::TCppMethod_t method, return PyStrings::gEmptyString; } - return cpyrt_PyText_FromString(result); + return PyUnicode_FromString(result); } //---------------------------------------------------------------------------- @@ -482,7 +482,7 @@ PyObject* cpyrt::CStringRefExecutor::Execute(interop::TCppMethod_t method, return PyStrings::gEmptyString; } - return cpyrt_PyText_FromString(*result); + return PyUnicode_FromString(*result); } //---------------------------------------------------------------------------- @@ -627,7 +627,7 @@ PyObject* cpyrt::STLStringExecutor::Execute(interop::TCppMethod_t method, } PyObject* pyresult = - cpyrt_PyText_FromStringAndSize(result->c_str(), result->size()); + PyUnicode_FromStringAndSize(result->c_str(), result->size()); delete result; // interop::CallO allocates and constructs a string, so it must // be properly destroyed @@ -719,9 +719,9 @@ PyObject* cpyrt::InstanceRefExecutor::Execute(interop::TCppMethod_t method, if (!assign) { PyErr_Clear(); PyObject* descr = PyObject_Str(result); - if (descr && cpyrt_PyText_CheckExact(descr)) { + if (descr && PyUnicode_CheckExact(descr)) { PyErr_Format(PyExc_TypeError, "cannot assign to return object (%s)", - cpyrt_PyText_AsString(descr)); + PyUnicode_AsUTF8(descr)); } else { PyErr_SetString(PyExc_TypeError, "cannot assign to result"); } @@ -754,7 +754,7 @@ static inline PyObject* SetInstanceCheckError(PyObject* pyobj) { PyObject* pystr = PyObject_Str(pyobj); if (pystr) { PyErr_Format(PyExc_TypeError, "C++ object expected, got %s", - cpyrt_PyText_AsString(pystr)); + PyUnicode_AsUTF8(pystr)); Py_DECREF(pystr); } else PyErr_SetString(PyExc_TypeError, "C++ object expected"); diff --git a/bindings/pyroot/cppjit/src/cpyrt/LowLevelViews.cxx b/bindings/pyroot/cppjit/src/cpyrt/LowLevelViews.cxx index 11f725c633082..d4d81fa536a43 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/LowLevelViews.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/LowLevelViews.cxx @@ -111,7 +111,7 @@ CPYRT_LL_FLAG_GETSET(cpparray, cpyrt::LowLevelView::kIsCppArray, __cpp_array__) //--------------------------------------------------------------------------- static PyObject* ll_typecode(cpyrt::LowLevelView* self, void*) { - return cpyrt_PyText_FromString((char*)self->fBufInfo.format); + return PyUnicode_FromString((char*)self->fBufInfo.format); } //- Copy memoryview buffers ================================================= @@ -694,7 +694,7 @@ static PyObject* ll_shape(cpyrt::LowLevelView* self) { if (!shape) return nullptr; for (Py_ssize_t idim = 0; idim < view.ndim; ++idim) { - PyObject* pydim = PyInt_FromSsize_t(view.shape[idim]); + PyObject* pydim = PyLong_FromSsize_t(view.shape[idim]); if (!pydim) { Py_DECREF(shape); return nullptr; @@ -734,7 +734,7 @@ static PyObject* ll_reshape(cpyrt::LowLevelView* self, PyObject* shape) { PyObject* pystr = PyObject_Str(shape); if (pystr) { PyErr_Format(PyExc_TypeError, "tuple object expected, received %s", - cpyrt_PyText_AsStringChecked(pystr)); + PyUnicode_AsUTF8(pystr)); Py_DECREF(pystr); return nullptr; } @@ -762,7 +762,7 @@ static PyObject* ll_reshape(cpyrt::LowLevelView* self, PyObject* shape) { Py_ssize_t ndim = PyTuple_GET_SIZE(shape); cpyrt::dims_t dims(ndim); for (Py_ssize_t idim = 0; idim < ndim; ++idim) { - Py_ssize_t nlen = PyInt_AsSsize_t(PyTuple_GET_ITEM(shape, idim)); + Py_ssize_t nlen = PyLong_AsSsize_t(PyTuple_GET_ITEM(shape, idim)); if (nlen == -1 && PyErr_Occurred()) return nullptr; if (nlen < cpyrt::UNKNOWN_SIZE) { @@ -893,7 +893,7 @@ static PyObject* ll_as_string(cpyrt::LowLevelView* self) { char* buf = (char*)self->get_buf(); size_t sz = strnlen(buf, (size_t)view.shape[0]); - return cpyrt_PyText_FromStringAndSize(buf, sz); + return PyUnicode_FromStringAndSize(buf, sz); } //--------------------------------------------------------------------------- @@ -921,57 +921,57 @@ static PyGetSetDef ll_getset[] = { namespace cppjit::cpyrt { //= cpyrt low level view type ============================================ -PyTypeObject LowLevelView_Type = {PyVarObject_HEAD_INIT(&PyType_Type, 0)( - char*) "cppjit.LowLevelView", // tp_name - sizeof(cpyrt::LowLevelView), // tp_basicsize - 0, // tp_itemsize - (destructor)ll_dealloc, // tp_dealloc - 0, // tp_vectorcall_offset / tp_print - 0, // tp_getattr - 0, // tp_setattr - 0, // itp_as_async / tp_compare - 0, // tp_repr - 0, // tp_as_number - &ll_as_sequence, // tp_as_sequence - &ll_as_mapping, // tp_as_mapping - 0, // tp_hash - 0, // tp_call - 0, // tp_str - 0, // tp_getattro - 0, // tp_setattro - &ll_as_buffer, // tp_as_buffer - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | - Py_TPFLAGS_BASETYPE, // tp_flags - (char*)"memory view on C++ pointer", // tp_doc - 0, // tp_traverse - 0, // tp_clear - 0, // tp_richcompare - 0, // tp_weaklistoffset - (getiterfunc)ll_iter, // tp_iter - 0, // tp_iternext - ll_methods, // tp_methods - 0, // tp_members - ll_getset, // tp_getset - 0, // tp_base - 0, // tp_dict - 0, // tp_descr_get - 0, // tp_descr_set - 0, // tp_dictoffset - 0, // tp_init - 0, // tp_alloc - (newfunc)ll_new, // tp_new - 0, // tp_free - 0, // tp_is_gc - 0, // tp_bases - 0, // tp_mro - 0, // tp_cache - 0, // tp_subclasses - 0, // tp_weaklist - 0, // tp_del - 0, // tp_version_tag - 0, // tp_finalize - 0 // tp_vectorcall - CPYRT_PYTYPE_TAIL}; +PyTypeObject LowLevelView_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, + 0)(char*) "cppjit.LowLevelView", // tp_name + sizeof(cpyrt::LowLevelView), // tp_basicsize + 0, // tp_itemsize + (destructor)ll_dealloc, // tp_dealloc + 0, // tp_vectorcall_offset / tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // itp_as_async / tp_compare + 0, // tp_repr + 0, // tp_as_number + &ll_as_sequence, // tp_as_sequence + &ll_as_mapping, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + &ll_as_buffer, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags + (char*)"memory view on C++ pointer", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + (getiterfunc)ll_iter, // tp_iter + 0, // tp_iternext + ll_methods, // tp_methods + 0, // tp_members + ll_getset, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + 0, // tp_alloc + (newfunc)ll_new, // tp_new + 0, // tp_free + 0, // tp_is_gc + 0, // tp_bases + 0, // tp_mro + 0, // tp_cache + 0, // tp_subclasses + 0, // tp_weaklist + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall + CPYRT_PYTYPE_TAIL}; } // namespace cppjit::cpyrt diff --git a/bindings/pyroot/cppjit/src/cpyrt/MemoryRegulator.cxx b/bindings/pyroot/cppjit/src/cpyrt/MemoryRegulator.cxx index 6ea76716f431f..a7a06c63b20d8 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/MemoryRegulator.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/MemoryRegulator.cxx @@ -44,7 +44,7 @@ struct Initcpyrt_NoneType_t { ((PyVarObject&)cpyrt_NoneType).ob_size = 0; cpyrt_NoneType.tp_name = const_cast("cpyrt_NoneType"); - cpyrt_NoneType.tp_flags = Py_TPFLAGS_HAVE_RICHCOMPARE; + cpyrt_NoneType.tp_flags = 0; cpyrt_NoneType.tp_traverse = (traverseproc)0; cpyrt_NoneType.tp_clear = (inquiry)0; diff --git a/bindings/pyroot/cppjit/src/cpyrt/ProxyWrappers.cxx b/bindings/pyroot/cppjit/src/cpyrt/ProxyWrappers.cxx index e1cc9e93344a0..0b8f9c5fcf7f6 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/ProxyWrappers.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/ProxyWrappers.cxx @@ -94,7 +94,7 @@ static inline void AddPropertyToClass(PyObject* pyclass, interop::TCppScope_t scope, interop::TCppScope_t data) { cpyrt::CPPDataMember* property = cpyrt::CPPDataMember_New(scope, data); - PyObject* pname = cpyrt_PyText_InternFromString( + PyObject* pname = PyUnicode_InternFromString( const_cast(property->GetName().c_str())); // allow access at the instance level @@ -121,7 +121,7 @@ static inline void AddPropertyToClass(PyObject* pyclass, static inline void AddScopeToParent(PyObject* parent, const std::string& name, PyObject* newscope) { - PyObject* pyname = cpyrt_PyText_InternFromString((char*)name.c_str()); + PyObject* pyname = PyUnicode_InternFromString((char*)name.c_str()); if (CPPScope_Check(parent)) PyType_Type.tp_setattro(parent, pyname, newscope); else @@ -149,7 +149,7 @@ static inline void sync_templates(PyObject* pyclass, const std::string& mtCppName, const std::string& mtName) { PyObject* dct = PyObject_GetAttr(pyclass, PyStrings::gDict); - PyObject* pyname = cpyrt_PyText_FromString(const_cast(mtName.c_str())); + PyObject* pyname = PyUnicode_FromString(const_cast(mtName.c_str())); PyObject* attr = PyObject_GetItem(dct, pyname); if (!attr) PyErr_Clear(); @@ -399,7 +399,7 @@ static int BuildScopeProxyDict(interop::TCppScope_t scope, PyObject* pyclass, // add this method flagged as a generic one (to be picked up by the // templated one as appropriate) if a template exists PyObject* pyname = - cpyrt_PyText_FromString(const_cast(imd->first.c_str())); + PyUnicode_FromString(const_cast(imd->first.c_str())); PyObject* attr = PyObject_GetItem(dct, pyname); Py_DECREF(pyname); if (TemplateProxy_Check(attr)) { @@ -411,7 +411,7 @@ static int BuildScopeProxyDict(interop::TCppScope_t scope, PyObject* pyclass, PyErr_Clear(); // normal case, add a new method CPPOverload* method = CPPOverload_New(imd->first, imd->second); - PyObject* pymname = cpyrt_PyText_InternFromString( + PyObject* pymname = PyUnicode_InternFromString( const_cast(method->GetName().c_str())); PyType_Type.tp_setattro(pyclass, pymname, (PyObject*)method); Py_DECREF(pymname); @@ -597,7 +597,7 @@ PyObject* CppType_To_PyObject(interop::TCppType_t type, std::string name, //---------------------------------------------------------------------------- PyObject* cpyrt::CreateScopeProxy(PyObject*, PyObject* args) { // Build a python shadow class for the named C++ class. - std::string cname = cpyrt_PyText_AsString(PyTuple_GetItem(args, 0)); + std::string cname = PyUnicode_AsUTF8(PyTuple_GetItem(args, 0)); if (PyErr_Occurred()) return nullptr; @@ -623,7 +623,7 @@ PyObject* cpyrt::CreateScopeProxy(const std::string& name, PyObject* parent, } // should be a string - std::string scName = cpyrt_PyText_AsString(parname); + std::string scName = PyUnicode_AsUTF8(parname); Py_DECREF(parname); if (PyErr_Occurred()) return nullptr; @@ -771,9 +771,9 @@ PyObject* cpyrt::CreateScopeProxy(interop::TCppScope_t scope, PyObject* parent, } else { // add to sys.modules to allow importing from this namespace PyObject* pyfullname = PyObject_GetAttr(pyscope, PyStrings::gModule); - cpyrt_PyText_AppendAndDel(&pyfullname, cpyrt_PyText_FromString(".")); - cpyrt_PyText_AppendAndDel(&pyfullname, - PyObject_GetAttr(pyscope, PyStrings::gName)); + PyUnicode_AppendAndDel(&pyfullname, PyUnicode_FromString(".")); + PyUnicode_AppendAndDel(&pyfullname, + PyObject_GetAttr(pyscope, PyStrings::gName)); PyObject* modules = PySys_GetObject(const_cast("modules")); if (modules && PyDict_Check(modules)) PyDict_SetItem(modules, pyfullname, pyscope); diff --git a/bindings/pyroot/cppjit/src/cpyrt/PyException.cxx b/bindings/pyroot/cppjit/src/cpyrt/PyException.cxx index 5840faf1dc983..58fe7d42d607a 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/PyException.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/PyException.cxx @@ -51,7 +51,7 @@ cppjit::cpyrt::PyException::PyException() { PyObject* msg = PyObject_Str(pyvalue); if (msg) { - fMsg += cpyrt_PyText_AsString(msg); + fMsg += PyUnicode_AsUTF8(msg); Py_DECREF(msg); } } @@ -69,13 +69,13 @@ cppjit::cpyrt::PyException::PyException() { Py_DECREF(code); PyObject* filenameStr = PyObject_Str(filename); - locFile = cpyrt_PyText_AsString(filenameStr); + locFile = PyUnicode_AsUTF8(filenameStr); Py_DECREF(filenameStr); Py_DECREF(filename); PyObject* name = PyObject_GetAttrString(code, "co_name"); PyObject* nameStr = PyObject_Str(name); - locName = cpyrt_PyText_AsString(nameStr); + locName = PyUnicode_AsUTF8(nameStr); Py_DECREF(nameStr); Py_DECREF(name); diff --git a/bindings/pyroot/cppjit/src/cpyrt/PyObjectDir27.inc b/bindings/pyroot/cppjit/src/cpyrt/PyObjectDir27.inc index 88f276215b3fd..a06ec272faa15 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/PyObjectDir27.inc +++ b/bindings/pyroot/cppjit/src/cpyrt/PyObjectDir27.inc @@ -130,29 +130,27 @@ _specialized_dir_type(PyObject *obj) static PyObject* _generic_dir(PyObject *obj) { - if (PyType_Check(obj) || PyClass_Check(obj)) - return _specialized_dir_type(obj); - - PyObject *result = NULL; - PyObject *dict = NULL; - PyObject *itsclass = NULL; - - /* Get __dict__ (which may or may not be a real dict...) */ - dict = PyObject_GetAttrString(obj, "__dict__"); - if (dict == NULL) { - PyErr_Clear(); - dict = PyDict_New(); - } - else if (!PyDict_Check(dict)) { - Py_DECREF(dict); - dict = PyDict_New(); - } - else { - /* Copy __dict__ to avoid mutating it. */ - PyObject *temp = PyDict_Copy(dict); - Py_DECREF(dict); - dict = temp; - } + if (PyType_Check(obj)) + return _specialized_dir_type(obj); + + PyObject* result = NULL; + PyObject* dict = NULL; + PyObject* itsclass = NULL; + + /* Get __dict__ (which may or may not be a real dict...) */ + dict = PyObject_GetAttrString(obj, "__dict__"); + if (dict == NULL) { + PyErr_Clear(); + dict = PyDict_New(); + } else if (!PyDict_Check(dict)) { + Py_DECREF(dict); + dict = PyDict_New(); + } else { + /* Copy __dict__ to avoid mutating it. */ + PyObject* temp = PyDict_Copy(dict); + Py_DECREF(dict); + dict = temp; + } if (dict == NULL) goto error; diff --git a/bindings/pyroot/cppjit/src/cpyrt/PyStrings.cxx b/bindings/pyroot/cppjit/src/cpyrt/PyStrings.cxx index 132878c7c5c1c..a0e957f3787f9 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/PyStrings.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/PyStrings.cxx @@ -82,7 +82,7 @@ PyObject* cpyrt::PyStrings::gFromBuffer = nullptr; //----------------------------------------------------------------------------- #define CPPJIT_INITIALIZE_STRING(var, str) \ - if (!(PyStrings::var = cpyrt_PyText_InternFromString((char*)#str))) \ + if (!(PyStrings::var = PyUnicode_InternFromString((char*)#str))) \ return false bool cpyrt::CreatePyStrings() { @@ -102,7 +102,7 @@ bool cpyrt::CreatePyStrings() { CPPJIT_INITIALIZE_STRING(gPreInc, __preinc__); CPPJIT_INITIALIZE_STRING(gPostInc, __postinc__); CPPJIT_INITIALIZE_STRING(gDict, __dict__); - if (!(PyStrings::gEmptyString = cpyrt_PyText_FromString((char*)""))) + if (!(PyStrings::gEmptyString = PyUnicode_FromString((char*)""))) return false; CPPJIT_INITIALIZE_STRING(gEq, __eq__); CPPJIT_INITIALIZE_STRING(gFollow, __follow__); @@ -131,7 +131,7 @@ bool cpyrt::CreatePyStrings() { CPPJIT_INITIALIZE_STRING(gAdd, __add__); CPPJIT_INITIALIZE_STRING(gSub, __sub__); CPPJIT_INITIALIZE_STRING(gMul, __mul__); - CPPJIT_INITIALIZE_STRING(gDiv, CPPJIT__div__); + CPPJIT_INITIALIZE_STRING(gDiv, __truediv__); CPPJIT_INITIALIZE_STRING(gLShift, __lshift__); CPPJIT_INITIALIZE_STRING(gLShiftC, __lshiftc__); diff --git a/bindings/pyroot/cppjit/src/cpyrt/Pythonize.cxx b/bindings/pyroot/cppjit/src/cpyrt/Pythonize.cxx index cc1575bafce7e..5781345db3fdd 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/Pythonize.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/Pythonize.cxx @@ -102,7 +102,7 @@ inline PyObject* CallPyObjMethod(PyObject* obj, const char* meth, //----------------------------------------------------------------------------- PyObject* PyStyleIndex(PyObject* self, PyObject* index) { // Helper; converts python index into straight C index. - Py_ssize_t idx = PyInt_AsSsize_t(index); + Py_ssize_t idx = PyLong_AsSsize_t(index); if (idx == (Py_ssize_t)-1 && PyErr_Occurred()) return nullptr; @@ -161,7 +161,7 @@ inline PyObject* CallSelfIndex(CPPInstance* self, PyObject* idx, //- "smart pointer" behavior --------------------------------------------------- static PyObject* smart_follow(PyObject* self, PyObject* name, PyObject* method) { - if (!cpyrt_PyText_Check(name)) + if (!PyUnicode_Check(name)) PyErr_SetString(PyExc_TypeError, "getattr(): attribute name must be string"); @@ -174,7 +174,7 @@ static PyObject* smart_follow(PyObject* self, PyObject* name, PyObject* val1 = PyObject_Str((PyObject*)Py_TYPE(self)); PyObject* val2 = PyObject_Str(name); PyErr_Format(PyExc_AttributeError, "%s object has no attribute \'%s\'", - cpyrt_PyText_AsString(val1), cpyrt_PyText_AsString(val2)); + PyUnicode_AsUTF8(val1), PyUnicode_AsUTF8(val2)); Py_DECREF(val2); Py_DECREF(val1); @@ -195,7 +195,7 @@ PyObject* DeRefGetAttr(PyObject* self, PyObject* name) { // in practice, whereas as-is, they can accidentally dereference the result // of end() on some STL containers. Obviously, this is a dumb hack that // should be resolved more fundamentally. - PyErr_SetString(PyExc_AttributeError, cpyrt_PyText_AsString(name)); + PyErr_SetString(PyExc_AttributeError, PyUnicode_AsUTF8(name)); return nullptr; } @@ -312,7 +312,7 @@ static ItemGetter* GetGetter(PyObject* args) { if (PyTuple_GET_SIZE(args) == 1) { PyObject* fi = PyTuple_GET_ITEM(args, 0); - if (cpyrt_PyText_Check(fi) || PyBytes_Check(fi)) + if (PyUnicode_Check(fi) || PyBytes_Check(fi)) return nullptr; // do not accept string to fill std::vector // TODO: this only tests for new-style buffers, which is too strict, but a @@ -580,7 +580,7 @@ PyObject* VectorIAdd(PyObject* self, PyObject* args, PyObject* /* kwds */) { if (PyTuple_GET_SIZE(args) == 1) { PyObject* fi = PyTuple_GET_ITEM(args, 0); if (PyObject_CheckBuffer(fi) && - !(cpyrt_PyText_Check(fi) || PyBytes_Check(fi))) { + !(PyUnicode_Check(fi) || PyBytes_Check(fi))) { PyObject* vend = PyObject_CallMethodNoArgs(self, PyStrings::gEnd); if (vend) { // when __iadd__ is overriden, the operation does not end with @@ -658,7 +658,7 @@ PyObject* VectorData(PyObject* self, PyObject*) { return pydata; } - long clen = PyInt_AsLong(pylen); + long clen = PyLong_AsLong(pylen); Py_DECREF(pylen); if (CPPInstance_Check(pydata)) { @@ -792,8 +792,8 @@ PyObject* VectorGetItem(CPPInstance* self, PySliceObject* index) { PyObject* nseq = PyObject_CallObject(pyclass, nullptr); Py_ssize_t start, stop, step; - PySlice_GetIndices((cpyrt_PySliceCast)index, - PyObject_Length((PyObject*)self), &start, &stop, &step); + PySlice_GetIndices((PyObject*)index, PyObject_Length((PyObject*)self), + &start, &stop, &step); const Py_ssize_t nlen = PySequence_Size((PyObject*)self); if (!AdjustSlice(nlen, start, stop, step)) @@ -801,7 +801,7 @@ PyObject* VectorGetItem(CPPInstance* self, PySliceObject* index) { const Py_ssize_t sign = step < 0 ? -1 : 1; for (Py_ssize_t i = start; i * sign < stop * sign; i += step) { - PyObject* pyidx = PyInt_FromSsize_t(i); + PyObject* pyidx = PyLong_FromSsize_t(i); PyObject* item = PyObject_CallMethodOneArg((PyObject*)self, PyStrings::gGetNoCheck, pyidx); CallPyObjMethod(nseq, "push_back", item); @@ -837,15 +837,15 @@ PyObject* VectorBoolGetItem(CPPInstance* self, PyObject* idx) { PyObject* nseq = PyObject_CallObject(pyclass, nullptr); Py_ssize_t start, stop, step; - PySlice_GetIndices((cpyrt_PySliceCast)idx, PyObject_Length((PyObject*)self), - &start, &stop, &step); + PySlice_GetIndices((PyObject*)idx, PyObject_Length((PyObject*)self), &start, + &stop, &step); const Py_ssize_t nlen = PySequence_Size((PyObject*)self); if (!AdjustSlice(nlen, start, stop, step)) return nseq; const Py_ssize_t sign = step < 0 ? -1 : 1; for (Py_ssize_t i = start; i * sign < stop * sign; i += step) { - PyObject* pyidx = PyInt_FromSsize_t(i); + PyObject* pyidx = PyLong_FromSsize_t(i); PyObject* item = PyObject_CallMethodOneArg((PyObject*)self, PyStrings::gGetItem, pyidx); CallPyObjMethod(nseq, "push_back", item); @@ -936,7 +936,7 @@ PyObject* ArrayInit(PyObject* self, PyObject* args, PyObject* /* kwds */) { PyObject* si_call = PyObject_GetAttr(self, PyStrings::gSetItem); for (Py_ssize_t i = 0; i < fillsz; ++i) { PyObject* item = PySequence_GetItem(items, i); - PyObject* index = PyInt_FromSsize_t(i); + PyObject* index = PyLong_FromSsize_t(i); PyObject* sires = PyObject_CallFunctionObjArgs(si_call, index, item, nullptr); Py_DECREF(index); @@ -1174,7 +1174,7 @@ PyObject* STLSequenceIter(PyObject* self) { if (!PyIter_Check(iter)) { // no tp_iternext, or the // _PyObject_NextNotImplemented sentinel itype->tp_iternext = (iternextfunc)STLIterNext; - Utility::AddToClass((PyObject*)itype, CPPJIT__next__, + Utility::AddToClass((PyObject*)itype, "__next__", (PyCFunction)STLIterNext, METH_NOARGS); if (!itype->tp_iter) { itype->tp_iter = (getiterfunc)PyObject_SelfIter; @@ -1232,7 +1232,7 @@ the of // consecutive indices, it such index is of integer type. Py_ssize_t size = PySequence_Size(self); - Py_ssize_t idx = PyInt_AsSsize_t(obj); + Py_ssize_t idx = PyLong_AsSsize_t(obj); if ((size == (Py_ssize_t)-1 || idx == (Py_ssize_t)-1) && PyErr_Occurred()) { // argument conversion problem: let method itself resolve anew and report PyErr_Clear(); @@ -1275,7 +1275,7 @@ PyObject* PairUnpack(PyObject* self, PyObject* pyindex) { } //- simplistic len() functions ----------------------------------------------- -PyObject* ReturnTwo(CPPInstance*, PyObject*) { return PyInt_FromLong(2); } +PyObject* ReturnTwo(CPPInstance*, PyObject*) { return PyLong_FromLong(2); } //- shared/unique_ptr behavior ----------------------------------------------- PyObject* SmartPtrInit(PyObject* self, PyObject* args, PyObject* /* kwds */) { @@ -1305,7 +1305,7 @@ static inline PyObject* cpyrt_PyString_FromCppString(std::string_view s, bool native = true) { if (native) return PyBytes_FromStringAndSize(s.data(), s.size()); - return cpyrt_PyText_FromStringAndSize(s.data(), s.size()); + return PyUnicode_FromStringAndSize(s.data(), s.size()); } static inline PyObject* cpyrt_PyString_FromCppString(std::wstring_view s, @@ -1391,7 +1391,7 @@ static inline PyObject* cpyrt_PyString_FromCppString(std::wstring_view s, } \ if (PyErr_Occurred()) \ return nullptr; \ - return PyInt_FromLong(result); \ + return PyLong_FromLong(result); \ } CPPJIT_IMPL_STRING_PYTHONIZATION_CMP(std::string, STL) @@ -1431,7 +1431,7 @@ PyObject* STLStringContains(CPPInstance* self, PyObject* pyobj) { if (!obj) return nullptr; - const char* needle = cpyrt_PyText_AsString(pyobj); + const char* needle = PyUnicode_AsUTF8(pyobj); if (!needle) return nullptr; @@ -1453,8 +1453,8 @@ PyObject* STLStringReplace(CPPInstance* self, PyObject* args, // version has no overload that takes a string if (2 <= PyTuple_GET_SIZE(args) && - cpyrt_PyText_Check(PyTuple_GET_ITEM(args, 0))) { - PyObject* pystr = cpyrt_PyText_FromStringAndSize(obj->data(), obj->size()); + PyUnicode_Check(PyTuple_GET_ITEM(args, 0))) { + PyObject* pystr = PyUnicode_FromStringAndSize(obj->data(), obj->size()); PyObject* meth = PyObject_GetAttrString(pystr, (char*)"replace"); Py_DECREF(pystr); PyObject* result = PyObject_CallObject(meth, args); @@ -1491,15 +1491,14 @@ PyObject* STLStringReplace(CPPInstance* self, PyObject* args, if (PyLongOrInt_AsULong64(result) == \ (PY_ULONG_LONG)std::string::npos) { \ Py_DECREF(result); \ - return PyInt_FromLong(-1); \ + return PyLong_FromLong(-1); \ } \ return result; \ } \ PyErr_Clear(); \ } \ \ - PyObject* pystr = \ - cpyrt_PyText_FromStringAndSize(obj->data(), obj->size()); \ + PyObject* pystr = PyUnicode_FromStringAndSize(obj->data(), obj->size()); \ PyObject* pymeth = PyObject_GetAttrString(pystr, (char*)#pyname); \ Py_DECREF(pystr); \ PyObject* result = PyObject_CallObject(pymeth, args); \ @@ -1519,7 +1518,7 @@ PyObject* STLStringGetAttr(CPPInstance* self, PyObject* attr_name) { if (!obj) return nullptr; - PyObject* pystr = cpyrt_PyText_FromStringAndSize(obj->data(), obj->size()); + PyObject* pystr = PyUnicode_FromStringAndSize(obj->data(), obj->size()); PyObject* attr = PyObject_GetAttr(pystr, attr_name); Py_DECREF(pystr); return attr; @@ -1529,7 +1528,7 @@ Py_hash_t STLStringHash(PyObject* self) { // std::string objects hash to the same values as Python strings to allow // matches in dictionaries etc. PyObject* data = STLStringGetData(self, false); - Py_hash_t h = cpyrt_PyText_Type.tp_hash(data); + Py_hash_t h = PyUnicode_Type.tp_hash(data); Py_DECREF(data); return h; } @@ -1607,7 +1606,7 @@ PyObject* STLIterNext(PyObject* self) { PyObject* iter = PyObject_CallMethodNoArgs(self, PyStrings::gPreInc); if (!iter) { PyErr_Clear(); - static PyObject* dummy = PyInt_FromLong(1l); + static PyObject* dummy = PyLong_FromLong(1l); iter = PyObject_CallMethodOneArg(self, PyStrings::gPostInc, dummy); } iter_valid = iter && PyObject_RichCompareBool(last, self, Py_NE); @@ -1686,7 +1685,7 @@ static PyObject* ComplexRepr(PyObject* self) { std::ostringstream s; s << '(' << r << '+' << i << "j)"; - return cpyrt_PyText_FromString(s.str().c_str()); + return PyUnicode_FromString(s.str().c_str()); } static PyObject* ComplexDRealGet(CPPInstance* self, void*) { @@ -2199,7 +2198,7 @@ bool cpyrt::Pythonize(PyObject* pyclass, interop::TCppScope_t scope) { (name.find("iterator") != std::string::npos || gIteratorTypes.find(name) != gIteratorTypes.end())) { ((PyTypeObject*)pyclass)->tp_iternext = (iternextfunc)STLIterNext; - Utility::AddToClass(pyclass, CPPJIT__next__, (PyCFunction)STLIterNext, + Utility::AddToClass(pyclass, "__next__", (PyCFunction)STLIterNext, METH_NOARGS); ((PyTypeObject*)pyclass)->tp_iter = (getiterfunc)PyObject_SelfIter; Utility::AddToClass(pyclass, "__iter__", (PyCFunction)PyObject_SelfIter, @@ -2340,7 +2339,7 @@ bool cpyrt::Pythonize(PyObject* pyclass, interop::TCppScope_t scope) { // through to base classes bool bUserOk = true; PyObject* res = nullptr; - PyObject* pyname = cpyrt_PyText_FromString(name.c_str()); + PyObject* pyname = PyUnicode_FromString(name.c_str()); if (HasAttrDirect(pyclass, PyStrings::gExPythonize)) { res = PyObject_CallMethodObjArgs(pyclass, PyStrings::gExPythonize, pyclass, pyname, nullptr); @@ -2371,7 +2370,7 @@ bool cpyrt::Pythonize(PyObject* pyclass, interop::TCppScope_t scope) { if (!outer_scope.empty()) { auto p = pyzMap.find(outer_scope); if (p != pyzMap.end()) { - PyObject* subname = cpyrt_PyText_FromString( + PyObject* subname = PyUnicode_FromString( name.substr(outer_scope.size() + 2, std::string::npos).c_str()); pstatus = run_pythonizors(pyclass, subname, p->second); Py_DECREF(subname); diff --git a/bindings/pyroot/cppjit/src/cpyrt/TemplateProxy.cxx b/bindings/pyroot/cppjit/src/cpyrt/TemplateProxy.cxx index 1308c0e0885ab..4fc68fbde55c3 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/TemplateProxy.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/TemplateProxy.cxx @@ -21,7 +21,7 @@ namespace cppjit::cpyrt { static inline std::string targs2str(TemplateProxy* pytmpl) { if (!pytmpl || !pytmpl->fTemplateArgs) return ""; - return cpyrt_PyText_AsString(pytmpl->fTemplateArgs); + return PyUnicode_AsUTF8(pytmpl->fTemplateArgs); } //---------------------------------------------------------------------------- @@ -206,7 +206,7 @@ PyObject* TemplateProxy::Instantiate(const std::string& fname, nullptr, subtypes, itemi, pref, 0, nullptr); Py_DECREF(subtypes); if (!PyErr_Occurred() && 2 < sub.size()) - entry = cpyrt_PyText_FromString(("std::pair" + sub).c_str()); + entry = PyUnicode_FromString(("std::pair" + sub).c_str()); else PyErr_Clear(); } @@ -292,7 +292,7 @@ PyObject* TemplateProxy::Instantiate(const std::string& fname, // lookup on existing name in case this was an overload, not a caching, // failure PyObject* dct = PyObject_GetAttr(fTI->fPyClass, PyStrings::gDict); - PyObject* pycachename = cpyrt_PyText_InternFromString(fname.c_str()); + PyObject* pycachename = PyUnicode_InternFromString(fname.c_str()); PyObject* pyol = PyObject_GetItem(dct, pycachename); if (!pyol) PyErr_Clear(); @@ -301,7 +301,7 @@ PyObject* TemplateProxy::Instantiate(const std::string& fname, // find the full name if the requested one was partial PyObject* exact = nullptr; - PyObject* pyresname = cpyrt_PyText_FromString(resname.c_str()); + PyObject* pyresname = PyUnicode_FromString(resname.c_str()); if (!bExactMatch) { exact = PyObject_GetItem(dct, pyresname); if (!exact) @@ -461,8 +461,8 @@ static PyObject* tpp_doc(TemplateProxy* pytmpl, void*) { PyObject* doc2 = PyObject_GetAttrString((PyObject*)pytmpl->fTI->fTemplated, "__doc__"); if (doc && doc2) { - cpyrt_PyText_AppendAndDel(&doc, cpyrt_PyText_FromString("\n")); - cpyrt_PyText_AppendAndDel(&doc, doc2); + PyUnicode_AppendAndDel(&doc, PyUnicode_FromString("\n")); + PyUnicode_AppendAndDel(&doc, doc2); } else if (!doc && doc2) { doc = doc2; } @@ -471,8 +471,8 @@ static PyObject* tpp_doc(TemplateProxy* pytmpl, void*) { PyObject* doc2 = PyObject_GetAttrString((PyObject*)pytmpl->fTI->fLowPriority, "__doc__"); if (doc && doc2) { - cpyrt_PyText_AppendAndDel(&doc, cpyrt_PyText_FromString("\n")); - cpyrt_PyText_AppendAndDel(&doc, doc2); + PyUnicode_AppendAndDel(&doc, PyUnicode_FromString("\n")); + PyUnicode_AppendAndDel(&doc, doc2); } else if (!doc && doc2) { doc = doc2; } @@ -481,7 +481,7 @@ static PyObject* tpp_doc(TemplateProxy* pytmpl, void*) { if (doc) return doc; - return cpyrt_PyText_FromString(TemplateProxy_Type.tp_doc); + return PyUnicode_FromString(TemplateProxy_Type.tp_doc); } static int tpp_doc_set(TemplateProxy* pytmpl, PyObject* val, void*) { @@ -650,9 +650,8 @@ static PyObject* tpp_vectorcall(TemplateProxy* pytmpl, PyObject* const* args, // case 1: explicit template previously selected through subscript if (pytmpl->fTemplateArgs) { // instantiate explicitly - PyObject* pyfullname = - cpyrt_PyText_FromString(pytmpl->fTI->fCppName.c_str()); - cpyrt_PyText_Append(&pyfullname, pytmpl->fTemplateArgs); + PyObject* pyfullname = PyUnicode_FromString(pytmpl->fTI->fCppName.c_str()); + PyUnicode_Append(&pyfullname, pytmpl->fTemplateArgs); // first, lookup by full name, if previously stored bool isNS = @@ -686,8 +685,8 @@ static PyObject* tpp_vectorcall(TemplateProxy* pytmpl, PyObject* const* args, PyErr_Clear(); // not cached or failed call; try instantiation - pymeth = pytmpl->Instantiate(cpyrt_PyText_AsString(pyfullname), args, - nargsf, Utility::kNone); + pymeth = pytmpl->Instantiate(PyUnicode_AsUTF8(pyfullname), args, nargsf, + Utility::kNone); if (pymeth) { // attempt actual call; same as above, allow implicit conversion of // arguments @@ -702,9 +701,9 @@ static PyObject* tpp_vectorcall(TemplateProxy* pytmpl, PyObject* const* args, // no drop through if failed (if implicit was desired, don't provide // template args) Utility::FetchError(errors); - PyObject* topmsg = cpyrt_PyText_FromFormat( + PyObject* topmsg = PyUnicode_FromFormat( "Could not find \"%s\" (set cppjit.set_debug() for C++ errors):", - cpyrt_PyText_AsString(pyfullname)); + PyUnicode_AsUTF8(pyfullname)); Py_DECREF(pyfullname); Utility::SetDetailedException(std::move(errors), topmsg /* steals */, PyExc_TypeError /* default error */); @@ -757,7 +756,7 @@ static PyObject* tpp_vectorcall(TemplateProxy* pytmpl, PyObject* const* args, // seems better if (!errors.empty()) { PyObject* topmsg = - cpyrt_PyText_FromString("Template method resolution failed:"); + PyUnicode_FromString("Template method resolution failed:"); Utility::SetDetailedException(std::move(errors), topmsg /* steals */, PyExc_TypeError /* default error */); } else { @@ -804,7 +803,7 @@ static PyObject* tpp_subscript(TemplateProxy* pytmpl, PyObject* args) { TemplateProxy* typeBoundMethod = tpp_descr_get(pytmpl, pytmpl->fSelf, nullptr); Py_XDECREF(typeBoundMethod->fTemplateArgs); - typeBoundMethod->fTemplateArgs = cpyrt_PyText_FromString( + typeBoundMethod->fTemplateArgs = PyUnicode_FromString( Utility::ConstructTemplateArgs(nullptr, args).c_str()); // Propagate the error that occurs if we can't construct the C++ name // from the provided template argument @@ -816,7 +815,7 @@ static PyObject* tpp_subscript(TemplateProxy* pytmpl, PyObject* args) { //----------------------------------------------------------------------------- static PyObject* tpp_getuseffi(CPPOverload*, void*) { - return PyInt_FromLong(0); // dummy (__useffi__ unused) + return PyLong_FromLong(0); // dummy (__useffi__ unused) } //----------------------------------------------------------------------------- @@ -943,12 +942,12 @@ static PyObject* tpp_overload(TemplateProxy* pytmpl, PyObject* args) { Py_ssize_t n = PyTuple_Size(sigarg_tuple); for (int i = 0; i < n; i++) { PyObject* pItem = PyTuple_GetItem(sigarg_tuple, i); - if (!cpyrt_PyText_Check(pItem)) { + if (!PyUnicode_Check(pItem)) { PyErr_Format(PyExc_LookupError, "argument types should be in string format"); return (PyObject*)nullptr; } - proto.append(cpyrt_PyText_AsString(pItem)); + proto.append(PyUnicode_AsUTF8(pItem)); if (i < n - 1) proto.push_back(','); } diff --git a/bindings/pyroot/cppjit/src/cpyrt/TupleOfInstances.cxx b/bindings/pyroot/cppjit/src/cpyrt/TupleOfInstances.cxx index 624c4497d3912..db31fa36b927d 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/TupleOfInstances.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/TupleOfInstances.cxx @@ -35,11 +35,11 @@ static PyObject* ia_iternext(ia_iterobject* ia) { static int ia_traverse(ia_iterobject*, visitproc, void*) { return 0; } static PyObject* ia_getsize(ia_iterobject* ia, void*) { - return PyInt_FromSsize_t(ia->ia_len); + return PyLong_FromSsize_t(ia->ia_len); } static int ia_setsize(ia_iterobject* ia, PyObject* pysize, void*) { - Py_ssize_t size = PyInt_AsSsize_t(pysize); + Py_ssize_t size = PyLong_AsSsize_t(pysize); if (size == (Py_ssize_t)-1 && PyErr_Occurred()) return -1; ia->ia_len = size; @@ -57,7 +57,7 @@ static PyObject* ia_subscript(ia_iterobject* ia, PyObject* pyidx) { // Subscripting the iterator allows direct access through indexing on arrays // that do not have a defined length. This way, the return from accessing such // an array as a data member can both be used in a loop and directly. - Py_ssize_t idx = PyInt_AsSsize_t(pyidx); + Py_ssize_t idx = PyLong_AsSsize_t(pyidx); if (idx == (Py_ssize_t)-1 && PyErr_Occurred()) return nullptr; @@ -216,51 +216,50 @@ PyTypeObject TupleOfInstances_Type = { 0, // tp_basicsize 0, // tp_itemsize 0, // tp_dealloc - 0, // tp_vectorcall_offset / tp_print - 0, // tp_getattr - 0, // tp_setattr - 0, // tp_as_async / tp_compare - 0, // tp_repr - 0, // tp_as_number - 0, // tp_as_sequence - 0, // tp_as_mapping - 0, // tp_hash - 0, // tp_call - 0, // tp_str - 0, // tp_getattro - 0, // tp_setattro - 0, // tp_as_buffer - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | - Py_TPFLAGS_BASETYPE, // tp_flags - (char*)"array of C++ instances", // tp_doc - 0, // tp_traverse - 0, // tp_clear - 0, // tp_richcompare - 0, // tp_weaklistoffset - 0, // tp_iter - 0, // tp_iternext - 0, // tp_methods - 0, // tp_members - 0, // tp_getset - &PyTuple_Type, // tp_base - 0, // tp_dict - 0, // tp_descr_get - 0, // tp_descr_set - 0, // tp_dictoffset - 0, // tp_init - 0, // tp_alloc - 0, // tp_new - 0, // tp_free - 0, // tp_is_gc - 0, // tp_bases - 0, // tp_mro - 0, // tp_cache - 0, // tp_subclasses - 0, // tp_weaklist - 0, // tp_del - 0, // tp_version_tag - 0, // tp_finalize - 0 // tp_vectorcall + 0, // tp_vectorcall_offset / tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_as_async / tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags + (char*)"array of C++ instances", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + 0, // tp_methods + 0, // tp_members + 0, // tp_getset + &PyTuple_Type, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + 0, // tp_alloc + 0, // tp_new + 0, // tp_free + 0, // tp_is_gc + 0, // tp_bases + 0, // tp_mro + 0, // tp_cache + 0, // tp_subclasses + 0, // tp_weaklist + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall CPYRT_PYTYPE_TAIL}; } // namespace cppjit::cpyrt diff --git a/bindings/pyroot/cppjit/src/cpyrt/Utility.cxx b/bindings/pyroot/cppjit/src/cpyrt/Utility.cxx index f83d39c27fcd7..9538a57b46260 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/Utility.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/Utility.cxx @@ -73,7 +73,7 @@ struct InitOperatorMapping_t { gC2POperatorMapping["+="] = "__iadd__"; gC2POperatorMapping["-="] = "__isub__"; gC2POperatorMapping["*="] = "__imul__"; - gC2POperatorMapping["/="] = CPPJIT__idiv__; + gC2POperatorMapping["/="] = "__itruediv__"; gC2POperatorMapping["%="] = "__imod__"; gC2POperatorMapping["**="] = "__ipow__"; gC2POperatorMapping["<<="] = "__ilshift__"; @@ -94,7 +94,7 @@ struct InitOperatorMapping_t { gC2POperatorMapping["const char *"] = gC2POperatorMapping["const char*"]; gC2POperatorMapping["char *"] = gC2POperatorMapping["char*"]; gC2POperatorMapping["int"] = "__int__"; - gC2POperatorMapping["long"] = CPPJIT__long__; + gC2POperatorMapping["long"] = "__int__"; gC2POperatorMapping["double"] = "__float__"; // the following type mappings are "okay"; the assumption is that they @@ -102,10 +102,10 @@ struct InitOperatorMapping_t { // they are, that it is done consistently) gC2POperatorMapping["short"] = "__int__"; gC2POperatorMapping["unsigned short"] = "__int__"; - gC2POperatorMapping["unsigned int"] = CPPJIT__long__; - gC2POperatorMapping["unsigned long"] = CPPJIT__long__; - gC2POperatorMapping["long long"] = CPPJIT__long__; - gC2POperatorMapping["unsigned long long"] = CPPJIT__long__; + gC2POperatorMapping["unsigned int"] = "__int__"; + gC2POperatorMapping["unsigned long"] = "__int__"; + gC2POperatorMapping["long long"] = "__int__"; + gC2POperatorMapping["unsigned long long"] = "__int__"; gC2POperatorMapping["float"] = "__float__"; gC2POperatorMapping["->"] = "__follow__"; // not an actual python operator @@ -133,9 +133,9 @@ unsigned long cpyrt::PyLongOrInt_AsULong(PyObject* pyobject) { } unsigned long ul = PyLong_AsUnsignedLong(pyobject); - if (ul == (unsigned long)-1 && PyErr_Occurred() && PyInt_Check(pyobject)) { + if (ul == (unsigned long)-1 && PyErr_Occurred() && PyLong_Check(pyobject)) { PyErr_Clear(); - long i = PyInt_AS_LONG(pyobject); + long i = PyLong_AsLong(pyobject); if (0 <= i) { ul = (unsigned long)i; } else { @@ -160,9 +160,9 @@ PY_ULONG_LONG cpyrt::PyLongOrInt_AsULong64(PyObject* pyobject) { } PY_ULONG_LONG ull = PyLong_AsUnsignedLongLong(pyobject); - if (PyErr_Occurred() && PyInt_Check(pyobject)) { + if (PyErr_Occurred() && PyLong_Check(pyobject)) { PyErr_Clear(); - long i = PyInt_AS_LONG(pyobject); + long i = PyLong_AsLong(pyobject); if (0 <= i) { ull = (PY_ULONG_LONG)i; } else { @@ -190,7 +190,7 @@ bool cpyrt::Utility::AddToClass(PyObject* pyclass, const char* label, pdef->ml_doc = nullptr; PyObject* func = PyCFunction_New(pdef, nullptr); - PyObject* name = cpyrt_PyText_InternFromString(pdef->ml_name); + PyObject* name = PyUnicode_InternFromString(pdef->ml_name); PyObject* method = CustomInstanceMethod_New(func, nullptr, pyclass); PyObject *pytype = 0, *pyvalue = 0, *pytrace = 0; PyErr_Fetch(&pytype, &pyvalue, &pytrace); @@ -219,7 +219,7 @@ bool cpyrt::Utility::AddToClass(PyObject* pyclass, const char* label, if (!pyfunc) return false; - PyObject* pylabel = cpyrt_PyText_InternFromString(const_cast(label)); + PyObject* pylabel = PyUnicode_InternFromString(const_cast(label)); bool isOk = PyType_Type.tp_setattro(pyclass, pylabel, pyfunc) == 0; Py_DECREF(pylabel); @@ -240,7 +240,7 @@ bool cpyrt::Utility::AddToClass(PyObject* pyclass, const char* label, PyErr_Clear(); Py_XDECREF((PyObject*)method); method = CPPOverload_New(label, pyfunc); - PyObject* pylabel = cpyrt_PyText_InternFromString(const_cast(label)); + PyObject* pylabel = PyUnicode_InternFromString(const_cast(label)); bool isOk = PyType_Type.tp_setattro(pyclass, pylabel, (PyObject*)method) == 0; Py_DECREF(pylabel); @@ -408,18 +408,18 @@ cpyrt::Utility::FindBinaryOperator(const std::string& lcname, //---------------------------------------------------------------------------- static inline std::string AnnotationAsText(PyObject* pyobj) { - if (!cpyrt_PyText_Check(pyobj)) { + if (!PyUnicode_Check(pyobj)) { PyObject* pystr = PyObject_GetAttr(pyobj, cpyrt::PyStrings::gName); if (!pystr) { PyErr_Clear(); pystr = PyObject_Str(pyobj); } - std::string str = cpyrt_PyText_AsString(pystr); + std::string str = PyUnicode_AsUTF8(pystr); Py_DECREF(pystr); return str; } - return cpyrt_PyText_AsString(pyobj); + return PyUnicode_AsUTF8(pyobj); } static bool AddTypeName(std::string& tmpl_name, PyObject* tn, PyObject* arg, @@ -431,7 +431,7 @@ static bool AddTypeName(std::string& tmpl_name, PyObject* tn, PyObject* arg, using namespace cppjit::cpyrt; using namespace cpyrt::Utility; - if (tn == (PyObject*)&PyInt_Type) { + if (tn == (PyObject*)&PyLong_Type) { if (arg) { PY_LONG_LONG ll = PyLong_AsLongLong(arg); if (ll == (PY_LONG_LONG)-1 && PyErr_Occurred()) { @@ -516,8 +516,8 @@ static bool AddTypeName(std::string& tmpl_name, PyObject* tn, PyObject* arg, if (tn == (PyObject*)&CPPOverload_Type) { PyObject* tpName = arg ? PyObject_GetAttr(arg, PyStrings::gCppName) - : cpyrt_PyText_FromString("void* (*)(...)"); - tmpl_name.append(cpyrt_PyText_AsString(tpName)); + : PyUnicode_FromString("void* (*)(...)"); + tmpl_name.append(PyUnicode_AsUTF8(tpName)); Py_DECREF(tpName); return true; @@ -595,7 +595,7 @@ static bool AddTypeName(std::string& tmpl_name, PyObject* tn, PyObject* arg, // callable C++ type (e.g. std::function) PyObject* tpName = PyObject_GetAttr(arg, PyStrings::gCppName); if (tpName) { - const char* cname = cpyrt_PyText_AsString(tpName); + const char* cname = PyUnicode_AsUTF8(tpName); tmpl_name.append(CPPScope_Check(arg) ? full_scope(cname) : cname); Py_DECREF(tpName); return true; @@ -606,19 +606,19 @@ static bool AddTypeName(std::string& tmpl_name, PyObject* tn, PyObject* arg, for (auto nn : {PyStrings::gCppName, PyStrings::gName}) { PyObject* tpName = PyObject_GetAttr(tn, nn); if (tpName) { - tmpl_name.append(cpyrt_PyText_AsString(tpName)); + tmpl_name.append(PyUnicode_AsUTF8(tpName)); Py_DECREF(tpName); return true; } PyErr_Clear(); } - if (PyInt_Check(tn) || PyLong_Check(tn) || PyFloat_Check(tn)) { + if (PyLong_Check(tn) || PyFloat_Check(tn)) { // last ditch attempt, works for things like int values; since this is a // source of errors otherwise, it is limited to specific types and not // generally used (str(obj) can print anything ...) PyObject* pystr = PyObject_Str(tn); - tmpl_name.append(cpyrt_PyText_AsString(pystr)); + tmpl_name.append(PyUnicode_AsUTF8(pystr)); Py_DECREF(pystr); return true; } @@ -650,7 +650,7 @@ std::string cpyrt::Utility::ConstructTemplateArgs(PyObject* pyname, std::string tmpl_name; tmpl_name.reserve(128); if (pyname) - tmpl_name.append(cpyrt_PyText_AsString(pyname)); + tmpl_name.append(PyUnicode_AsUTF8(pyname)); tmpl_name.push_back('<'); if (pcnt) @@ -660,8 +660,8 @@ std::string cpyrt::Utility::ConstructTemplateArgs(PyObject* pyname, for (int i = argoff; i < nArgs; ++i) { // add type as string to name PyObject* tn = justOne ? tpArgs : PyTuple_GET_ITEM(tpArgs, i); - if (cpyrt_PyText_Check(tn)) { - tmpl_name.append(cpyrt_PyText_AsString(tn)); + if (PyUnicode_Check(tn)) { + tmpl_name.append(PyUnicode_AsUTF8(tn)); // some common numeric types (separated out for performance: checking for // __cpp_name__ and/or __name__ is rather expensive) } else { @@ -697,7 +697,7 @@ static bool AddTypeName(std::vector& types, PyObject* tn, using namespace cppjit::cpyrt; using namespace cpyrt::Utility; - if (tn == (PyObject*)&PyInt_Type) { + if (tn == (PyObject*)&PyLong_Type) { if (arg) { PY_LONG_LONG ll = PyLong_AsLongLong(arg); if (ll == (PY_LONG_LONG)-1 && PyErr_Occurred()) { @@ -782,8 +782,8 @@ static bool AddTypeName(std::vector& types, PyObject* tn, if (tn == (PyObject*)&CPPOverload_Type) { PyObject* tpName = arg ? PyObject_GetAttr(arg, PyStrings::gCppName) - : cpyrt_PyText_FromString("void* (*)(...)"); - types.push_back(interop::GetType(cpyrt_PyText_AsString(tpName), + : PyUnicode_FromString("void* (*)(...)"); + types.push_back(interop::GetType(PyUnicode_AsUTF8(tpName), /* enable_slow_lookup */ true) .data); Py_DECREF(tpName); @@ -799,8 +799,7 @@ static bool AddTypeName(std::vector& types, PyObject* tn, if (ret) { // dict is ordered, with the last value being the return type std::ostringstream tpn; - tpn << (CPPScope_Check(ret) ? ClassName(ret) - : cpyrt_PyText_AsString(ret)) + tpn << (CPPScope_Check(ret) ? ClassName(ret) : PyUnicode_AsUTF8(ret)) << " (*)("; PyObject* values = PyDict_Values(annot); @@ -809,7 +808,7 @@ static bool AddTypeName(std::vector& types, PyObject* tn, tpn << ", "; PyObject* item = PyList_GET_ITEM(values, i); tpn << (CPPScope_Check(item) ? ClassName(item) - : cpyrt_PyText_AsString(item)); + : PyUnicode_AsUTF8(item)); } Py_DECREF(values); @@ -830,7 +829,7 @@ static bool AddTypeName(std::vector& types, PyObject* tn, PyObject* tpName = PyObject_GetAttr(arg, PyStrings::gCppName); if (tpName) { - types.push_back(interop::GetType(cpyrt_PyText_AsString(tpName), + types.push_back(interop::GetType(PyUnicode_AsUTF8(tpName), /* enable_slow_lookup */ true) .data); Py_DECREF(tpName); @@ -843,7 +842,7 @@ static bool AddTypeName(std::vector& types, PyObject* tn, PyObject* tpName = PyObject_GetAttr(tn, nn); if (tpName) { interop::TCppType_t type = interop::GetType( - cpyrt_PyText_AsString(tpName), /* enable_slow_lookup */ true); + PyUnicode_AsUTF8(tpName), /* enable_slow_lookup */ true); if (!type) { // any Python object has a __name__; one that does not name a C++ // type contributes no argument @@ -857,7 +856,7 @@ static bool AddTypeName(std::vector& types, PyObject* tn, PyErr_Clear(); } else { PyObject* pystr = PyObject_Str(tn); - std::string num = cpyrt_PyText_AsString(pystr); + std::string num = PyUnicode_AsUTF8(pystr); types.push_back({type.data, strdup(num.c_str())}); Py_DECREF(pystr); Py_DECREF(value_int); @@ -871,12 +870,12 @@ static bool AddTypeName(std::vector& types, PyObject* tn, PyErr_Clear(); } - if (PyInt_Check(tn) || PyLong_Check(tn) || PyFloat_Check(tn)) { + if (PyLong_Check(tn) || PyFloat_Check(tn)) { // last ditch attempt, works for things like int values; since this is a // source of errors otherwise, it is limited to specific types and not // generally used (str(obj) can print anything ...) PyObject* pystr = PyObject_Str(tn); - std::string num = cpyrt_PyText_AsString(pystr); + std::string num = PyUnicode_AsUTF8(pystr); if (num == "True") num = "1"; else if (num == "False") @@ -908,8 +907,8 @@ cpyrt::Utility::GetTemplateArgsTypes(PyObject* /*scope*/, PyObject* tpArgs, for (int i = argoff; i < nArgs; ++i) { // add type as string to name PyObject* tn = justOne ? tpArgs : PyTuple_GET_ITEM(tpArgs, i); - if (cpyrt_PyText_Check(tn)) { - const char* tn_string = cpyrt_PyText_AsString(tn); + if (PyUnicode_Check(tn)) { + const char* tn_string = PyUnicode_AsUTF8(tn); if (interop::AppendTypesSlow(tn_string, types)) { PyErr_Format(PyExc_TypeError, "Cannot find Templated Arg: %s", @@ -936,8 +935,8 @@ cpyrt::Utility::GetTemplateArgsTypes(PyObject* /*scope*/, PyObject* tpArgs, std::string cpyrt::Utility::CT2CppNameS(PyObject* pytc, bool allow_voidp) { // helper to convert ctypes' `_type_` info to the equivalent C++ name const char* name = ""; - if (cpyrt_PyText_Check(pytc)) { - char tc = ((char*)cpyrt_PyText_AsString(pytc))[0]; + if (PyUnicode_Check(pytc)) { + char tc = ((char*)PyUnicode_AsUTF8(pytc))[0]; switch (tc) { case '?': name = "bool"; @@ -1294,7 +1293,7 @@ Py_ssize_t cpyrt::Utility::GetBuffer(PyObject* pyobject, char tc, int size, ? PyObject_GetAttr(pyobject, PyStrings::gTypeCode) : nullptr; if (pytc != 0) { // for array objects - char cpytc = cpyrt_PyText_AsString(pytc)[0]; + char cpytc = PyUnicode_AsUTF8(pytc)[0]; if (!(cpytc == tc || (tc == '?' && cpytc == 'b'))) buf = 0; // no match Py_DECREF(pytc); @@ -1311,9 +1310,9 @@ Py_ssize_t cpyrt::Utility::GetBuffer(PyObject* pyobject, char tc, int size, // clarify error message auto error = FetchPyError(); - PyObject* pyvalue2 = cpyrt_PyText_FromFormat( + PyObject* pyvalue2 = PyUnicode_FromFormat( (char*)"%s and given element size (%ld) do not match needed (%d)", - cpyrt_PyText_AsString(error.fValue.get()), + PyUnicode_AsUTF8(error.fValue.get()), seqmeths->sq_length ? (long)(buflen / (*(seqmeths->sq_length))(pyobject)) : (long)buflen, @@ -1367,7 +1366,7 @@ std::string cpyrt::Utility::MapOperatorName(const std::string& name, } else if (op == "/") { // no unary, but is stubbed - return CPPJIT__div__; + return "__truediv__"; } else if (op == "+") { // unary positive v.s. addition of two instances @@ -1411,7 +1410,7 @@ std::string cpyrt::Utility::ClassName(PyObject* pyobj) { } if (pyname) { - clname = cpyrt_PyText_AsString(pyname); + clname = PyUnicode_AsUTF8(pyname); Py_DECREF(pyname); } else PyErr_Clear(); @@ -1515,7 +1514,7 @@ void cpyrt::Utility::SetDetailedException(std::vector&& errors, // Use the collected exceptions to build up a detailed error log. if (errors.empty()) { // should not happen ... - PyErr_SetString(defexc, cpyrt_PyText_AsString(topmsg)); + PyErr_SetString(defexc, PyUnicode_AsUTF8(topmsg)); Py_DECREF(topmsg); return; } @@ -1561,29 +1560,29 @@ void cpyrt::Utility::SetDetailedException(std::vector&& errors, } // add the details to the topmsg - PyObject* separator = cpyrt_PyText_FromString("\n "); + PyObject* separator = PyUnicode_FromString("\n "); for (auto& e : errors) { PyObject* pyvalue = e.fValue.get(); - cpyrt_PyText_Append(&topmsg, separator); - if (cpyrt_PyText_Check(pyvalue)) { - cpyrt_PyText_Append(&topmsg, pyvalue); + PyUnicode_Append(&topmsg, separator); + if (PyUnicode_Check(pyvalue)) { + PyUnicode_Append(&topmsg, pyvalue); } else if (pyvalue) { PyObject* excstr = PyObject_Str(pyvalue); if (!excstr) { PyErr_Clear(); excstr = PyObject_Str((PyObject*)Py_TYPE(pyvalue)); } - cpyrt_PyText_AppendAndDel(&topmsg, excstr); + PyUnicode_AppendAndDel(&topmsg, excstr); } else { - cpyrt_PyText_AppendAndDel(&topmsg, - cpyrt_PyText_FromString("unknown exception")); + PyUnicode_AppendAndDel(&topmsg, + PyUnicode_FromString("unknown exception")); } } Py_DECREF(separator); // set the python exception - PyErr_SetString(exc_type, cpyrt_PyText_AsString(topmsg)); + PyErr_SetString(exc_type, PyUnicode_AsUTF8(topmsg)); } Py_DECREF(topmsg); diff --git a/bindings/pyroot/cppjit/src/cpyrt/cpyrt.h b/bindings/pyroot/cppjit/src/cpyrt/cpyrt.h index 0e15b3e8de618..a1486222206ce 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/cpyrt.h +++ b/bindings/pyroot/cppjit/src/cpyrt/cpyrt.h @@ -40,29 +40,10 @@ namespace cppjit::cpyrt { typedef Py_ssize_t dim_t; } // namespace cppjit::cpyrt -typedef Py_ssize_t (*dict_lookup_func)(PyDictObject*, PyObject*, Py_hash_t, - PyObject**); - -// for 3.0 support (backwards compatibility, really) -#define cpyrt_PyText_Check PyUnicode_Check -#define cpyrt_PyText_CheckExact PyUnicode_CheckExact -#define cpyrt_PyText_AsString PyUnicode_AsUTF8 -#define cpyrt_PyText_AsStringChecked PyUnicode_AsUTF8 -#define cpyrt_PyText_GetSize PyUnicode_GetSize -#define cpyrt_PyText_GET_SIZE PyUnicode_GET_LENGTH -#define cpyrt_PyUnicode_GET_SIZE PyUnicode_GET_LENGTH -#define cpyrt_PyText_FromFormat PyUnicode_FromFormat -#define cpyrt_PyText_FromString PyUnicode_FromString -#define cpyrt_PyText_InternFromString PyUnicode_InternFromString -#define cpyrt_PyText_Append PyUnicode_Append -#define cpyrt_PyText_AppendAndDel PyUnicode_AppendAndDel -#define cpyrt_PyText_FromStringAndSize PyUnicode_FromStringAndSize - -#define _cpyrt_PyText_AsStringAndSize PyUnicode_AsUTF8AndSize - +// like PyUnicode_AsUTF8AndSize, but also accepts bytes static inline const char* cpyrt_PyText_AsStringAndSize(PyObject* pystr, Py_ssize_t* size) { - const char* cstr = _cpyrt_PyText_AsStringAndSize(pystr, size); + const char* cstr = PyUnicode_AsUTF8AndSize(pystr, size); if (!cstr && PyBytes_CheckExact(pystr)) { PyErr_Clear(); PyBytes_AsStringAndSize(pystr, (char**)&cstr, size); @@ -70,40 +51,6 @@ static inline const char* cpyrt_PyText_AsStringAndSize(PyObject* pystr, return cstr; } -#define cpyrt_PyText_Type PyUnicode_Type - -#define PyIntObject PyLongObject -#define PyInt_Check PyLong_Check -#define PyInt_AsLong PyLong_AsLong -#define PyInt_AS_LONG PyLong_AsLong -#define PyInt_AsSsize_t PyLong_AsSsize_t -#define PyInt_CheckExact PyLong_CheckExact -#define PyInt_FromLong PyLong_FromLong -#define PyInt_FromSsize_t PyLong_FromSsize_t - -#define PyInt_Type PyLong_Type - -#define cpyrt_PyCapsule_New PyCapsule_New -#define cpyrt_PyCapsule_CheckExact PyCapsule_CheckExact -#define cpyrt_PyCapsule_GetPointer PyCapsule_GetPointer - -#define CPPJIT__long__ "__int__" -#define CPPJIT__idiv__ "__itruediv__" -#define CPPJIT__div__ "__truediv__" -#define CPPJIT__next__ "__next__" - -#define Py_TPFLAGS_HAVE_RICHCOMPARE 0 -#define Py_TPFLAGS_CHECKTYPES 0 - -#define PyClass_Check PyType_Check - -#define PyBuffer_Type PyMemoryView_Type - -#define cpyrt_PySliceCast PyObject* -#define PyUnicode_GetSize PyUnicode_GetLength - -#define cpyrt_PyUnicode_AsWideChar PyUnicode_AsWideChar - #ifdef R__MACOSX #if SIZEOF_SIZE_T == SIZEOF_INT #if defined(MAC_OS_X_VERSION_10_4) @@ -130,9 +77,6 @@ static inline const char* cpyrt_PyText_AsStringAndSize(PyObject* pystr, #define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False #endif -// vector call support -#define cpyrt_PyCFunction_Call PyObject_Call - // vector call support typedef PyObject* const* cpyrt_PyArgs_t; static inline PyObject* cpyrt_PyArgs_GET_ITEM(cpyrt_PyArgs_t args, diff --git a/bindings/pyroot/cppjit/src/cpyrt/cpyrtModule.cxx b/bindings/pyroot/cppjit/src/cpyrt/cpyrtModule.cxx index ac88c10c270de..880b1466d8ff5 100644 --- a/bindings/pyroot/cppjit/src/cpyrt/cpyrtModule.cxx +++ b/bindings/pyroot/cppjit/src/cpyrt/cpyrtModule.cxx @@ -16,6 +16,7 @@ using namespace cppjit; #include "PyStrings.h" #include "TemplateProxy.h" #include "TupleOfInstances.h" +#include "TypeManip.h" #include "Utility.h" #include "cppjit_interop.h" #include @@ -43,7 +44,7 @@ std::unordered_map TypeReductionMap; //- data ----------------------------------------------------------------------- static PyObject* nullptr_repr(PyObject*) { - return cpyrt_PyText_FromString("nullptr"); + return PyUnicode_FromString("nullptr"); } static void nullptr_dealloc(PyObject*) { @@ -147,7 +148,7 @@ static PyTypeObject PyNullPtr_t_Type = { CPYRT_PYTYPE_TAIL}; static PyObject* default_repr(PyObject*) { - return cpyrt_PyText_FromString("type default"); + return PyUnicode_FromString("type default"); } static void default_dealloc(PyObject*) { @@ -282,7 +283,7 @@ static PyObject* MakeCppTemplateClass(PyObject* /* self */, PyObject* args) { PyErr_Format(PyExc_TypeError, "Template instantiation failed: '%s' with args: '%s\n'", interop::GetScopedFinalName(tmpl).c_str(), - cpyrt_PyText_AsString(PyObject_Repr(args))); + PyUnicode_AsUTF8(PyObject_Repr(args))); return nullptr; } @@ -300,8 +301,8 @@ static void* GetCPPInstanceAddress(const char* fname, PyObject* args, PyObject* pyname = 0; int byref = 0; if (PyArg_ParseTupleAndKeywords(args, kwds, const_cast("O|O!b"), - GCIA_kwlist, &pyobj, &cpyrt_PyText_Type, - &pyname, &byref)) { + GCIA_kwlist, &pyobj, &PyUnicode_Type, &pyname, + &byref)) { if (CPPInstance_Check(pyobj)) { if (pyname != 0) { @@ -323,7 +324,7 @@ static void* GetCPPInstanceAddress(const char* fname, PyObject* args, Py_XDECREF(pyprop); PyErr_Format(PyExc_TypeError, "%s is not a valid data member", - cpyrt_PyText_AsString(pyname)); + PyUnicode_AsUTF8(pyname)); return nullptr; } @@ -333,9 +334,9 @@ static void* GetCPPInstanceAddress(const char* fname, PyObject* args, return ((CPPInstance*)pyobj)->GetObject(); return &((CPPInstance*)pyobj)->GetObjectRaw(); - } else if (cpyrt_PyText_Check(pyobj)) { + } else if (PyUnicode_Check(pyobj)) { // special cases for access to the cpyrt API - std::string req = cpyrt_PyText_AsString((PyObject*)pyobj); + std::string req = PyUnicode_AsUTF8((PyObject*)pyobj); if (req == "Instance_AsVoidPtr") return (void*)&Instance_AsVoidPtr; else if (req == "Instance_FromVoidPtr") @@ -364,7 +365,7 @@ static PyObject* addressof(PyObject* /* dummy */, PyObject* args, // nullptr special case if (arg0 == gNullPtrObject || - (PyInt_Check(arg0) && PyInt_AsLong(arg0) == 0)) + (PyLong_Check(arg0) && PyLong_AsLong(arg0) == 0)) return PyLong_FromLong(0); // overload if unambiguous @@ -401,9 +402,9 @@ static PyObject* addressof(PyObject* /* dummy */, PyObject* args, if (!PyErr_Occurred()) { if (PyTuple_CheckExact(args) && PyTuple_GET_SIZE(args)) { PyObject* str = PyObject_Str(PyTuple_GET_ITEM(args, 0)); - if (str && cpyrt_PyText_Check(str)) + if (str && PyUnicode_Check(str)) PyErr_Format(PyExc_TypeError, "unknown object %s", - cpyrt_PyText_AsString(str)); + PyUnicode_AsUTF8(str)); else PyErr_Format(PyExc_TypeError, "unknown object at %p", (void*)PyTuple_GET_ITEM(args, 0)); @@ -419,7 +420,7 @@ static PyObject* AsCObject(PyObject* /* unused */, PyObject* args, // Return object proxy as an opaque CObject. void* addr = GetCPPInstanceAddress("as_cobject", args, kwds); if (addr) - return cpyrt_PyCapsule_New((void*)addr, nullptr, nullptr); + return PyCapsule_New((void*)addr, nullptr, nullptr); return nullptr; } @@ -514,7 +515,7 @@ static PyObject* BindObject(PyObject*, PyObject* args, PyObject* kwds) { // convert 2nd argument first (used for both pointer value and instance cases) interop::TCppScope_t cast_type = nullptr; PyObject* arg1 = PyTuple_GET_ITEM(args, 1); - if (!cpyrt_PyText_Check(arg1)) { // not string, then class + if (!PyUnicode_Check(arg1)) { // not string, then class if (CPPScope_Check(arg1)) cast_type = ((CPPClass*)arg1)->fCppType; else @@ -523,8 +524,7 @@ static PyObject* BindObject(PyObject*, PyObject* args, PyObject* kwds) { Py_INCREF(arg1); if (!cast_type && arg1) { - cast_type = - (interop::TCppScope_t)interop::GetScope(cpyrt_PyText_AsString(arg1)); + cast_type = (interop::TCppScope_t)interop::GetScope(PyUnicode_AsUTF8(arg1)); Py_DECREF(arg1); } @@ -633,7 +633,7 @@ static PyObject* BindObject(PyObject*, PyObject* args, PyObject* kwds) { // not a pre-existing object; get the address and bind void* addr = nullptr; if (arg0 != gNullPtrObject) { - addr = cpyrt_PyCapsule_GetPointer(arg0, nullptr); + addr = PyCapsule_GetPointer(arg0, nullptr); if (PyErr_Occurred()) { PyErr_Clear(); @@ -666,6 +666,95 @@ static PyObject* BindObject(PyObject*, PyObject* args, PyObject* kwds) { return BindCppObjectNoCast(addr, cast_type); } +//---------------------------------------------------------------------------- +static PyObject* BindValue(PyObject*, PyObject* args, PyObject* kwds) { + // Read through the converter for the given type name, at the given address. + static const char* kwlist[] = {(char*)"type_name", (char*)"address", + (char*)"dims", nullptr}; + + const char* type_name = nullptr; + PyObject* pyaddress = nullptr; + PyObject* pydims = nullptr; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|O:bind_value", + (char**)kwlist, &type_name, &pyaddress, + &pydims)) + return nullptr; + + void* address = PyLong_AsVoidPtr(pyaddress); + if (PyErr_Occurred()) + return nullptr; + + std::vector dims; + if (pydims && pydims != Py_None) { + PyObject* seq = + PySequence_Fast(pydims, "dims must be a sequence of integers"); + if (!seq) + return nullptr; + Py_ssize_t ndim = PySequence_Fast_GET_SIZE(seq); + if (ndim == 0) { + Py_DECREF(seq); + PyErr_SetString(PyExc_ValueError, "dims must be a non-empty sequence"); + return nullptr; + } + dims.reserve(ndim); + for (Py_ssize_t i = 0; i < ndim; ++i) { + cpyrt::dim_t d = + (cpyrt::dim_t)PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(seq, i)); + if (d == (cpyrt::dim_t)-1 && PyErr_Occurred()) { + Py_DECREF(seq); + return nullptr; + } + // an UNKNOWN_SIZE view would keep the address of the pointer slot on + // our expired stack frame as its data + if (d < 0) { + Py_DECREF(seq); + PyErr_SetString(PyExc_ValueError, "dims entries must be non-negative"); + return nullptr; + } + dims.push_back(d); + } + Py_DECREF(seq); + + // scalar converters ignore dims entirely and would silently read the + // pointer bits; only array and pointer type names can take a shape + const std::string cpd = cpyrt::TypeManip::compound(type_name); + if (cpd != "[]" && cpd != "*") { + PyErr_Format(PyExc_TypeError, + "dims given but \'%s\' is not an array or pointer type", + type_name); + return nullptr; + } + } + + // an unresolvable name crashes deeper down in the type lookup instead of + // producing a nullptr converter, so validate first + const std::string resolvedType = interop::ResolveName(type_name); + const std::string baseType = + cpyrt::TypeManip::clean_type(resolvedType, false, true); + if (!interop::GetType(baseType, /* enable_slow_lookup */ true)) { + PyErr_Format(PyExc_TypeError, "no converter available for type \'%s\'", + type_name); + return nullptr; + } + + cpyrt::Converter* cnv = cpyrt::CreateConverter( + type_name, cpyrt::Dimensions((cpyrt::dim_t)dims.size(), dims.data())); + + // an array converter reads through the data pointer, so it wants the + // address of that pointer; a scalar converter wants the address of the + // value itself + PyObject* result = + dims.empty() ? cnv->FromMemory(address) : cnv->FromMemory(&address); + cpyrt::DestroyConverter(cnv); + + if (!result && !PyErr_Occurred()) + PyErr_Format(PyExc_TypeError, + "failed to convert a value of type \'%s\' from memory", + type_name); + + return result; +} + //---------------------------------------------------------------------------- static PyObject* Move(PyObject*, PyObject* pyobject) { // Prepare the given C++ object for moving. @@ -690,7 +779,7 @@ static PyObject* AddPythonization(PyObject*, PyObject* args) { if (!PyCallable_Check(pythonizor)) { PyObject* pystr = PyObject_Str(pythonizor); PyErr_Format(PyExc_TypeError, "given \'%s\' object is not callable", - cpyrt_PyText_AsString(pystr)); + PyUnicode_AsUTF8(pystr)); Py_DECREF(pystr); return nullptr; } @@ -777,7 +866,7 @@ static PyObject* SetOwnership(PyObject*, PyObject* args) { CPPInstance* pyobj = nullptr; PyObject* pykeep = nullptr; if (!PyArg_ParseTuple(args, const_cast("O!O!"), &CPPInstance_Type, - (void*)&pyobj, &PyInt_Type, &pykeep)) + (void*)&pyobj, &PyLong_Type, &pykeep)) return nullptr; (bool)PyLong_AsLong(pykeep) ? pyobj->PythonOwns() : pyobj->CppOwns(); @@ -858,6 +947,8 @@ static PyMethodDef gcpyrtMethods[] = { {(char*)"bind_object", (PyCFunction)BindObject, METH_VARARGS | METH_KEYWORDS, (char*)"Create an object of given type, from given address."}, + {(char*)"bind_value", (PyCFunction)BindValue, METH_VARARGS | METH_KEYWORDS, + (char*)"Read a value of given type, from given address."}, {(char*)"move", (PyCFunction)Move, METH_O, (char*)"Cast the C++ object to become movable."}, {(char*)"add_pythonization", (PyCFunction)AddPythonization, METH_VARARGS, diff --git a/bindings/pyroot/cppjit/src/interop/interop_wrapper.cxx b/bindings/pyroot/cppjit/src/interop/interop_wrapper.cxx index 0358941f9fe78..ef48f950e50d2 100644 --- a/bindings/pyroot/cppjit/src/interop/interop_wrapper.cxx +++ b/bindings/pyroot/cppjit/src/interop/interop_wrapper.cxx @@ -210,7 +210,7 @@ static void configureInterpreter(const InterOpPaths& Paths) { Cpp::LoadLibrary("libstdc++", /* lookup= */ true); } -static void preloadHeaders() { +static bool preloadHeaders() { const char* code = "#include \n" "#include \n" "#include \n" @@ -235,7 +235,7 @@ static void preloadHeaders() { "#include \n" "#endif\n" "#include \n"; - Cpp::Process(code); + return Cpp::Process(code) == 0; } static void defineRuntimeHelpers() { @@ -272,9 +272,18 @@ extern "C" int LoadCppInterOp() { if (!loadDispatchAPI(Paths)) return; - acquireOrCreateInterpreter(Paths); + if (!acquireOrCreateInterpreter(Paths)) { + std::cerr << "[cppjit] Failed to create the C++ interpreter" << std::endl; + return; + } configureInterpreter(Paths); - preloadHeaders(); + if (!preloadHeaders()) { + std::cerr << "[cppjit] The C++ standard headers do not parse, see the " + "diagnostic above. Install a C++ toolchain such as g++ or " + "the conda package cxx-compiler." + << std::endl; + return; + } defineRuntimeHelpers(); Loaded = 1; @@ -860,8 +869,14 @@ interop::TCppType_t interop::GetTypeFromScope(TCppScope_t klass) { } interop::TCppScope_t interop::GetGlobalScope() { - std::lock_guard Lock(InterOpMutex); - return Cpp::GetGlobalScope(); + // The global scope (the first declaration of the interpreter's translation + // unit) never changes, but this is called on every method call that + // receives 'self' as its first argument, so avoid the lock and the query. + static const TCppScope_t s_global = [] { + std::lock_guard Lock(InterOpMutex); + return Cpp::GetGlobalScope(); + }(); + return s_global; } bool interop::IsTemplate(TCppScope_t handle) { return Cpp::IsTemplate(handle); } @@ -1320,6 +1335,9 @@ bool interop::HasVirtualDestructor(TCppScope_t scope) { interop::TCppIndex_t interop::GetNumBases(TCppScope_t klass) { // Get the total number of base classes that this class has. std::lock_guard Lock(InterOpMutex); + // Autoloading may have registered only a forward declaration of the class; + // complete it, or the proxy is built without its bases. + Cpp::GetOrForceDefinition(klass); return Cpp::GetNumBases(klass); } diff --git a/bindings/pyroot/cppjit/test/test_lowlevel.py b/bindings/pyroot/cppjit/test/test_lowlevel.py index a5789ff48482d..d741f7f07179f 100644 --- a/bindings/pyroot/cppjit/test/test_lowlevel.py +++ b/bindings/pyroot/cppjit/test/test_lowlevel.py @@ -1167,6 +1167,147 @@ def test08_reshape_sets_unknown_dimensions_only(self): raises(TypeError, v.reshape, ()) +class TestBINDVALUE: + def _buffer_of(self, ctype, *values): + import ctypes + + buf = (ctype * len(values))(*values) + return buf, ctypes.addressof(buf) + + def test01_scalar(self): + """A builtin type yields the stored Python value""" + + import ctypes + + from cppjit import ll + + buf, addr = self._buffer_of(ctypes.c_double, 3.5) + v = ll.bind_value("double", addr) + assert isinstance(v, float) + assert v == 3.5 + + ibuf, iaddr = self._buffer_of(ctypes.c_int32, -7) + assert ll.bind_value("int", iaddr) == -7 + + def test02_class(self): + """A class type yields a bound proxy for the same object + + The object comes from C++ so the test does not depend on the + platform's set of std::string constructors (libc++ vs libstdc++). + """ + + import cppjit + from cppjit import ll + + cppjit.cppdef(""" + std::string bind_value_make_string() { return "hello"; } + std::string bind_value_string_object = bind_value_make_string(); + """) + # ROOT returns std::string values as Python str; the address comes + # from a C++ object + assert cppjit.gbl.bind_value_make_string() == "hello" + s = cppjit.gbl.bind_value_string_object + ps = ll.bind_value("std::string", cppjit.addressof(s)) + assert type(ps) is type(s) + assert ps == "hello" + + def test03_array_with_dims(self): + """'T[]' with dims yields a shaped LowLevelView over the data""" + + import ctypes + + from cppjit import ll + + buf, addr = self._buffer_of(ctypes.c_double, *[float(i) for i in range(6)]) + + flat = ll.bind_value("double[]", addr, (6,)) + assert flat.shape == (6,) + assert flat[4] == 4.0 + + matrix = ll.bind_value("double[]", addr, (2, 3)) + assert matrix.shape == (2, 3) + assert matrix[1][2] == 5.0 + + def test04_pointer_type_with_dims(self): + """'T*' with dims is accepted as an array denotation""" + + import ctypes + + from cppjit import ll + + buf, addr = self._buffer_of(ctypes.c_int, 11, 22, 33) + view = ll.bind_value("int*", addr, (3,)) + assert view.shape == (3,) + assert [view[i] for i in range(3)] == [11, 22, 33] + + def test05_unknown_type_raises(self): + """An unresolvable type name raises TypeError, not a crash""" + + import ctypes + + from cppjit import ll + + buf, addr = self._buffer_of(ctypes.c_double, 1.0) + with raises(TypeError): + ll.bind_value("nosuchtype_xyz", addr) + + def test06_dims_require_array_type(self): + """dims with a scalar type raise TypeError (scalars ignore dims)""" + + import ctypes + + from cppjit import ll + + buf, addr = self._buffer_of(ctypes.c_double, 1.0, 2.0) + with raises(TypeError): + ll.bind_value("double", addr, (2,)) + + def test07_bad_dims_raise(self): + """Empty and negative dims are rejected""" + + import ctypes + + from cppjit import ll + + buf, addr = self._buffer_of(ctypes.c_double, 1.0, 2.0) + with raises(ValueError): + ll.bind_value("double[]", addr, ()) + with raises(ValueError): + # -1 is the UNKNOWN_SIZE sentinel, which has no meaning here + ll.bind_value("double[]", addr, (-1,)) + with raises(ValueError): + ll.bind_value("double[]", addr, (2, -3)) + + def test08_cstring_pointer_as_str(self): + """'char*' yields a str, like a char*-returning bound function does + + This is the motivating case from the TTree leaf pythonization: + the address points at the char* slot, whose target is read as a + C string. + """ + + import ctypes + + from cppjit import ll + + cp = ctypes.c_char_p(b"hello leaf") + v = ll.bind_value("char*", ctypes.addressof(cp)) + assert type(v) is str + assert v == "hello leaf" + + def test09_char_array_with_dims(self): + """'char[]' with dims yields a view of one-character strings""" + + import ctypes + + from cppjit import ll + + buf = ctypes.create_string_buffer(b"abcd") + v = ll.bind_value("char[]", ctypes.addressof(buf), (4,)) + assert v.shape == (4,) + assert [v[i] for i in range(4)] == ["a", "b", "c", "d"] + + class TestCSTRINGARRAY: def test01_cstring_array_from_str(self): """A Python string can be assigned to a const char** data member""" diff --git a/bindings/pyroot/cppjit/test/test_regression.py b/bindings/pyroot/cppjit/test/test_regression.py index c879ec1b8d2af..cbd5f566667d4 100644 --- a/bindings/pyroot/cppjit/test/test_regression.py +++ b/bindings/pyroot/cppjit/test/test_regression.py @@ -1716,3 +1716,20 @@ def test55_enum_arg_standard_conversion(self): # a standard conversion outranks a user-defined one, as in C++ assert ns.pick(ns.Color.Blue) == 2 + + def test56_enum_arg_typedefed_underlying_type(self): + """An enum whose underlying type is spelled through a typedef converts""" + + import cppjit + + cppjit.cppdef("""\ + #include + namespace EnumArgTypedefUnderlying { + struct S { enum L : std::int32_t { A = 1, B = 5 }; }; + int pick(S::L l) { return (int)l; } + }""") + + ns = cppjit.gbl.EnumArgTypedefUnderlying + + assert ns.pick(ns.S.A) == 1 + assert ns.pick(ns.S.B) == 5