diff --git a/abi/abi3t-linux.txt b/abi/abi3t-linux.txt
new file mode 100644
index 0000000000..cf51ec1899
--- /dev/null
+++ b/abi/abi3t-linux.txt
@@ -0,0 +1,958 @@
+# Generated by scripts/csignature.py. Do not edit manually.
+# Profile: ABI3T; source: CPython v3.15.0rc1 (37e98da7c19a9e5892ee756d6dee08225422cd49)
+# Source file: Misc/stable_abi.toml
+# Disabled features: MS_WINDOWS, Py_REF_DEBUG, USE_STACKCHECK
+# Format:
+
+FUNC PyABIInfo_Check
+FUNC PyAIter_Check
+FUNC PyArg_Parse
+FUNC PyArg_ParseTuple
+FUNC PyArg_ParseTupleAndKeywords
+FUNC PyArg_UnpackTuple
+FUNC PyArg_VaParse
+FUNC PyArg_VaParseTupleAndKeywords
+FUNC PyArg_ValidateKeywordArguments
+OBJECT PyBaseObject_Type
+FUNC PyBool_FromLong
+OBJECT PyBool_Type
+FUNC PyBuffer_FillContiguousStrides
+FUNC PyBuffer_FillInfo
+FUNC PyBuffer_FromContiguous
+FUNC PyBuffer_GetPointer
+FUNC PyBuffer_IsContiguous
+FUNC PyBuffer_Release
+FUNC PyBuffer_SizeFromFormat
+FUNC PyBuffer_ToContiguous
+OBJECT PyByteArrayIter_Type
+FUNC PyByteArray_AsString
+FUNC PyByteArray_Concat
+FUNC PyByteArray_FromObject
+FUNC PyByteArray_FromStringAndSize
+FUNC PyByteArray_Resize
+FUNC PyByteArray_Size
+OBJECT PyByteArray_Type
+OBJECT PyBytesIter_Type
+FUNC PyBytes_AsString
+FUNC PyBytes_AsStringAndSize
+FUNC PyBytes_Concat
+FUNC PyBytes_ConcatAndDel
+FUNC PyBytes_DecodeEscape
+FUNC PyBytes_FromFormat
+FUNC PyBytes_FromFormatV
+FUNC PyBytes_FromObject
+FUNC PyBytes_FromString
+FUNC PyBytes_FromStringAndSize
+FUNC PyBytes_Repr
+FUNC PyBytes_Size
+OBJECT PyBytes_Type
+FUNC PyCFunction_Call
+FUNC PyCFunction_GetFlags
+FUNC PyCFunction_GetFunction
+FUNC PyCFunction_GetSelf
+FUNC PyCFunction_New
+FUNC PyCFunction_NewEx
+OBJECT PyCFunction_Type
+FUNC PyCMethod_New
+FUNC PyCallIter_New
+OBJECT PyCallIter_Type
+FUNC PyCallable_Check
+FUNC PyCapsule_GetContext
+FUNC PyCapsule_GetDestructor
+FUNC PyCapsule_GetName
+FUNC PyCapsule_GetPointer
+FUNC PyCapsule_Import
+FUNC PyCapsule_IsValid
+FUNC PyCapsule_New
+FUNC PyCapsule_SetContext
+FUNC PyCapsule_SetDestructor
+FUNC PyCapsule_SetName
+FUNC PyCapsule_SetPointer
+OBJECT PyCapsule_Type
+OBJECT PyClassMethodDescr_Type
+FUNC PyCodec_BackslashReplaceErrors
+FUNC PyCodec_Decode
+FUNC PyCodec_Decoder
+FUNC PyCodec_Encode
+FUNC PyCodec_Encoder
+FUNC PyCodec_IgnoreErrors
+FUNC PyCodec_IncrementalDecoder
+FUNC PyCodec_IncrementalEncoder
+FUNC PyCodec_KnownEncoding
+FUNC PyCodec_LookupError
+FUNC PyCodec_NameReplaceErrors
+FUNC PyCodec_Register
+FUNC PyCodec_RegisterError
+FUNC PyCodec_ReplaceErrors
+FUNC PyCodec_StreamReader
+FUNC PyCodec_StreamWriter
+FUNC PyCodec_StrictErrors
+FUNC PyCodec_Unregister
+FUNC PyCodec_XMLCharRefReplaceErrors
+FUNC PyComplex_FromDoubles
+FUNC PyComplex_ImagAsDouble
+FUNC PyComplex_RealAsDouble
+OBJECT PyComplex_Type
+FUNC PyCriticalSection2_Begin
+FUNC PyCriticalSection2_End
+FUNC PyCriticalSection_Begin
+FUNC PyCriticalSection_End
+FUNC PyDescr_NewClassMethod
+FUNC PyDescr_NewGetSet
+FUNC PyDescr_NewMember
+FUNC PyDescr_NewMethod
+OBJECT PyDictItems_Type
+OBJECT PyDictIterItem_Type
+OBJECT PyDictIterKey_Type
+OBJECT PyDictIterValue_Type
+OBJECT PyDictKeys_Type
+FUNC PyDictProxy_New
+OBJECT PyDictProxy_Type
+OBJECT PyDictRevIterItem_Type
+OBJECT PyDictRevIterKey_Type
+OBJECT PyDictRevIterValue_Type
+OBJECT PyDictValues_Type
+FUNC PyDict_Clear
+FUNC PyDict_Contains
+FUNC PyDict_Copy
+FUNC PyDict_DelItem
+FUNC PyDict_DelItemString
+FUNC PyDict_GetItem
+FUNC PyDict_GetItemRef
+FUNC PyDict_GetItemString
+FUNC PyDict_GetItemStringRef
+FUNC PyDict_GetItemWithError
+FUNC PyDict_Items
+FUNC PyDict_Keys
+FUNC PyDict_Merge
+FUNC PyDict_MergeFromSeq2
+FUNC PyDict_New
+FUNC PyDict_Next
+FUNC PyDict_SetDefaultRef
+FUNC PyDict_SetItem
+FUNC PyDict_SetItemString
+FUNC PyDict_Size
+OBJECT PyDict_Type
+FUNC PyDict_Update
+FUNC PyDict_Values
+OBJECT PyEllipsis_Type
+OBJECT PyEnum_Type
+FUNC PyErr_BadArgument
+FUNC PyErr_BadInternalCall
+FUNC PyErr_CheckSignals
+FUNC PyErr_Clear
+FUNC PyErr_Display
+FUNC PyErr_DisplayException
+FUNC PyErr_ExceptionMatches
+FUNC PyErr_Fetch
+FUNC PyErr_Format
+FUNC PyErr_FormatV
+FUNC PyErr_GetExcInfo
+FUNC PyErr_GetHandledException
+FUNC PyErr_GetRaisedException
+FUNC PyErr_GivenExceptionMatches
+FUNC PyErr_NewException
+FUNC PyErr_NewExceptionWithDoc
+FUNC PyErr_NoMemory
+FUNC PyErr_NormalizeException
+FUNC PyErr_Occurred
+FUNC PyErr_Print
+FUNC PyErr_PrintEx
+FUNC PyErr_ProgramText
+FUNC PyErr_ResourceWarning
+FUNC PyErr_Restore
+FUNC PyErr_SetExcInfo
+FUNC PyErr_SetFromErrno
+FUNC PyErr_SetFromErrnoWithFilename
+FUNC PyErr_SetFromErrnoWithFilenameObject
+FUNC PyErr_SetFromErrnoWithFilenameObjects
+FUNC PyErr_SetHandledException
+FUNC PyErr_SetImportError
+FUNC PyErr_SetImportErrorSubclass
+FUNC PyErr_SetInterrupt
+FUNC PyErr_SetInterruptEx
+FUNC PyErr_SetNone
+FUNC PyErr_SetObject
+FUNC PyErr_SetRaisedException
+FUNC PyErr_SetString
+FUNC PyErr_SyntaxLocation
+FUNC PyErr_SyntaxLocationEx
+FUNC PyErr_WarnEx
+FUNC PyErr_WarnExplicit
+FUNC PyErr_WarnFormat
+FUNC PyErr_WriteUnraisable
+FUNC PyEval_AcquireLock
+FUNC PyEval_AcquireThread
+FUNC PyEval_CallFunction
+FUNC PyEval_CallMethod
+FUNC PyEval_CallObjectWithKeywords
+FUNC PyEval_EvalCode
+FUNC PyEval_EvalCodeEx
+FUNC PyEval_EvalFrame
+FUNC PyEval_EvalFrameEx
+FUNC PyEval_GetBuiltins
+FUNC PyEval_GetFrame
+FUNC PyEval_GetFrameBuiltins
+FUNC PyEval_GetFrameGlobals
+FUNC PyEval_GetFrameLocals
+FUNC PyEval_GetFuncDesc
+FUNC PyEval_GetFuncName
+FUNC PyEval_GetGlobals
+FUNC PyEval_GetLocals
+FUNC PyEval_InitThreads
+FUNC PyEval_ReleaseLock
+FUNC PyEval_ReleaseThread
+FUNC PyEval_RestoreThread
+FUNC PyEval_SaveThread
+FUNC PyEval_ThreadsInitialized
+OBJECT PyExc_ArithmeticError
+OBJECT PyExc_AssertionError
+OBJECT PyExc_AttributeError
+OBJECT PyExc_BaseException
+OBJECT PyExc_BaseExceptionGroup
+OBJECT PyExc_BlockingIOError
+OBJECT PyExc_BrokenPipeError
+OBJECT PyExc_BufferError
+OBJECT PyExc_BytesWarning
+OBJECT PyExc_ChildProcessError
+OBJECT PyExc_ConnectionAbortedError
+OBJECT PyExc_ConnectionError
+OBJECT PyExc_ConnectionRefusedError
+OBJECT PyExc_ConnectionResetError
+OBJECT PyExc_DeprecationWarning
+OBJECT PyExc_EOFError
+OBJECT PyExc_EncodingWarning
+OBJECT PyExc_EnvironmentError
+OBJECT PyExc_Exception
+OBJECT PyExc_FileExistsError
+OBJECT PyExc_FileNotFoundError
+OBJECT PyExc_FloatingPointError
+OBJECT PyExc_FutureWarning
+OBJECT PyExc_GeneratorExit
+OBJECT PyExc_IOError
+OBJECT PyExc_ImportError
+OBJECT PyExc_ImportWarning
+OBJECT PyExc_IndentationError
+OBJECT PyExc_IndexError
+OBJECT PyExc_InterruptedError
+OBJECT PyExc_IsADirectoryError
+OBJECT PyExc_KeyError
+OBJECT PyExc_KeyboardInterrupt
+OBJECT PyExc_LookupError
+OBJECT PyExc_MemoryError
+OBJECT PyExc_ModuleNotFoundError
+OBJECT PyExc_NameError
+OBJECT PyExc_NotADirectoryError
+OBJECT PyExc_NotImplementedError
+OBJECT PyExc_OSError
+OBJECT PyExc_OverflowError
+OBJECT PyExc_PendingDeprecationWarning
+OBJECT PyExc_PermissionError
+OBJECT PyExc_ProcessLookupError
+OBJECT PyExc_RecursionError
+OBJECT PyExc_ReferenceError
+OBJECT PyExc_ResourceWarning
+OBJECT PyExc_RuntimeError
+OBJECT PyExc_RuntimeWarning
+OBJECT PyExc_StopAsyncIteration
+OBJECT PyExc_StopIteration
+OBJECT PyExc_SyntaxError
+OBJECT PyExc_SyntaxWarning
+OBJECT PyExc_SystemError
+OBJECT PyExc_SystemExit
+OBJECT PyExc_TabError
+OBJECT PyExc_TimeoutError
+OBJECT PyExc_TypeError
+OBJECT PyExc_UnboundLocalError
+OBJECT PyExc_UnicodeDecodeError
+OBJECT PyExc_UnicodeEncodeError
+OBJECT PyExc_UnicodeError
+OBJECT PyExc_UnicodeTranslateError
+OBJECT PyExc_UnicodeWarning
+OBJECT PyExc_UserWarning
+OBJECT PyExc_ValueError
+OBJECT PyExc_Warning
+OBJECT PyExc_ZeroDivisionError
+FUNC PyExceptionClass_Name
+FUNC PyException_GetArgs
+FUNC PyException_GetCause
+FUNC PyException_GetContext
+FUNC PyException_GetTraceback
+FUNC PyException_SetArgs
+FUNC PyException_SetCause
+FUNC PyException_SetContext
+FUNC PyException_SetTraceback
+FUNC PyFile_FromFd
+FUNC PyFile_GetLine
+FUNC PyFile_WriteObject
+FUNC PyFile_WriteString
+OBJECT PyFilter_Type
+FUNC PyFloat_AsDouble
+FUNC PyFloat_FromDouble
+FUNC PyFloat_FromString
+FUNC PyFloat_GetInfo
+FUNC PyFloat_GetMax
+FUNC PyFloat_GetMin
+OBJECT PyFloat_Type
+FUNC PyFrame_GetCode
+FUNC PyFrame_GetLineNumber
+FUNC PyFrozenSet_New
+OBJECT PyFrozenSet_Type
+FUNC PyGC_Collect
+FUNC PyGC_Disable
+FUNC PyGC_Enable
+FUNC PyGC_IsEnabled
+FUNC PyGILState_Ensure
+FUNC PyGILState_GetThisThreadState
+FUNC PyGILState_Release
+OBJECT PyGetSetDescr_Type
+FUNC PyImport_AddModule
+FUNC PyImport_AddModuleObject
+FUNC PyImport_AddModuleRef
+FUNC PyImport_AppendInittab
+FUNC PyImport_ExecCodeModule
+FUNC PyImport_ExecCodeModuleEx
+FUNC PyImport_ExecCodeModuleObject
+FUNC PyImport_ExecCodeModuleWithPathnames
+FUNC PyImport_GetImporter
+FUNC PyImport_GetMagicNumber
+FUNC PyImport_GetMagicTag
+FUNC PyImport_GetModule
+FUNC PyImport_GetModuleDict
+FUNC PyImport_Import
+FUNC PyImport_ImportFrozenModule
+FUNC PyImport_ImportFrozenModuleObject
+FUNC PyImport_ImportModule
+FUNC PyImport_ImportModuleLevel
+FUNC PyImport_ImportModuleLevelObject
+FUNC PyImport_ImportModuleNoBlock
+FUNC PyImport_ReloadModule
+FUNC PyIndex_Check
+FUNC PyInterpreterGuard_Close
+FUNC PyInterpreterGuard_FromCurrent
+FUNC PyInterpreterGuard_FromView
+FUNC PyInterpreterState_Clear
+FUNC PyInterpreterState_Delete
+FUNC PyInterpreterState_Get
+FUNC PyInterpreterState_GetDict
+FUNC PyInterpreterState_GetID
+FUNC PyInterpreterState_New
+FUNC PyInterpreterView_Close
+FUNC PyInterpreterView_FromCurrent
+FUNC PyInterpreterView_FromMain
+FUNC PyIter_Check
+FUNC PyIter_Next
+FUNC PyIter_NextItem
+FUNC PyIter_Send
+OBJECT PyListIter_Type
+OBJECT PyListRevIter_Type
+FUNC PyList_Append
+FUNC PyList_AsTuple
+FUNC PyList_GetItem
+FUNC PyList_GetItemRef
+FUNC PyList_GetSlice
+FUNC PyList_Insert
+FUNC PyList_New
+FUNC PyList_Reverse
+FUNC PyList_SetItem
+FUNC PyList_SetSlice
+FUNC PyList_Size
+FUNC PyList_Sort
+OBJECT PyList_Type
+OBJECT PyLongRangeIter_Type
+FUNC PyLongWriter_Create
+FUNC PyLongWriter_Discard
+FUNC PyLongWriter_Finish
+FUNC PyLong_AsDouble
+FUNC PyLong_AsInt
+FUNC PyLong_AsInt32
+FUNC PyLong_AsInt64
+FUNC PyLong_AsLong
+FUNC PyLong_AsLongAndOverflow
+FUNC PyLong_AsLongLong
+FUNC PyLong_AsLongLongAndOverflow
+FUNC PyLong_AsNativeBytes
+FUNC PyLong_AsSize_t
+FUNC PyLong_AsSsize_t
+FUNC PyLong_AsUInt32
+FUNC PyLong_AsUInt64
+FUNC PyLong_AsUnsignedLong
+FUNC PyLong_AsUnsignedLongLong
+FUNC PyLong_AsUnsignedLongLongMask
+FUNC PyLong_AsUnsignedLongMask
+FUNC PyLong_AsVoidPtr
+FUNC PyLong_Export
+FUNC PyLong_FreeExport
+FUNC PyLong_FromDouble
+FUNC PyLong_FromInt32
+FUNC PyLong_FromInt64
+FUNC PyLong_FromLong
+FUNC PyLong_FromLongLong
+FUNC PyLong_FromNativeBytes
+FUNC PyLong_FromSize_t
+FUNC PyLong_FromSsize_t
+FUNC PyLong_FromString
+FUNC PyLong_FromUInt32
+FUNC PyLong_FromUInt64
+FUNC PyLong_FromUnsignedLong
+FUNC PyLong_FromUnsignedLongLong
+FUNC PyLong_FromUnsignedNativeBytes
+FUNC PyLong_FromVoidPtr
+FUNC PyLong_GetInfo
+FUNC PyLong_GetNativeLayout
+OBJECT PyLong_Type
+OBJECT PyMap_Type
+FUNC PyMapping_Check
+FUNC PyMapping_GetItemString
+FUNC PyMapping_GetOptionalItem
+FUNC PyMapping_GetOptionalItemString
+FUNC PyMapping_HasKey
+FUNC PyMapping_HasKeyString
+FUNC PyMapping_HasKeyStringWithError
+FUNC PyMapping_HasKeyWithError
+FUNC PyMapping_Items
+FUNC PyMapping_Keys
+FUNC PyMapping_Length
+FUNC PyMapping_SetItemString
+FUNC PyMapping_Size
+FUNC PyMapping_Values
+FUNC PyMarshal_ReadObjectFromString
+FUNC PyMarshal_WriteObjectToString
+FUNC PyMem_Calloc
+FUNC PyMem_Free
+FUNC PyMem_Malloc
+FUNC PyMem_RawCalloc
+FUNC PyMem_RawFree
+FUNC PyMem_RawMalloc
+FUNC PyMem_RawRealloc
+FUNC PyMem_Realloc
+OBJECT PyMemberDescr_Type
+FUNC PyMember_GetOne
+FUNC PyMember_SetOne
+FUNC PyMemoryView_FromBuffer
+FUNC PyMemoryView_FromMemory
+FUNC PyMemoryView_FromObject
+FUNC PyMemoryView_GetContiguous
+OBJECT PyMemoryView_Type
+OBJECT PyMethodDescr_Type
+FUNC PyModuleDef_Init
+OBJECT PyModuleDef_Type
+FUNC PyModule_Add
+FUNC PyModule_AddFunctions
+FUNC PyModule_AddIntConstant
+FUNC PyModule_AddObject
+FUNC PyModule_AddObjectRef
+FUNC PyModule_AddStringConstant
+FUNC PyModule_AddType
+FUNC PyModule_Create2
+FUNC PyModule_Exec
+FUNC PyModule_ExecDef
+FUNC PyModule_FromDefAndSpec2
+FUNC PyModule_FromSlotsAndSpec
+FUNC PyModule_GetDef
+FUNC PyModule_GetDict
+FUNC PyModule_GetFilename
+FUNC PyModule_GetFilenameObject
+FUNC PyModule_GetName
+FUNC PyModule_GetNameObject
+FUNC PyModule_GetState
+FUNC PyModule_GetStateSize
+FUNC PyModule_GetState_DuringGC
+FUNC PyModule_GetToken
+FUNC PyModule_GetToken_DuringGC
+FUNC PyModule_New
+FUNC PyModule_NewObject
+FUNC PyModule_SetDocString
+OBJECT PyModule_Type
+FUNC PyNumber_Absolute
+FUNC PyNumber_Add
+FUNC PyNumber_And
+FUNC PyNumber_AsSsize_t
+FUNC PyNumber_Check
+FUNC PyNumber_Divmod
+FUNC PyNumber_Float
+FUNC PyNumber_FloorDivide
+FUNC PyNumber_InPlaceAdd
+FUNC PyNumber_InPlaceAnd
+FUNC PyNumber_InPlaceFloorDivide
+FUNC PyNumber_InPlaceLshift
+FUNC PyNumber_InPlaceMatrixMultiply
+FUNC PyNumber_InPlaceMultiply
+FUNC PyNumber_InPlaceOr
+FUNC PyNumber_InPlacePower
+FUNC PyNumber_InPlaceRemainder
+FUNC PyNumber_InPlaceRshift
+FUNC PyNumber_InPlaceSubtract
+FUNC PyNumber_InPlaceTrueDivide
+FUNC PyNumber_InPlaceXor
+FUNC PyNumber_Index
+FUNC PyNumber_Invert
+FUNC PyNumber_Long
+FUNC PyNumber_Lshift
+FUNC PyNumber_MatrixMultiply
+FUNC PyNumber_Multiply
+FUNC PyNumber_Negative
+FUNC PyNumber_Or
+FUNC PyNumber_Positive
+FUNC PyNumber_Power
+FUNC PyNumber_Remainder
+FUNC PyNumber_Rshift
+FUNC PyNumber_Subtract
+FUNC PyNumber_ToBase
+FUNC PyNumber_TrueDivide
+FUNC PyNumber_Xor
+FUNC PyOS_AfterFork
+FUNC PyOS_AfterFork_Child
+FUNC PyOS_AfterFork_Parent
+FUNC PyOS_BeforeFork
+FUNC PyOS_FSPath
+OBJECT PyOS_InputHook
+FUNC PyOS_InterruptOccurred
+FUNC PyOS_double_to_string
+FUNC PyOS_getsig
+FUNC PyOS_mystricmp
+FUNC PyOS_mystrnicmp
+FUNC PyOS_setsig
+FUNC PyOS_snprintf
+FUNC PyOS_string_to_double
+FUNC PyOS_strtol
+FUNC PyOS_strtoul
+FUNC PyOS_vsnprintf
+FUNC PyObject_ASCII
+FUNC PyObject_AsCharBuffer
+FUNC PyObject_AsFileDescriptor
+FUNC PyObject_AsReadBuffer
+FUNC PyObject_AsWriteBuffer
+FUNC PyObject_Bytes
+FUNC PyObject_Call
+FUNC PyObject_CallFinalizerFromDealloc
+FUNC PyObject_CallFunction
+FUNC PyObject_CallFunctionObjArgs
+FUNC PyObject_CallMethod
+FUNC PyObject_CallMethodObjArgs
+FUNC PyObject_CallNoArgs
+FUNC PyObject_CallObject
+FUNC PyObject_Calloc
+FUNC PyObject_CheckBuffer
+FUNC PyObject_CheckReadBuffer
+FUNC PyObject_ClearWeakRefs
+FUNC PyObject_CopyData
+FUNC PyObject_DelAttr
+FUNC PyObject_DelAttrString
+FUNC PyObject_DelItem
+FUNC PyObject_DelItemString
+FUNC PyObject_Dir
+FUNC PyObject_Format
+FUNC PyObject_Free
+FUNC PyObject_GC_Del
+FUNC PyObject_GC_IsFinalized
+FUNC PyObject_GC_IsTracked
+FUNC PyObject_GC_Track
+FUNC PyObject_GC_UnTrack
+FUNC PyObject_GenericGetAttr
+FUNC PyObject_GenericGetDict
+FUNC PyObject_GenericSetAttr
+FUNC PyObject_GenericSetDict
+FUNC PyObject_GetAIter
+FUNC PyObject_GetAttr
+FUNC PyObject_GetAttrString
+FUNC PyObject_GetBuffer
+FUNC PyObject_GetItem
+FUNC PyObject_GetIter
+FUNC PyObject_GetOptionalAttr
+FUNC PyObject_GetOptionalAttrString
+FUNC PyObject_GetTypeData
+FUNC PyObject_GetTypeData_DuringGC
+FUNC PyObject_HasAttr
+FUNC PyObject_HasAttrString
+FUNC PyObject_HasAttrStringWithError
+FUNC PyObject_HasAttrWithError
+FUNC PyObject_Hash
+FUNC PyObject_HashNotImplemented
+FUNC PyObject_Init
+FUNC PyObject_InitVar
+FUNC PyObject_IsInstance
+FUNC PyObject_IsSubclass
+FUNC PyObject_IsTrue
+FUNC PyObject_Length
+FUNC PyObject_Malloc
+FUNC PyObject_Not
+FUNC PyObject_Realloc
+FUNC PyObject_Repr
+FUNC PyObject_RichCompare
+FUNC PyObject_RichCompareBool
+FUNC PyObject_SelfIter
+FUNC PyObject_SetAttr
+FUNC PyObject_SetAttrString
+FUNC PyObject_SetItem
+FUNC PyObject_Size
+FUNC PyObject_Str
+FUNC PyObject_Type
+FUNC PyObject_Vectorcall
+FUNC PyObject_VectorcallMethod
+OBJECT PyProperty_Type
+OBJECT PyRangeIter_Type
+OBJECT PyRange_Type
+OBJECT PyReversed_Type
+FUNC PySeqIter_New
+OBJECT PySeqIter_Type
+FUNC PySequence_Check
+FUNC PySequence_Concat
+FUNC PySequence_Contains
+FUNC PySequence_Count
+FUNC PySequence_DelItem
+FUNC PySequence_DelSlice
+FUNC PySequence_Fast
+FUNC PySequence_GetItem
+FUNC PySequence_GetSlice
+FUNC PySequence_In
+FUNC PySequence_InPlaceConcat
+FUNC PySequence_InPlaceRepeat
+FUNC PySequence_Index
+FUNC PySequence_Length
+FUNC PySequence_List
+FUNC PySequence_Repeat
+FUNC PySequence_SetItem
+FUNC PySequence_SetSlice
+FUNC PySequence_Size
+FUNC PySequence_Tuple
+OBJECT PySetIter_Type
+FUNC PySet_Add
+FUNC PySet_Clear
+FUNC PySet_Contains
+FUNC PySet_Discard
+FUNC PySet_New
+FUNC PySet_Pop
+FUNC PySet_Size
+OBJECT PySet_Type
+FUNC PySlice_AdjustIndices
+FUNC PySlice_GetIndices
+FUNC PySlice_GetIndicesEx
+FUNC PySlice_New
+OBJECT PySlice_Type
+FUNC PySlice_Unpack
+FUNC PyState_AddModule
+FUNC PyState_FindModule
+FUNC PyState_RemoveModule
+FUNC PyStructSequence_GetItem
+FUNC PyStructSequence_New
+FUNC PyStructSequence_NewType
+FUNC PyStructSequence_SetItem
+OBJECT PyStructSequence_UnnamedField
+OBJECT PySuper_Type
+FUNC PySys_AddWarnOption
+FUNC PySys_AddWarnOptionUnicode
+FUNC PySys_AddXOption
+FUNC PySys_Audit
+FUNC PySys_AuditTuple
+FUNC PySys_FormatStderr
+FUNC PySys_FormatStdout
+FUNC PySys_GetAttr
+FUNC PySys_GetAttrString
+FUNC PySys_GetObject
+FUNC PySys_GetOptionalAttr
+FUNC PySys_GetOptionalAttrString
+FUNC PySys_GetXOptions
+FUNC PySys_HasWarnOptions
+FUNC PySys_ResetWarnOptions
+FUNC PySys_SetArgv
+FUNC PySys_SetArgvEx
+FUNC PySys_SetObject
+FUNC PySys_SetPath
+FUNC PySys_WriteStderr
+FUNC PySys_WriteStdout
+FUNC PyThreadState_Clear
+FUNC PyThreadState_Delete
+FUNC PyThreadState_DeleteCurrent
+FUNC PyThreadState_Ensure
+FUNC PyThreadState_EnsureFromView
+FUNC PyThreadState_Get
+FUNC PyThreadState_GetDict
+FUNC PyThreadState_GetFrame
+FUNC PyThreadState_GetID
+FUNC PyThreadState_GetInterpreter
+FUNC PyThreadState_New
+FUNC PyThreadState_Release
+FUNC PyThreadState_SetAsyncExc
+FUNC PyThreadState_Swap
+FUNC PyThread_GetInfo
+FUNC PyThread_ReInitTLS
+FUNC PyThread_acquire_lock
+FUNC PyThread_acquire_lock_timed
+FUNC PyThread_allocate_lock
+FUNC PyThread_create_key
+FUNC PyThread_delete_key
+FUNC PyThread_delete_key_value
+FUNC PyThread_exit_thread
+FUNC PyThread_free_lock
+FUNC PyThread_get_key_value
+FUNC PyThread_get_stacksize
+FUNC PyThread_get_thread_ident
+FUNC PyThread_get_thread_native_id
+FUNC PyThread_init_thread
+FUNC PyThread_release_lock
+FUNC PyThread_set_key_value
+FUNC PyThread_set_stacksize
+FUNC PyThread_start_new_thread
+FUNC PyThread_tss_alloc
+FUNC PyThread_tss_create
+FUNC PyThread_tss_delete
+FUNC PyThread_tss_free
+FUNC PyThread_tss_get
+FUNC PyThread_tss_is_created
+FUNC PyThread_tss_set
+FUNC PyTraceBack_Here
+FUNC PyTraceBack_Print
+OBJECT PyTraceBack_Type
+OBJECT PyTupleIter_Type
+FUNC PyTuple_GetItem
+FUNC PyTuple_GetSlice
+FUNC PyTuple_New
+FUNC PyTuple_Pack
+FUNC PyTuple_SetItem
+FUNC PyTuple_Size
+OBJECT PyTuple_Type
+FUNC PyType_ClearCache
+FUNC PyType_Freeze
+FUNC PyType_FromMetaclass
+FUNC PyType_FromModuleAndSpec
+FUNC PyType_FromSlots
+FUNC PyType_FromSpec
+FUNC PyType_FromSpecWithBases
+FUNC PyType_GenericAlloc
+FUNC PyType_GenericNew
+FUNC PyType_GetBaseByToken
+FUNC PyType_GetBaseByToken_DuringGC
+FUNC PyType_GetFlags
+FUNC PyType_GetFullyQualifiedName
+FUNC PyType_GetModule
+FUNC PyType_GetModuleByDef
+FUNC PyType_GetModuleByToken
+FUNC PyType_GetModuleByToken_DuringGC
+FUNC PyType_GetModuleName
+FUNC PyType_GetModuleState
+FUNC PyType_GetModuleState_DuringGC
+FUNC PyType_GetModule_DuringGC
+FUNC PyType_GetName
+FUNC PyType_GetQualName
+FUNC PyType_GetSlot
+FUNC PyType_GetTypeDataSize
+FUNC PyType_IsSubtype
+FUNC PyType_Modified
+FUNC PyType_Ready
+OBJECT PyType_Type
+FUNC PyUnicodeDecodeError_Create
+FUNC PyUnicodeDecodeError_GetEncoding
+FUNC PyUnicodeDecodeError_GetEnd
+FUNC PyUnicodeDecodeError_GetObject
+FUNC PyUnicodeDecodeError_GetReason
+FUNC PyUnicodeDecodeError_GetStart
+FUNC PyUnicodeDecodeError_SetEnd
+FUNC PyUnicodeDecodeError_SetReason
+FUNC PyUnicodeDecodeError_SetStart
+FUNC PyUnicodeEncodeError_GetEncoding
+FUNC PyUnicodeEncodeError_GetEnd
+FUNC PyUnicodeEncodeError_GetObject
+FUNC PyUnicodeEncodeError_GetReason
+FUNC PyUnicodeEncodeError_GetStart
+FUNC PyUnicodeEncodeError_SetEnd
+FUNC PyUnicodeEncodeError_SetReason
+FUNC PyUnicodeEncodeError_SetStart
+OBJECT PyUnicodeIter_Type
+FUNC PyUnicodeTranslateError_GetEnd
+FUNC PyUnicodeTranslateError_GetObject
+FUNC PyUnicodeTranslateError_GetReason
+FUNC PyUnicodeTranslateError_GetStart
+FUNC PyUnicodeTranslateError_SetEnd
+FUNC PyUnicodeTranslateError_SetReason
+FUNC PyUnicodeTranslateError_SetStart
+FUNC PyUnicode_Append
+FUNC PyUnicode_AppendAndDel
+FUNC PyUnicode_AsASCIIString
+FUNC PyUnicode_AsCharmapString
+FUNC PyUnicode_AsDecodedObject
+FUNC PyUnicode_AsDecodedUnicode
+FUNC PyUnicode_AsEncodedObject
+FUNC PyUnicode_AsEncodedString
+FUNC PyUnicode_AsEncodedUnicode
+FUNC PyUnicode_AsLatin1String
+FUNC PyUnicode_AsRawUnicodeEscapeString
+FUNC PyUnicode_AsUCS4
+FUNC PyUnicode_AsUCS4Copy
+FUNC PyUnicode_AsUTF16String
+FUNC PyUnicode_AsUTF32String
+FUNC PyUnicode_AsUTF8AndSize
+FUNC PyUnicode_AsUTF8String
+FUNC PyUnicode_AsUnicodeEscapeString
+FUNC PyUnicode_AsWideChar
+FUNC PyUnicode_AsWideCharString
+FUNC PyUnicode_BuildEncodingMap
+FUNC PyUnicode_Compare
+FUNC PyUnicode_CompareWithASCIIString
+FUNC PyUnicode_Concat
+FUNC PyUnicode_Contains
+FUNC PyUnicode_Count
+FUNC PyUnicode_Decode
+FUNC PyUnicode_DecodeASCII
+FUNC PyUnicode_DecodeCharmap
+FUNC PyUnicode_DecodeFSDefault
+FUNC PyUnicode_DecodeFSDefaultAndSize
+FUNC PyUnicode_DecodeLatin1
+FUNC PyUnicode_DecodeLocale
+FUNC PyUnicode_DecodeLocaleAndSize
+FUNC PyUnicode_DecodeRawUnicodeEscape
+FUNC PyUnicode_DecodeUTF16
+FUNC PyUnicode_DecodeUTF16Stateful
+FUNC PyUnicode_DecodeUTF32
+FUNC PyUnicode_DecodeUTF32Stateful
+FUNC PyUnicode_DecodeUTF7
+FUNC PyUnicode_DecodeUTF7Stateful
+FUNC PyUnicode_DecodeUTF8
+FUNC PyUnicode_DecodeUTF8Stateful
+FUNC PyUnicode_DecodeUnicodeEscape
+FUNC PyUnicode_EncodeFSDefault
+FUNC PyUnicode_EncodeLocale
+FUNC PyUnicode_Equal
+FUNC PyUnicode_EqualToUTF8
+FUNC PyUnicode_EqualToUTF8AndSize
+FUNC PyUnicode_FSConverter
+FUNC PyUnicode_FSDecoder
+FUNC PyUnicode_Find
+FUNC PyUnicode_FindChar
+FUNC PyUnicode_Format
+FUNC PyUnicode_FromEncodedObject
+FUNC PyUnicode_FromFormat
+FUNC PyUnicode_FromFormatV
+FUNC PyUnicode_FromObject
+FUNC PyUnicode_FromOrdinal
+FUNC PyUnicode_FromString
+FUNC PyUnicode_FromStringAndSize
+FUNC PyUnicode_FromWideChar
+FUNC PyUnicode_GetDefaultEncoding
+FUNC PyUnicode_GetLength
+FUNC PyUnicode_GetSize
+FUNC PyUnicode_InternFromString
+FUNC PyUnicode_InternImmortal
+FUNC PyUnicode_InternInPlace
+FUNC PyUnicode_IsIdentifier
+FUNC PyUnicode_Join
+FUNC PyUnicode_Partition
+FUNC PyUnicode_RPartition
+FUNC PyUnicode_RSplit
+FUNC PyUnicode_ReadChar
+FUNC PyUnicode_Replace
+FUNC PyUnicode_Resize
+FUNC PyUnicode_RichCompare
+FUNC PyUnicode_Split
+FUNC PyUnicode_Splitlines
+FUNC PyUnicode_Substring
+FUNC PyUnicode_Tailmatch
+FUNC PyUnicode_Translate
+OBJECT PyUnicode_Type
+FUNC PyUnicode_WriteChar
+FUNC PyVectorcall_Call
+FUNC PyVectorcall_NARGS
+FUNC PyWeakref_GetObject
+FUNC PyWeakref_GetRef
+FUNC PyWeakref_NewProxy
+FUNC PyWeakref_NewRef
+OBJECT PyWrapperDescr_Type
+FUNC PyWrapper_New
+OBJECT PyZip_Type
+FUNC Py_AddPendingCall
+FUNC Py_AtExit
+FUNC Py_BuildValue
+FUNC Py_BytesMain
+FUNC Py_CompileString
+FUNC Py_DecRef
+FUNC Py_DecodeLocale
+FUNC Py_EncodeLocale
+FUNC Py_EndInterpreter
+FUNC Py_EnterRecursiveCall
+FUNC Py_Exit
+FUNC Py_FatalError
+OBJECT Py_FileSystemDefaultEncodeErrors
+OBJECT Py_FileSystemDefaultEncoding
+FUNC Py_Finalize
+FUNC Py_FinalizeEx
+FUNC Py_GenericAlias
+OBJECT Py_GenericAliasType
+FUNC Py_GetArgcArgv
+FUNC Py_GetBuildInfo
+FUNC Py_GetCompiler
+FUNC Py_GetConstant
+FUNC Py_GetConstantBorrowed
+FUNC Py_GetCopyright
+FUNC Py_GetExecPrefix
+FUNC Py_GetPath
+FUNC Py_GetPlatform
+FUNC Py_GetPrefix
+FUNC Py_GetProgramFullPath
+FUNC Py_GetProgramName
+FUNC Py_GetPythonHome
+FUNC Py_GetRecursionLimit
+FUNC Py_GetVersion
+OBJECT Py_HasFileSystemDefaultEncoding
+FUNC Py_IS_TYPE
+FUNC Py_IncRef
+FUNC Py_Initialize
+FUNC Py_InitializeEx
+FUNC Py_Is
+FUNC Py_IsFalse
+FUNC Py_IsFinalizing
+FUNC Py_IsInitialized
+FUNC Py_IsNone
+FUNC Py_IsTrue
+FUNC Py_LeaveRecursiveCall
+FUNC Py_Main
+FUNC Py_MakePendingCalls
+FUNC Py_NewInterpreter
+FUNC Py_NewRef
+FUNC Py_PACK_FULL_VERSION
+FUNC Py_PACK_VERSION
+FUNC Py_REFCNT
+FUNC Py_ReprEnter
+FUNC Py_ReprLeave
+FUNC Py_SET_SIZE
+FUNC Py_SIZE
+FUNC Py_SetPath
+FUNC Py_SetProgramName
+FUNC Py_SetPythonHome
+FUNC Py_SetRecursionLimit
+FUNC Py_TYPE
+OBJECT Py_UTF8Mode
+FUNC Py_VaBuildValue
+OBJECT Py_Version
+FUNC Py_XNewRef
+FUNC _PyArg_ParseTupleAndKeywords_SizeT
+FUNC _PyArg_ParseTuple_SizeT
+FUNC _PyArg_Parse_SizeT
+FUNC _PyArg_VaParseTupleAndKeywords_SizeT
+FUNC _PyArg_VaParse_SizeT
+FUNC _PyErr_BadInternalCall
+FUNC _PyObject_CallFunction_SizeT
+FUNC _PyObject_CallMethod_SizeT
+FUNC _PyObject_GC_New
+FUNC _PyObject_GC_NewVar
+FUNC _PyObject_GC_Resize
+FUNC _PyObject_New
+FUNC _PyObject_NewVar
+FUNC _PyState_AddModule
+FUNC _PyThreadState_Init
+FUNC _PyThreadState_Prealloc
+OBJECT _PyWeakref_CallableProxyType
+OBJECT _PyWeakref_ProxyType
+OBJECT _PyWeakref_RefType
+FUNC _Py_BuildValue_SizeT
+FUNC _Py_CheckRecursiveCall
+FUNC _Py_Dealloc
+FUNC _Py_DecRef
+OBJECT _Py_EllipsisObject
+OBJECT _Py_FalseStruct
+FUNC _Py_IncRef
+OBJECT _Py_NoneStruct
+OBJECT _Py_NotImplementedStruct
+FUNC _Py_SetRefcnt
+OBJECT _Py_SwappedOp
+OBJECT _Py_TrueStruct
+FUNC _Py_VaBuildValue_SizeT
diff --git a/abi/abi3t-missing-linux.txt b/abi/abi3t-missing-linux.txt
new file mode 100644
index 0000000000..f1612ec40d
--- /dev/null
+++ b/abi/abi3t-missing-linux.txt
@@ -0,0 +1,64 @@
+FUNC PyEval_AcquireLock
+FUNC PyEval_CallFunction
+FUNC PyEval_CallMethod
+FUNC PyEval_ReleaseLock
+FUNC PyInterpreterGuard_Close
+FUNC PyInterpreterGuard_FromCurrent
+FUNC PyInterpreterGuard_FromView
+FUNC PyInterpreterView_Close
+FUNC PyInterpreterView_FromCurrent
+FUNC PyInterpreterView_FromMain
+FUNC PyIter_NextItem
+FUNC PyLongWriter_Create
+FUNC PyLongWriter_Discard
+FUNC PyLongWriter_Finish
+FUNC PyLong_AsInt32
+FUNC PyLong_AsInt64
+FUNC PyLong_AsUInt32
+FUNC PyLong_AsUInt64
+FUNC PyLong_Export
+FUNC PyLong_FreeExport
+FUNC PyLong_FromInt32
+FUNC PyLong_FromInt64
+FUNC PyLong_FromUInt32
+FUNC PyLong_FromUInt64
+FUNC PyLong_GetNativeLayout
+FUNC PyObject_GetTypeData_DuringGC
+FUNC PySys_AddWarnOption
+FUNC PySys_AddWarnOptionUnicode
+FUNC PySys_AddXOption
+FUNC PySys_GetAttr
+FUNC PySys_GetAttrString
+FUNC PySys_GetOptionalAttr
+FUNC PySys_GetOptionalAttrString
+FUNC PySys_HasWarnOptions
+FUNC PySys_SetPath
+FUNC PyThreadState_Ensure
+FUNC PyThreadState_EnsureFromView
+FUNC PyThreadState_Release
+FUNC PyType_Freeze
+FUNC PyType_FromSlots
+FUNC PyType_GetBaseByToken
+FUNC PyType_GetBaseByToken_DuringGC
+FUNC PyType_GetModuleByToken
+FUNC PyType_GetModuleByToken_DuringGC
+FUNC PyType_GetModuleState_DuringGC
+FUNC PyType_GetModule_DuringGC
+FUNC PyUnicode_Equal
+OBJECT Py_FileSystemDefaultEncodeErrors
+OBJECT Py_HasFileSystemDefaultEncoding
+FUNC Py_IS_TYPE
+FUNC Py_PACK_FULL_VERSION
+FUNC Py_PACK_VERSION
+FUNC Py_SET_SIZE
+FUNC Py_SIZE
+FUNC Py_SetPath
+FUNC Py_TYPE
+FUNC _PyState_AddModule
+FUNC _PyThreadState_Init
+FUNC _PyThreadState_Prealloc
+OBJECT _Py_EllipsisObject
+OBJECT _Py_FalseStruct
+OBJECT _Py_NoneStruct
+OBJECT _Py_NotImplementedStruct
+OBJECT _Py_TrueStruct
diff --git a/abi/suppressions-graalpy253.ini b/abi/suppressions-graalpy253.ini
new file mode 100644
index 0000000000..b2827350e6
--- /dev/null
+++ b/abi/suppressions-graalpy253.ini
@@ -0,0 +1,4 @@
+[suppress_function]
+ label = Experimental ABI3T functions
+ symbol_name_regexp = ^(PyABIInfo_Check|PyModule_Exec|PyModule_FromSlotsAndSpec|PyModule_GetStateSize|PyModule_GetState_DuringGC|PyModule_GetToken|PyModule_GetToken_DuringGC)$
+ drop = true
diff --git a/capi-abis.toml b/capi-abis.toml
new file mode 100644
index 0000000000..ab371a470c
--- /dev/null
+++ b/capi-abis.toml
@@ -0,0 +1,19 @@
+# C API signature and export profiles. Paths are relative to the repository root.
+
+[profiles.GRAALPY]
+python_version = "3.13"
+source_ref = "v3.13.14"
+mode = "full"
+defines = []
+inventory = "graalpython/com.oracle.graal.python.cext/CAPIFunctions.txt"
+
+[profiles.ABI3T]
+parent = "GRAALPY"
+python_version = "3.15"
+source_ref = "v3.15.0rc1"
+mode = "stable"
+defines = ["Py_TARGET_ABI3T=0x030f0000"]
+inventory = "graalpython/com.oracle.graal.python.cext/CAPIFunctions-ABI3T.txt"
+export_manifest = "abi/abi3t-linux.txt"
+missing_symbols = "abi/abi3t-missing-linux.txt"
+disabled_features = ["MS_WINDOWS", "Py_REF_DEBUG", "USE_STACKCHECK"]
diff --git a/graalpython/com.oracle.graal.python.cext/CAPIFunctions-ABI3T.txt b/graalpython/com.oracle.graal.python.cext/CAPIFunctions-ABI3T.txt
new file mode 100644
index 0000000000..1c710a7b83
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.cext/CAPIFunctions-ABI3T.txt
@@ -0,0 +1,762 @@
+PyABIInfo_Check;int;PyABIInfo*|const char*
+PyAIter_Check;int;PyObject*
+PyArg_Parse;int;PyObject*|const char*|...
+PyArg_ParseTuple;int;PyObject*|const char*|...
+PyArg_ParseTupleAndKeywords;int;PyObject*|PyObject*|const char*|char*const*|...
+PyArg_UnpackTuple;int;PyObject*|const char*|Py_ssize_t|Py_ssize_t|...
+PyArg_VaParse;int;PyObject*|const char*|va_list
+PyArg_VaParseTupleAndKeywords;int;PyObject*|PyObject*|const char*|char*const*|va_list
+PyArg_ValidateKeywordArguments;int;PyObject*
+PyBool_FromLong;PyObject*;long
+PyBuffer_FillContiguousStrides;void;int|Py_ssize_t*|Py_ssize_t*|int|char
+PyBuffer_FillInfo;int;Py_buffer*|PyObject*|void*|Py_ssize_t|int|int
+PyBuffer_FromContiguous;int;const Py_buffer*|const void*|Py_ssize_t|char
+PyBuffer_GetPointer;void*;const Py_buffer*|const Py_ssize_t*
+PyBuffer_IsContiguous;int;const Py_buffer*|char
+PyBuffer_Release;void;Py_buffer*
+PyBuffer_SizeFromFormat;Py_ssize_t;const char*
+PyBuffer_ToContiguous;int;void*|const Py_buffer*|Py_ssize_t|char
+PyByteArray_AsString;char*;PyObject*
+PyByteArray_Concat;PyObject*;PyObject*|PyObject*
+PyByteArray_FromObject;PyObject*;PyObject*
+PyByteArray_FromStringAndSize;PyObject*;const char*|Py_ssize_t
+PyByteArray_Resize;int;PyObject*|Py_ssize_t
+PyByteArray_Size;Py_ssize_t;PyObject*
+PyBytes_AsString;char*;PyObject*
+PyBytes_AsStringAndSize;int;PyObject*|char**|Py_ssize_t*
+PyBytes_Concat;void;PyObject**|PyObject*
+PyBytes_ConcatAndDel;void;PyObject**|PyObject*
+PyBytes_DecodeEscape;PyObject*;const char*|Py_ssize_t|const char*|Py_ssize_t|const char*
+PyBytes_FromFormat;PyObject*;const char*|...
+PyBytes_FromFormatV;PyObject*;const char*|va_list
+PyBytes_FromObject;PyObject*;PyObject*
+PyBytes_FromString;PyObject*;const char*
+PyBytes_FromStringAndSize;PyObject*;const char*|Py_ssize_t
+PyBytes_Repr;PyObject*;PyObject*|int
+PyBytes_Size;Py_ssize_t;PyObject*
+PyCFunction_GetFlags;int;PyObject*
+PyCFunction_GetFunction;PyCFunction;PyObject*
+PyCFunction_GetSelf;PyObject*;PyObject*
+PyCFunction_New;PyObject*;PyMethodDef*|PyObject*
+PyCFunction_NewEx;PyObject*;PyMethodDef*|PyObject*|PyObject*
+PyCMethod_New;PyObject*;PyMethodDef*|PyObject*|PyObject*|PyTypeObject*
+PyCallIter_New;PyObject*;PyObject*|PyObject*
+PyCallable_Check;int;PyObject*
+PyCapsule_GetContext;void*;PyObject*
+PyCapsule_GetDestructor;PyCapsule_Destructor;PyObject*
+PyCapsule_GetName;const char*;PyObject*
+PyCapsule_GetPointer;void*;PyObject*|const char*
+PyCapsule_Import;void*;const char*|int
+PyCapsule_IsValid;int;PyObject*|const char*
+PyCapsule_New;PyObject*;void*|const char*|PyCapsule_Destructor
+PyCapsule_SetContext;int;PyObject*|void*
+PyCapsule_SetDestructor;int;PyObject*|PyCapsule_Destructor
+PyCapsule_SetName;int;PyObject*|const char*
+PyCapsule_SetPointer;int;PyObject*|void*
+PyCodec_BackslashReplaceErrors;PyObject*;PyObject*
+PyCodec_Decode;PyObject*;PyObject*|const char*|const char*
+PyCodec_Decoder;PyObject*;const char*
+PyCodec_Encode;PyObject*;PyObject*|const char*|const char*
+PyCodec_Encoder;PyObject*;const char*
+PyCodec_IgnoreErrors;PyObject*;PyObject*
+PyCodec_IncrementalDecoder;PyObject*;const char*|const char*
+PyCodec_IncrementalEncoder;PyObject*;const char*|const char*
+PyCodec_KnownEncoding;int;const char*
+PyCodec_LookupError;PyObject*;const char*
+PyCodec_NameReplaceErrors;PyObject*;PyObject*
+PyCodec_Register;int;PyObject*
+PyCodec_RegisterError;int;const char*|PyObject*
+PyCodec_ReplaceErrors;PyObject*;PyObject*
+PyCodec_StreamReader;PyObject*;const char*|PyObject*|const char*
+PyCodec_StreamWriter;PyObject*;const char*|PyObject*|const char*
+PyCodec_StrictErrors;PyObject*;PyObject*
+PyCodec_Unregister;int;PyObject*
+PyCodec_XMLCharRefReplaceErrors;PyObject*;PyObject*
+PyComplex_FromDoubles;PyObject*;double|double
+PyComplex_ImagAsDouble;double;PyObject*
+PyComplex_RealAsDouble;double;PyObject*
+PyCriticalSection2_Begin;void;PyCriticalSection2*|PyObject*|PyObject*
+PyCriticalSection2_End;void;PyCriticalSection2*
+PyCriticalSection_Begin;void;PyCriticalSection*|PyObject*
+PyCriticalSection_End;void;PyCriticalSection*
+PyDescr_NewClassMethod;PyObject*;PyTypeObject*|PyMethodDef*
+PyDescr_NewGetSet;PyObject*;PyTypeObject*|PyGetSetDef*
+PyDescr_NewMember;PyObject*;PyTypeObject*|PyMemberDef*
+PyDescr_NewMethod;PyObject*;PyTypeObject*|PyMethodDef*
+PyDictProxy_New;PyObject*;PyObject*
+PyDict_Clear;void;PyObject*
+PyDict_Contains;int;PyObject*|PyObject*
+PyDict_Copy;PyObject*;PyObject*
+PyDict_DelItem;int;PyObject*|PyObject*
+PyDict_DelItemString;int;PyObject*|const char*
+PyDict_GetItem;PyObject*;PyObject*|PyObject*
+PyDict_GetItemRef;int;PyObject*|PyObject*|PyObject**
+PyDict_GetItemString;PyObject*;PyObject*|const char*
+PyDict_GetItemStringRef;int;PyObject*|const char*|PyObject**
+PyDict_GetItemWithError;PyObject*;PyObject*|PyObject*
+PyDict_Items;PyObject*;PyObject*
+PyDict_Keys;PyObject*;PyObject*
+PyDict_Merge;int;PyObject*|PyObject*|int
+PyDict_MergeFromSeq2;int;PyObject*|PyObject*|int
+PyDict_New;PyObject*;void
+PyDict_Next;int;PyObject*|Py_ssize_t*|PyObject**|PyObject**
+PyDict_SetDefaultRef;int;PyObject*|PyObject*|PyObject*|PyObject**
+PyDict_SetItem;int;PyObject*|PyObject*|PyObject*
+PyDict_SetItemString;int;PyObject*|const char*|PyObject*
+PyDict_Size;Py_ssize_t;PyObject*
+PyDict_Update;int;PyObject*|PyObject*
+PyDict_Values;PyObject*;PyObject*
+PyErr_BadArgument;int;void
+PyErr_BadInternalCall;void;void
+PyErr_CheckSignals;int;void
+PyErr_Clear;void;void
+PyErr_Display;void;PyObject*|PyObject*|PyObject*
+PyErr_DisplayException;void;PyObject*
+PyErr_ExceptionMatches;int;PyObject*
+PyErr_Fetch;void;PyObject**|PyObject**|PyObject**
+PyErr_Format;PyObject*;PyObject*|const char*|...
+PyErr_FormatV;PyObject*;PyObject*|const char*|va_list
+PyErr_GetExcInfo;void;PyObject**|PyObject**|PyObject**
+PyErr_GetHandledException;PyObject*;void
+PyErr_GetRaisedException;PyObject*;void
+PyErr_GivenExceptionMatches;int;PyObject*|PyObject*
+PyErr_NewException;PyObject*;const char*|PyObject*|PyObject*
+PyErr_NewExceptionWithDoc;PyObject*;const char*|const char*|PyObject*|PyObject*
+PyErr_NoMemory;PyObject*;void
+PyErr_NormalizeException;void;PyObject**|PyObject**|PyObject**
+PyErr_Occurred;PyObject*;void
+PyErr_Print;void;void
+PyErr_PrintEx;void;int
+PyErr_ProgramText;PyObject*;const char*|int
+PyErr_ResourceWarning;int;PyObject*|Py_ssize_t|const char*|...
+PyErr_Restore;void;PyObject*|PyObject*|PyObject*
+PyErr_SetExcInfo;void;PyObject*|PyObject*|PyObject*
+PyErr_SetFromErrno;PyObject*;PyObject*
+PyErr_SetFromErrnoWithFilename;PyObject*;PyObject*|const char*
+PyErr_SetFromErrnoWithFilenameObject;PyObject*;PyObject*|PyObject*
+PyErr_SetFromErrnoWithFilenameObjects;PyObject*;PyObject*|PyObject*|PyObject*
+PyErr_SetHandledException;void;PyObject*
+PyErr_SetImportError;PyObject*;PyObject*|PyObject*|PyObject*
+PyErr_SetImportErrorSubclass;PyObject*;PyObject*|PyObject*|PyObject*|PyObject*
+PyErr_SetInterrupt;void;void
+PyErr_SetInterruptEx;int;int
+PyErr_SetNone;void;PyObject*
+PyErr_SetObject;void;PyObject*|PyObject*
+PyErr_SetRaisedException;void;PyObject*
+PyErr_SetString;void;PyObject*|const char*
+PyErr_SyntaxLocation;void;const char*|int
+PyErr_SyntaxLocationEx;void;const char*|int|int
+PyErr_WarnEx;int;PyObject*|const char*|Py_ssize_t
+PyErr_WarnExplicit;int;PyObject*|const char*|const char*|int|const char*|PyObject*
+PyErr_WarnFormat;int;PyObject*|Py_ssize_t|const char*|...
+PyErr_WriteUnraisable;void;PyObject*
+PyEval_AcquireThread;void;PyThreadState*
+PyEval_EvalCode;PyObject*;PyObject*|PyObject*|PyObject*
+PyEval_EvalCodeEx;PyObject*;PyObject*|PyObject*|PyObject*|PyObject*const*|int|PyObject*const*|int|PyObject*const*|int|PyObject*|PyObject*
+PyEval_EvalFrame;PyObject*;PyFrameObject*
+PyEval_EvalFrameEx;PyObject*;PyFrameObject*|int
+PyEval_GetBuiltins;PyObject*;void
+PyEval_GetFrame;PyFrameObject*;void
+PyEval_GetFrameBuiltins;PyObject*;void
+PyEval_GetFrameGlobals;PyObject*;void
+PyEval_GetFrameLocals;PyObject*;void
+PyEval_GetFuncDesc;const char*;PyObject*
+PyEval_GetFuncName;const char*;PyObject*
+PyEval_GetGlobals;PyObject*;void
+PyEval_GetLocals;PyObject*;void
+PyEval_InitThreads;void;void
+PyEval_ReleaseThread;void;PyThreadState*
+PyEval_RestoreThread;void;PyThreadState*
+PyEval_SaveThread;PyThreadState*;void
+PyExceptionClass_Name;const char*;PyObject*
+PyException_GetArgs;PyObject*;PyObject*
+PyException_GetCause;PyObject*;PyObject*
+PyException_GetContext;PyObject*;PyObject*
+PyException_GetTraceback;PyObject*;PyObject*
+PyException_SetArgs;void;PyObject*|PyObject*
+PyException_SetCause;void;PyObject*|PyObject*
+PyException_SetContext;void;PyObject*|PyObject*
+PyException_SetTraceback;int;PyObject*|PyObject*
+PyFile_FromFd;PyObject*;int|const char*|const char*|int|const char*|const char*|const char*|int
+PyFile_GetLine;PyObject*;PyObject*|int
+PyFile_WriteObject;int;PyObject*|PyObject*|int
+PyFile_WriteString;int;const char*|PyObject*
+PyFloat_AsDouble;double;PyObject*
+PyFloat_FromDouble;PyObject*;double
+PyFloat_FromString;PyObject*;PyObject*
+PyFloat_GetInfo;PyObject*;void
+PyFloat_GetMax;double;void
+PyFloat_GetMin;double;void
+PyFrame_GetCode;PyCodeObject*;PyFrameObject*
+PyFrame_GetLineNumber;int;PyFrameObject*
+PyFrozenSet_New;PyObject*;PyObject*
+PyGC_Collect;Py_ssize_t;void
+PyGC_Disable;int;void
+PyGC_Enable;int;void
+PyGC_IsEnabled;int;void
+PyGILState_Ensure;PyGILState_STATE;void
+PyGILState_GetThisThreadState;PyThreadState*;void
+PyGILState_Release;void;PyGILState_STATE
+PyImport_AddModule;PyObject*;const char*
+PyImport_AddModuleObject;PyObject*;PyObject*
+PyImport_AddModuleRef;PyObject*;const char*
+PyImport_AppendInittab;int;const char*|PyObject*(*)(void)
+PyImport_ExecCodeModule;PyObject*;const char*|PyObject*
+PyImport_ExecCodeModuleEx;PyObject*;const char*|PyObject*|const char*
+PyImport_ExecCodeModuleObject;PyObject*;PyObject*|PyObject*|PyObject*|PyObject*
+PyImport_ExecCodeModuleWithPathnames;PyObject*;const char*|PyObject*|const char*|const char*
+PyImport_GetImporter;PyObject*;PyObject*
+PyImport_GetMagicNumber;long;void
+PyImport_GetMagicTag;const char*;void
+PyImport_GetModule;PyObject*;PyObject*
+PyImport_GetModuleDict;PyObject*;void
+PyImport_Import;PyObject*;PyObject*
+PyImport_ImportFrozenModule;int;const char*
+PyImport_ImportFrozenModuleObject;int;PyObject*
+PyImport_ImportModule;PyObject*;const char*
+PyImport_ImportModuleLevel;PyObject*;const char*|PyObject*|PyObject*|PyObject*|int
+PyImport_ImportModuleLevelObject;PyObject*;PyObject*|PyObject*|PyObject*|PyObject*|int
+PyImport_ReloadModule;PyObject*;PyObject*
+PyIndex_Check;int;PyObject*
+PyInterpreterGuard_Close;void;PyInterpreterGuard*
+PyInterpreterGuard_FromCurrent;PyInterpreterGuard*;void
+PyInterpreterGuard_FromView;PyInterpreterGuard*;PyInterpreterView*
+PyInterpreterState_Clear;void;PyInterpreterState*
+PyInterpreterState_Delete;void;PyInterpreterState*
+PyInterpreterState_Get;PyInterpreterState*;void
+PyInterpreterState_GetDict;PyObject*;PyInterpreterState*
+PyInterpreterState_GetID;int64_t;PyInterpreterState*
+PyInterpreterState_New;PyInterpreterState*;void
+PyInterpreterView_Close;void;PyInterpreterView*
+PyInterpreterView_FromCurrent;PyInterpreterView*;void
+PyInterpreterView_FromMain;PyInterpreterView*;void
+PyIter_Check;int;PyObject*
+PyIter_Next;PyObject*;PyObject*
+PyIter_NextItem;int;PyObject*|PyObject**
+PyIter_Send;PySendResult;PyObject*|PyObject*|PyObject**
+PyList_Append;int;PyObject*|PyObject*
+PyList_AsTuple;PyObject*;PyObject*
+PyList_GetItem;PyObject*;PyObject*|Py_ssize_t
+PyList_GetItemRef;PyObject*;PyObject*|Py_ssize_t
+PyList_GetSlice;PyObject*;PyObject*|Py_ssize_t|Py_ssize_t
+PyList_Insert;int;PyObject*|Py_ssize_t|PyObject*
+PyList_New;PyObject*;Py_ssize_t
+PyList_Reverse;int;PyObject*
+PyList_SetItem;int;PyObject*|Py_ssize_t|PyObject*
+PyList_SetSlice;int;PyObject*|Py_ssize_t|Py_ssize_t|PyObject*
+PyList_Size;Py_ssize_t;PyObject*
+PyList_Sort;int;PyObject*
+PyLongWriter_Create;PyLongWriter*;int|Py_ssize_t|void**
+PyLongWriter_Discard;void;PyLongWriter*
+PyLongWriter_Finish;PyObject*;PyLongWriter*
+PyLong_AsDouble;double;PyObject*
+PyLong_AsInt32;int;PyObject*|int32_t*
+PyLong_AsInt64;int;PyObject*|int64_t*
+PyLong_AsInt;int;PyObject*
+PyLong_AsLong;long;PyObject*
+PyLong_AsLongAndOverflow;long;PyObject*|int*
+PyLong_AsLongLong;long long;PyObject*
+PyLong_AsLongLongAndOverflow;long long;PyObject*|int*
+PyLong_AsNativeBytes;Py_ssize_t;PyObject*|void*|Py_ssize_t|int
+PyLong_AsSize_t;size_t;PyObject*
+PyLong_AsSsize_t;Py_ssize_t;PyObject*
+PyLong_AsUInt32;int;PyObject*|uint32_t*
+PyLong_AsUInt64;int;PyObject*|uint64_t*
+PyLong_AsUnsignedLong;unsigned long;PyObject*
+PyLong_AsUnsignedLongLong;unsigned long long;PyObject*
+PyLong_AsUnsignedLongLongMask;unsigned long long;PyObject*
+PyLong_AsUnsignedLongMask;unsigned long;PyObject*
+PyLong_AsVoidPtr;void*;PyObject*
+PyLong_Export;int;PyObject*|PyLongExport*
+PyLong_FreeExport;void;PyLongExport*
+PyLong_FromDouble;PyObject*;double
+PyLong_FromInt32;PyObject*;int32_t
+PyLong_FromInt64;PyObject*;int64_t
+PyLong_FromLong;PyObject*;long
+PyLong_FromLongLong;PyObject*;long long
+PyLong_FromNativeBytes;PyObject*;const void*|size_t|int
+PyLong_FromSize_t;PyObject*;size_t
+PyLong_FromSsize_t;PyObject*;Py_ssize_t
+PyLong_FromString;PyObject*;const char*|char**|int
+PyLong_FromUInt32;PyObject*;uint32_t
+PyLong_FromUInt64;PyObject*;uint64_t
+PyLong_FromUnsignedLong;PyObject*;unsigned long
+PyLong_FromUnsignedLongLong;PyObject*;unsigned long long
+PyLong_FromUnsignedNativeBytes;PyObject*;const void*|size_t|int
+PyLong_FromVoidPtr;PyObject*;void*
+PyLong_GetInfo;PyObject*;void
+PyLong_GetNativeLayout;const PyLongLayout*;void
+PyMapping_Check;int;PyObject*
+PyMapping_GetItemString;PyObject*;PyObject*|const char*
+PyMapping_GetOptionalItem;int;PyObject*|PyObject*|PyObject**
+PyMapping_GetOptionalItemString;int;PyObject*|const char*|PyObject**
+PyMapping_HasKey;int;PyObject*|PyObject*
+PyMapping_HasKeyString;int;PyObject*|const char*
+PyMapping_HasKeyStringWithError;int;PyObject*|const char*
+PyMapping_HasKeyWithError;int;PyObject*|PyObject*
+PyMapping_Items;PyObject*;PyObject*
+PyMapping_Keys;PyObject*;PyObject*
+PyMapping_Length;Py_ssize_t;PyObject*
+PyMapping_SetItemString;int;PyObject*|const char*|PyObject*
+PyMapping_Size;Py_ssize_t;PyObject*
+PyMapping_Values;PyObject*;PyObject*
+PyMem_Calloc;void*;size_t|size_t
+PyMem_Free;void;void*
+PyMem_Malloc;void*;size_t
+PyMem_RawCalloc;void*;size_t|size_t
+PyMem_RawFree;void;void*
+PyMem_RawMalloc;void*;size_t
+PyMem_RawRealloc;void*;void*|size_t
+PyMem_Realloc;void*;void*|size_t
+PyMember_GetOne;PyObject*;const char*|PyMemberDef*
+PyMember_SetOne;int;char*|PyMemberDef*|PyObject*
+PyMemoryView_FromBuffer;PyObject*;const Py_buffer*
+PyMemoryView_FromMemory;PyObject*;char*|Py_ssize_t|int
+PyMemoryView_FromObject;PyObject*;PyObject*
+PyMemoryView_GetContiguous;PyObject*;PyObject*|int|char
+PyModuleDef_Init;PyObject*;PyModuleDef*
+PyModule_Add;int;PyObject*|const char*|PyObject*
+PyModule_AddFunctions;int;PyObject*|PyMethodDef*
+PyModule_AddIntConstant;int;PyObject*|const char*|long
+PyModule_AddObject;int;PyObject*|const char*|PyObject*
+PyModule_AddObjectRef;int;PyObject*|const char*|PyObject*
+PyModule_AddStringConstant;int;PyObject*|const char*|const char*
+PyModule_AddType;int;PyObject*|PyTypeObject*
+PyModule_Create2;PyObject*;PyModuleDef*|int
+PyModule_Exec;int;PyObject*
+PyModule_ExecDef;int;PyObject*|PyModuleDef*
+PyModule_FromDefAndSpec2;PyObject*;PyModuleDef*|PyObject*|int
+PyModule_FromSlotsAndSpec;PyObject*;const PySlot*|PyObject*
+PyModule_GetDef;PyModuleDef*;PyObject*
+PyModule_GetDict;PyObject*;PyObject*
+PyModule_GetFilename;const char*;PyObject*
+PyModule_GetFilenameObject;PyObject*;PyObject*
+PyModule_GetName;const char*;PyObject*
+PyModule_GetNameObject;PyObject*;PyObject*
+PyModule_GetState;void*;PyObject*
+PyModule_GetStateSize;int;PyObject*|Py_ssize_t*
+PyModule_GetState_DuringGC;void*;PyObject*
+PyModule_GetToken;int;PyObject*|void**
+PyModule_GetToken_DuringGC;int;PyObject*|void**
+PyModule_New;PyObject*;const char*
+PyModule_NewObject;PyObject*;PyObject*
+PyModule_SetDocString;int;PyObject*|const char*
+PyNumber_Absolute;PyObject*;PyObject*
+PyNumber_Add;PyObject*;PyObject*|PyObject*
+PyNumber_And;PyObject*;PyObject*|PyObject*
+PyNumber_AsSsize_t;Py_ssize_t;PyObject*|PyObject*
+PyNumber_Check;int;PyObject*
+PyNumber_Divmod;PyObject*;PyObject*|PyObject*
+PyNumber_Float;PyObject*;PyObject*
+PyNumber_FloorDivide;PyObject*;PyObject*|PyObject*
+PyNumber_InPlaceAdd;PyObject*;PyObject*|PyObject*
+PyNumber_InPlaceAnd;PyObject*;PyObject*|PyObject*
+PyNumber_InPlaceFloorDivide;PyObject*;PyObject*|PyObject*
+PyNumber_InPlaceLshift;PyObject*;PyObject*|PyObject*
+PyNumber_InPlaceMatrixMultiply;PyObject*;PyObject*|PyObject*
+PyNumber_InPlaceMultiply;PyObject*;PyObject*|PyObject*
+PyNumber_InPlaceOr;PyObject*;PyObject*|PyObject*
+PyNumber_InPlacePower;PyObject*;PyObject*|PyObject*|PyObject*
+PyNumber_InPlaceRemainder;PyObject*;PyObject*|PyObject*
+PyNumber_InPlaceRshift;PyObject*;PyObject*|PyObject*
+PyNumber_InPlaceSubtract;PyObject*;PyObject*|PyObject*
+PyNumber_InPlaceTrueDivide;PyObject*;PyObject*|PyObject*
+PyNumber_InPlaceXor;PyObject*;PyObject*|PyObject*
+PyNumber_Index;PyObject*;PyObject*
+PyNumber_Invert;PyObject*;PyObject*
+PyNumber_Long;PyObject*;PyObject*
+PyNumber_Lshift;PyObject*;PyObject*|PyObject*
+PyNumber_MatrixMultiply;PyObject*;PyObject*|PyObject*
+PyNumber_Multiply;PyObject*;PyObject*|PyObject*
+PyNumber_Negative;PyObject*;PyObject*
+PyNumber_Or;PyObject*;PyObject*|PyObject*
+PyNumber_Positive;PyObject*;PyObject*
+PyNumber_Power;PyObject*;PyObject*|PyObject*|PyObject*
+PyNumber_Remainder;PyObject*;PyObject*|PyObject*
+PyNumber_Rshift;PyObject*;PyObject*|PyObject*
+PyNumber_Subtract;PyObject*;PyObject*|PyObject*
+PyNumber_ToBase;PyObject*;PyObject*|int
+PyNumber_TrueDivide;PyObject*;PyObject*|PyObject*
+PyNumber_Xor;PyObject*;PyObject*|PyObject*
+PyOS_AfterFork;void;void
+PyOS_AfterFork_Child;void;void
+PyOS_AfterFork_Parent;void;void
+PyOS_BeforeFork;void;void
+PyOS_FSPath;PyObject*;PyObject*
+PyOS_InterruptOccurred;int;void
+PyOS_double_to_string;char*;double|char|int|int|int*
+PyOS_getsig;PyOS_sighandler_t;int
+PyOS_mystricmp;int;const char*|const char*
+PyOS_mystrnicmp;int;const char*|const char*|Py_ssize_t
+PyOS_setsig;PyOS_sighandler_t;int|PyOS_sighandler_t
+PyOS_snprintf;int;char*|size_t|const char*|...
+PyOS_string_to_double;double;const char*|char**|PyObject*
+PyOS_strtol;long;const char*|char**|int
+PyOS_strtoul;unsigned long;const char*|char**|int
+PyOS_vsnprintf;int;char*|size_t|const char*|va_list
+PyObject_ASCII;PyObject*;PyObject*
+PyObject_AsFileDescriptor;int;PyObject*
+PyObject_Bytes;PyObject*;PyObject*
+PyObject_Call;PyObject*;PyObject*|PyObject*|PyObject*
+PyObject_CallFinalizerFromDealloc;int;PyObject*
+PyObject_CallFunction;PyObject*;PyObject*|const char*|...
+PyObject_CallFunctionObjArgs;PyObject*;PyObject*|...
+PyObject_CallMethod;PyObject*;PyObject*|const char*|const char*|...
+PyObject_CallMethodObjArgs;PyObject*;PyObject*|PyObject*|...
+PyObject_CallNoArgs;PyObject*;PyObject*
+PyObject_CallObject;PyObject*;PyObject*|PyObject*
+PyObject_Calloc;void*;size_t|size_t
+PyObject_CheckBuffer;int;PyObject*
+PyObject_ClearWeakRefs;void;PyObject*
+PyObject_CopyData;int;PyObject*|PyObject*
+PyObject_DelAttr;int;PyObject*|PyObject*
+PyObject_DelAttrString;int;PyObject*|const char*
+PyObject_DelItem;int;PyObject*|PyObject*
+PyObject_DelItemString;int;PyObject*|const char*
+PyObject_Dir;PyObject*;PyObject*
+PyObject_Format;PyObject*;PyObject*|PyObject*
+PyObject_Free;void;void*
+PyObject_GC_Del;void;void*
+PyObject_GC_IsFinalized;int;PyObject*
+PyObject_GC_IsTracked;int;PyObject*
+PyObject_GC_Track;void;void*
+PyObject_GC_UnTrack;void;void*
+PyObject_GenericGetAttr;PyObject*;PyObject*|PyObject*
+PyObject_GenericGetDict;PyObject*;PyObject*|void*
+PyObject_GenericSetAttr;int;PyObject*|PyObject*|PyObject*
+PyObject_GenericSetDict;int;PyObject*|PyObject*|void*
+PyObject_GetAIter;PyObject*;PyObject*
+PyObject_GetAttr;PyObject*;PyObject*|PyObject*
+PyObject_GetAttrString;PyObject*;PyObject*|const char*
+PyObject_GetBuffer;int;PyObject*|Py_buffer*|int
+PyObject_GetItem;PyObject*;PyObject*|PyObject*
+PyObject_GetIter;PyObject*;PyObject*
+PyObject_GetOptionalAttr;int;PyObject*|PyObject*|PyObject**
+PyObject_GetOptionalAttrString;int;PyObject*|const char*|PyObject**
+PyObject_GetTypeData;void*;PyObject*|PyTypeObject*
+PyObject_GetTypeData_DuringGC;void*;PyObject*|PyTypeObject*
+PyObject_HasAttr;int;PyObject*|PyObject*
+PyObject_HasAttrString;int;PyObject*|const char*
+PyObject_HasAttrStringWithError;int;PyObject*|const char*
+PyObject_HasAttrWithError;int;PyObject*|PyObject*
+PyObject_Hash;Py_hash_t;PyObject*
+PyObject_HashNotImplemented;Py_hash_t;PyObject*
+PyObject_Init;PyObject*;PyObject*|PyTypeObject*
+PyObject_InitVar;PyVarObject*;PyVarObject*|PyTypeObject*|Py_ssize_t
+PyObject_IsInstance;int;PyObject*|PyObject*
+PyObject_IsSubclass;int;PyObject*|PyObject*
+PyObject_IsTrue;int;PyObject*
+PyObject_Length;Py_ssize_t;PyObject*
+PyObject_Malloc;void*;size_t
+PyObject_Not;int;PyObject*
+PyObject_Realloc;void*;void*|size_t
+PyObject_Repr;PyObject*;PyObject*
+PyObject_RichCompare;PyObject*;PyObject*|PyObject*|int
+PyObject_RichCompareBool;int;PyObject*|PyObject*|int
+PyObject_SelfIter;PyObject*;PyObject*
+PyObject_SetAttr;int;PyObject*|PyObject*|PyObject*
+PyObject_SetAttrString;int;PyObject*|const char*|PyObject*
+PyObject_SetItem;int;PyObject*|PyObject*|PyObject*
+PyObject_Size;Py_ssize_t;PyObject*
+PyObject_Str;PyObject*;PyObject*
+PyObject_Type;PyObject*;PyObject*
+PyObject_Vectorcall;PyObject*;PyObject*|PyObject*const*|size_t|PyObject*
+PyObject_VectorcallMethod;PyObject*;PyObject*|PyObject*const*|size_t|PyObject*
+PySeqIter_New;PyObject*;PyObject*
+PySequence_Check;int;PyObject*
+PySequence_Concat;PyObject*;PyObject*|PyObject*
+PySequence_Contains;int;PyObject*|PyObject*
+PySequence_Count;Py_ssize_t;PyObject*|PyObject*
+PySequence_DelItem;int;PyObject*|Py_ssize_t
+PySequence_DelSlice;int;PyObject*|Py_ssize_t|Py_ssize_t
+PySequence_Fast;PyObject*;PyObject*|const char*
+PySequence_GetItem;PyObject*;PyObject*|Py_ssize_t
+PySequence_GetSlice;PyObject*;PyObject*|Py_ssize_t|Py_ssize_t
+PySequence_In;int;PyObject*|PyObject*
+PySequence_InPlaceConcat;PyObject*;PyObject*|PyObject*
+PySequence_InPlaceRepeat;PyObject*;PyObject*|Py_ssize_t
+PySequence_Index;Py_ssize_t;PyObject*|PyObject*
+PySequence_Length;Py_ssize_t;PyObject*
+PySequence_List;PyObject*;PyObject*
+PySequence_Repeat;PyObject*;PyObject*|Py_ssize_t
+PySequence_SetItem;int;PyObject*|Py_ssize_t|PyObject*
+PySequence_SetSlice;int;PyObject*|Py_ssize_t|Py_ssize_t|PyObject*
+PySequence_Size;Py_ssize_t;PyObject*
+PySequence_Tuple;PyObject*;PyObject*
+PySet_Add;int;PyObject*|PyObject*
+PySet_Clear;int;PyObject*
+PySet_Contains;int;PyObject*|PyObject*
+PySet_Discard;int;PyObject*|PyObject*
+PySet_New;PyObject*;PyObject*
+PySet_Pop;PyObject*;PyObject*
+PySet_Size;Py_ssize_t;PyObject*
+PySlice_AdjustIndices;Py_ssize_t;Py_ssize_t|Py_ssize_t*|Py_ssize_t*|Py_ssize_t
+PySlice_GetIndices;int;PyObject*|Py_ssize_t|Py_ssize_t*|Py_ssize_t*|Py_ssize_t*
+PySlice_GetIndicesEx;int;PyObject*|Py_ssize_t|Py_ssize_t*|Py_ssize_t*|Py_ssize_t*|Py_ssize_t*
+PySlice_New;PyObject*;PyObject*|PyObject*|PyObject*
+PySlice_Unpack;int;PyObject*|Py_ssize_t*|Py_ssize_t*|Py_ssize_t*
+PyState_AddModule;int;PyObject*|PyModuleDef*
+PyState_FindModule;PyObject*;PyModuleDef*
+PyState_RemoveModule;int;PyModuleDef*
+PyStructSequence_GetItem;PyObject*;PyObject*|Py_ssize_t
+PyStructSequence_New;PyObject*;PyTypeObject*
+PyStructSequence_NewType;PyTypeObject*;PyStructSequence_Desc*
+PyStructSequence_SetItem;void;PyObject*|Py_ssize_t|PyObject*
+PySys_Audit;int;const char*|const char*|...
+PySys_AuditTuple;int;const char*|PyObject*
+PySys_FormatStderr;void;const char*|...
+PySys_FormatStdout;void;const char*|...
+PySys_GetAttr;PyObject*;PyObject*
+PySys_GetAttrString;PyObject*;const char*
+PySys_GetObject;PyObject*;const char*
+PySys_GetOptionalAttr;int;PyObject*|PyObject**
+PySys_GetOptionalAttrString;int;const char*|PyObject**
+PySys_GetXOptions;PyObject*;void
+PySys_SetArgv;void;int|wchar_t**
+PySys_SetArgvEx;void;int|wchar_t**|int
+PySys_SetObject;int;const char*|PyObject*
+PySys_WriteStderr;void;const char*|...
+PySys_WriteStdout;void;const char*|...
+PyThreadState_Clear;void;PyThreadState*
+PyThreadState_Delete;void;PyThreadState*
+PyThreadState_Ensure;PyThreadStateToken*;PyInterpreterGuard*
+PyThreadState_EnsureFromView;PyThreadStateToken*;PyInterpreterView*
+PyThreadState_Get;PyThreadState*;void
+PyThreadState_GetDict;PyObject*;void
+PyThreadState_GetFrame;PyFrameObject*;PyThreadState*
+PyThreadState_GetID;uint64_t;PyThreadState*
+PyThreadState_GetInterpreter;PyInterpreterState*;PyThreadState*
+PyThreadState_New;PyThreadState*;PyInterpreterState*
+PyThreadState_Release;void;PyThreadStateToken*
+PyThreadState_SetAsyncExc;int;unsigned long|PyObject*
+PyThreadState_Swap;PyThreadState*;PyThreadState*
+PyThread_GetInfo;PyObject*;void
+PyThread_ReInitTLS;void;void
+PyThread_acquire_lock;int;PyThread_type_lock|int
+PyThread_acquire_lock_timed;PyLockStatus;PyThread_type_lock|long long|int
+PyThread_allocate_lock;PyThread_type_lock;void
+PyThread_create_key;int;void
+PyThread_delete_key;void;int
+PyThread_delete_key_value;void;int
+PyThread_exit_thread;void;void
+PyThread_free_lock;void;PyThread_type_lock
+PyThread_get_key_value;void*;int
+PyThread_get_stacksize;size_t;void
+PyThread_get_thread_ident;unsigned long;void
+PyThread_get_thread_native_id;unsigned long;void
+PyThread_init_thread;void;void
+PyThread_release_lock;void;PyThread_type_lock
+PyThread_set_key_value;int;int|void*
+PyThread_set_stacksize;int;size_t
+PyThread_start_new_thread;unsigned long;void (*)(void*)|void*
+PyThread_tss_alloc;Py_tss_t*;void
+PyThread_tss_create;int;Py_tss_t*
+PyThread_tss_delete;void;Py_tss_t*
+PyThread_tss_free;void;Py_tss_t*
+PyThread_tss_get;void*;Py_tss_t*
+PyThread_tss_is_created;int;Py_tss_t*
+PyThread_tss_set;int;Py_tss_t*|void*
+PyTraceBack_Here;int;PyFrameObject*
+PyTraceBack_Print;int;PyObject*|PyObject*
+PyTuple_GetItem;PyObject*;PyObject*|Py_ssize_t
+PyTuple_GetSlice;PyObject*;PyObject*|Py_ssize_t|Py_ssize_t
+PyTuple_New;PyObject*;Py_ssize_t
+PyTuple_Pack;PyObject*;Py_ssize_t|...
+PyTuple_SetItem;int;PyObject*|Py_ssize_t|PyObject*
+PyTuple_Size;Py_ssize_t;PyObject*
+PyType_ClearCache;unsigned int;void
+PyType_Freeze;int;PyTypeObject*
+PyType_FromMetaclass;PyObject*;PyTypeObject*|PyObject*|PyType_Spec*|PyObject*
+PyType_FromModuleAndSpec;PyObject*;PyObject*|PyType_Spec*|PyObject*
+PyType_FromSlots;PyObject*;struct PySlot*
+PyType_FromSpec;PyObject*;PyType_Spec*
+PyType_FromSpecWithBases;PyObject*;PyType_Spec*|PyObject*
+PyType_GenericAlloc;PyObject*;PyTypeObject*|Py_ssize_t
+PyType_GenericNew;PyObject*;PyTypeObject*|PyObject*|PyObject*
+PyType_GetBaseByToken;int;PyTypeObject*|void*|PyTypeObject**
+PyType_GetBaseByToken_DuringGC;int;PyTypeObject*|void*|PyTypeObject**
+PyType_GetFlags;unsigned long;PyTypeObject*
+PyType_GetFullyQualifiedName;PyObject*;PyTypeObject*
+PyType_GetModule;PyObject*;PyTypeObject*
+PyType_GetModuleByDef;PyObject*;PyTypeObject*|PyModuleDef*
+PyType_GetModuleByToken;PyObject*;PyTypeObject*|const void*
+PyType_GetModuleByToken_DuringGC;PyObject*;PyTypeObject*|const void*
+PyType_GetModuleName;PyObject*;PyTypeObject*
+PyType_GetModuleState;void*;PyTypeObject*
+PyType_GetModuleState_DuringGC;void*;PyTypeObject*
+PyType_GetModule_DuringGC;PyObject*;PyTypeObject*
+PyType_GetName;PyObject*;PyTypeObject*
+PyType_GetQualName;PyObject*;PyTypeObject*
+PyType_GetSlot;void*;PyTypeObject*|int
+PyType_GetTypeDataSize;Py_ssize_t;PyTypeObject*
+PyType_IsSubtype;int;PyTypeObject*|PyTypeObject*
+PyType_Modified;void;PyTypeObject*
+PyType_Ready;int;PyTypeObject*
+PyUnicodeDecodeError_Create;PyObject*;const char*|const char*|Py_ssize_t|Py_ssize_t|Py_ssize_t|const char*
+PyUnicodeDecodeError_GetEncoding;PyObject*;PyObject*
+PyUnicodeDecodeError_GetEnd;int;PyObject*|Py_ssize_t*
+PyUnicodeDecodeError_GetObject;PyObject*;PyObject*
+PyUnicodeDecodeError_GetReason;PyObject*;PyObject*
+PyUnicodeDecodeError_GetStart;int;PyObject*|Py_ssize_t*
+PyUnicodeDecodeError_SetEnd;int;PyObject*|Py_ssize_t
+PyUnicodeDecodeError_SetReason;int;PyObject*|const char*
+PyUnicodeDecodeError_SetStart;int;PyObject*|Py_ssize_t
+PyUnicodeEncodeError_GetEncoding;PyObject*;PyObject*
+PyUnicodeEncodeError_GetEnd;int;PyObject*|Py_ssize_t*
+PyUnicodeEncodeError_GetObject;PyObject*;PyObject*
+PyUnicodeEncodeError_GetReason;PyObject*;PyObject*
+PyUnicodeEncodeError_GetStart;int;PyObject*|Py_ssize_t*
+PyUnicodeEncodeError_SetEnd;int;PyObject*|Py_ssize_t
+PyUnicodeEncodeError_SetReason;int;PyObject*|const char*
+PyUnicodeEncodeError_SetStart;int;PyObject*|Py_ssize_t
+PyUnicodeTranslateError_GetEnd;int;PyObject*|Py_ssize_t*
+PyUnicodeTranslateError_GetObject;PyObject*;PyObject*
+PyUnicodeTranslateError_GetReason;PyObject*;PyObject*
+PyUnicodeTranslateError_GetStart;int;PyObject*|Py_ssize_t*
+PyUnicodeTranslateError_SetEnd;int;PyObject*|Py_ssize_t
+PyUnicodeTranslateError_SetReason;int;PyObject*|const char*
+PyUnicodeTranslateError_SetStart;int;PyObject*|Py_ssize_t
+PyUnicode_Append;void;PyObject**|PyObject*
+PyUnicode_AppendAndDel;void;PyObject**|PyObject*
+PyUnicode_AsASCIIString;PyObject*;PyObject*
+PyUnicode_AsCharmapString;PyObject*;PyObject*|PyObject*
+PyUnicode_AsEncodedString;PyObject*;PyObject*|const char*|const char*
+PyUnicode_AsLatin1String;PyObject*;PyObject*
+PyUnicode_AsRawUnicodeEscapeString;PyObject*;PyObject*
+PyUnicode_AsUCS4;Py_UCS4*;PyObject*|Py_UCS4*|Py_ssize_t|int
+PyUnicode_AsUCS4Copy;Py_UCS4*;PyObject*
+PyUnicode_AsUTF16String;PyObject*;PyObject*
+PyUnicode_AsUTF32String;PyObject*;PyObject*
+PyUnicode_AsUTF8AndSize;const char*;PyObject*|Py_ssize_t*
+PyUnicode_AsUTF8String;PyObject*;PyObject*
+PyUnicode_AsUnicodeEscapeString;PyObject*;PyObject*
+PyUnicode_AsWideChar;Py_ssize_t;PyObject*|wchar_t*|Py_ssize_t
+PyUnicode_AsWideCharString;wchar_t*;PyObject*|Py_ssize_t*
+PyUnicode_BuildEncodingMap;PyObject*;PyObject*
+PyUnicode_Compare;int;PyObject*|PyObject*
+PyUnicode_CompareWithASCIIString;int;PyObject*|const char*
+PyUnicode_Concat;PyObject*;PyObject*|PyObject*
+PyUnicode_Contains;int;PyObject*|PyObject*
+PyUnicode_Count;Py_ssize_t;PyObject*|PyObject*|Py_ssize_t|Py_ssize_t
+PyUnicode_Decode;PyObject*;const char*|Py_ssize_t|const char*|const char*
+PyUnicode_DecodeASCII;PyObject*;const char*|Py_ssize_t|const char*
+PyUnicode_DecodeCharmap;PyObject*;const char*|Py_ssize_t|PyObject*|const char*
+PyUnicode_DecodeFSDefault;PyObject*;const char*
+PyUnicode_DecodeFSDefaultAndSize;PyObject*;const char*|Py_ssize_t
+PyUnicode_DecodeLatin1;PyObject*;const char*|Py_ssize_t|const char*
+PyUnicode_DecodeLocale;PyObject*;const char*|const char*
+PyUnicode_DecodeLocaleAndSize;PyObject*;const char*|Py_ssize_t|const char*
+PyUnicode_DecodeRawUnicodeEscape;PyObject*;const char*|Py_ssize_t|const char*
+PyUnicode_DecodeUTF16;PyObject*;const char*|Py_ssize_t|const char*|int*
+PyUnicode_DecodeUTF16Stateful;PyObject*;const char*|Py_ssize_t|const char*|int*|Py_ssize_t*
+PyUnicode_DecodeUTF32;PyObject*;const char*|Py_ssize_t|const char*|int*
+PyUnicode_DecodeUTF32Stateful;PyObject*;const char*|Py_ssize_t|const char*|int*|Py_ssize_t*
+PyUnicode_DecodeUTF7;PyObject*;const char*|Py_ssize_t|const char*
+PyUnicode_DecodeUTF7Stateful;PyObject*;const char*|Py_ssize_t|const char*|Py_ssize_t*
+PyUnicode_DecodeUTF8;PyObject*;const char*|Py_ssize_t|const char*
+PyUnicode_DecodeUTF8Stateful;PyObject*;const char*|Py_ssize_t|const char*|Py_ssize_t*
+PyUnicode_DecodeUnicodeEscape;PyObject*;const char*|Py_ssize_t|const char*
+PyUnicode_EncodeFSDefault;PyObject*;PyObject*
+PyUnicode_EncodeLocale;PyObject*;PyObject*|const char*
+PyUnicode_Equal;int;PyObject*|PyObject*
+PyUnicode_EqualToUTF8;int;PyObject*|const char*
+PyUnicode_EqualToUTF8AndSize;int;PyObject*|const char*|Py_ssize_t
+PyUnicode_FSConverter;int;PyObject*|void*
+PyUnicode_FSDecoder;int;PyObject*|void*
+PyUnicode_Find;Py_ssize_t;PyObject*|PyObject*|Py_ssize_t|Py_ssize_t|int
+PyUnicode_FindChar;Py_ssize_t;PyObject*|Py_UCS4|Py_ssize_t|Py_ssize_t|int
+PyUnicode_Format;PyObject*;PyObject*|PyObject*
+PyUnicode_FromEncodedObject;PyObject*;PyObject*|const char*|const char*
+PyUnicode_FromFormat;PyObject*;const char*|...
+PyUnicode_FromFormatV;PyObject*;const char*|va_list
+PyUnicode_FromObject;PyObject*;PyObject*
+PyUnicode_FromOrdinal;PyObject*;int
+PyUnicode_FromString;PyObject*;const char*
+PyUnicode_FromStringAndSize;PyObject*;const char*|Py_ssize_t
+PyUnicode_FromWideChar;PyObject*;const wchar_t*|Py_ssize_t
+PyUnicode_GetDefaultEncoding;const char*;void
+PyUnicode_GetLength;Py_ssize_t;PyObject*
+PyUnicode_InternFromString;PyObject*;const char*
+PyUnicode_InternInPlace;void;PyObject**
+PyUnicode_IsIdentifier;int;PyObject*
+PyUnicode_Join;PyObject*;PyObject*|PyObject*
+PyUnicode_Partition;PyObject*;PyObject*|PyObject*
+PyUnicode_RPartition;PyObject*;PyObject*|PyObject*
+PyUnicode_RSplit;PyObject*;PyObject*|PyObject*|Py_ssize_t
+PyUnicode_ReadChar;Py_UCS4;PyObject*|Py_ssize_t
+PyUnicode_Replace;PyObject*;PyObject*|PyObject*|PyObject*|Py_ssize_t
+PyUnicode_Resize;int;PyObject**|Py_ssize_t
+PyUnicode_RichCompare;PyObject*;PyObject*|PyObject*|int
+PyUnicode_Split;PyObject*;PyObject*|PyObject*|Py_ssize_t
+PyUnicode_Splitlines;PyObject*;PyObject*|int
+PyUnicode_Substring;PyObject*;PyObject*|Py_ssize_t|Py_ssize_t
+PyUnicode_Tailmatch;Py_ssize_t;PyObject*|PyObject*|Py_ssize_t|Py_ssize_t|int
+PyUnicode_Translate;PyObject*;PyObject*|PyObject*|const char*
+PyUnicode_WriteChar;int;PyObject*|Py_ssize_t|Py_UCS4
+PyVectorcall_Call;PyObject*;PyObject*|PyObject*|PyObject*
+PyVectorcall_NARGS;Py_ssize_t;size_t
+PyWeakref_GetRef;int;PyObject*|PyObject**
+PyWeakref_NewProxy;PyObject*;PyObject*|PyObject*
+PyWeakref_NewRef;PyObject*;PyObject*|PyObject*
+PyWrapper_New;PyObject*;PyObject*|PyObject*
+Py_AddPendingCall;int;int (*)(void*)|void*
+Py_AtExit;int;void (*)(void)
+Py_BuildValue;PyObject*;const char*|...
+Py_BytesMain;int;int|char**
+Py_CompileString;PyObject*;const char*|const char*|int
+Py_DecRef;void;PyObject*
+Py_DecodeLocale;wchar_t*;const char*|size_t*
+Py_EncodeLocale;char*;const wchar_t*|size_t*
+Py_EndInterpreter;void;PyThreadState*
+Py_EnterRecursiveCall;int;const char*
+Py_Exit;void;int
+Py_FatalError;void;const char*
+Py_Finalize;void;void
+Py_FinalizeEx;int;void
+Py_GenericAlias;PyObject*;PyObject*|PyObject*
+Py_GetBuildInfo;const char*;void
+Py_GetCompiler;const char*;void
+Py_GetConstant;PyObject*;unsigned int
+Py_GetConstantBorrowed;PyObject*;unsigned int
+Py_GetCopyright;const char*;void
+Py_GetPlatform;const char*;void
+Py_GetRecursionLimit;int;void
+Py_GetVersion;const char*;void
+Py_IS_TYPE;int;PyObject*|PyTypeObject*
+Py_IncRef;void;PyObject*
+Py_Initialize;void;void
+Py_InitializeEx;void;int
+Py_Is;int;PyObject*|PyObject*
+Py_IsFalse;int;PyObject*
+Py_IsFinalizing;int;void
+Py_IsInitialized;int;void
+Py_IsNone;int;PyObject*
+Py_IsTrue;int;PyObject*
+Py_LeaveRecursiveCall;void;void
+Py_Main;int;int|wchar_t**
+Py_MakePendingCalls;int;void
+Py_NewInterpreter;PyThreadState*;void
+Py_NewRef;PyObject*;PyObject*
+Py_PACK_FULL_VERSION;uint32_t;int|int|int|int|int
+Py_PACK_VERSION;uint32_t;int|int
+Py_REFCNT;Py_ssize_t;PyObject*
+Py_ReprEnter;int;PyObject*
+Py_ReprLeave;void;PyObject*
+Py_SET_SIZE;void;PyVarObject*|Py_ssize_t
+Py_SIZE;Py_ssize_t;PyObject*
+Py_SetProgramName;void;const wchar_t*
+Py_SetPythonHome;void;const wchar_t*
+Py_SetRecursionLimit;void;int
+Py_TYPE;PyTypeObject*;PyObject*
+Py_VaBuildValue;PyObject*;const char*|va_list
+Py_XNewRef;PyObject*;PyObject*
+_PyErr_BadInternalCall;void;const char*|int
+_PyObject_GC_New;PyObject*;PyTypeObject*
+_PyObject_GC_NewVar;PyVarObject*;PyTypeObject*|Py_ssize_t
+_PyObject_GC_Resize;PyVarObject*;PyVarObject*|Py_ssize_t
+_PyObject_New;PyObject*;PyTypeObject*
+_PyObject_NewVar;PyVarObject*;PyTypeObject*|Py_ssize_t
+_Py_Dealloc;void;PyObject*
+_Py_DecRef;void;PyObject*
+_Py_IncRef;void;PyObject*
+_Py_SetRefcnt;void;PyObject*|Py_ssize_t
diff --git a/graalpython/com.oracle.graal.python.cext/include/Python.h b/graalpython/com.oracle.graal.python.cext/include/Python.h
index fc8b857125..7722a01768 100644
--- a/graalpython/com.oracle.graal.python.cext/include/Python.h
+++ b/graalpython/com.oracle.graal.python.cext/include/Python.h
@@ -72,6 +72,15 @@ struct timeval;
// Include Python header files
#include "pyport.h"
+
+#if (defined(Py_BUILD_CORE) || defined(Py_TARGET_ABI3T)) && defined(_MSC_VER)
+// Ignore MSC warning C4201: "nonstandard extension used: nameless
+// struct/union". (Only generated for C standard versions less than C11, which
+// we don't *officially* support.)
+__pragma(warning(push))
+__pragma(warning(disable: 4201))
+#endif
+
#include "pymacro.h"
#include "pymath.h"
#include "pymem.h"
@@ -82,6 +91,9 @@ struct timeval;
#include "lock.h"
#include "object.h"
#include "objimpl.h"
+#if defined(Py_BUILD_CORE) || defined(Py_TARGET_ABI3T)
+# include "slots.h"
+#endif
#include "typeslots.h"
#include "pyhash.h"
#include "cpython/pydebug.h"
@@ -143,6 +155,10 @@ struct timeval;
#include "cpython/pyfpe.h"
#include "cpython/tracemalloc.h"
+#if (defined(Py_BUILD_CORE) || defined(Py_TARGET_ABI3T)) && defined(_MSC_VER)
+__pragma(warning(pop)) // warning(disable: 4201)
+#endif
+
// helper macro for quick printf debugging
#define __PD {printf("%i \t%s\n", __LINE__, __func__); }
diff --git a/graalpython/com.oracle.graal.python.cext/include/exports.h b/graalpython/com.oracle.graal.python.cext/include/exports.h
index dba2258190..be606ce139 100644
--- a/graalpython/com.oracle.graal.python.cext/include/exports.h
+++ b/graalpython/com.oracle.graal.python.cext/include/exports.h
@@ -14,6 +14,8 @@
inside the Python core, they are private to the core.
If in an extension module, it may be declared with
external linkage depending on the platform.
+ PyMODEXPORT_FUNC: Like PyMODINIT_FUNC, but for a slots array. This is only
+ available when experimental ABI3T support is enabled.
As a number of platforms support/require "__declspec(dllimport/dllexport)",
we support a HAVE_DECLSPEC_DLL macro to save duplication.
@@ -108,6 +110,13 @@
# define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject*
# endif /* __cplusplus */
#endif
+#if defined(Py_TARGET_ABI3T) && !defined(PyMODEXPORT_FUNC)
+# if defined(__cplusplus)
+# define PyMODEXPORT_FUNC extern "C" Py_EXPORTED_SYMBOL PySlot*
+# else
+# define PyMODEXPORT_FUNC Py_EXPORTED_SYMBOL PySlot*
+# endif
+#endif /* Py_TARGET_ABI3T && !PyMODEXPORT_FUNC */
#endif /* Py_EXPORTS_H */
diff --git a/graalpython/com.oracle.graal.python.cext/include/modsupport.h b/graalpython/com.oracle.graal.python.cext/include/modsupport.h
index 3c479ce12a..acdde387ac 100644
--- a/graalpython/com.oracle.graal.python.cext/include/modsupport.h
+++ b/graalpython/com.oracle.graal.python.cext/include/modsupport.h
@@ -139,6 +139,38 @@ PyAPI_FUNC(PyObject *) PyModule_FromDefAndSpec2(PyModuleDef *def,
#endif /* New in 3.5 */
+#if defined(Py_BUILD_CORE) || defined(Py_TARGET_ABI3T)
+typedef struct PyABIInfo {
+ uint8_t abiinfo_major_version;
+ uint8_t abiinfo_minor_version;
+ uint16_t flags;
+ uint32_t build_version;
+ uint32_t abi_version;
+} PyABIInfo;
+
+#define PyABIInfo_STABLE 0x0001
+#define PyABIInfo_GIL 0x0002
+#define PyABIInfo_FREETHREADED 0x0004
+#define PyABIInfo_INTERNAL 0x0008
+#define PyABIInfo_FREETHREADING_AGNOSTIC (PyABIInfo_GIL | PyABIInfo_FREETHREADED)
+
+PyAPI_FUNC(int) PyABIInfo_Check(PyABIInfo *info, const char *module_name);
+
+#if defined(Py_TARGET_ABI3T)
+# define PyABIInfo_DEFAULT_ABI_VERSION Py_TARGET_ABI3T
+# define PyABIInfo_DEFAULT_FLAGS (PyABIInfo_STABLE | PyABIInfo_FREETHREADING_AGNOSTIC)
+#elif defined(Py_LIMITED_API)
+# define PyABIInfo_DEFAULT_ABI_VERSION Py_LIMITED_API
+# define PyABIInfo_DEFAULT_FLAGS (PyABIInfo_STABLE | PyABIInfo_GIL)
+#else
+# define PyABIInfo_DEFAULT_ABI_VERSION PY_VERSION_HEX
+# define PyABIInfo_DEFAULT_FLAGS PyABIInfo_GIL
+#endif
+
+#define PyABIInfo_VAR(NAME) \
+ static PyABIInfo NAME = {1, 0, PyABIInfo_DEFAULT_FLAGS, PY_VERSION_HEX, PyABIInfo_DEFAULT_ABI_VERSION};
+#endif /* Py_BUILD_CORE || Py_TARGET_ABI3T */
+
#ifndef Py_LIMITED_API
# define Py_CPYTHON_MODSUPPORT_H
# include "cpython/modsupport.h"
diff --git a/graalpython/com.oracle.graal.python.cext/include/moduleobject.h b/graalpython/com.oracle.graal.python.cext/include/moduleobject.h
index d800ba8c38..e63556ca38 100644
--- a/graalpython/com.oracle.graal.python.cext/include/moduleobject.h
+++ b/graalpython/com.oracle.graal.python.cext/include/moduleobject.h
@@ -76,14 +76,32 @@ struct PyModuleDef_Slot {
void *value;
};
-#define Py_mod_create 1
-#define Py_mod_exec 2
+#if defined(Py_TARGET_ABI3T)
+# define Py_mod_create 84
+# define Py_mod_exec 85
+# define Py_mod_multiple_interpreters 86
+# define Py_mod_gil 87
+# define Py_slot_subslots 92
+# define Py_mod_slots 94
+# define Py_mod_name 100
+# define Py_mod_doc 101
+# define Py_mod_state_size 102
+# define Py_mod_methods 103
+# define Py_mod_state_traverse 104
+# define Py_mod_state_clear 105
+# define Py_mod_state_free 106
+# define Py_mod_abi 109
+# define Py_mod_token 110
+#else
+# define Py_mod_create 1
+# define Py_mod_exec 2
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030c0000
# define Py_mod_multiple_interpreters 3
#endif
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000
# define Py_mod_gil 4
#endif
+#endif
#ifndef Py_LIMITED_API
@@ -109,6 +127,15 @@ struct PyModuleDef_Slot {
PyAPI_FUNC(int) PyUnstable_Module_SetGIL(PyObject *module, void *gil);
#endif
+#if defined(Py_BUILD_CORE) || defined(Py_TARGET_ABI3T)
+PyAPI_FUNC(PyObject *) PyModule_FromSlotsAndSpec(const PySlot *slots, PyObject *spec);
+PyAPI_FUNC(int) PyModule_Exec(PyObject *module);
+PyAPI_FUNC(int) PyModule_GetStateSize(PyObject *module, Py_ssize_t *result);
+PyAPI_FUNC(int) PyModule_GetToken(PyObject *module, void **result);
+PyAPI_FUNC(void *) PyModule_GetState_DuringGC(PyObject *module);
+PyAPI_FUNC(int) PyModule_GetToken_DuringGC(PyObject *module, void **result);
+#endif /* Py_BUILD_CORE || Py_TARGET_ABI3T */
+
struct PyModuleDef {
PyModuleDef_Base m_base;
const char* m_name;
diff --git a/graalpython/com.oracle.graal.python.cext/include/pymacro.h b/graalpython/com.oracle.graal.python.cext/include/pymacro.h
index 16b9d4e0bb..7dcd927547 100644
--- a/graalpython/com.oracle.graal.python.cext/include/pymacro.h
+++ b/graalpython/com.oracle.graal.python.cext/include/pymacro.h
@@ -28,6 +28,29 @@
# define static_assert _Static_assert
#endif
+#if defined(Py_BUILD_CORE) || defined(Py_TARGET_ABI3T)
+// GraalPy change: added _Py_ANONYMOUS from 3.15
+// _Py_ANONYMOUS: modifier for declaring an anonymous union.
+// Usage: _Py_ANONYMOUS union { ... };
+// Standards/compiler support:
+// - C++ allows anonymous unions, but not structs
+// - C11 and above allows anonymous unions and structs
+// - MSVC has warning(disable: 4201) "nonstandard extension used : nameless
+// struct/union". This is specific enough that we disable it for all of
+// Python.h.
+// - GCC & clang needs __extension__ before C11
+// To allow unsupported platforms which need other spellings, we use a
+// predefined value of _Py_ANONYMOUS if it exists.
+#ifndef _Py_ANONYMOUS
+# if (defined(__GNUC__) || defined(__clang__)) \
+ && !(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L)
+# define _Py_ANONYMOUS __extension__
+# else
+# define _Py_ANONYMOUS
+# endif
+#endif
+#endif /* Py_BUILD_CORE || Py_TARGET_ABI3T */
+
/* Minimum value between x and y */
#define Py_MIN(x, y) (((x) > (y)) ? (y) : (x))
diff --git a/graalpython/com.oracle.graal.python.cext/include/pytypedefs.h b/graalpython/com.oracle.graal.python.cext/include/pytypedefs.h
index 6141fb0305..c96dd6498d 100644
--- a/graalpython/com.oracle.graal.python.cext/include/pytypedefs.h
+++ b/graalpython/com.oracle.graal.python.cext/include/pytypedefs.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2023, Oracle and/or its affiliates.
+/* Copyright (c) 2023, 2026, Oracle and/or its affiliates.
* Copyright (C) 1996-2023 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -19,6 +19,9 @@ typedef struct PyModuleDef_Slot PyModuleDef_Slot;
typedef struct PyMethodDef PyMethodDef;
typedef struct PyGetSetDef PyGetSetDef;
typedef struct PyMemberDef PyMemberDef;
+#if defined(Py_BUILD_CORE) || defined(Py_TARGET_ABI3T)
+typedef struct PySlot PySlot;
+#endif
typedef struct _object PyObject;
typedef struct _longobject PyLongObject;
diff --git a/graalpython/com.oracle.graal.python.cext/include/slots.h b/graalpython/com.oracle.graal.python.cext/include/slots.h
new file mode 100644
index 0000000000..408966e8eb
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.cext/include/slots.h
@@ -0,0 +1,65 @@
+/* Copyright (c) 2026, Oracle and/or its affiliates.
+ * Copyright (C) 1996-2026 Python Software Foundation
+ *
+ * Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
+ */
+#ifndef _Py_HAVE_SLOTS_H
+#define _Py_HAVE_SLOTS_H
+
+#if !defined(Py_BUILD_CORE) && !defined(Py_TARGET_ABI3T)
+# error "slots.h requires Py_TARGET_ABI3T"
+#endif
+
+typedef void (*_Py_funcptr_t)(void);
+
+struct PySlot {
+ uint16_t sl_id;
+ uint16_t sl_flags;
+ _Py_ANONYMOUS union {
+ uint32_t sl_reserved; // must be 0
+ };
+ _Py_ANONYMOUS union {
+ void *sl_ptr;
+ _Py_funcptr_t sl_func;
+ Py_ssize_t sl_size;
+ int64_t sl_int64;
+ uint64_t sl_uint64;
+ };
+};
+
+#define PySlot_OPTIONAL 0x0001
+#define PySlot_STATIC 0x0002
+#define PySlot_INTPTR 0x0004
+
+#define Py_slot_invalid 0xffff
+
+#define PySlot_DATA(NAME, VALUE) \
+ {.sl_id=(NAME), .sl_flags=PySlot_INTPTR, .sl_ptr=(void*)(VALUE)}
+
+#define PySlot_FUNC(NAME, VALUE) \
+ {.sl_id=(NAME), .sl_func=(_Py_funcptr_t)(VALUE)}
+
+#define PySlot_SIZE(NAME, VALUE) \
+ {.sl_id=(NAME), .sl_size=(Py_ssize_t)(VALUE)}
+
+#define PySlot_INT64(NAME, VALUE) \
+ {.sl_id=(NAME), .sl_int64=(int64_t)(VALUE)}
+
+#define PySlot_UINT64(NAME, VALUE) \
+ {.sl_id=(NAME), .sl_uint64=(uint64_t)(VALUE)}
+
+#define PySlot_STATIC_DATA(NAME, VALUE) \
+ {.sl_id=(NAME), .sl_flags=PySlot_STATIC, .sl_ptr=(VALUE)}
+
+#define PySlot_END {0}
+
+
+// Macros without designated initializers (for C++11 and below):
+
+#define PySlot_PTR(NAME, VALUE) \
+ {(NAME), PySlot_INTPTR, {0}, {(void*)(VALUE)}}
+
+#define PySlot_PTR_STATIC(NAME, VALUE) \
+ {(NAME), PySlot_INTPTR | PySlot_STATIC, {0}, {(void*)(VALUE)}}
+
+#endif // _Py_HAVE_SLOTS_H
diff --git a/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java
index 4d6a7a094b..8e459f98dc 100644
--- a/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java
+++ b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java
@@ -245,9 +245,12 @@ private static final class CApiBuiltinDesc {
public final boolean canRaise;
public final String call;
public final String factory;
+ /** ABIs explicitly named by the annotation. Parent availability is computed at validation. */
+ public final Set abis;
public int id;
- public CApiBuiltinDesc(Element origin, String name, VariableElement returnType, VariableElement[] arguments, boolean acquireGil, boolean canRaise, String call, String factory) {
+ public CApiBuiltinDesc(Element origin, String name, VariableElement returnType, VariableElement[] arguments, boolean acquireGil, boolean canRaise, String call, String factory,
+ Set abis) {
this.origin = origin;
this.name = name;
this.returnType = returnType;
@@ -256,6 +259,7 @@ public CApiBuiltinDesc(Element origin, String name, VariableElement returnType,
this.canRaise = canRaise;
this.call = call;
this.factory = factory;
+ this.abis = new HashSet<>(abis);
}
}
@@ -466,6 +470,10 @@ private T findValue(AnnotationMirror am, String key, Class type) {
}
}
+ private static Set abiNames(List abis) {
+ return abis.stream().map(CApiBuiltinsProcessor::name).collect(Collectors.toSet());
+ }
+
private void addCApiBuiltins(RoundEnvironment re, List javaBuiltins, List additionalBuiltins) {
for (var root : re.getRootElements()) {
for (var element : root.getEnclosedElements()) {
@@ -495,10 +503,11 @@ private void addCApiBuiltins(RoundEnvironment re, List javaBuil
continue;
}
String call = name(findValue(builtin, "call", VariableElement.class));
+ Set abis = abiNames(findValues(builtin, "abi", VariableElement.class));
// boolean inlined = findValue(builtin, "inlined", Boolean.class);
VariableElement[] args = findValues(builtin, "args", VariableElement.class).toArray(new VariableElement[0]);
if (element instanceof TypeElement te && te.getQualifiedName().toString().equals("com.oracle.graal.python.builtins.objects.cext.capi.CApiFunction.Dummy")) {
- additionalBuiltins.add(new CApiBuiltinDesc(element, builtinName, ret, args, acquireGil, canRaise, call, null));
+ additionalBuiltins.add(new CApiBuiltinDesc(element, builtinName, ret, args, acquireGil, canRaise, call, null, abis));
} else {
if (!isValidReturnType(ret)) {
processingEnv.getMessager().printError(
@@ -529,7 +538,7 @@ private void addCApiBuiltins(RoundEnvironment re, List javaBuil
} else {
verifyStaticMethod((ExecutableElement) element, builtin);
}
- javaBuiltins.add(new CApiBuiltinDesc(element, name, ret, args, acquireGil, canRaise, call, genName));
+ javaBuiltins.add(new CApiBuiltinDesc(element, name, ret, args, acquireGil, canRaise, call, genName, abis));
}
}
}
@@ -1232,6 +1241,17 @@ private static Path resolvePath(Path path) {
"PyUnicode_DecodeCodePageStateful", "PyUnicode_DecodeMBCSStateful",
};
+ private static final Map ABI_PARENTS = Map.of("ABI3T", "GRAALPY");
+
+ private static boolean isAvailableIn(CApiBuiltinDesc builtin, String profile) {
+ for (String current = profile; current != null; current = ABI_PARENTS.get(current)) {
+ if (builtin.abis.contains(current)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public String resolveArgDescriptor(String sig) {
switch (sig) {
case "struct _typeobject*":
@@ -1266,7 +1286,13 @@ public String resolveTypeAlias(String sig) {
* suggests the appropriate @CApiBuiltin specification.
*/
private void checkImports(List builtins) throws IOException {
- var path = resolvePath(Path.of("com.oracle.graal.python.cext", "CAPIFunctions.txt"));
+ checkImports(builtins, "GRAALPY");
+ checkImports(builtins, "ABI3T");
+ }
+
+ private void checkImports(List builtins, String profile) throws IOException {
+ String filename = profile.equals("GRAALPY") ? "CAPIFunctions.txt" : "CAPIFunctions-" + profile + ".txt";
+ var path = resolvePath(Path.of("com.oracle.graal.python.cext", filename));
if (path == null) {
return;
}
@@ -1274,7 +1300,7 @@ private void checkImports(List builtins) throws IOException {
TreeSet newBuiltins = new TreeSet<>();
TreeSet names = new TreeSet<>();
- builtins.forEach(n -> names.add(n.name));
+ builtins.stream().filter(n -> n.abis.contains(profile)).forEach(n -> names.add(n.name));
for (String line : lines) {
String[] s = line.split(";");
@@ -1285,23 +1311,25 @@ private void checkImports(List builtins) throws IOException {
String[] argSplit = s[2].isBlank() || "void".equals(s[2]) ? new String[0] : s[2].trim().split("\\|");
String[] args = Arrays.stream(argSplit).map(this::resolveArgDescriptor).toArray(String[]::new);
- Optional existing = findBuiltin(builtins, name);
+ Optional existing = builtins.stream().filter(n -> n.name.equals(name) && isAvailableIn(n, profile)).findFirst();
if (existing.isPresent()) {
compareFunction(name, existing.get().returnType, retSig, existing.get().arguments, argSplit);
} else {
String argString = Arrays.stream(args).map(t -> String.valueOf(t)).collect(Collectors.joining(", "));
- newBuiltins.add(" @CApiBuiltin(name = \"" + name + "\", ret = " + ret + ", args = {" + argString + "}, call = NotImplemented)");
+ String abi = profile.equals("GRAALPY") ? "" : ", abi = ABI." + profile;
+ newBuiltins.add(" @CApiBuiltin(name = \"" + name + "\", ret = " + ret + ", args = {" + argString + "}, call = NotImplemented" + abi + ")");
}
}
if (!newBuiltins.isEmpty()) {
- processingEnv.getMessager().printError("missing builtins (defined in CPython, but not in GraalPy):");
+ processingEnv.getMessager().printError("missing " + profile + " builtins (defined in CPython, but not in GraalPy):");
newBuiltins.stream().forEach(processingEnv.getMessager()::printError);
}
names.removeIf(n -> n.startsWith("GraalPy"));
names.removeAll(Arrays.asList(ADDITIONAL));
if (!names.isEmpty()) {
- processingEnv.getMessager().printError("extra builtins (defined in GraalPy, but not in CPython - some of these are necessary for internal modules like 'math'):");
+ processingEnv.getMessager().printError("extra explicitly " + profile +
+ " builtins (defined in GraalPy, but not in CPython - some of these are necessary for internal modules like 'math'):");
processingEnv.getMessager().printError(" " + names.stream().collect(Collectors.joining(", ")));
}
}
@@ -2233,6 +2261,7 @@ public boolean process(Set extends TypeElement> annotations, RoundEnvironment
Optional existing1 = findBuiltin(allBuiltins, entry.name);
if (existing1.isPresent()) {
compareFunction(entry.name, entry.returnType, existing1.get().returnType, entry.arguments, existing1.get().arguments);
+ existing1.get().abis.addAll(entry.abis);
} else {
allBuiltins.add(entry);
}
diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/cext/CExtContextTest.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/cext/CExtContextTest.java
index 5988f5c1bb..74c01a9b80 100644
--- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/cext/CExtContextTest.java
+++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/cext/CExtContextTest.java
@@ -46,9 +46,7 @@
import org.junit.Before;
import org.junit.Test;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtContext;
-import com.oracle.graal.python.runtime.nativeaccess.NativeLibrary;
-import com.oracle.graal.python.runtime.PythonContext;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
import com.oracle.graal.python.test.PythonTests;
import com.oracle.truffle.api.strings.TruffleString;
@@ -70,24 +68,13 @@ static TruffleString ts(String s) {
@Test
public void testGetBaseName() {
- assertEquals(ts(""), TestCExtContext.getBN(ts("")));
- assertEquals(ts("a"), TestCExtContext.getBN(ts("a")));
- assertEquals(ts("aa"), TestCExtContext.getBN(ts("aa")));
- assertEquals(ts("aa"), TestCExtContext.getBN(ts("a.aa")));
- assertEquals(ts("bb"), TestCExtContext.getBN(ts("a.aa.bb")));
- assertEquals(ts(""), TestCExtContext.getBN(ts("a.aa.bb.")));
- assertEquals(ts("b"), TestCExtContext.getBN(ts("a.b")));
- assertEquals(ts(""), TestCExtContext.getBN(ts("a.b.")));
+ assertEquals(ts(""), CApiContext.getBaseName(ts("")));
+ assertEquals(ts("a"), CApiContext.getBaseName(ts("a")));
+ assertEquals(ts("aa"), CApiContext.getBaseName(ts("aa")));
+ assertEquals(ts("aa"), CApiContext.getBaseName(ts("a.aa")));
+ assertEquals(ts("bb"), CApiContext.getBaseName(ts("a.aa.bb")));
+ assertEquals(ts(""), CApiContext.getBaseName(ts("a.aa.bb.")));
+ assertEquals(ts("b"), CApiContext.getBaseName(ts("a.b")));
+ assertEquals(ts(""), CApiContext.getBaseName(ts("a.b.")));
}
-
- private static class TestCExtContext extends CExtContext {
- public TestCExtContext(PythonContext context, NativeLibrary library) {
- super(context, library, null);
- }
-
- public static TruffleString getBN(TruffleString s) {
- return getBaseName(s);
- }
- }
-
}
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/__init__.py b/graalpython/com.oracle.graal.python.test/src/tests/__init__.py
index e984e54a60..fb4e1534dc 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/__init__.py
+++ b/graalpython/com.oracle.graal.python.test/src/tests/__init__.py
@@ -190,10 +190,20 @@ def install_venv(venv_path: Path, use_current_python=False) -> bool:
def compile_module_from_string(c_source: str, name: str):
+ install_dir = compile_module_from_string_no_import(c_source, name)
+ sys.path.insert(0, install_dir)
+ try:
+ cmodule = __import__(name)
+ finally:
+ sys.path.pop(0)
+ return cmodule
+
+
+def compile_module_from_string_no_import(c_source: str, name: str):
source_file = DIR / f'{name}.c'
with open(source_file, "wb", buffering=0) as f:
f.write(bytes(c_source, 'utf-8'))
- return compile_module_from_file(name)
+ return ccompile(None, name)
def compile_module_from_file(module_name: str, sibling_to=None):
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/__init__.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/__init__.py
index 7ce28b853e..a34d0ec67b 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/__init__.py
+++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# The Universal Permissive License (UPL), Version 1.0
@@ -45,7 +45,7 @@
from io import StringIO
from string import Formatter
-from .. import ccompile, compile_module_from_file, compile_module_from_string, DIR
+from .. import ccompile, compile_module_from_file, compile_module_from_string, compile_module_from_string_no_import, DIR
GRAALPYTHON = sys.implementation.name == "graalpy"
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_abi3t.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_abi3t.py
new file mode 100644
index 0000000000..2c4c768e9f
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_abi3t.py
@@ -0,0 +1,295 @@
+# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# The Universal Permissive License (UPL), Version 1.0
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import ensurepip
+import os
+import platform
+import subprocess
+import sys
+import tempfile
+import unittest
+import zipfile
+from pathlib import Path
+
+from . import compile_module_from_string_no_import
+
+
+ABI3T_SMOKE_SOURCE = r"""
+#define Py_TARGET_ABI3T 0x030f0000
+#include
+
+static char module_token;
+static PyABIInfo abi_info;
+
+static int
+exec_module(PyObject *module)
+{
+ int *state = PyModule_GetState(module);
+ if (state == NULL) {
+ return -1;
+ }
+ *state = 40;
+ return 0;
+}
+
+static PyObject *
+increment(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+ int *state = PyModule_GetState_DuringGC(module);
+ Py_ssize_t state_size = -1;
+ void *token = NULL;
+ void *gc_token = NULL;
+
+ if (PyModule_GetStateSize(module, &state_size) < 0 ||
+ PyModule_GetToken(module, &token) < 0 ||
+ PyModule_GetToken_DuringGC(module, &gc_token) < 0) {
+ return NULL;
+ }
+ if (state == NULL || state_size != sizeof(*state) ||
+ token != &module_token || gc_token != &module_token) {
+ PyErr_SetString(PyExc_AssertionError, "invalid abi3t module metadata");
+ return NULL;
+ }
+ return PyLong_FromLong(++*state);
+}
+
+static PyObject *
+from_slots(PyObject *Py_UNUSED(module), PyObject *spec)
+{
+ static PySlot slots[] = {
+ PySlot_DATA(Py_mod_abi, &abi_info),
+ PySlot_END,
+ };
+ return PyModule_FromSlotsAndSpec(slots, spec);
+}
+
+static PyMethodDef methods[] = {
+ {"increment", increment, METH_NOARGS,
+ "increment($module, /)\n"
+ "--\n\n"
+ "Increment the module state."},
+ {"from_slots", from_slots, METH_O,
+ "from_slots($module, spec, /)\n"
+ "--\n\n"
+ "Create a module from slots."},
+ {NULL, NULL, 0, NULL},
+};
+
+static PyABIInfo abi_info = {
+ 1,
+ 0,
+ PyABIInfo_STABLE | PyABIInfo_FREETHREADING_AGNOSTIC,
+ PY_VERSION_HEX,
+ 0x030f0000,
+};
+
+static PyModuleDef_Slot legacy_slots[] = {
+ {2, exec_module},
+ {0, NULL},
+};
+
+PyMODEXPORT_FUNC
+PyModExport_abi3t_smoke(PyObject *Py_UNUSED(spec))
+{
+ static PySlot slots[] = {
+ PySlot_DATA(Py_mod_abi, &abi_info),
+ PySlot_STATIC_DATA(Py_mod_methods, methods),
+ PySlot_SIZE(Py_mod_state_size, sizeof(int)),
+ PySlot_DATA(Py_mod_token, &module_token),
+ PySlot_DATA(Py_mod_slots, legacy_slots),
+ {.sl_id=0xfbad, .sl_flags=PySlot_OPTIONAL},
+ PySlot_END,
+ };
+ return slots;
+}
+
+#ifdef MS_WINDOWS
+/* Setuptools still requires PyInit_ when linking extensions on Windows. */
+PyMODINIT_FUNC
+PyInit_abi3t_smoke(void)
+{
+ PyErr_SetString(PyExc_ImportError, "PyInit_abi3t_smoke must not be called");
+ return NULL;
+}
+#endif
+"""
+
+
+@unittest.skipUnless(
+ sys.implementation.name == "graalpy" and os.environ.get("GRAALPY_TEST_ABI3T"),
+ "requires the GraalPy ABI3T opt-in test run",
+)
+class TestAbi3t(unittest.TestCase):
+ def test_option_exposed(self):
+ code = "import __graalpython__; print(__graalpython__.abi3t_enabled)"
+ for enabled, expected in ((False, "False"), (True, "True")):
+ proc = subprocess.run(
+ [
+ sys.executable,
+ "--experimental-options=true",
+ f"--python.EnableAbi3t={str(enabled).lower()}",
+ "-S",
+ "-c",
+ code,
+ ],
+ capture_output=True,
+ text=True,
+ check=True,
+ )
+ self.assertEqual(proc.stdout.strip(), expected)
+
+ def test_pip_abi3t_tags(self):
+ pip_wheel = next(Path(ensurepip.__file__).parent.joinpath("_bundled").glob("pip-*.whl"))
+ code = (
+ "import sys; from pip._vendor.packaging import tags; "
+ "values = set(tags.sys_tags()); "
+ "current = f'cp{sys.version_info[0]}{sys.version_info[1]}'; "
+ "print(any(tag.interpreter == current and tag.abi == 'abi3t' for tag in values)); "
+ "print(any(tag.abi == 'abi3' for tag in values)); "
+ "print(any(tag.interpreter == current and tag.abi == current + 't' for tag in values))"
+ )
+ env = dict(os.environ, PYTHONPATH=str(pip_wheel))
+ expected_tags = {
+ False: ("False", "False", "False"),
+ True: ("True", "False", "False"),
+ }
+ for enabled, expected in expected_tags.items():
+ proc = subprocess.run(
+ [
+ sys.executable,
+ "--experimental-options=true",
+ f"--python.EnableAbi3t={str(enabled).lower()}",
+ "-S",
+ "-c",
+ code,
+ ],
+ env=env,
+ capture_output=True,
+ text=True,
+ check=True,
+ )
+ self.assertEqual(tuple(proc.stdout.strip().splitlines()), expected)
+
+ @unittest.skipUnless(sys.platform == "linux", "requires Linux extension wheel tags")
+ def test_pip_abi3t_wheel_install(self):
+ module_dir = Path(compile_module_from_string_no_import(ABI3T_SMOKE_SOURCE, "abi3t_smoke"))
+ extension = next(module_dir.glob("abi3t_smoke*.so"))
+ pip_wheel = next(Path(ensurepip.__file__).parent.joinpath("_bundled").glob("pip-*.whl"))
+ machine = platform.machine().lower().replace("-", "_")
+ interpreter_tag = f"cp{sys.version_info[0]}{sys.version_info[1]}"
+ wheel_name = f"abi3t_smoke-0.0.0-{interpreter_tag}-abi3.abi3t-linux_{machine}.whl"
+ with tempfile.TemporaryDirectory() as tmp:
+ tmp_path = Path(tmp)
+ wheel_path = tmp_path / wheel_name
+ dist_info = "abi3t_smoke-0.0.0.dist-info"
+ wheel_metadata = (
+ "Wheel-Version: 1.0\n"
+ "Generator: graalpython-abi3t-test\n"
+ "Root-Is-Purelib: false\n"
+ f"Tag: {interpreter_tag}-abi3.abi3t-linux_{machine}\n"
+ )
+ metadata = "Metadata-Version: 2.1\nName: abi3t-smoke\nVersion: 0.0.0\n"
+ with zipfile.ZipFile(wheel_path, "w", compression=zipfile.ZIP_DEFLATED) as wheel:
+ wheel.write(extension, "abi3t_smoke.so")
+ wheel.writestr(f"{dist_info}/WHEEL", wheel_metadata)
+ wheel.writestr(f"{dist_info}/METADATA", metadata)
+ wheel.writestr(f"{dist_info}/RECORD", "")
+
+ install_dir = tmp_path / "installed"
+ install_dir.mkdir()
+ env = dict(os.environ, PYTHONPATH=str(pip_wheel))
+ install = subprocess.run(
+ [
+ sys.executable,
+ "--experimental-options=true",
+ "--python.EnableAbi3t=true",
+ "-S",
+ "-m",
+ "pip",
+ "install",
+ "--no-index",
+ "--no-deps",
+ "-vv",
+ "--target",
+ str(install_dir),
+ str(wheel_path),
+ ],
+ env=env,
+ capture_output=True,
+ text=True,
+ )
+ self.assertEqual(install.returncode, 0, install.stderr)
+ import_env = dict(os.environ, PYTHONPATH=str(install_dir))
+ proc = subprocess.run(
+ [
+ sys.executable,
+ "--experimental-options=true",
+ "--python.EnableAbi3t=true",
+ "-S",
+ "-c",
+ "import abi3t_smoke as m; print(m.increment())",
+ ],
+ env=import_env,
+ capture_output=True,
+ text=True,
+ check=True,
+ )
+ self.assertEqual(proc.stdout.strip(), "41")
+
+ def test_modexport_smoke(self):
+ module_dir = compile_module_from_string_no_import(ABI3T_SMOKE_SOURCE, "abi3t_smoke")
+ env = dict(os.environ)
+ env["PYTHONPATH"] = module_dir
+ code = (
+ "import importlib.machinery; import abi3t_smoke as m; "
+ "n = m.from_slots(importlib.machinery.ModuleSpec('from_slots', None)); "
+ "assert m.increment.__name__ == 'increment'; "
+ "assert m.increment.__module__ == 'abi3t_smoke'; "
+ "assert m.increment.__doc__ == 'Increment the module state.'; "
+ "assert m.increment.__text_signature__ == '($module, /)'; "
+ "print(m.increment(), m.increment(), n.__name__)"
+ )
+ proc = subprocess.run(
+ [sys.executable, "--experimental-options=true", "--python.EnableAbi3t=true", "-S", "-c", code],
+ env=env,
+ capture_output=True,
+ text=True,
+ check=True,
+ )
+ self.assertEqual(proc.stdout.strip(), "41 42 from_slots")
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_module.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_module.py
index e0a89a85bf..c8243839fd 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_module.py
+++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_module.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# The Universal Permissive License (UPL), Version 1.0
@@ -39,7 +39,7 @@
import sys
-from . import CPyExtTestCase, CPyExtFunction, unhandled_error_compare
+from . import CPyExtTestCase, CPyExtFunction, compile_module_from_string, unhandled_error_compare
ModuleType = type(sys)
@@ -72,6 +72,122 @@ def _reference_get_filename(args):
class TestPyModule(CPyExtTestCase):
+ def test_add_functions_installs_each_method_definition(self):
+ module = compile_module_from_string(r'''
+ #include
+
+ // CPython and other don't have this function; so define it
+ #ifndef GRAALVM_PYTHON
+ #define GraalPyCFunction_GetMethodDef(OBJ) (((PyCFunctionObject*) (OBJ))->m_ml)
+ #endif
+
+ static PyObject *first(PyObject *module, PyObject *Py_UNUSED(ignored)) {
+ Py_RETURN_TRUE;
+ }
+
+ static PyObject *second(PyObject *module, PyObject *Py_UNUSED(ignored)) {
+ Py_RETURN_FALSE;
+ }
+
+ static PyMethodDef added_methods[] = {
+ {"first", first, METH_NOARGS,
+ "first($module, /)\n"
+ "--\n\n"
+ "Return true."},
+ {"second", second, METH_NOARGS,
+ "second($module, /)\n"
+ "--\n\n"
+ "Return false."},
+ {NULL, NULL, 0, NULL},
+ };
+
+ static PyObject *verify_method_def_pointers(PyObject *module, PyObject *Py_UNUSED(ignored)) {
+ PyObject *first_method = PyObject_GetAttrString(module, "first");
+ PyObject *second_method = PyObject_GetAttrString(module, "second");
+ if (first_method == NULL || second_method == NULL) {
+ Py_XDECREF(first_method);
+ Py_XDECREF(second_method);
+ return NULL;
+ }
+ int matches = GraalPyCFunction_GetMethodDef(first_method) == &added_methods[0] &&
+ GraalPyCFunction_GetMethodDef(second_method) == &added_methods[1];
+ Py_DECREF(first_method);
+ Py_DECREF(second_method);
+ return PyBool_FromLong(matches);
+ }
+
+ static PyMethodDef module_methods[] = {
+ {"verify_method_def_pointers", verify_method_def_pointers, METH_NOARGS, NULL},
+ {NULL, NULL, 0, NULL},
+ };
+
+ static PyModuleDef module_def = {
+ PyModuleDef_HEAD_INIT,
+ "add_functions",
+ NULL,
+ -1,
+ module_methods,
+ };
+
+ PyMODINIT_FUNC PyInit_add_functions(void) {
+ PyObject *module = PyModule_Create(&module_def);
+ if (module == NULL || PyModule_AddFunctions(module, added_methods) < 0) {
+ Py_XDECREF(module);
+ return NULL;
+ }
+ return module;
+ }
+ ''', "add_functions")
+ self.assertTrue(module.first())
+ self.assertFalse(module.second())
+ self.assertTrue(module.verify_method_def_pointers())
+ self.assertEqual(module.first.__name__, "first")
+ self.assertEqual(module.first.__module__, "add_functions")
+ self.assertEqual(module.first.__doc__, "Return true.")
+ self.assertEqual(module.first.__text_signature__, "($module, /)")
+ self.assertEqual(module.second.__doc__, "Return false.")
+ self.assertEqual(module.second.__text_signature__, "($module, /)")
+
+ def test_multiple_exec_slots(self):
+ module = compile_module_from_string(r'''
+ #include
+
+ static int first_exec(PyObject *module) {
+ int *state = PyModule_GetState(module);
+ *state = 1;
+ return 0;
+ }
+
+ static int second_exec(PyObject *module) {
+ int *state = PyModule_GetState(module);
+ *state = *state * 10 + 2;
+ return PyModule_AddIntConstant(module, "exec_order", *state);
+ }
+
+ static PyModuleDef_Slot slots[] = {
+ {Py_mod_exec, first_exec},
+ {Py_mod_exec, second_exec},
+ {0, NULL}
+ };
+
+ static PyModuleDef module_def = {
+ PyModuleDef_HEAD_INIT,
+ "multiple_exec_slots",
+ NULL,
+ sizeof(int),
+ NULL,
+ slots,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ PyMODINIT_FUNC PyInit_multiple_exec_slots(void) {
+ return PyModuleDef_Init(&module_def);
+ }
+ ''', "multiple_exec_slots")
+ self.assertEqual(module.exec_order, 12)
+
test_PyModule_Check = CPyExtFunction(
lambda args: isinstance(args[0], ModuleType),
lambda: (
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_imports.py b/graalpython/com.oracle.graal.python.test/src/tests/test_imports.py
index 8088257962..e9dcafd0f3 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/test_imports.py
+++ b/graalpython/com.oracle.graal.python.test/src/tests/test_imports.py
@@ -59,6 +59,17 @@ def test_module_docstring():
assert moduleA.__doc__ == "MODULE A DOC"
+def test_imp_override_multi_interp_extensions_check_is_rejected_in_main_interpreter():
+ import _imp
+
+ try:
+ _imp._override_multi_interp_extensions_check(1)
+ except RuntimeError as e:
+ assert str(e) == "_imp._override_multi_interp_extensions_check() cannot be used in the main interpreter"
+ else:
+ raise AssertionError("expected RuntimeError in the main interpreter")
+
+
def test_dotted_import():
# this is to prevent ides from optimising out the unused import
try:
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_multiprocessing_spawn.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_multiprocessing_spawn.txt
index 6fa3218f57..ac1be2a6b4 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_multiprocessing_spawn.txt
+++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_multiprocessing_spawn.txt
@@ -60,7 +60,7 @@ test.test_multiprocessing_spawn.test_manager.WithManagerTestSemaphore.test_bound
test.test_multiprocessing_spawn.test_manager.WithManagerTestSemaphore.test_semaphore @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64-github
test.test_multiprocessing_spawn.test_misc.ChallengeResponseTest.test_challengeresponse @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github
test.test_multiprocessing_spawn.test_misc.MiscTestCase.test__all__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github
-test.test_multiprocessing_spawn.test_misc.MiscTestCase.test_large_pool @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64-github
+test.test_multiprocessing_spawn.test_misc.MiscTestCase.test_large_pool @ darwin-arm64,linux-aarch64-github,linux-x86_64-github
test.test_multiprocessing_spawn.test_misc.MiscTestCase.test_spawn_sys_executable_none_allows_import @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github
test.test_multiprocessing_spawn.test_misc.OtherTest.test_answer_challenge_auth_failure @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github
test.test_multiprocessing_spawn.test_misc.OtherTest.test_deliver_challenge_auth_failure @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java
index 3a96270123..9c163df123 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java
@@ -286,6 +286,7 @@ public void postInitialize(Python3Core core) {
mod.setAttribute(tsLiteral("core_home"), coreHome);
mod.setAttribute(tsLiteral("stdlib_home"), stdlibHome);
mod.setAttribute(tsLiteral("capi_home"), capiHome);
+ mod.setAttribute(tsLiteral("abi3t_enabled"), context.getOption(PythonOptions.EnableAbi3t));
Object[] arr = convertToObjectArray(PythonOptions.getExecutableList(context));
PList executableList = PFactory.createList(language, arr);
mod.setAttribute(tsLiteral("executable_list"), executableList);
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ImpModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ImpModuleBuiltins.java
index 01d1fbed92..061d2bf9e4 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ImpModuleBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ImpModuleBuiltins.java
@@ -40,6 +40,7 @@
*/
package com.oracle.graal.python.builtins.modules;
+import static com.oracle.graal.python.builtins.PythonBuiltinClassType.RuntimeError;
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError;
import static com.oracle.graal.python.builtins.modules.ImpModuleBuiltins.FrozenStatus.FROZEN_DISABLED;
import static com.oracle.graal.python.builtins.modules.ImpModuleBuiltins.FrozenStatus.FROZEN_EXCLUDED;
@@ -50,6 +51,8 @@
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___ORIGNAME__;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___PATH__;
import static com.oracle.graal.python.nodes.StringLiterals.J_PY_EXTENSION;
+import static com.oracle.graal.python.nodes.StringLiterals.T_ABI3T_EXT_SO;
+import static com.oracle.graal.python.nodes.StringLiterals.T_ABI3T_MULTIARCH;
import static com.oracle.graal.python.nodes.StringLiterals.T_EXT_PYD;
import static com.oracle.graal.python.nodes.StringLiterals.T_EXT_SO;
import static com.oracle.graal.python.nodes.StringLiterals.T_NAME;
@@ -260,21 +263,17 @@ static int doPythonModule(VirtualFrame frame, PythonModule extensionModule,
@Bind PythonContext context,
@Bind Node inliningTarget,
@Cached("createFor($node)") BoundaryCallData boundaryCallData) {
- long nativeModuleDef = extensionModule.getNativeModuleDef();
- if (nativeModuleDef == NULLPTR) {
- return 0;
- }
PythonLanguage language = context.getLanguage(inliningTarget);
Object state = BoundaryCallContext.enter(frame, language, context, boundaryCallData);
try {
- return doExec(inliningTarget, context, extensionModule, nativeModuleDef);
+ return doExec(inliningTarget, context, extensionModule);
} finally {
BoundaryCallContext.exit(frame, language, context, state);
}
}
@TruffleBoundary
- private static int doExec(Node node, PythonContext context, PythonModule extensionModule, long nativeModuleDef) {
+ private static int doExec(Node node, PythonContext context, PythonModule extensionModule) {
/*
* Check if module is already initialized. CPython does that by testing if 'md_state !=
* NULL'. So, we do the same.
@@ -292,7 +291,7 @@ private static int doExec(Node node, PythonContext context, PythonModule extensi
* ExecModuleNode will run the module definition's exec function which may run arbitrary
* C code. So we need to setup an indirect call.
*/
- return CExtNodes.execModule(node, context.getCApiContext(), extensionModule, nativeModuleDef);
+ return CExtNodes.execModule(node, context.getCApiContext(), extensionModule);
}
@Fallback
@@ -830,9 +829,16 @@ protected ArgumentClinicProvider getArgumentClinic() {
@GenerateNodeFactory
public abstract static class ExtensionSuffixesNode extends PythonBuiltinNode {
@Specialization
- Object run(
- @Bind PythonLanguage language) {
- return PFactory.createList(language, new Object[]{PythonContext.get(this).getExtensionSuffix(), T_EXT_SO, T_EXT_PYD});
+ @TruffleBoundary
+ static Object run(
+ @Bind PythonLanguage language,
+ @Bind PythonContext context) {
+ if (context.getOption(PythonOptions.EnableAbi3t)) {
+ TruffleString multiarch = PythonLanguage.getPlatformInfo().multiarch();
+ TruffleString abi3tMultiarch = T_ABI3T_MULTIARCH.concatUncached(multiarch, TS_ENCODING, false).concatUncached(T_EXT_SO, TS_ENCODING, false);
+ return PFactory.createList(language, new Object[]{context.getExtensionSuffix(), abi3tMultiarch, T_ABI3T_EXT_SO, T_EXT_SO, T_EXT_PYD});
+ }
+ return PFactory.createList(language, new Object[]{context.getExtensionSuffix(), T_EXT_SO, T_EXT_PYD});
}
}
@@ -909,4 +915,28 @@ protected ArgumentClinicProvider getArgumentClinic() {
return ImpModuleBuiltinsClinicProviders.OverrideFrozenModulesForTestsClinicProviderGen.INSTANCE;
}
}
+
+ @Builtin(name = "_override_multi_interp_extensions_check", minNumOfPositionalArgs = 1, parameterNames = {"override"})
+ @ArgumentClinic(name = "override", conversion = ClinicConversion.Int)
+ @GenerateNodeFactory
+ abstract static class OverrideMultiInterpExtensionsCheck extends PythonUnaryClinicBuiltinNode {
+
+ @Specialization
+ @TruffleBoundary
+ static Object doInt(int override,
+ @Bind Node node,
+ @Bind PythonContext context) {
+ if (context.isMainInterpreter()) {
+ throw PRaiseNode.raiseStatic(node, RuntimeError, ErrorMessages.CANNOT_BE_USED_IN_MAIN_INTERP);
+ }
+ int old = context.getOverrideMultiInterpExtensionsCheck();
+ context.setOverrideMultiInterpExtensionsCheck(override);
+ return old;
+ }
+
+ @Override
+ protected ArgumentClinicProvider getArgumentClinic() {
+ return ImpModuleBuiltinsClinicProviders.OverrideMultiInterpExtensionsCheckClinicProviderGen.INSTANCE;
+ }
+ }
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/CFunctionDocUtils.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/CFunctionDocUtils.java
index 19fd7423d3..e8b11eddc4 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/CFunctionDocUtils.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/CFunctionDocUtils.java
@@ -40,11 +40,11 @@
*/
package com.oracle.graal.python.builtins.modules.cext;
-import static com.oracle.graal.python.builtins.objects.cext.common.CExtContext.METH_CLASS;
-import static com.oracle.graal.python.builtins.objects.cext.common.CExtContext.METH_COEXIST;
-import static com.oracle.graal.python.builtins.objects.cext.common.CExtContext.METH_NOARGS;
-import static com.oracle.graal.python.builtins.objects.cext.common.CExtContext.METH_O;
-import static com.oracle.graal.python.builtins.objects.cext.common.CExtContext.METH_STATIC;
+import static com.oracle.graal.python.builtins.objects.cext.capi.PyMethodFlags.METH_CLASS;
+import static com.oracle.graal.python.builtins.objects.cext.capi.PyMethodFlags.METH_COEXIST;
+import static com.oracle.graal.python.builtins.objects.cext.capi.PyMethodFlags.METH_NOARGS;
+import static com.oracle.graal.python.builtins.objects.cext.capi.PyMethodFlags.METH_O;
+import static com.oracle.graal.python.builtins.objects.cext.capi.PyMethodFlags.METH_STATIC;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DOC__;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___TEXT_SIGNATURE__;
import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java
index 49db8bed93..971153959a 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java
@@ -763,6 +763,27 @@ public enum CApiCallPath {
Ignored,
}
+ public enum ABI {
+ GRAALPY(null),
+ ABI3T(GRAALPY);
+
+ private final ABI parent;
+
+ ABI(ABI parent) {
+ this.parent = parent;
+ }
+
+ /** Whether this profile includes implementations declared for {@code candidate}. */
+ public boolean inherits(ABI candidate) {
+ for (ABI current = this; current != null; current = current.parent) {
+ if (current == candidate) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface CApiBuiltins {
@@ -824,6 +845,9 @@ public enum CApiCallPath {
* Comment to explain, e.g., why a builtin is ignored.
*/
String comment() default "";
+
+ /** Specifies to which ABIs this builtin belongs to. A builtin can belong to several ABIs */
+ ABI[] abi() default {ABI.GRAALPY};
}
@CApiBuiltin(ret = PyObjectRawPointer, call = Ignored)
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextMethodBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextMethodBuiltins.java
index 1b0759e39e..108856cc77 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextMethodBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextMethodBuiltins.java
@@ -48,7 +48,10 @@
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObject;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectTransfer;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyTypeObject;
-import static com.oracle.graal.python.builtins.objects.cext.common.CExtContext.METH_METHOD;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.getArrayElementPtr;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readIntField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readPtrField;
+import static com.oracle.graal.python.builtins.objects.cext.capi.PyMethodFlags.METH_METHOD;
import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___MODULE__;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___NAME__;
@@ -57,16 +60,21 @@
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.FromCharPointerNode;
import com.oracle.graal.python.builtins.objects.cext.capi.MethodDescriptorWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.CharPtrToPythonNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode;
+import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
+import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
import com.oracle.graal.python.nodes.HiddenAttr;
import com.oracle.graal.python.nodes.attributes.WriteAttributeToPythonObjectNode;
+import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
import com.oracle.graal.python.runtime.object.PFactory;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.strings.TruffleString;
public final class PythonCextMethodBuiltins {
@@ -94,6 +102,30 @@ static PythonBuiltinObject cFunctionNewExMethodNode(PythonLanguage language, lon
return method;
}
+ /**
+ * Implementation of {@code moduleobject.c: _add_methods_to_object}.
+ */
+ @TruffleBoundary
+ public static void addMethodsToObject(PythonLanguage language, long methods, Object object, Object moduleName) {
+ for (int i = 0;; i++) {
+ long methodDef = getArrayElementPtr(methods, i, CStructs.PyMethodDef);
+ long nameRaw = readPtrField(methodDef, CFields.PyMethodDef__ml_name);
+ if (nameRaw == NULLPTR) {
+ break;
+ }
+ long methPtr = readPtrField(methodDef, CFields.PyMethodDef__ml_meth);
+ int flags = readIntField(methodDef, CFields.PyMethodDef__ml_flags);
+ long docRaw = readPtrField(methodDef, CFields.PyMethodDef__ml_doc);
+
+ TruffleString name = FromCharPointerNode.executeUncached(nameRaw);
+ Object doc = CharPtrToPythonNode.executeUncached(docRaw);
+ assert doc == PNone.NO_VALUE || doc instanceof TruffleString;
+
+ PythonBuiltinObject function = cFunctionNewExMethodNode(language, methodDef, name, methPtr, flags, object, moduleName, PNone.NO_VALUE, doc);
+ WriteAttributeToObjectNode.getUncached().execute(object, name, function);
+ }
+ }
+
@CApiBuiltin(ret = PyObjectTransfer, args = {PyMethodDef, ConstCharPtrAsTruffleString, Pointer, Int, PyObject, PyObject, PyTypeObject, ConstCharPtrAsTruffleString}, call = Ignored)
public static long GraalPyPrivate_CMethod_NewEx(long methodDefPtr, long nameRaw, long methPtr, int flags, long selfRaw, long moduleRaw, long clsRaw, long docRaw) {
// errors are expected to be thrown already in native code
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextModuleBuiltins.java
index 7731e3ab25..fbfba567f4 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextModuleBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextModuleBuiltins.java
@@ -44,8 +44,12 @@
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError;
import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Direct;
import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Ignored;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.CONST_PY_SLOT_PTR;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.ConstCharPtr;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.ConstCharPtrAsTruffleString;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Int;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_ABI_INFO_PTR;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_SSIZE_T_PTR;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Pointer;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyMethodDef;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyModuleDef;
@@ -54,45 +58,45 @@
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObject;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectAsTruffleString;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectTransfer;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.VOID_PTR_LIST;
import static com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.bindFunctionPointer;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readIntField;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readPtrField;
-import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
import static com.oracle.graal.python.nodes.ErrorMessages.NAMELESS_MODULE;
import static com.oracle.graal.python.nodes.ErrorMessages.S_NEEDS_S_AS_FIRST_ARG;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DOC__;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___FILE__;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___NAME__;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___PACKAGE__;
+import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING;
+import static com.oracle.graal.python.nodes.StringLiterals.T_NAME;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
import static com.oracle.graal.python.util.PythonUtils.EMPTY_OBJECT_ARRAY;
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBinaryBuiltinNode;
+import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.ABI;
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin;
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiTernaryBuiltinNode;
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiUnaryBuiltinNode;
import com.oracle.graal.python.builtins.objects.PNone;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext.ModuleSpec;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodesFactory.PRaiseNativeNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.CheckPrimitiveFunctionResultNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.CheckPrimitiveFunctionResultNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionSignature;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.CharPtrToPythonNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode;
-import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
import com.oracle.graal.python.builtins.objects.module.PythonModule;
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins;
-import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
import com.oracle.graal.python.builtins.objects.str.StringBuiltins.PrefixSuffixNode;
+import com.oracle.graal.python.lib.PyObjectGetAttr;
import com.oracle.graal.python.lib.PyUnicodeCheckNode;
-import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.StringLiterals;
@@ -102,8 +106,12 @@
import com.oracle.graal.python.nodes.call.CallNode;
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
import com.oracle.graal.python.nodes.object.GetClassNode;
+import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
+import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
+import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer;
+import com.oracle.graal.python.runtime.nativeaccess.NativeMemory;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Bind;
@@ -265,7 +273,7 @@ public static int GraalPyPrivate_Module_AddFunctions(long moduleRaw, long functi
return PRaiseNativeNodeGen.getUncached().raiseIntWithoutFrame(-1, SystemError, NAMELESS_MODULE, EMPTY_OBJECT_ARRAY);
}
- addMethodsToObject(functions, module, modName);
+ PythonCextMethodBuiltins.addMethodsToObject(PythonLanguage.get(null), functions, module, modName);
return 0;
}
@@ -274,67 +282,98 @@ public static int GraalPyPrivate_AddMethodsToObject(long moduleRaw, long nameRaw
CompilerAsserts.neverPartOfCompilation();
Object module = NativeToPythonInternalNode.executeUncached(moduleRaw, false);
Object name = NativeToPythonInternalNode.executeUncached(nameRaw, false);
- addMethodsToObject(functions, module, name);
+ PythonCextMethodBuiltins.addMethodsToObject(PythonLanguage.get(null), functions, module, name);
return 0;
}
- /**
- * Implementation of {@code moduleobject.c: _add_methods_to_object}.
- *
- * TODO(fa): overlaps with
- * {@link com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes#createLegacyMethod}
- */
- private static void addMethodsToObject(long functions, Object module, Object modName) {
- PythonLanguage language = PythonLanguage.get(null);
- long nameRaw;
-
- // iterate over a native array of PyModuleDef elements
- for (long def = functions; (nameRaw = readPtrField(def, CFields.PyMethodDef__ml_name)) != NULLPTR; def += CStructs.PyMethodDef.size()) {
- long cfunc = readPtrField(def, CFields.PyMethodDef__ml_meth);
- int flags = readIntField(def, CFields.PyMethodDef__ml_flags);
- long docRaw = readPtrField(def, CFields.PyMethodDef__ml_doc);
-
- TruffleString name = (TruffleString) CharPtrToPythonNode.executeUncached(nameRaw);
- Object doc = CharPtrToPythonNode.executeUncached(docRaw);
- assert doc == PNone.NO_VALUE || doc instanceof TruffleString;
-
- PythonBuiltinObject func = PythonCextMethodBuiltins.cFunctionNewExMethodNode(language, def, name, cfunc, flags, module, modName, PNone.NO_VALUE, doc);
- WriteAttributeToObjectNode.getUncached().execute(module, name, func);
+ @CApiBuiltin(ret = PyObjectTransfer, args = {CONST_PY_SLOT_PTR, PyObject}, call = Direct, abi = ABI.ABI3T)
+ public static long PyModule_FromSlotsAndSpec(long slots, long specPtr) {
+ if (slots == NULLPTR) {
+ throw PRaiseNode.raiseStatic(null, SystemError, ErrorMessages.PYMODULE_FROMSLOTS_NULL_SLOTS);
}
+ Object spec = NativeToPythonInternalNode.executeUncached(specPtr, false);
+ TruffleString name = CastToTruffleStringNode.executeUncached(PyObjectGetAttr.executeUncached(spec, T_NAME));
+ ModuleSpec moduleSpec = new ModuleSpec(name, T_EMPTY_STRING, spec);
+ Object module = CExtNodes.createModuleFromSlotsAndSpec(null, PythonContext.get(null).getCApiContext(), slots, moduleSpec);
+ return PythonToNativeInternalNode.executeNewRefUncached(module);
}
- @CApiBuiltin(ret = Int, args = {PyObject, Pointer, Pointer}, call = Ignored)
- abstract static class GraalPyPrivate_Module_Traverse extends CApiTernaryBuiltinNode {
- private static final CApiTiming TIMING_INVOKE_TRAVERSE_PROC = CApiTiming.create(true, "invokeTraverseProc");
+ @CApiBuiltin(ret = Int, args = {PyObject}, call = Direct, abi = ABI.ABI3T)
+ public static int PyModule_Exec(long modulePtr) {
+ Object object = NativeToPythonInternalNode.executeUncached(modulePtr, false);
+ if (object instanceof PythonModule module) {
+ return CExtNodes.execModule(null, PythonContext.get(null).getCApiContext(), module);
+ }
+ throw PRaiseNode.raiseStatic(null, TypeError, ErrorMessages.EXPECTED_MODULE_GOT_T, object);
+ }
- @Specialization
- static int doGeneric(PythonModule self, long visitFun, long arg,
- @Bind Node inliningTarget,
- @Cached CheckPrimitiveFunctionResultNode checkPrimitiveFunctionResultNode,
- @Cached PythonToNativeInternalNode toNativeNode) {
+ @CApiBuiltin(ret = Int, args = {PyObject, PY_SSIZE_T_PTR}, call = Direct, abi = ABI.ABI3T)
+ public static int PyModule_GetStateSize(long modulePtr, long result) {
+ Object object = NativeToPythonInternalNode.executeUncached(modulePtr, false);
+ if (object instanceof PythonModule module) {
+ NativeMemory.writeLong(result, module.getNativeModuleStateSize());
+ return 0;
+ }
+ NativeMemory.writeLong(result, -1);
+ throw PRaiseNode.raiseStatic(null, TypeError, ErrorMessages.EXPECTED_MODULE_GOT_T, object);
+ }
- /*
- * As in 'moduleobject.c: module_traverse': 'if (m->md_def && m->md_def->m_traverse &&
- * (m->md_def->m_size <= 0 || m->md_state != NULL))'
- */
- long mdDef = self.getNativeModuleDef();
- if (mdDef != NULLPTR) {
- long mTraverse = readPtrField(mdDef, CFields.PyModuleDef__m_traverse);
- if (mTraverse != NULLPTR) {
- long mSize = readLongField(mdDef, CFields.PyModuleDef__m_size);
- long mdState = self.getNativeModuleState();
- if (mSize <= 0 || mdState != NULLPTR) {
- PythonContext ctx = PythonContext.get(inliningTarget);
- NativeFunctionPointer traverseExecutable = bindFunctionPointer(mTraverse, ExternalFunctionSignature.TRAVERSEPROC);
- int ires = ExternalFunctionInvoker.invokeTRAVERSEPROC(null, TIMING_INVOKE_TRAVERSE_PROC, ctx.ensureNativeContext(), BoundaryCallData.getUncached(),
- ctx.getThreadState(PythonLanguage.get(inliningTarget)), traverseExecutable, toNativeNode.execute(inliningTarget, self), visitFun, arg);
- checkPrimitiveFunctionResultNode.executeLong(inliningTarget, ctx.getThreadState(PythonLanguage.get(inliningTarget)), StringLiterals.T_VISIT, ires);
- return ires;
- }
- }
- }
+ @CApiBuiltin(ret = Int, args = {PyObject, VOID_PTR_LIST}, call = Direct, abi = ABI.ABI3T)
+ public static int PyModule_GetToken(long modulePtr, long result) {
+ Object object = NativeToPythonInternalNode.executeUncached(modulePtr, false);
+ if (object instanceof PythonModule module) {
+ NativeMemory.writeLong(result, module.getNativeModuleToken());
return 0;
}
+ NativeMemory.writeLong(result, NULLPTR);
+ throw PRaiseNode.raiseStatic(null, TypeError, ErrorMessages.EXPECTED_MODULE_GOT_T, object);
+ }
+
+ @CApiBuiltin(ret = Int, args = {PyObject, VOID_PTR_LIST}, call = Direct, abi = ABI.ABI3T)
+ public static int PyModule_GetToken_DuringGC(long modulePtr, long result) {
+ Object object = NativeToPythonInternalNode.executeUncached(modulePtr, false);
+ if (object instanceof PythonModule module) {
+ NativeMemory.writeLong(result, module.getNativeModuleToken());
+ return 0;
+ }
+ NativeMemory.writeLong(result, NULLPTR);
+ return -1;
+ }
+
+ @CApiBuiltin(ret = Pointer, args = {PyObject}, call = Direct, abi = ABI.ABI3T)
+ public static long PyModule_GetState_DuringGC(long modulePtr) {
+ Object object = NativeToPythonInternalNode.executeUncached(modulePtr, false);
+ if (object instanceof PythonModule module) {
+ return module.getNativeModuleState();
+ }
+ return NULLPTR;
+ }
+
+ @CApiBuiltin(ret = Int, args = {PY_ABI_INFO_PTR, ConstCharPtr}, call = Direct, abi = ABI.ABI3T)
+ public static int PyABIInfo_Check(long info, long moduleName) {
+ TruffleString name = moduleName == NULLPTR ? T_EMPTY_STRING : (TruffleString) CharPtrToPythonNode.executeUncached(moduleName);
+ return CExtNodes.checkAbiInfo(null, info, name);
+ }
+
+ private static final CApiTiming TIMING_INVOKE_TRAVERSE_PROC = CApiTiming.create(true, "invokeTraverseProc");
+
+ @CApiBuiltin(ret = Int, args = {PyObject, Pointer, Pointer}, call = Ignored)
+ public static int GraalPyPrivate_Module_Traverse(long selfPtr, long visitFun, long arg) {
+
+ PythonModule self = (PythonModule) NativeToPythonInternalNode.executeUncached(selfPtr, false);
+ long traverse = self.getNativeModuleTraverse();
+ long stateSize = self.getNativeModuleStateSize();
+ long state = self.getNativeModuleState();
+ if (traverse != NULLPTR && (stateSize <= 0 || state != NULLPTR)) {
+ PythonContext ctx = PythonContext.get(null);
+ PythonThreadState threadState = ctx.getThreadState(ctx.getLanguage());
+ NativeFunctionPointer traverseExecutable = bindFunctionPointer(traverse, ExternalFunctionSignature.TRAVERSEPROC);
+ int ires = ExternalFunctionInvoker.invokeTRAVERSEPROC(null, TIMING_INVOKE_TRAVERSE_PROC, ctx.ensureNativeContext(), BoundaryCallData.getUncached(),
+ threadState, traverseExecutable, PythonToNativeInternalNode.executeUncached(self, false), visitFun, arg);
+ CheckPrimitiveFunctionResultNodeGen.getUncached().executeLong(null, threadState, StringLiterals.T_VISIT, ires);
+ return ires;
+ }
+ return 0;
}
@CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct)
@@ -360,20 +399,14 @@ static Object error(@SuppressWarnings("unused") Object module,
}
@CApiBuiltin(ret = ArgDescriptor.Void, args = {PyModuleObject, PyModuleDef}, call = Ignored)
- abstract static class GraalPyPrivate_Module_SetDef extends CApiBinaryBuiltinNode {
- @Specialization
- static Object set(PythonModule object, long value) {
- object.setNativeModuleDef(value);
- return PNone.NO_VALUE;
- }
+ public static void GraalPyPrivate_Module_SetDef(long object, long value) {
+ PythonModule module = (PythonModule) NativeToPythonInternalNode.executeUncached(object, false);
+ module.setNativeModuleDef(value);
}
@CApiBuiltin(ret = ArgDescriptor.Void, args = {PyModuleObject, Pointer}, call = Ignored)
- abstract static class GraalPyPrivate_Module_SetState extends CApiBinaryBuiltinNode {
- @Specialization
- static Object set(PythonModule object, long value) {
- object.setNativeModuleState(value);
- return PNone.NO_VALUE;
- }
+ public static void GraalPyPrivate_Module_SetState(long object, long value) {
+ PythonModule module = (PythonModule) NativeToPythonInternalNode.executeUncached(object, false);
+ module.setNativeModuleState(value);
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTypeBuiltins.java
index 479a151df7..c5b2d46b31 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTypeBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTypeBuiltins.java
@@ -82,6 +82,7 @@
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.SetterRoot;
import com.oracle.graal.python.builtins.objects.cext.capi.MethodDescriptorWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.PyMethodFlags;
import com.oracle.graal.python.builtins.objects.cext.capi.WrapperDescriptorRootNodesGen;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
@@ -90,7 +91,6 @@
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformExceptionToNativeNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtContext;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.ReadObjectNode;
@@ -311,14 +311,14 @@ private static PythonBuiltinObject typeAddMethod(PythonLanguage language, long m
WriteAttributeToPythonObjectNode.executeUncached(func, T___NAME__, name);
CFunctionDocUtils.writeDocAndTextSignature(func, name, doc, flags);
}
- if (CExtContext.isMethClass(flags)) {
- if (CExtContext.isMethStatic(flags)) {
+ if (PyMethodFlags.isMethClass(flags)) {
+ if (PyMethodFlags.isMethStatic(flags)) {
assert func == null;
throw PRaiseNode.raiseStatic(EncapsulatingNodeReference.getCurrent().get(), PythonBuiltinClassType.ValueError, ErrorMessages.METHOD_CANNOT_BE_BOTH_CLASS_AND_STATIC);
}
assert func != null;
return PFactory.createBuiltinClassmethodFromCallableObj(language, func);
- } else if (CExtContext.isMethStatic(flags)) {
+ } else if (PyMethodFlags.isMethStatic(flags)) {
return PFactory.createStaticmethodFromCallableObj(language, func);
}
HiddenAttr.WriteLongNode.executeUncached(func, METHOD_DEF_PTR, methodDefPtr);
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java
index ff972999d4..e9389454b3 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java
@@ -41,6 +41,7 @@
package com.oracle.graal.python.builtins.objects.cext.capi;
import static com.oracle.graal.python.PythonLanguage.CONTEXT_INSENSITIVE_SINGLETONS;
+import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError;
import static com.oracle.graal.python.builtins.objects.PythonAbstractObject.UNINITIALIZED;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.drainReferenceQueueNow;
import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readIntField;
@@ -49,9 +50,11 @@
import static com.oracle.graal.python.nodes.BuiltinNames.T___GRAALPYTHON__;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___FILE__;
import static com.oracle.graal.python.nodes.StringLiterals.T_DASH;
+import static com.oracle.graal.python.nodes.StringLiterals.T_DOT;
import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING;
import static com.oracle.graal.python.nodes.StringLiterals.T_UNDERSCORE;
import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached;
import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
@@ -64,8 +67,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -96,7 +97,7 @@
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ReferenceQueueCoordinator;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtContext;
+import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformExceptionFromNativeNode;
import com.oracle.graal.python.builtins.objects.cext.common.LoadCExtException.ApiInitException;
import com.oracle.graal.python.builtins.objects.cext.common.LoadCExtException.ImportException;
import com.oracle.graal.python.builtins.objects.cext.copying.NativeLibraryLocator;
@@ -104,6 +105,7 @@
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
import com.oracle.graal.python.builtins.objects.dict.PDict;
+import com.oracle.graal.python.builtins.objects.exception.PBaseException;
import com.oracle.graal.python.builtins.objects.frame.PFrame;
import com.oracle.graal.python.builtins.objects.ints.PInt;
import com.oracle.graal.python.builtins.objects.module.PythonModule;
@@ -122,6 +124,7 @@
import com.oracle.graal.python.runtime.PythonContext.CApiState;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.graal.python.runtime.PythonOptions;
+import com.oracle.graal.python.runtime.exception.ExceptionUtils;
import com.oracle.graal.python.runtime.exception.PException;
import com.oracle.graal.python.runtime.nativeaccess.NativeAccessSupport;
import com.oracle.graal.python.runtime.nativeaccess.NativeContext;
@@ -158,9 +161,11 @@
import sun.misc.Unsafe;
-public final class CApiContext extends CExtContext {
+public final class CApiContext {
private static final TruffleString T_PY_INIT = tsLiteral("PyInit_");
private static final TruffleString T_PY_INIT_U = tsLiteral("PyInitU_");
+ private static final TruffleString T_PY_MOD_EXPORT = tsLiteral("PyModExport_");
+ private static final TruffleString T_PY_MOD_EXPORT_U = tsLiteral("PyModExportU_");
private static final TruffleString T_GRAALPY_TEST_CAPI = tsLiteral("_testcapi");
private static final TruffleString T_GRAALPY_TEST_CAPI_NAME = tsLiteral("__graalpython__._testcapi");
@@ -174,9 +179,18 @@ public final class CApiContext extends CExtContext {
private static final TruffleLogger LOGGER = PythonLanguage.getLogger(LOGGER_CAPI_NAME);
public static final TruffleLogger GC_LOGGER = PythonLanguage.getLogger(CApiContext.LOGGER_CAPI_NAME + ".gc");
+ private static final String COULD_NOT_LOAD_MODULE_FORMAT = """
+ could not load module %s (real path: %s) from virtual file system.
+
+ !!! Please try to run with java system property org.graalvm.python.vfs.extractOnStartup=true !!!
+ See also: https://www.graalvm.org/python/docs/#graalpy-troubleshooting""";
/** Native pointers for context-insensitive singletons like {@link PNone#NONE}. */
@CompilationFinal(dimensions = 1) private final long[] singletonNativePtrs;
+ private final PythonContext context;
+ /** The library object representing 'libpython.*.so' or similar. */
+ private final NativeLibrary library;
+ private final String libraryName;
/**
* Pointer to the native {@code GCState GC state}. This corresponds to CPython's
@@ -211,6 +225,42 @@ public static boolean isSpecialSingleton(Object delegate) {
return getSingletonNativeWrapperIdx(delegate) != -1;
}
+ @TruffleBoundary
+ public static TruffleString getBaseName(TruffleString name) {
+ int len = TruffleString.CodePointLengthNode.getUncached().execute(name, TS_ENCODING);
+ if (len == 1) {
+ return name.equalsUncached(T_DOT, TS_ENCODING) ? T_EMPTY_STRING : name;
+ }
+ int idx = name.lastIndexOfStringUncached(T_DOT, len, 0, TS_ENCODING);
+ if (idx < 0) {
+ return name;
+ }
+ if (idx == len - 1) {
+ return T_EMPTY_STRING;
+ }
+ return name.substringUncached(idx + 1, len - idx - 1, TS_ENCODING, true);
+ }
+
+ @TruffleBoundary
+ public static PException wrapJavaException(Throwable e, Node raisingNode) {
+ TruffleString message = toTruffleStringUncached(e.getMessage());
+ PBaseException excObject = PFactory.createBaseException(PythonLanguage.get(null), SystemError, message != null ? message : toTruffleStringUncached(e.toString()),
+ PythonUtils.EMPTY_OBJECT_ARRAY);
+ return ExceptionUtils.wrapJavaException(e, raisingNode, excObject);
+ }
+
+ public PythonContext getContext() {
+ return context;
+ }
+
+ public NativeLibrary getLibrary() {
+ return library;
+ }
+
+ public String getLibraryName() {
+ return libraryName;
+ }
+
private record ClosureInfo(Object delegate, Object executable, long pointer) {
}
@@ -270,13 +320,18 @@ private static boolean canEncode(TruffleString basename) {
@TruffleBoundary
public TruffleString getInitFunctionName() {
- /*
- * n.b.: 'getEncodedName' also sets 'ascii' and must therefore be called before 'ascii'
- * is queried
- */
+ // 'getEncodedName' also sets 'ascii' and must therefore be called before 'ascii' is queried
TruffleString s = getEncodedName();
return StringUtils.cat((ascii ? T_PY_INIT : T_PY_INIT_U), s);
}
+
+ public TruffleString getModExecFunctionName() {
+ CompilerAsserts.neverPartOfCompilation();
+ assert PythonContext.get(null).getOption(PythonOptions.EnableAbi3t);
+ // 'getEncodedName' also sets 'ascii' and must therefore be called before 'ascii' is queried
+ TruffleString s = getEncodedName();
+ return StringUtils.cat((ascii ? T_PY_MOD_EXPORT : T_PY_MOD_EXPORT_U), s);
+ }
}
/*
@@ -293,25 +348,9 @@ public TruffleString getInitFunctionName() {
*/
private final HashMap methodDefinitions = new HashMap<>(4);
- /**
- * This list holds a strong reference to all loaded extension libraries to keep the library
- * objects alive. This is necessary because native library handles may {@code dlclose} the
- * library (and thus {@code munmap} all code) if the library object is no longer reachable.
- * However, it can happen
- * that we still store raw function pointers (as Java {@code long} values) in a native object
- * that is referenced by a
- * {@link com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeObjectReference}.
- * For example, the {code tp_dealloc} functions may be executed a long time after all managed
- * objects of the extension died and the native library has been {@code dlclosed}'d.
- *
- * Since we have no control over the timing when certain garbage will be collected, we need to
- * ensure that the code is still mapped.
- */
- private final List loadedExtensions = new LinkedList<>();
-
private final NativeLibraryLocator nativeLibraryLocator;
- public final BackgroundGCTask gcTask;
+ private final BackgroundGCTask gcTask;
private Thread backgroundGCTaskThread;
private final ReferenceQueueWatcherTask referenceQueueWatcherTask;
@@ -320,7 +359,10 @@ public static TruffleLogger getLogger(Class> clazz) {
}
public CApiContext(PythonContext context, NativeLibrary library, NativeLibraryLocator locator) {
- super(context, library, locator.getCapiLibrary());
+ this.context = context;
+ this.library = library;
+ this.libraryName = locator.getCapiLibrary();
+
this.nativeCAPISymbols = new NativeFunctionPointer[NativeCAPISymbol.values().length];
this.nativeLibraryLocator = locator;
@@ -332,11 +374,6 @@ public CApiContext(PythonContext context, NativeLibrary library, NativeLibraryLo
this.referenceQueueWatcherTask = new ReferenceQueueWatcherTask(context);
}
- @TruffleBoundary
- void addLoadedExtensionLibrary(Object nativeLibrary) {
- loadedExtensions.add(nativeLibrary);
- }
-
@TruffleBoundary
public static Object asHex(Object ptr) {
if (ptr instanceof Number) {
@@ -1094,20 +1131,6 @@ private static CApiContext loadCApi(Node node, PythonContext context, TruffleStr
"termios",
"pyexpat");
- private static String dlopenFlagsToString(int flags) {
- String str = "RTLD_NOW";
- if ((flags & PosixConstants.RTLD_LAZY.value) != 0) {
- str = "RTLD_LAZY";
- }
- if ((flags & PosixConstants.RTLD_GLOBAL.value) != 0) {
- str += "|RTLD_GLOBAL";
- }
- if ((flags & PosixConstants.RTLD_LOCAL.value) != 0) {
- str += "|RTLD_LOCAL";
- }
- return str;
- }
-
/**
* This method loads a C extension module (C API) and will initialize the corresponding native
* contexts if necessary.
@@ -1154,30 +1177,27 @@ public static Object loadCExtModule(Node location, PythonContext context, Module
dlopenFlags |= PosixConstants.RTLD_LOCAL.value;
}
+ String format = String.format(COULD_NOT_LOAD_MODULE_FORMAT, spec.path, realPath);
try {
library = context.ensureNativeContext().loadLibrary(loadPath, dlopenFlags);
} catch (PException e) {
throw e;
} catch (NativeLibraryLoadException e) {
if (!realPath.exists() && realPath.toString().contains("org.graalvm.python.vfsx")) {
- getLogger(CApiContext.class).severe(String.format("could not load module %s (real path: %s) from virtual file system.\n\n" +
- "!!! Please try to run with java system property org.graalvm.python.vfs.extractOnStartup=true !!!\n" +
- "See also: https://www.graalvm.org/python/docs/#graalpy-troubleshooting", spec.path, realPath));
+ getLogger(CApiContext.class).severe(format);
}
throw new ImportException(null, spec.name, spec.path, ErrorMessages.CANNOT_LOAD, spec.path, e.getMessage());
} catch (AbstractTruffleException e) {
if (!realPath.exists() && realPath.toString().contains("org.graalvm.python.vfsx")) {
// file does not exist and it is from VirtualFileSystem
// => we probably failed to extract it due to unconventional libs location
- getLogger(CApiContext.class).severe(String.format("could not load module %s (real path: %s) from virtual file system.\n\n" +
- "!!! Please try to run with java system property org.graalvm.python.vfs.extractOnStartup=true !!!\n" +
- "See also: https://www.graalvm.org/python/docs/#graalpy-troubleshooting", spec.path, realPath));
+ getLogger(CApiContext.class).severe(format);
}
- throw new ImportException(CExtContext.wrapJavaException(e, location), spec.name, spec.path, ErrorMessages.CANNOT_LOAD_M, spec.path, e);
+ throw new ImportException(CApiContext.wrapJavaException(e, location), spec.name, spec.path, ErrorMessages.CANNOT_LOAD_M, spec.path, e);
}
- return cApiContext.initCApiModule(location, library, spec.getInitFunctionName(), spec);
+ return cApiContext.loadCApiModule(location, library, spec);
}
/**
@@ -1321,12 +1341,22 @@ public void finalizeCApi(boolean cancelling) {
}
}
- @TruffleBoundary
- public Object initCApiModule(Node node, NativeLibrary sharedLibrary, TruffleString initFuncName, ModuleSpec spec) throws ImportException {
- PythonContext context = getContext();
- CApiContext cApiContext = context.getCApiContext();
+ public Object loadCApiModule(Node node, NativeLibrary sharedLibrary, ModuleSpec spec) throws ImportException {
+ CompilerAsserts.neverPartOfCompilation();
+ if (context.getOption(PythonOptions.EnableAbi3t)) {
+ TruffleString modExportFuncName = spec.getModExecFunctionName();
+ long modExportFunc = sharedLibrary.lookupOptionalSymbol(modExportFuncName.toJavaStringUncached());
+ if (modExportFunc != NULLPTR) {
+ return loadCApiModuleFromPyModExport(node, modExportFunc, spec);
+ }
+ }
+ return loadCApiModuleFromPyInit(node, sharedLibrary, spec.getInitFunctionName(), spec);
+ }
+
+ private Object loadCApiModuleFromPyInit(Node node, NativeLibrary sharedLibrary, TruffleString initFuncName, ModuleSpec spec) throws ImportException {
+ CompilerAsserts.neverPartOfCompilation();
long pyinitFunc = sharedLibrary.lookupOptionalSymbol(initFuncName.toJavaStringUncached());
- if (pyinitFunc == 0L) {
+ if (pyinitFunc == NULLPTR) {
throw new ImportException(null, spec.name, spec.path, ErrorMessages.NO_FUNCTION_FOUND, "", initFuncName, spec.path);
}
NativeContext nativeContext = context.ensureNativeContext();
@@ -1334,7 +1364,7 @@ public Object initCApiModule(Node node, NativeLibrary sharedLibrary, TruffleStri
ExternalFunctionSignature.MODINIT.bind(nativeContext, pyinitFunc));
Object result = PyObjectCheckFunctionResultNodeGen.getUncached().execute(context, initFuncName, NativeToPythonInternalNode.executeUncached(nativeResult, false));
- if (!(result instanceof PythonModule)) {
+ if (!(result instanceof PythonModule module)) {
// Multi-phase extension module initialization
/*
@@ -1349,13 +1379,10 @@ public Object initCApiModule(Node node, NativeLibrary sharedLibrary, TruffleStri
throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.SystemError, ErrorMessages.INIT_FUNC_RETURNED_UNINT_OBJ, initFuncName);
}
- return CExtNodes.createModule(node, cApiContext, spec, nativeResult, sharedLibrary);
+ return CExtNodes.createModuleFromDefAndSpec(node, this, spec, nativeResult);
} else {
// see: 'import.c: _PyImport_FixupExtensionObject'
- PythonModule module = (PythonModule) result;
module.setAttribute(T___FILE__, spec.path);
- addLoadedExtensionLibrary(sharedLibrary);
-
// add to 'sys.modules'
PDict sysModules = context.getSysModules();
sysModules.setItem(spec.name, result);
@@ -1374,6 +1401,24 @@ public Object initCApiModule(Node node, NativeLibrary sharedLibrary, TruffleStri
}
}
+ // import.c: import_run_modexport
+ private Object loadCApiModuleFromPyModExport(Node node, long modExportFunc, ModuleSpec spec) {
+ CompilerAsserts.neverPartOfCompilation();
+ NativeContext nativeContext = context.ensureNativeContext();
+ PythonThreadState threadState = context.getThreadState(context.getLanguage());
+ // return value is of type 'PySlot *'
+ long slots = ExternalFunctionInvoker.invokeMODINIT(null, TIMING_INVOKE_MODULE_INIT, nativeContext, BoundaryCallData.getUncached(), threadState,
+ ExternalFunctionSignature.MODINIT.bind(nativeContext, modExportFunc));
+ TransformExceptionFromNativeNode.getUncached().execute(node, threadState, spec.name, slots == NULLPTR, true,
+ ErrorMessages.MODULE_EXPORT_HOOK_FAILED, ErrorMessages.MODULE_EXPORT_HOOK_RAISED_EXCEPTION);
+ Object module = CExtNodes.createModuleFromSlotsAndSpec(node, context.getCApiContext(), slots, spec);
+ if (module instanceof PythonModule pythonModule && pythonModule.getNativeModuleToken() == NULLPTR) {
+ // import_run_modexport uses the static top-level slots array as the default token.
+ pythonModule.setNativeModuleToken(slots);
+ }
+ return module;
+ }
+
@TruffleBoundary
public PythonModule findExtension(TruffleString filename, TruffleString name) {
return extensions.get(Pair.create(filename, name));
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java
index 7263c8724f..b476b7c003 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java
@@ -65,7 +65,9 @@
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.FILE_PTR;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.INITTAB;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.INT32_T;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.INT32_T_PTR;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.INT64_T;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.INT64_T_PTR;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.INT_LIST;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Int;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.LONG_LONG;
@@ -89,7 +91,11 @@
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_GEN_OBJECT;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_GIL_STATE_STATE;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_IDENTIFIER;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_INTERPRETER_GUARD_PTR;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_INTERPRETER_VIEW_PTR;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_LOCK_STATUS;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_LONG_EXPORT_PTR;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_LONG_WRITER_PTR;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_MONITORING_STATE_PTR;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_MUTEX_PTR;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_OPENCODEHOOKFUNCTION;
@@ -97,10 +103,13 @@
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_REF_TRACER;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_SSIZE_T_PTR;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_STRUCT_SEQUENCE_DESC;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_THREAD_STATE_TOKEN_PTR;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_THREAD_TYPE_LOCK;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_TRACEFUNC;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_TSS_T_PTR;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_TYPE_SPEC;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_SLOT_PTR;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_TYPE_OBJECT_PTR_LIST;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_UCS4;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_UCS4_PTR;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Pointer;
@@ -140,10 +149,13 @@
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyVarObject;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Py_hash_t;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Py_ssize_t;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.CONST_PY_LONG_LAYOUT_PTR;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.SIZE_T;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.SIZE_T_PTR;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UINT64_T;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UINT64_T_PTR;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UINT32_T;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UINT32_T_PTR;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UINTPTR_T;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UNSIGNED_INT;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UNSIGNED_LONG;
@@ -175,6 +187,7 @@
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.visitproc;
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins;
+import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.ABI;
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;
@@ -1053,6 +1066,55 @@ public final class CApiFunction {
@CApiBuiltin(name = "_PyLong_FromDigits", ret = PyLongObject, args = {PrimitiveResult32, Py_ssize_t, DIGIT_PTR}, call = NotImplemented)
@CApiBuiltin(name = "_Py_NewReferenceNoTotal", ret = Void, args = {PyObjectRawPointer}, call = CImpl)
+ // ABI3T-only functions that are not supported yet. Use Ignored to validate their signatures
+ // without generating C stubs against the 3.13 headers, which lack ABI3T declarations.
+ @CApiBuiltin(name = "PyInterpreterGuard_Close", ret = Void, args = {PY_INTERPRETER_GUARD_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyInterpreterGuard_FromCurrent", ret = PY_INTERPRETER_GUARD_PTR, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyInterpreterGuard_FromView", ret = PY_INTERPRETER_GUARD_PTR, args = {PY_INTERPRETER_VIEW_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyInterpreterView_Close", ret = Void, args = {PY_INTERPRETER_VIEW_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyInterpreterView_FromCurrent", ret = PY_INTERPRETER_VIEW_PTR, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyInterpreterView_FromMain", ret = PY_INTERPRETER_VIEW_PTR, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyIter_NextItem", ret = PrimitiveResult32, args = {PyObjectReturn, PyObjectPtr}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyLongWriter_Create", ret = PY_LONG_WRITER_PTR, args = {PrimitiveResult32, Py_ssize_t, VOID_PTR_LIST}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyLongWriter_Discard", ret = Void, args = {PY_LONG_WRITER_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyLongWriter_Finish", ret = PyObjectReturn, args = {PY_LONG_WRITER_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyLong_AsInt32", ret = PrimitiveResult32, args = {PyObjectReturn, INT32_T_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyLong_AsInt64", ret = PrimitiveResult32, args = {PyObjectReturn, INT64_T_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyLong_AsUInt32", ret = PrimitiveResult32, args = {PyObjectReturn, UINT32_T_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyLong_AsUInt64", ret = PrimitiveResult32, args = {PyObjectReturn, UINT64_T_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyLong_Export", ret = PrimitiveResult32, args = {PyObjectReturn, PY_LONG_EXPORT_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyLong_FreeExport", ret = Void, args = {PY_LONG_EXPORT_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyLong_FromInt32", ret = PyObjectReturn, args = {INT32_T}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyLong_FromInt64", ret = PyObjectReturn, args = {INT64_T}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyLong_FromUInt32", ret = PyObjectReturn, args = {UINT32_T}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyLong_FromUInt64", ret = PyObjectReturn, args = {UINT64_T}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyLong_GetNativeLayout", ret = CONST_PY_LONG_LAYOUT_PTR, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyObject_GetTypeData_DuringGC", ret = Pointer, args = {PyObjectReturn, PyTypeObjectRawPointer}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PySys_GetAttr", ret = PyObjectReturn, args = {PyObjectReturn}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PySys_GetAttrString", ret = PyObjectReturn, args = {ConstCharPtr}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PySys_GetOptionalAttr", ret = PrimitiveResult32, args = {PyObjectReturn, PyObjectPtr}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PySys_GetOptionalAttrString", ret = PrimitiveResult32, args = {ConstCharPtr, PyObjectPtr}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyThreadState_Ensure", ret = PY_THREAD_STATE_TOKEN_PTR, args = {PY_INTERPRETER_GUARD_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyThreadState_EnsureFromView", ret = PY_THREAD_STATE_TOKEN_PTR, args = {PY_INTERPRETER_VIEW_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyThreadState_Release", ret = Void, args = {PY_THREAD_STATE_TOKEN_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyType_Freeze", ret = PrimitiveResult32, args = {PyTypeObjectRawPointer}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyType_FromSlots", ret = PyObjectReturn, args = {PY_SLOT_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyType_GetBaseByToken", ret = PrimitiveResult32, args = {PyTypeObjectRawPointer, Pointer, PY_TYPE_OBJECT_PTR_LIST}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyType_GetBaseByToken_DuringGC", ret = PrimitiveResult32, args = {PyTypeObjectRawPointer, Pointer, PY_TYPE_OBJECT_PTR_LIST}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyType_GetModuleByToken", ret = PyObjectReturn, args = {PyTypeObjectRawPointer, CONST_VOID_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyType_GetModuleByToken_DuringGC", ret = PyObjectReturn, args = {PyTypeObjectRawPointer, CONST_VOID_PTR}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyType_GetModuleState_DuringGC", ret = Pointer, args = {PyTypeObjectRawPointer}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyType_GetModule_DuringGC", ret = PyObjectReturn, args = {PyTypeObjectRawPointer}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "PyUnicode_Equal", ret = PrimitiveResult32, args = {PyObjectReturn, PyObjectReturn}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "Py_IS_TYPE", ret = PrimitiveResult32, args = {PyObjectReturn, PyTypeObjectRawPointer}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "Py_PACK_FULL_VERSION", ret = UINT32_T, args = {PrimitiveResult32, PrimitiveResult32, PrimitiveResult32, PrimitiveResult32,
+ PrimitiveResult32}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "Py_PACK_VERSION", ret = UINT32_T, args = {PrimitiveResult32, PrimitiveResult32}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "Py_REFCNT", ret = Py_ssize_t, args = {PyObjectReturn}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "Py_SET_SIZE", ret = Void, args = {PyVarObject, Py_ssize_t}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "Py_SIZE", ret = Py_ssize_t, args = {PyObjectReturn}, call = Ignored, abi = ABI.ABI3T)
+ @CApiBuiltin(name = "Py_TYPE", ret = PyTypeObjectRawPointer, args = {PyObjectReturn}, call = Ignored, abi = ABI.ABI3T)
+
// Windows only functions
@CApiBuiltin(name = "PyUnicode_DecodeMBCS", ret = PyObject, args = {ConstCharPtr, Py_ssize_t, ConstCharPtr}, call = NotImplemented)
@CApiBuiltin(name = "PyUnicode_DecodeMBCSStateful", ret = PyObject, args = {ConstCharPtr, Py_ssize_t, ConstCharPtr, PY_SSIZE_T_PTR}, call = NotImplemented)
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java
index e106a8dc70..9103e137ef 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java
@@ -51,32 +51,27 @@
import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_SUBTYPE_TRAVERSE;
import static com.oracle.graal.python.builtins.objects.cext.structs.CConstants.PYLONG_BITS_IN_DIGIT;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyFloatObject__ob_fval;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyMethodDef__ml_doc;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyMethodDef__ml_flags;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyMethodDef__ml_meth;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyMethodDef__ml_name;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyModuleDef_Slot__slot;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyModuleDef_Slot__value;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyModuleDef__m_clear;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyModuleDef__m_doc;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyModuleDef__m_free;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyModuleDef__m_methods;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyModuleDef__m_name;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyModuleDef__m_size;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyModuleDef__m_slots;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyModuleDef__m_traverse;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyObject__ob_type;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_as_buffer;
import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readDoubleField;
import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readPtrField;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readStructArrayIntField;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readStructArrayPtrField;
import static com.oracle.graal.python.builtins.objects.object.PythonObject.MANAGED_REFCNT;
+import static com.oracle.graal.python.nodes.SpecialMethodNames.T___COMPLEX__;
+import static com.oracle.graal.python.runtime.exception.PythonErrorType.SystemError;
import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.calloc;
import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.mallocByteArray;
import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.writeByteArrayElement;
import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.writeByteArrayElements;
-import static com.oracle.graal.python.nodes.HiddenAttr.METHOD_DEF_PTR;
-import static com.oracle.graal.python.nodes.SpecialMethodNames.T___COMPLEX__;
-import static com.oracle.graal.python.runtime.exception.PythonErrorType.SystemError;
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
import java.lang.ref.Reference;
@@ -84,8 +79,7 @@
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
-import com.oracle.graal.python.builtins.modules.cext.CFunctionDocUtils;
-import com.oracle.graal.python.builtins.objects.PNone;
+import com.oracle.graal.python.builtins.modules.cext.PythonCextMethodBuiltins;
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
import com.oracle.graal.python.builtins.objects.bytes.PByteArray;
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
@@ -93,10 +87,10 @@
import com.oracle.graal.python.builtins.objects.cext.PythonNativeClass;
import com.oracle.graal.python.builtins.objects.cext.PythonNativeObject;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext.ModuleSpec;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PyObjectCheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodesFactory.AsCharPointerNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodesFactory.EnsurePythonObjectNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodesFactory.FromCharPointerNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PyObjectCheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandlePointerConverter;
@@ -104,21 +98,16 @@
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.UpdateStrongRefNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformExceptionFromNativeNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformPExceptionToNativeNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtContext;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
import com.oracle.graal.python.builtins.objects.complex.PComplex;
import com.oracle.graal.python.builtins.objects.floats.PFloat;
-import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
-import com.oracle.graal.python.builtins.objects.function.PKeyword;
import com.oracle.graal.python.builtins.objects.ints.PInt;
import com.oracle.graal.python.builtins.objects.memoryview.PMemoryView;
-import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
import com.oracle.graal.python.builtins.objects.module.ModuleGetNameNode;
import com.oracle.graal.python.builtins.objects.module.PythonModule;
import com.oracle.graal.python.builtins.objects.object.PythonObject;
@@ -135,18 +124,15 @@
import com.oracle.graal.python.lib.PyFloatAsDoubleNode;
import com.oracle.graal.python.lib.PyNumberAsSizeNode;
import com.oracle.graal.python.lib.PyObjectSizeNode;
-import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.HiddenAttr;
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PNodeWithContext;
import com.oracle.graal.python.nodes.PRaiseNode;
-import com.oracle.graal.python.nodes.PRootNode;
import com.oracle.graal.python.nodes.SpecialAttributeNames;
import com.oracle.graal.python.nodes.SpecialMethodNames;
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
-import com.oracle.graal.python.nodes.attributes.WriteAttributeToPythonObjectNode;
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode.LookupAndCallUnaryDynamicNode;
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
import com.oracle.graal.python.nodes.object.GetClassNode;
@@ -157,6 +143,7 @@
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.graal.python.runtime.exception.PException;
import com.oracle.graal.python.runtime.exception.PythonErrorType;
+import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer;
import com.oracle.graal.python.runtime.object.PFactory;
import com.oracle.graal.python.runtime.sequence.storage.MroSequenceStorage;
import com.oracle.graal.python.util.PythonUtils;
@@ -957,12 +944,46 @@ static long doOther(Node inliningTarget, Object object,
}
}
- // according to definitions in 'moduleobject.h'
+ // according to definitions in 'moduleobject.h' (for Python 3.15: defined in 'slots.toml')
private static final int SLOT_PY_MOD_CREATE = 1;
private static final int SLOT_PY_MOD_EXEC = 2;
private static final int SLOT_PY_MOD_MULTIPLE_INTERPRETERS = 3;
private static final int SLOT_PY_MOD_GIL = 4;
+ /*
+ * The following slot values are the "new" values required for abi3t.
+ * They are defined in the future Python version 3.15. Eventually, they will become the default and the above values will be legacy.
+ */
+ private static final int SLOT_ABI3T_PY_MOD_CREATE = 84;
+ private static final int SLOT_ABI3T_PY_MOD_EXEC = 85;
+ private static final int SLOT_ABI3T_PY_MOD_MULTIPLE_INTERPRETERS = 86;
+ private static final int SLOT_ABI3T_PY_MOD_GIL = 87;
+ private static final int SLOT_ABI3T_PY_MOD_NAME = 100;
+ private static final int SLOT_ABI3T_PY_MOD_DOC = 101;
+ private static final int SLOT_ABI3T_PY_MOD_STATE_SIZE = 102;
+ private static final int SLOT_ABI3T_PY_MOD_METHODS = 103;
+ private static final int SLOT_ABI3T_PY_MOD_STATE_TRAVERSE = 104;
+ private static final int SLOT_ABI3T_PY_MOD_STATE_CLEAR = 105;
+ private static final int SLOT_ABI3T_PY_MOD_STATE_FREE = 106;
+ private static final int SLOT_ABI3T_PY_MOD_ABI = 109;
+ private static final int SLOT_ABI3T_PY_MOD_TOKEN = 110;
+
+ private static final int PY_ABI_INFO_STABLE = 0x0001;
+ private static final int PY_ABI_INFO_GIL = 0x0002;
+ private static final int PY_ABI_INFO_FREETHREADED = 0x0004;
+ private static final int PY_ABI_INFO_INTERNAL = 0x0008;
+ private static final int ABI3T_VERSION_HEX = 0x030f0000;
+ private static final int MINIMUM_STABLE_ABI_VERSION_HEX = 0x03020000;
+
+ // for Py_mod_gil
+ private static final int Py_MOD_GIL_USED = 0;
+ private static final int Py_MOD_GIL_NOT_USED = 1;
+
+ // for Py_mod_multiple_interpreters
+ private static final int Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED = 0;
+ private static final int Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED = 1;
+ private static final int Py_MOD_PER_INTERPRETER_GIL_SUPPORTED = 2;
+
private static final CApiTiming TIMING_MOD_CREATE = CApiTiming.create(true, "Py_mod_create");
private static final CApiTiming TIMING_MOD_EXEC = CApiTiming.create(true, "Py_mod_exec");
@@ -985,219 +1006,351 @@ static long doOther(Node inliningTarget, Object object,
*
*/
@TruffleBoundary
- static Object createModule(Node node, CApiContext capiContext, ModuleSpec moduleSpec, long moduleDefPtr, Object library) {
+ static Object createModuleFromDefAndSpec(Node node, CApiContext capiContext, ModuleSpec moduleSpec, long moduleDefPtr) {
/*
* The name of the module is taken from the module spec and *NOT* from the module
* definition.
*/
TruffleString mName = moduleSpec.name;
- Object mDoc;
- long mSize;
- // do not eagerly read the doc string; this turned out to be unnecessarily expensive
- long docPtr = readPtrField(moduleDefPtr, PyModuleDef__m_doc);
- if (docPtr == NULLPTR) {
- mDoc = NO_VALUE;
- } else {
- mDoc = FromCharPointerNode.executeUncached(docPtr);
+ NativeModuleDefinition definition = new NativeModuleDefinition(moduleDefPtr, true);
+ definition.name = readPtrField(moduleDefPtr, PyModuleDef__m_name);
+ definition.doc = readPtrField(moduleDefPtr, PyModuleDef__m_doc);
+ definition.stateSize = readLongField(moduleDefPtr, PyModuleDef__m_size);
+
+ long slotDefinitions = readPtrField(moduleDefPtr, PyModuleDef__m_slots);
+ definition.methods = readPtrField(moduleDefPtr, PyModuleDef__m_methods);
+ definition.traverseFunction = readPtrField(moduleDefPtr, PyModuleDef__m_traverse);
+ definition.clearFunction = readPtrField(moduleDefPtr, PyModuleDef__m_clear);
+ definition.freeFunction = readPtrField(moduleDefPtr, PyModuleDef__m_free);
+ definition.token = moduleDefPtr;
+ return createModule(node, capiContext, moduleSpec,
+ parseModuleSlots(node, PySlotIterator.initLegacy(node, mName, slotDefinitions, PySlotIterator.SlotKind.MODULE), definition, mName).getDefinition());
+ }
+
+ /**
+ * Equivalent of {@code moduleobject.c: PyModule_FromSlotsAndSpec}. Creates a Python module from slots definition.
+ *
+ *
+ * struct PySlot {
+ * uint16_t sl_id;
+ * uint16_t sl_flags;
+ * _Py_ANONYMOUS union {
+ * uint32_t sl_reserved; // must be 0
+ * };
+ * _Py_ANONYMOUS union {
+ * void *sl_ptr;
+ * _Py_funcptr_t sl_func;
+ * Py_ssize_t sl_size;
+ * int64_t sl_int64;
+ * uint64_t sl_uint64;
+ * };
+ * };
+ *
+ */
+ @TruffleBoundary
+ public static Object createModuleFromSlotsAndSpec(Node node, CApiContext capiContext, long slots, ModuleSpec moduleSpec) {
+ PythonContext context = capiContext.getContext();
+ ParsedModuleSlots parsed = parseModuleSlots(node, PySlotIterator.init(node, moduleSpec.name, slots, PySlotIterator.SlotKind.MODULE), null, moduleSpec.name);
+ if (!parsed.sawAbi) {
+ throw PRaiseNode.raiseStatic(node, SystemError, ErrorMessages.MODULE_DOES_NOT_DEFINE_ABI, moduleSpec.name);
}
- mSize = readLongField(moduleDefPtr, PyModuleDef__m_size);
+ if (parsed.definition.requiresGil) {
+ // TODO(fa): enable gil
+ }
- if (mSize < 0) {
- throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.SystemError, ErrorMessages.M_SIZE_CANNOT_BE_NEGATIVE, mName);
+ // By default, multi-phase init modules are expected to work under multiple interpreters.
+ if (parsed.multipleInterpreters == Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED) {
+ if (!context.isMainInterpreter()) {
+ checkSubinterpIncompatibleExtensionAllowed(node, context, moduleSpec.name);
+ }
+ } else if (parsed.multipleInterpreters != Py_MOD_PER_INTERPRETER_GIL_SUPPORTED && context.ownsGil() && !context.isMainInterpreter()) {
+ checkSubinterpIncompatibleExtensionAllowed(node, context, moduleSpec.name);
}
- // parse slot definitions
- long createFunction = NULLPTR;
- boolean hasExecutionSlots = false;
- long slotDefinitions = readPtrField(moduleDefPtr, PyModuleDef__m_slots);
- if (slotDefinitions != NULLPTR) {
- loop: for (int i = 0;; i++) {
- int slotId = readStructArrayIntField(slotDefinitions, i, PyModuleDef_Slot__slot);
- switch (slotId) {
- case 0:
- break loop;
- case SLOT_PY_MOD_CREATE:
- if (createFunction != NULLPTR) {
- throw PRaiseNode.raiseStatic(node, SystemError, ErrorMessages.MODULE_HAS_MULTIPLE_CREATE_SLOTS, mName);
+ return createModule(node, capiContext, moduleSpec, parsed.getDefinition());
+ }
+
+ private static final class ParsedModuleSlots {
+ private final NativeModuleDefinition definition;
+ private long multipleInterpreters = Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED;
+ private boolean sawAbi;
+
+ private ParsedModuleSlots(NativeModuleDefinition definition) {
+ this.definition = definition;
+ }
+
+ private NativeModuleDefinition getDefinition() {
+ return definition;
+ }
+ }
+
+ private static final class NativeModuleDefinition {
+ private final long moduleDef;
+ private final boolean tokenIsDef;
+ private long createFunction;
+ private long execFunction;
+ private boolean hasExecSlots;
+ private long name;
+ private long stateSize;
+ private long doc;
+ private long methods;
+ private long traverseFunction;
+ private long clearFunction;
+ private long freeFunction;
+ private long token;
+ private boolean requiresGil = true;
+
+ private NativeModuleDefinition(long moduleDef, boolean tokenIsDef) {
+ this.moduleDef = moduleDef;
+ this.tokenIsDef = tokenIsDef;
+ }
+ }
+
+ private static ParsedModuleSlots parseModuleSlots(Node node, PySlotIterator iterator, NativeModuleDefinition originalDefinition, TruffleString moduleName) {
+ ParsedModuleSlots parsed = new ParsedModuleSlots(originalDefinition == null ? new NativeModuleDefinition(NULLPTR, false) : originalDefinition);
+ while (iterator.next()) {
+ PySlotIterator.Slot slot = iterator.current();
+ long value = slot.pointer();
+ switch (slot.id()) {
+ case SLOT_ABI3T_PY_MOD_CREATE:
+ parsed.definition.createFunction = value;
+ break;
+ case SLOT_ABI3T_PY_MOD_EXEC:
+ if (originalDefinition == null) {
+ if (parsed.definition.hasExecSlots) {
+ throw PRaiseNode.raiseStatic(node, SystemError, ErrorMessages.MODULE_HAS_MULTIPLE_EXEC_SLOTS, moduleName);
}
- createFunction = readStructArrayPtrField(slotDefinitions, i, PyModuleDef_Slot__value);
- break;
- case SLOT_PY_MOD_EXEC:
- hasExecutionSlots = true;
- break;
- case SLOT_PY_MOD_MULTIPLE_INTERPRETERS:
- // ignored
- // (mq) TODO: handle multiple interpreter cases
- break;
- case SLOT_PY_MOD_GIL:
- // ignored
- break;
- default:
- throw PRaiseNode.raiseStatic(node, SystemError, ErrorMessages.MODULE_USES_UNKNOW_SLOT_ID, mName, slotId);
- }
+ parsed.definition.execFunction = value;
+ }
+ parsed.definition.hasExecSlots = true;
+ break;
+ case SLOT_ABI3T_PY_MOD_MULTIPLE_INTERPRETERS:
+ parsed.multipleInterpreters = value;
+ break;
+ case SLOT_ABI3T_PY_MOD_GIL:
+ parsed.definition.requiresGil = value != Py_MOD_GIL_NOT_USED;
+ break;
+ case SLOT_ABI3T_PY_MOD_ABI:
+ checkAbiInfo(node, value, moduleName);
+ parsed.sawAbi = true;
+ break;
+ case SLOT_ABI3T_PY_MOD_TOKEN:
+ if (originalDefinition != null && originalDefinition.moduleDef != value) {
+ throw PRaiseNode.raiseStatic(node, SystemError, ErrorMessages.MODULE_ARBITRARY_TOKEN_WITH_MODULE_DEF, moduleName);
+ }
+ parsed.definition.token = value;
+ break;
+ case SLOT_ABI3T_PY_MOD_NAME:
+ checkDefinitionField(node, originalDefinition, originalDefinition == null ? 0 : originalDefinition.name, value, moduleName, "m_name");
+ parsed.definition.name = value;
+ break;
+ case SLOT_ABI3T_PY_MOD_DOC:
+ checkDefinitionField(node, originalDefinition, originalDefinition == null ? 0 : originalDefinition.doc, value, moduleName, "m_doc");
+ parsed.definition.doc = value;
+ break;
+ case SLOT_ABI3T_PY_MOD_STATE_SIZE:
+ checkDefinitionField(node, originalDefinition, originalDefinition == null ? 0 : originalDefinition.stateSize, slot.size(), moduleName, "m_size");
+ parsed.definition.stateSize = slot.size();
+ break;
+ case SLOT_ABI3T_PY_MOD_METHODS:
+ checkDefinitionField(node, originalDefinition, originalDefinition == null ? 0 : originalDefinition.methods, value, moduleName, "m_methods");
+ parsed.definition.methods = value;
+ break;
+ case SLOT_ABI3T_PY_MOD_STATE_TRAVERSE:
+ checkDefinitionField(node, originalDefinition, originalDefinition == null ? 0 : originalDefinition.traverseFunction, value, moduleName, "m_traverse");
+ parsed.definition.traverseFunction = value;
+ break;
+ case SLOT_ABI3T_PY_MOD_STATE_CLEAR:
+ checkDefinitionField(node, originalDefinition, originalDefinition == null ? 0 : originalDefinition.clearFunction, value, moduleName, "m_clear");
+ parsed.definition.clearFunction = value;
+ break;
+ case SLOT_ABI3T_PY_MOD_STATE_FREE:
+ checkDefinitionField(node, originalDefinition, originalDefinition == null ? 0 : originalDefinition.freeFunction, value, moduleName, "m_free");
+ parsed.definition.freeFunction = value;
+ break;
}
}
+ parsed.sawAbi = iterator.sawSlot(SLOT_ABI3T_PY_MOD_ABI);
+ return parsed;
+ }
+
+ private static void checkDefinitionField(Node node, NativeModuleDefinition originalDefinition, long originalValue, long newValue, TruffleString moduleName, String field) {
+ if (originalDefinition != null && originalValue != newValue) {
+ throw PRaiseNode.raiseStatic(node, SystemError, ErrorMessages.MODULE_SLOT_CONFLICTS_WITH_MODULE_DEF, moduleName, field);
+ }
+ }
+
+ private static Object createModule(Node node, CApiContext capiContext, ModuleSpec moduleSpec, NativeModuleDefinition definition) {
+
+ if (definition.stateSize < 0) {
+ throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.SystemError, ErrorMessages.M_SIZE_CANNOT_BE_NEGATIVE, moduleSpec.name);
+ }
PythonContext context = capiContext.getContext();
Object module;
- if (createFunction != NULLPTR) {
- PythonThreadState threadState = context.getThreadState(context.getLanguage());
- NativeFunctionPointer modCreate = ExternalFunctionSignature.MODCREATE.bind(context.ensureNativeContext(), createFunction);
- long result = ExternalFunctionInvoker.invokeMODCREATE(null, TIMING_MOD_CREATE, context.ensureNativeContext(),
- BoundaryCallData.getUncached(), threadState, modCreate,
- PythonToNativeInternalNode.executeUncached(moduleSpec.originalModuleSpec, false), moduleDefPtr);
- TransformExceptionFromNativeNode.getUncached().execute(null, threadState, mName, result == NULLPTR, true,
- ErrorMessages.CREATION_FAILD_WITHOUT_EXCEPTION, ErrorMessages.CREATION_RAISED_EXCEPTION);
- module = NativeToPythonInternalNode.executeUncached(result, true);
-
- /*
- * We are more strict than CPython and require this to be a PythonModule object. This
- * means, if the custom 'create' function uses a native subtype of the module type, then
- * we require it to call our new function.
- */
- if (!(module instanceof PythonModule)) {
- if (mSize > 0) {
- throw PRaiseNode.raiseStatic(node, SystemError, ErrorMessages.NOT_A_MODULE_OBJECT_BUT_REQUESTS_MODULE_STATE, mName);
- }
- if (hasExecutionSlots) {
- throw PRaiseNode.raiseStatic(node, SystemError, ErrorMessages.MODULE_SPECIFIES_EXEC_SLOTS_BUT_DIDNT_CREATE_INSTANCE, mName);
- }
- // otherwise CPython is just fine
- } else {
- ((PythonModule) module).setNativeModuleDef(moduleDefPtr);
- }
+ if (definition.createFunction != NULLPTR) {
+ module = callCreateAndCheckResult(node, moduleSpec, context, definition);
} else {
- PythonModule pythonModule = PFactory.createPythonModule(mName);
- pythonModule.setNativeModuleDef(moduleDefPtr);
- module = pythonModule;
+ module = PFactory.createPythonModule(moduleSpec.name);
+ }
+ if (module instanceof PythonModule pythonModule) {
+ initializeNativeModule(pythonModule, definition);
}
- long methodDefinitions = readPtrField(moduleDefPtr, PyModuleDef__m_methods);
- if (methodDefinitions != NULLPTR) {
- for (int i = 0;; i++) {
- PBuiltinFunction fun = createLegacyMethod(methodDefinitions, i, context.getLanguage());
- if (fun == null) {
- break;
- }
- PBuiltinMethod method = PFactory.createBuiltinMethod(context.getLanguage(), module, fun);
- WriteAttributeToPythonObjectNode.getUncached().execute(method, SpecialAttributeNames.T___MODULE__, mName);
- WriteAttributeToObjectNode.getUncached().execute(module, fun.getName(), method);
- }
+ if (definition.methods != NULLPTR) {
+ PythonCextMethodBuiltins.addMethodsToObject(context.getLanguage(), definition.methods, module, moduleSpec.name);
}
+ Object mDoc;
+ if (definition.doc == NULLPTR) {
+ mDoc = NO_VALUE;
+ } else {
+ mDoc = FromCharPointerNode.executeUncached(definition.doc);
+ }
WriteAttributeToObjectNode.getUncached().execute(module, SpecialAttributeNames.T___DOC__, mDoc);
- capiContext.addLoadedExtensionLibrary(library);
+
+ return module;
+
+ }
+
+ private static Object callCreateAndCheckResult(Node node, ModuleSpec moduleSpec, PythonContext context, NativeModuleDefinition definition) {
+ PythonThreadState threadState = context.getThreadState(context.getLanguage());
+ NativeFunctionPointer modCreate = ExternalFunctionSignature.MODCREATE.bind(context.ensureNativeContext(), definition.createFunction);
+ long result = ExternalFunctionInvoker.invokeMODCREATE(null, TIMING_MOD_CREATE, context.ensureNativeContext(),
+ BoundaryCallData.getUncached(), threadState, modCreate,
+ PythonToNativeInternalNode.executeUncached(moduleSpec.originalModuleSpec, false), definition.moduleDef);
+ TransformExceptionFromNativeNode.getUncached().execute(null, threadState, moduleSpec.name, result == NULLPTR, true,
+ ErrorMessages.CREATION_FAILD_WITHOUT_EXCEPTION, ErrorMessages.CREATION_RAISED_EXCEPTION);
+ Object module = NativeToPythonInternalNode.executeUncached(result, true);
+
+ /*
+ * We are stricter than CPython and require this to be a PythonModule object. This
+ * means, if the custom 'create' function uses a native subtype of the module type, then
+ * we require it to call our new function.
+ */
+ if (!(module instanceof PythonModule)) {
+ if (definition.stateSize > 0 || definition.traverseFunction != NULLPTR || definition.clearFunction != NULLPTR || definition.freeFunction != NULLPTR) {
+ throw PRaiseNode.raiseStatic(node, SystemError, ErrorMessages.NOT_A_MODULE_OBJECT_BUT_REQUESTS_MODULE_STATE, moduleSpec.name);
+ }
+ if (definition.hasExecSlots) {
+ throw PRaiseNode.raiseStatic(node, SystemError, ErrorMessages.MODULE_SPECIFIES_EXEC_SLOTS_BUT_DIDNT_CREATE_INSTANCE, moduleSpec.name);
+ }
+ if (!definition.tokenIsDef && definition.token != NULLPTR) {
+ throw PRaiseNode.raiseStatic(node, SystemError, ErrorMessages.MODULE_SPECIFIES_TOKEN_BUT_DIDNT_CREATE_INSTANCE, moduleSpec.name);
+ }
+ // otherwise CPython is just fine
+ }
return module;
}
+ public static int checkAbiInfo(Node node, long info, TruffleString moduleName) {
+ if (info == NULLPTR) {
+ throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.ImportError, ErrorMessages.MODULE_NULL_ABI_INFO, moduleName);
+ }
+ int majorVersion = com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readByte(info) & 0xff;
+ if (majorVersion == 0) {
+ return 0;
+ }
+ if (majorVersion > 1) {
+ throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.ImportError, ErrorMessages.MODULE_ABI_INFO_VERSION_TOO_HIGH, moduleName);
+ }
+ int flags = com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readShort(info + 2) & 0xffff;
+ long abiVersion = com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readInt(info + 8) & 0xffffffffL;
+ if ((flags & PY_ABI_INFO_INTERNAL) != 0) {
+ throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.ImportError, ErrorMessages.MODULE_INTERNAL_ABI_UNSUPPORTED, moduleName);
+ }
+ if ((flags & PY_ABI_INFO_STABLE) == 0) {
+ throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.ImportError, ErrorMessages.MODULE_NON_STABLE_ABI_UNSUPPORTED, moduleName);
+ }
+ long majorMinorMask = 0xffff0000L;
+ if (abiVersion != 0 && (abiVersion & majorMinorMask) > (ABI3T_VERSION_HEX & majorMinorMask)) {
+ throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.ImportError, ErrorMessages.MODULE_FUTURE_STABLE_ABI, moduleName,
+ (abiVersion >> 24) & 0xff, (abiVersion >> 16) & 0xff);
+ }
+ if (abiVersion != 0 && abiVersion < MINIMUM_STABLE_ABI_VERSION_HEX) {
+ throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.ImportError, ErrorMessages.MODULE_INVALID_STABLE_ABI, moduleName,
+ (abiVersion >> 24) & 0xff, (abiVersion >> 16) & 0xff);
+ }
+ int gilFlags = flags & (PY_ABI_INFO_GIL | PY_ABI_INFO_FREETHREADED);
+ if (gilFlags == PY_ABI_INFO_FREETHREADED) {
+ throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.ImportError, ErrorMessages.MODULE_ONLY_FREETHREADED, moduleName);
+ }
+ return 0;
+ }
+
+ private static void initializeNativeModule(PythonModule module, NativeModuleDefinition definition) {
+ // Equivalent to CPython's module_copy_members_from_deflike() plus token/exec setup.
+ module.setNativeModuleDef(definition.moduleDef);
+ module.setNativeModuleStateSize(definition.stateSize);
+ module.setNativeModuleTraverse(definition.traverseFunction);
+ module.setNativeModuleClear(definition.clearFunction);
+ module.setNativeModuleFree(definition.freeFunction);
+ module.setNativeModuleToken(definition.token);
+ module.setNativeModuleTokenIsDef(definition.tokenIsDef);
+ module.setNativeModuleExec(definition.moduleDef == NULLPTR ? definition.execFunction : NULLPTR);
+ module.setNativeModuleRequiresGil(definition.requiresGil);
+ }
+
+ // similar to 'import.c: _PyImport_CheckSubinterpIncompatibleExtensionAllowed'
+ private static void checkSubinterpIncompatibleExtensionAllowed(Node location, PythonContext context, TruffleString name) {
+ // similar to 'import.c: check_multi_interp_extensions'
+ if (context.getOverrideMultiInterpExtensionsCheck() > 0) {
+ assert !context.isMainInterpreter();
+ throw PRaiseNode.raiseStatic(location, PythonBuiltinClassType.ImportError,
+ ErrorMessages.MODULE_S_DOES_NOT_SUPPORT_LOADING_IN_SUBINTERPRETERS, name);
+ }
+ }
+
/**
* Equivalent of {@code PyModule_ExecDef}.
*/
@TruffleBoundary
- public static int execModule(Node node, CApiContext capiContext, PythonModule module, long moduleDef) {
+ public static int execModule(Node node, CApiContext capiContext, PythonModule module) {
TruffleString mName = ModuleGetNameNode.executeUncached(module);
- long mSize = readLongField(moduleDef, PyModuleDef__m_size);
+ long mSize = module.getNativeModuleStateSize();
// allocate md_state if necessary
- if (mSize >= 0) {
- /*
- * TODO(fa): We currently leak 'md_state' and need to use a shared finalizer or similar.
- * We ignore that for now since the size will usually be very small and/or we could also
- * use a Truffle buffer object.
- */
+ if (mSize >= 0 && module.getNativeModuleState() == NULLPTR) {
+ // TODO(fa): We currently leak 'md_state' and need to use something like a NativeStorageReference.
long mdState = calloc(mSize == 0 ? 1 : mSize); // ensure non-null value
assert mdState != NULLPTR;
module.setNativeModuleState(mdState);
}
- // parse slot definitions
+ long directExecFunction = module.getNativeModuleExec();
+ if (directExecFunction != NULLPTR) {
+ invokeModuleExec(node, capiContext, module, mName, directExecFunction);
+ return 0;
+ }
+
+ // Legacy definitions may contain multiple Py_mod_exec slots.
+ long moduleDef = module.getNativeModuleDef();
+ if (moduleDef == NULLPTR) {
+ return 0;
+ }
long slotDefinitions = readPtrField(moduleDef, PyModuleDef__m_slots);
if (slotDefinitions == NULLPTR) {
return 0;
}
- loop: for (int i = 0;; i++) {
- int slotId = readStructArrayIntField(slotDefinitions, i, PyModuleDef_Slot__slot);
- switch (slotId) {
- case 0:
- break loop;
- case SLOT_PY_MOD_CREATE:
- // handled in CreateModuleNode
- break;
- case SLOT_PY_MOD_EXEC:
- long execFunction = readStructArrayPtrField(slotDefinitions, i, PyModuleDef_Slot__value);
- PythonContext context = capiContext.getContext();
- PythonThreadState threadState = context.getThreadState(context.getLanguage());
- NativeFunctionPointer boundFunction = ExternalFunctionSignature.MODEXEC.bind(context.ensureNativeContext(), execFunction);
- int iResult = ExternalFunctionInvoker.invokeMODEXEC(null, TIMING_MOD_EXEC, context.ensureNativeContext(),
- BoundaryCallData.getUncached(), threadState, boundFunction,
- PythonToNativeInternalNode.executeUncached(module, false));
- /*
- * It's a bit counterintuitive that we use 'isPrimitiveValue = false' but the
- * function's return value is actually not a result but a status code. So, if
- * the status code is '!=0' we know that an error occurred and won't ignore this
- * if no error is set. This is then the same behaviour if we would have a
- * pointer return type and got 'NULL'.
- */
- TransformExceptionFromNativeNode.getUncached().execute(node, threadState, mName, iResult != 0, true,
- ErrorMessages.EXECUTION_FAILED_WITHOUT_EXCEPTION, ErrorMessages.EXECUTION_RAISED_EXCEPTION);
- break;
- case SLOT_PY_MOD_MULTIPLE_INTERPRETERS:
- // ignored
- // (mq) TODO: handle multiple interpreter cases
- break;
- case SLOT_PY_MOD_GIL:
- // ignored
- break;
- default:
- throw PRaiseNode.raiseStatic(node, SystemError, ErrorMessages.MODULE_INITIALIZED_WITH_UNKNOWN_SLOT, mName, slotId);
+ PySlotIterator iterator = PySlotIterator.initLegacy(node, mName, slotDefinitions, PySlotIterator.SlotKind.MODULE);
+ while (iterator.next()) {
+ if (iterator.current().id() == SLOT_ABI3T_PY_MOD_EXEC) {
+ invokeModuleExec(node, capiContext, module, mName, iterator.current().function());
}
}
return 0;
}
- /**
- * TODO(fa): overlaps with
- * {@link com.oracle.graal.python.builtins.modules.cext.PythonCextModuleBuiltins#GraalPyPrivate_Module_AddFunctions(long, long)}.
- *
- *
- * struct PyMethodDef {
- * const char * ml_name;
- * PyCFunction ml_meth;
- * int ml_flags;
- * const char * ml_doc;
- * };
- *
- */
- @TruffleBoundary
- static PBuiltinFunction createLegacyMethod(long methodDefPtr, int element, PythonLanguage language) {
- long methodNamePtr = readStructArrayPtrField(methodDefPtr, element, PyMethodDef__ml_name);
- if (methodNamePtr == NULLPTR) {
- return null;
- }
- TruffleString methodName = FromCharPointerNode.executeUncached(methodNamePtr);
- // note: 'ml_doc' may be NULL; in this case, we would store 'None'
- Object methodDoc = PNone.NONE;
- long methodDocPtr = readStructArrayPtrField(methodDefPtr, element, PyMethodDef__ml_doc);
- if (methodDocPtr != NULLPTR) {
- methodDoc = FromCharPointerNode.executeUncached(methodDocPtr);
- }
-
- int flags = readStructArrayIntField(methodDefPtr, element, PyMethodDef__ml_flags);
- long mlMethObj = readStructArrayPtrField(methodDefPtr, element, PyMethodDef__ml_meth);
- // CPy-style methods
- // TODO(fa) support static and class methods
- MethodDescriptorWrapper sig = MethodDescriptorWrapper.fromMethodFlags(flags);
- PRootNode rootNode = MethodDescriptorWrapper.getOrCreateRootNode(language, sig, methodName, CExtContext.isMethStatic(flags));
- NativeFunctionPointer fun = CExtCommonNodes.bindFunctionPointer(mlMethObj, sig);
- PKeyword[] kwDefaults = ExternalFunctionNodes.createKwDefaults(fun);
- PBuiltinFunction function = PFactory.createBuiltinFunction(language, methodName, null, PythonUtils.EMPTY_OBJECT_ARRAY, kwDefaults, flags, rootNode);
- HiddenAttr.WriteLongNode.executeUncached(function, METHOD_DEF_PTR, methodDefPtr);
-
- CFunctionDocUtils.writeDocAndTextSignature(function, methodName, methodDoc, flags);
-
- return function;
+ private static void invokeModuleExec(Node node, CApiContext capiContext, PythonModule module, TruffleString moduleName, long execFunction) {
+ PythonContext context = capiContext.getContext();
+ PythonThreadState threadState = context.getThreadState(context.getLanguage());
+ NativeFunctionPointer boundFunction = ExternalFunctionSignature.MODEXEC.bind(context.ensureNativeContext(), execFunction);
+ int result = ExternalFunctionInvoker.invokeMODEXEC(null, TIMING_MOD_EXEC, context.ensureNativeContext(), BoundaryCallData.getUncached(), threadState, boundFunction,
+ PythonToNativeInternalNode.executeUncached(module, false));
+ TransformExceptionFromNativeNode.getUncached().execute(node, threadState, moduleName, result != 0, true,
+ ErrorMessages.EXECUTION_FAILED_WITHOUT_EXCEPTION, ErrorMessages.EXECUTION_RAISED_EXCEPTION);
}
@GenerateInline
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ExternalFunctionSignature.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ExternalFunctionSignature.java
index 4a48f55c8d..53d9624e82 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ExternalFunctionSignature.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ExternalFunctionSignature.java
@@ -168,7 +168,8 @@ public enum ExternalFunctionSignature implements NativeCExtSymbol {
MODCREATE(false, PyObjectReturn, Pointer, Pointer),
// typedef int (*Py_mod_exec)(PyObject *);
MODEXEC(false, Int, Pointer),
- // typedef PyObject *(*PyInit_mod)(void);
+ // typedef PyObject *(*PyModInitFunction)(void);
+ // typedef PySlot *(*PyModExportFunction)(void);
MODINIT(false, Pointer),
// typedef PThreadState** (*initialize_graal_capi)(void *, void *, void *, void *, void *);
CAPIINIT(false, Pointer, Pointer, Pointer, Pointer, Pointer, Pointer),
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/MethodDescriptorWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/MethodDescriptorWrapper.java
index 571a88e9c6..0a12551eec 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/MethodDescriptorWrapper.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/MethodDescriptorWrapper.java
@@ -57,7 +57,6 @@
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.MethVarargsRoot;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtContext;
import com.oracle.graal.python.builtins.objects.cext.common.NativeCExtSymbol;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
import com.oracle.graal.python.builtins.objects.function.PKeyword;
@@ -173,7 +172,7 @@ public static PBuiltinFunction createWrapperFunction(PythonLanguage language, Tr
return null;
}
- PRootNode rootNode = getOrCreateRootNode(language, methodDescriptorWrapper, name, CExtContext.isMethStatic(flags));
+ PRootNode rootNode = getOrCreateRootNode(language, methodDescriptorWrapper, name, PyMethodFlags.isMethStatic(flags));
PKeyword[] kwDefaults = ExternalFunctionNodes.createKwDefaults(CExtCommonNodes.bindFunctionPointer(callable, methodDescriptorWrapper));
@@ -187,29 +186,29 @@ public static PBuiltinFunction createWrapperFunction(PythonLanguage language, Tr
/**
* See {@code PyDescr_NewMethod}
*
- * @param flags The method flags {@link CExtContext#METH_VARARGS} and others.
+ * @param flags The method flags {@link PyMethodFlags#METH_VARARGS} and others.
* @return
*/
static MethodDescriptorWrapper fromMethodFlags(int flags) {
- if (CExtContext.isMethVarargs(flags)) {
+ if (PyMethodFlags.isMethVarargs(flags)) {
return VARARGS;
}
- if (CExtContext.isMethVarargsWithKeywords(flags)) {
+ if (PyMethodFlags.isMethVarargsWithKeywords(flags)) {
return KEYWORDS;
}
- if (CExtContext.isMethFastcall(flags)) {
+ if (PyMethodFlags.isMethFastcall(flags)) {
return FASTCALL;
}
- if (CExtContext.isMethFastcallWithKeywords(flags)) {
+ if (PyMethodFlags.isMethFastcallWithKeywords(flags)) {
return FASTCALL_WITH_KEYWORDS;
}
- if (CExtContext.isMethNoArgs(flags)) {
+ if (PyMethodFlags.isMethNoArgs(flags)) {
return NOARGS;
}
- if (CExtContext.isMethO(flags)) {
+ if (PyMethodFlags.isMethO(flags)) {
return O;
}
- if (CExtContext.isMethMethod(flags)) {
+ if (PyMethodFlags.isMethMethod(flags)) {
return METHOD;
}
throw CompilerDirectives.shouldNotReachHere("illegal method flags");
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyCFunctionWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyCFunctionWrapper.java
index 6b1969a69a..2e8c666901 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyCFunctionWrapper.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyCFunctionWrapper.java
@@ -56,7 +56,6 @@
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformExceptionToNativeNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtContext;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
import com.oracle.graal.python.builtins.objects.function.PKeyword;
import com.oracle.graal.python.builtins.objects.function.Signature;
@@ -118,7 +117,7 @@ public abstract class PyCFunctionWrapper {
private final long pointer;
/**
- * Built-in functions may appear as {@link CExtContext#METH_VARARGS} etc. but we implement them
+ * Built-in functions may appear as {@link PyMethodFlags#METH_VARARGS} etc. but we implement them
* as nodes where the specializations have a fixed arity and then sometimes allow optional
* arguments (specified with {@link Builtin#minNumOfPositionalArgs()},
* {@link Builtin#maxNumOfPositionalArgs()}, and similar). This is usually not a problem because
@@ -182,13 +181,13 @@ public static PyCFunctionWrapper createFromBuiltinFunction(CApiContext cApiConte
RootCallTarget ct = builtinFunction.getCallTarget();
Signature signature = builtinFunction.getSignature();
Object[] defaults = builtinFunction.getDefaults();
- if (CExtContext.isMethNoArgs(flags)) {
+ if (PyMethodFlags.isMethNoArgs(flags)) {
return cApiContext.getOrCreatePyCFunctionWrapper(ct, k -> new PyCFunctionUnaryWrapper(k, signature, defaults));
- } else if (CExtContext.isMethO(flags)) {
+ } else if (PyMethodFlags.isMethO(flags)) {
return cApiContext.getOrCreatePyCFunctionWrapper(ct, k -> new PyCFunctionBinaryWrapper(k, signature, defaults));
- } else if (CExtContext.isMethVarargs(flags)) {
+ } else if (PyMethodFlags.isMethVarargs(flags)) {
return cApiContext.getOrCreatePyCFunctionWrapper(ct, k -> new PyCFunctionVarargsWrapper(k, signature, defaults));
- } else if (CExtContext.isMethVarargsWithKeywords(flags)) {
+ } else if (PyMethodFlags.isMethVarargsWithKeywords(flags)) {
return cApiContext.getOrCreatePyCFunctionWrapper(ct, k -> new PyCFunctionKeywordsWrapper(k, signature, defaults));
} else {
throw shouldNotReachHere("other signature " + Integer.toHexString(flags));
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyMethodFlags.java
similarity index 55%
rename from graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtContext.java
rename to graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyMethodFlags.java
index ff3fff9731..13b7cf9f2e 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtContext.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyMethodFlags.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2026, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
@@ -38,27 +38,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-package com.oracle.graal.python.builtins.objects.cext.common;
-import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError;
-import static com.oracle.graal.python.nodes.StringLiterals.T_DOT;
-import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING;
-import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
-import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached;
+package com.oracle.graal.python.builtins.objects.cext.capi;
-import com.oracle.graal.python.PythonLanguage;
-import com.oracle.graal.python.builtins.objects.exception.PBaseException;
-import com.oracle.graal.python.runtime.PythonContext;
-import com.oracle.graal.python.runtime.exception.ExceptionUtils;
-import com.oracle.graal.python.runtime.exception.PException;
-import com.oracle.graal.python.runtime.nativeaccess.NativeLibrary;
-import com.oracle.graal.python.runtime.object.PFactory;
-import com.oracle.graal.python.util.PythonUtils;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.strings.TruffleString;
-
-public abstract class CExtContext {
+public final class PyMethodFlags {
public static final int METH_VARARGS = 0x0001;
public static final int METH_KEYWORDS = 0x0002;
public static final int METH_NOARGS = 0x0004;
@@ -68,32 +51,10 @@ public abstract class CExtContext {
public static final int METH_COEXIST = 0x0040;
public static final int METH_FASTCALL = 0x0080;
public static final int METH_METHOD = 0x0200;
-
// Filter out only the base convention, without orthogonal modifiers
private static final int CALL_CONVENTION_MASK = METH_VARARGS | METH_KEYWORDS | METH_NOARGS | METH_O | METH_FASTCALL | METH_METHOD;
- private final PythonContext context;
-
- /** The library object representing 'libpython.*.so' or similar. */
- private final NativeLibrary library;
- private final String libraryName;
-
- public CExtContext(PythonContext context, NativeLibrary library, String libraryName) {
- this.context = context;
- this.library = library;
- this.libraryName = libraryName;
- }
-
- public final PythonContext getContext() {
- return context;
- }
-
- public final NativeLibrary getLibrary() {
- return library;
- }
-
- public final String getLibraryName() {
- return libraryName;
+ private PyMethodFlags() {
}
public static boolean isMethVarargs(int flags) {
@@ -131,32 +92,4 @@ public static boolean isMethStatic(int flags) {
public static boolean isMethClass(int flags) {
return (flags & METH_CLASS) != 0;
}
-
- public static boolean isClassOrStaticMethod(int flags) {
- return isMethClass(flags) || isMethStatic(flags);
- }
-
- @TruffleBoundary
- protected static TruffleString getBaseName(TruffleString name) {
- int len = TruffleString.CodePointLengthNode.getUncached().execute(name, TS_ENCODING);
- if (len == 1) {
- return name.equalsUncached(T_DOT, TS_ENCODING) ? T_EMPTY_STRING : name;
- }
- int idx = name.lastIndexOfStringUncached(T_DOT, len, 0, TS_ENCODING);
- if (idx < 0) {
- return name;
- }
- if (idx == len - 1) {
- return T_EMPTY_STRING;
- }
- return name.substringUncached(idx + 1, len - idx - 1, TS_ENCODING, true);
- }
-
- @TruffleBoundary
- public static PException wrapJavaException(Throwable e, Node raisingNode) {
- TruffleString message = toTruffleStringUncached(e.getMessage());
- PBaseException excObject = PFactory.createBaseException(PythonLanguage.get(null), SystemError, message != null ? message : toTruffleStringUncached(e.toString()),
- PythonUtils.EMPTY_OBJECT_ARRAY);
- return ExceptionUtils.wrapJavaException(e, raisingNode, excObject);
- }
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PySlotIterator.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PySlotIterator.java
new file mode 100644
index 0000000000..aac76a8655
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PySlotIterator.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.oracle.graal.python.builtins.objects.cext.capi;
+
+import static com.oracle.graal.python.builtins.PythonBuiltinClassType.DeprecationWarning;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyModuleDef_Slot__slot;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyModuleDef_Slot__value;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PySlot__sl_id;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PySlot__sl_uint64;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readStructArrayIntField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readStructArrayLongField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readStructArrayPtrField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readStructArrayShortField;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+
+import com.oracle.graal.python.builtins.PythonBuiltinClassType;
+import com.oracle.graal.python.builtins.modules.WarningsModuleBuiltins;
+import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
+import com.oracle.graal.python.nodes.ErrorMessages;
+import com.oracle.graal.python.nodes.PRaiseNode;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.strings.TruffleString;
+
+/** Java counterpart of CPython's {@code _PySlotIterator}; type slots can be added later. */
+final class PySlotIterator {
+ static final int SLOT_PY_MOD_CREATE = 1;
+ static final int SLOT_PY_MOD_EXEC = 2;
+ static final int SLOT_PY_MOD_MULTIPLE_INTERPRETERS = 3;
+ static final int SLOT_PY_MOD_GIL = 4;
+ static final int SLOT_PY_MOD_CREATE_NEW = 84;
+ static final int SLOT_PY_MOD_EXEC_NEW = 85;
+ static final int SLOT_PY_MOD_MULTIPLE_INTERPRETERS_NEW = 86;
+ static final int SLOT_PY_MOD_GIL_NEW = 87;
+ static final int SLOT_SUBSLOTS = 92;
+ static final int SLOT_PY_MOD_SLOTS = 94;
+ static final int SLOT_INVALID = 0xffff;
+ static final int SLOT_PY_MOD_NAME = 100;
+ static final int SLOT_PY_MOD_DOC = 101;
+ static final int SLOT_PY_MOD_STATE_SIZE = 102;
+ static final int SLOT_PY_MOD_METHODS = 103;
+ static final int SLOT_PY_MOD_STATE_TRAVERSE = 104;
+ static final int SLOT_PY_MOD_STATE_CLEAR = 105;
+ static final int SLOT_PY_MOD_STATE_FREE = 106;
+ static final int SLOT_PY_MOD_ABI = 109;
+ static final int SLOT_PY_MOD_TOKEN = 110;
+
+ static final int PY_SLOT_OPTIONAL = 0x0001;
+ static final int PY_SLOT_STATIC = 0x0002;
+ static final int PY_SLOT_INTPTR = 0x0004;
+ private static final int MAX_NESTING = 5;
+
+ enum SlotKind {
+ MODULE,
+ TYPE
+ }
+
+ private enum StructKind {
+ SLOT,
+ MODULE_DEF_SLOT,
+ TYPE_SLOT
+ }
+
+ static final class Slot {
+ private final int id;
+ private final int flags;
+ private final long value;
+
+ private Slot(int id, int flags, long value) {
+ this.id = id;
+ this.flags = flags;
+ this.value = value;
+ }
+
+ int id() {
+ return id;
+ }
+
+ int flags() {
+ return flags;
+ }
+
+ long pointer() {
+ return value;
+ }
+
+ long function() {
+ return value;
+ }
+
+ long size() {
+ return value;
+ }
+
+ long unsigned() {
+ return value;
+ }
+ }
+
+ private static final class State {
+ private long slots;
+ private int index;
+ private StructKind structKind;
+
+ private State(long slots, StructKind structKind) {
+ this.slots = slots;
+ this.structKind = structKind;
+ }
+ }
+
+ private final Node node;
+ private final TruffleString name;
+ private final SlotKind kind;
+ private final State[] states = new State[MAX_NESTING];
+ private final boolean[] seen = new boolean[SLOT_PY_MOD_TOKEN + 1];
+ private int level;
+ private Slot current;
+
+ private PySlotIterator(Node node, TruffleString name, long slots, SlotKind kind, StructKind structKind) {
+ this.node = node;
+ this.name = name;
+ this.kind = kind;
+ states[0] = new State(slots, structKind);
+ }
+
+ static PySlotIterator init(Node node, TruffleString name, long slots, SlotKind kind) {
+ return new PySlotIterator(node, name, slots, kind, StructKind.SLOT);
+ }
+
+ static PySlotIterator initLegacy(Node node, TruffleString name, long slots, SlotKind kind) {
+ return new PySlotIterator(node, name, slots, kind, StructKind.MODULE_DEF_SLOT);
+ }
+
+ Slot current() {
+ return current;
+ }
+
+ boolean sawSlot(int id) {
+ return id > 0 && id < seen.length && seen[id];
+ }
+
+ boolean next() {
+ while (true) {
+ State state = states[level];
+ if (state.slots == NULLPTR) {
+ if (level == 0) {
+ return false;
+ }
+ level--;
+ states[level].index++;
+ continue;
+ }
+ int originalId;
+ int flags;
+ long value;
+ if (state.structKind == StructKind.SLOT) {
+ originalId = readStructArrayShortField(state.slots, state.index, PySlot__sl_id);
+ flags = readStructArrayShortField(state.slots, state.index, CFields.PySlot__sl_flags);
+ value = readStructArrayLongField(state.slots, state.index, PySlot__sl_uint64);
+ } else if (state.structKind == StructKind.MODULE_DEF_SLOT) {
+ originalId = readStructArrayIntField(state.slots, state.index, PyModuleDef_Slot__slot);
+ flags = PY_SLOT_INTPTR;
+ value = readStructArrayPtrField(state.slots, state.index, PyModuleDef_Slot__value);
+ } else {
+ throw new UnsupportedOperationException("type slots are not supported yet");
+ }
+ int id = resolve(originalId);
+ if (id == SLOT_INVALID) {
+ if ((flags & PY_SLOT_OPTIONAL) != 0) {
+ state.index++;
+ continue;
+ }
+ throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.SystemError, ErrorMessages.MODULE_USES_UNKNOW_SLOT_ID, name, originalId);
+ }
+ if (id == 0) {
+ if ((flags & PY_SLOT_OPTIONAL) != 0) {
+ throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.SystemError, ErrorMessages.INVALID_FLAGS_FOR_SLOT_END, flags);
+ }
+ state.slots = NULLPTR;
+ continue;
+ }
+ if (id == SLOT_SUBSLOTS || id == SLOT_PY_MOD_SLOTS) {
+ if (value == NULLPTR) {
+ state.index++;
+ continue;
+ }
+ if (states[0].structKind == StructKind.MODULE_DEF_SLOT && state.structKind == StructKind.SLOT && (flags & PY_SLOT_STATIC) == 0) {
+ throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.SystemError, ErrorMessages.SLOTS_INCLUDED_FROM_MODULE_DEF_MUST_BE_STATIC);
+ }
+ if (++level >= MAX_NESTING) {
+ throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.SystemError, ErrorMessages.MODULE_SLOTS_TOO_DEEPLY_NESTED, name);
+ }
+ states[level] = new State(value, id == SLOT_SUBSLOTS ? StructKind.SLOT : StructKind.MODULE_DEF_SLOT);
+ continue;
+ }
+ current = new Slot(id, flags, value);
+ state.index++;
+ validate(state, originalId);
+ return true;
+ }
+ }
+
+ private void validate(State state, int originalId) {
+ int id = current.id;
+ if (id == SLOT_PY_MOD_METHODS && state.structKind == StructKind.SLOT && (current.flags & PY_SLOT_STATIC) == 0) {
+ throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.SystemError, ErrorMessages.MODULE_METHODS_SLOT_REQUIRES_STATIC, name);
+ }
+ if (current.value == NULLPTR && rejectsNull(id)) {
+ throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.SystemError, ErrorMessages.NULL_NOT_ALLOWED_FOR_MODULE_SLOT, name, id);
+ }
+ if (current.value == NULLPTR && id == SLOT_PY_MOD_CREATE && states[0].structKind == StructKind.SLOT) {
+ WarningsModuleBuiltins.WarnNode.getUncached().warnFormat(null, null, DeprecationWarning, 1,
+ ErrorMessages.NULL_VALUE_IN_SLOT_DEPRECATED, originalId);
+ }
+ if (id != SLOT_PY_MOD_EXEC_NEW && id != SLOT_PY_MOD_ABI && sawSlot(id)) {
+ throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.SystemError, ErrorMessages.MODULE_HAS_MULTIPLE_SLOT, name, id);
+ }
+ seen[id] = true;
+ }
+
+ private int resolve(int id) {
+ if (kind != SlotKind.MODULE) {
+ return SLOT_INVALID;
+ }
+ return switch (id) {
+ case 0, SLOT_PY_MOD_CREATE_NEW, SLOT_PY_MOD_EXEC_NEW, SLOT_PY_MOD_MULTIPLE_INTERPRETERS_NEW, SLOT_PY_MOD_GIL_NEW, SLOT_SUBSLOTS, SLOT_PY_MOD_SLOTS,
+ SLOT_PY_MOD_NAME, SLOT_PY_MOD_DOC, SLOT_PY_MOD_STATE_SIZE, SLOT_PY_MOD_METHODS, SLOT_PY_MOD_STATE_TRAVERSE, SLOT_PY_MOD_STATE_CLEAR,
+ SLOT_PY_MOD_STATE_FREE, SLOT_PY_MOD_ABI, SLOT_PY_MOD_TOKEN -> id;
+ case SLOT_PY_MOD_CREATE -> SLOT_PY_MOD_CREATE_NEW;
+ case SLOT_PY_MOD_EXEC -> SLOT_PY_MOD_EXEC_NEW;
+ case SLOT_PY_MOD_MULTIPLE_INTERPRETERS -> SLOT_PY_MOD_MULTIPLE_INTERPRETERS_NEW;
+ case SLOT_PY_MOD_GIL -> SLOT_PY_MOD_GIL_NEW;
+ default -> SLOT_INVALID;
+ };
+ }
+
+ private static boolean rejectsNull(int id) {
+ return switch (id) {
+ case SLOT_PY_MOD_EXEC_NEW, SLOT_PY_MOD_NAME, SLOT_PY_MOD_DOC, SLOT_PY_MOD_METHODS, SLOT_PY_MOD_STATE_TRAVERSE, SLOT_PY_MOD_STATE_CLEAR,
+ SLOT_PY_MOD_STATE_FREE, SLOT_PY_MOD_ABI, SLOT_PY_MOD_TOKEN -> true;
+ default -> false;
+ };
+ }
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/ArgDescriptor.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/ArgDescriptor.java
index 7ac639fcea..ff8f5cb5ab 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/ArgDescriptor.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/ArgDescriptor.java
@@ -80,6 +80,7 @@ enum ArgBehavior {
Char8(NativeSimpleType.SINT8),
UChar8(NativeSimpleType.SINT8),
Char16(NativeSimpleType.SINT16),
+ UInt16(NativeSimpleType.SINT16),
Int32(NativeSimpleType.SINT32),
UInt32(NativeSimpleType.SINT32),
Int64(NativeSimpleType.SINT64),
@@ -189,7 +190,9 @@ public enum ArgDescriptor {
FILE_PTR("FILE*"),
FREEFUNC("freefunc"),
INITTAB("struct _inittab*"),
+ UINT16_T(ArgBehavior.UInt16, "uint16_t"),
INT32_T(ArgBehavior.Int32, "int32_t"),
+ INT32_T_PTR(ArgBehavior.Pointer, "int32_t*"),
INT_LIST("int*"),
INT8_T_PTR(ArgBehavior.Pointer, "int8_t*"),
INT64_T(ArgBehavior.Int64, "int64_t"),
@@ -203,6 +206,7 @@ public enum ArgDescriptor {
Py_buffer("Py_buffer"),
PY_BUFFER_PTR(ArgBehavior.Pointer, "Py_buffer*"),
CONST_PY_BUFFER_PTR(ArgBehavior.Pointer, "const Py_buffer*"),
+ CONST_PY_SLOT_PTR(ArgBehavior.Pointer, "const PySlot*"),
PY_C_FUNCTION(ArgBehavior.Pointer, "PyCFunction"),
PyByteArrayObject(ArgBehavior.PyObject, "PyByteArrayObject*"),
PyCFunctionObject(ArgBehavior.PyObject, "PyCFunctionObject*"),
@@ -229,6 +233,9 @@ public enum ArgDescriptor {
PY_GEN_OBJECT(ArgBehavior.PyObject, "PyGenObject*"),
PyGetSetDef(ArgBehavior.Pointer, "PyGetSetDef*"),
PY_GIL_STATE_STATE(ArgBehavior.Int32, "PyGILState_STATE"),
+ PY_ABI_INFO_PTR(ArgBehavior.Pointer, "PyABIInfo*"),
+ PY_INTERPRETER_GUARD_PTR(ArgBehavior.Pointer, "PyInterpreterGuard*"),
+ PY_INTERPRETER_VIEW_PTR(ArgBehavior.Pointer, "PyInterpreterView*"),
PY_HASH_T_PTR(ArgBehavior.Pointer, "Py_hash_t*"),
PY_IDENTIFIER("_Py_Identifier*"),
PyInterpreterState(ArgBehavior.Pointer, "PyInterpreterState*"),
@@ -237,6 +244,9 @@ public enum ArgDescriptor {
PyLongObject(ArgBehavior.PyObject, "PyLongObject*"),
ConstPyLongObject(ArgBehavior.PyObject, "const PyLongObject*"),
PyLongObjectTransfer(ArgBehavior.PyObject, "PyLongObject*", true, false),
+ PY_LONG_EXPORT_PTR(ArgBehavior.Pointer, "PyLongExport*"),
+ PY_LONG_WRITER_PTR(ArgBehavior.Pointer, "PyLongWriter*"),
+ CONST_PY_LONG_LAYOUT_PTR(ArgBehavior.Pointer, "const PyLongLayout*"),
PyMemberDef(ArgBehavior.Pointer, "PyMemberDef*"),
PyModuleObject(ArgBehavior.PyObject, "PyModuleObject*"),
PyModuleObjectTransfer(ArgBehavior.PyObject, "PyModuleObject*", true, false),
@@ -262,6 +272,7 @@ public enum ArgDescriptor {
PY_STRUCT_SEQUENCE_DESC("PyStructSequence_Desc*"),
PyThreadState(ArgBehavior.Pointer, "PyThreadState*"),
PyThreadStatePtr(ArgBehavior.Pointer, "PyThreadState**"),
+ PY_THREAD_STATE_TOKEN_PTR(ArgBehavior.Pointer, "PyThreadStateToken*"),
PY_REF_TRACER(ArgBehavior.Pointer, "PyRefTracer"),
PY_THREAD_TYPE_LOCK(ArgBehavior.Int64, "PyThread_type_lock"),
PY_THREAD_TYPE_LOCK_PTR(ArgBehavior.Pointer, "PyThread_type_lock*"),
@@ -269,6 +280,8 @@ public enum ArgDescriptor {
PY_TRACEFUNC("Py_tracefunc"),
PY_TSS_T_PTR("Py_tss_t*"),
PY_TYPE_SPEC("PyType_Spec*"),
+ PY_SLOT_PTR(ArgBehavior.Pointer, "struct PySlot*"),
+ PY_TYPE_OBJECT_PTR_LIST(ArgBehavior.Pointer, "PyTypeObject**"),
PY_UCS4(ArgBehavior.Int32, "Py_UCS4"),
PY_UCS4_PTR("Py_UCS4*"),
PY_UNICODE("Py_UNICODE"),
@@ -308,6 +321,8 @@ public enum ArgDescriptor {
TIMEVAL_PTR("struct timeval*"),
TM_PTR("struct tm*"),
UINTPTR_T(ArgBehavior.UInt64, "uintptr_t"),
+ UINT32_T(ArgBehavior.UInt32, "uint32_t"),
+ UINT32_T_PTR(ArgBehavior.Pointer, "uint32_t*"),
UINT64_T(ArgBehavior.UInt64, "uint64_t"),
UINT64_T_PTR(ArgBehavior.Pointer, "uint64_t*"),
UNSIGNED_CHAR_PTR(ArgBehavior.Pointer, "unsigned char*"),
@@ -317,7 +332,7 @@ public enum ArgDescriptor {
VA_LIST(ArgBehavior.Pointer, "va_list"),
VA_LIST_PTR(ArgBehavior.Pointer, "va_list*"),
VARARGS("..."),
- VOID_PTR_LIST("void**"),
+ VOID_PTR_LIST(ArgBehavior.Pointer, "void**"),
WCHAR_T_PTR(ArgBehavior.Pointer, "wchar_t*"),
WCHAR_T_CONST_PTR(ArgBehavior.Pointer, "wchar_t*const*"),
WCHAR_T_PTR_LIST(ArgBehavior.Pointer, "wchar_t**"),
@@ -477,7 +492,7 @@ public boolean isI32() {
}
public boolean isI16() {
- return behavior == ArgBehavior.Char16;
+ return behavior == ArgBehavior.Char16 || behavior == ArgBehavior.UInt16;
}
public boolean isFloat() {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtCommonNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtCommonNodes.java
index fcd3cb5820..c1f2b61495 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtCommonNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtCommonNodes.java
@@ -57,9 +57,7 @@
import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readShortArrayElement;
import static com.oracle.graal.python.util.PythonUtils.SURROGATE_CODE_POINT_SET;
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
-import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
-import java.nio.charset.Charset;
import java.util.logging.Level;
import com.oracle.graal.python.PythonLanguage;
@@ -99,6 +97,7 @@
import com.oracle.graal.python.runtime.object.PFactory;
import com.oracle.graal.python.util.OverflowException;
import com.oracle.graal.python.util.PythonUtils;
+import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.TruffleLogger;
@@ -229,49 +228,6 @@ private static boolean isSurrogate(int codePoint) {
}
}
- public abstract static class Charsets {
- private static final int NATIVE_ORDER = 0;
- private static Charset UTF32;
- private static Charset UTF32LE;
- private static Charset UTF32BE;
-
- private static final TruffleString T_UTF_32 = tsLiteral("UTF-32");
- private static final TruffleString T_UTF_32LE = tsLiteral("UTF-32LE");
- private static final TruffleString T_UTF_32BE = tsLiteral("UTF-32BE");
-
- @TruffleBoundary
- public static Charset getUTF32Charset(int byteorder) {
- String utf32Name = getUTF32Name(byteorder).toJavaStringUncached();
- if (byteorder == NATIVE_ORDER) {
- if (UTF32 == null) {
- UTF32 = Charset.forName(utf32Name);
- }
- return UTF32;
- } else if (byteorder < NATIVE_ORDER) {
- if (UTF32LE == null) {
- UTF32LE = Charset.forName(utf32Name);
- }
- return UTF32LE;
- }
- if (UTF32BE == null) {
- UTF32BE = Charset.forName(utf32Name);
- }
- return UTF32BE;
- }
-
- public static TruffleString getUTF32Name(int byteorder) {
- TruffleString csName;
- if (byteorder == 0) {
- csName = T_UTF_32;
- } else if (byteorder < 0) {
- csName = T_UTF_32LE;
- } else {
- csName = T_UTF_32BE;
- }
- return csName;
- }
- }
-
@GenerateInline
@GenerateCached(false)
@GenerateUncached
@@ -590,7 +546,7 @@ public static GetIndexNode create() {
}
}
- private static final TruffleLogger LOGGER = CApiContext.getLogger(CExtContext.class);
+ private static final TruffleLogger LOGGER = CApiContext.getLogger(CExtCommonNodes.class);
/**
* Binds a native pointer with a signature to a typed native function pointer.
@@ -600,8 +556,8 @@ public static GetIndexNode create() {
* access} is not allowed
*
*/
- @TruffleBoundary
public static NativeFunctionPointer bindFunctionPointer(long pointer, NativeCExtSymbol descriptor) {
+ CompilerAsserts.neverPartOfCompilation();
PythonContext pythonContext = PythonContext.get(null);
if (!pythonContext.isNativeAccessAllowed()) {
LOGGER.severe(PythonUtils.formatJString("Attempting to bind %s to a native callable but native access is not allowed", pointer));
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/ReferenceStack.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/ReferenceStack.java
deleted file mode 100644
index c36dd4db70..0000000000
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/ReferenceStack.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * The Universal Permissive License (UPL), Version 1.0
- *
- * Subject to the condition set forth below, permission is hereby granted to any
- * person obtaining a copy of this software, associated documentation and/or
- * data (collectively the "Software"), free of charge and under any and all
- * copyright rights in the Software, and any and all patent rights owned or
- * freely licensable by each licensor hereunder covering either (i) the
- * unmodified Software as contributed to or provided by such licensor, or (ii)
- * the Larger Works (as defined below), to deal in both
- *
- * (a) the Software, and
- *
- * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
- * one is included with the Software each a "Larger Work" to which the Software
- * is contributed by such licensors),
- *
- * without restriction, including without limitation the rights to copy, create
- * derivative works of, display, perform, and distribute the Software and make,
- * use, sell, offer for sale, import, export, have made, and have sold the
- * Software and the Larger Work(s), and to sublicense the foregoing rights on
- * either these or other terms.
- *
- * This license is subject to the following condition:
- *
- * The above copyright notice and either this complete permission notice or at a
- * minimum a reference to the UPL must be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package com.oracle.graal.python.builtins.objects.cext.common;
-
-import java.util.Arrays;
-import java.util.Iterator;
-
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-
-public final class ReferenceStack implements Iterable {
- private static final int INITIAL_CAPACITY = 64;
-
- private final IntegerStack freeList;
- private Object[] nativeObjectWrapperList;
-
- @TruffleBoundary
- public ReferenceStack() {
- nativeObjectWrapperList = new Object[INITIAL_CAPACITY];
- freeList = new IntegerStack(INITIAL_CAPACITY);
- freeList.addToFreeList(0, INITIAL_CAPACITY);
- }
-
- @TruffleBoundary
- private void enlargeNativeReferenceList() {
- int oldSize = nativeObjectWrapperList.length;
- int newSize = oldSize * 2;
- nativeObjectWrapperList = Arrays.copyOf(nativeObjectWrapperList, newSize);
- freeList.addToFreeList(oldSize, newSize);
- }
-
- @SuppressWarnings("unchecked")
- public T get(int idx) {
- if (0 <= idx && idx < nativeObjectWrapperList.length) {
- return (T) nativeObjectWrapperList[idx];
- } else {
- assert false : "incorrect reference ID";
- }
- return null;
- }
-
- @SuppressWarnings("unchecked")
- public T remove(int idx) {
- assert 0 <= idx && idx < nativeObjectWrapperList.length;
- T ref = (T) nativeObjectWrapperList[idx];
- nativeObjectWrapperList[idx] = null;
- freeList.push(idx);
- return ref;
- }
-
- public int reserve() {
- int nativeRefID = freeList.pop();
- if (nativeRefID == -1) {
- enlargeNativeReferenceList();
- nativeRefID = freeList.pop();
- }
- assert nativeRefID != -1;
- return nativeRefID;
- }
-
- public void commit(int idx, T nativeObjectReference) {
- assert 0 <= idx && idx < nativeObjectWrapperList.length;
- assert nativeObjectWrapperList[idx] == null : "cannot overwrite an allocated native object reference slot";
- nativeObjectWrapperList[idx] = nativeObjectReference;
- }
-
- @SuppressWarnings("unchecked")
- public T resurrect(int idx, T nativeObjectReference) {
- assert 0 <= idx && idx < nativeObjectWrapperList.length;
- T old = (T) nativeObjectWrapperList[idx];
- nativeObjectWrapperList[idx] = nativeObjectReference;
- return old;
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public Iterator iterator() {
- return (Iterator) Arrays.asList(nativeObjectWrapperList).iterator();
- }
-
- static final class IntegerStack {
- private int[] stack;
- private int top = 0;
-
- public IntegerStack(int initialCapacity) {
- stack = new int[initialCapacity];
- }
-
- void push(int i) {
- if (top >= stack.length) {
- stack = Arrays.copyOf(stack, stack.length * 2);
- }
- stack[top++] = i;
- }
-
- int pop() {
- if (top <= 0) {
- return -1;
- }
- return stack[--top];
- }
-
- void addToFreeList(int start, int end) {
- for (int i = end - 1; i >= start; i--) {
- push(i);
- }
- }
- }
-}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CFields.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CFields.java
index d076b1423c..a16a9e6760 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CFields.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CFields.java
@@ -45,6 +45,7 @@
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.CharPtrAsTruffleString;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.ConstCharPtr;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Double;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.INT64_T;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.INT_LIST;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Int;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_C_FUNCTION;
@@ -66,8 +67,9 @@
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Py_hash_t;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Py_ssize_t;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UCHAR;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UINTPTR_T;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UINT16_T;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UINT64_T;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UINTPTR_T;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UNSIGNED_INT;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UNSIGNED_LONG;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.allocfunc;
@@ -141,6 +143,15 @@ public enum CFields {
PyModuleDef_Slot__slot(Int),
PyModuleDef_Slot__value(Pointer),
+ PySlot__sl_id(UINT16_T),
+ PySlot__sl_flags(UINT16_T),
+ PySlot__sl_reserved(UNSIGNED_INT),
+ PySlot__sl_ptr(Pointer),
+ PySlot__sl_func(Pointer),
+ PySlot__sl_size(Py_ssize_t),
+ PySlot__sl_int64(INT64_T),
+ PySlot__sl_uint64(UINT64_T),
+
PyMethodDef__ml_name(ConstCharPtr),
PyMethodDef__ml_meth(PY_C_FUNCTION),
PyMethodDef__ml_flags(Int),
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CStructAccess.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CStructAccess.java
index 7187290021..f2fd8314d9 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CStructAccess.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CStructAccess.java
@@ -90,6 +90,11 @@ public static void writeByteField(long structBasePtr, CFields field, byte value)
NativeMemory.writeByte(getFieldPtr(structBasePtr, field), value);
}
+ public static int readShortField(long structBasePtr, CFields field) {
+ assert field.type.isI16();
+ return NativeMemory.readShort(getFieldPtr(structBasePtr, field)) & 0xffff;
+ }
+
public static int readIntField(long structBasePtr, CFields field) {
assert field.type.isI32();
return NativeMemory.readInt(getFieldPtr(structBasePtr, field));
@@ -100,6 +105,10 @@ public static void writeIntField(long structBasePtr, CFields field, int value) {
NativeMemory.writeInt(getFieldPtr(structBasePtr, field), value);
}
+ public static int readStructArrayShortField(long arrayPtr, long index, CFields field) {
+ return readShortField(getArrayElementPtr(arrayPtr, index, field.struct), field);
+ }
+
public static int readStructArrayIntField(long arrayPtr, long index, CFields field) {
return readIntField(getArrayElementPtr(arrayPtr, index, field.struct), field);
}
@@ -154,7 +163,7 @@ public static void writeStructArrayPtrField(long arrayPtr, long index, CFields f
writePtrField(getArrayElementPtr(arrayPtr, index, field.struct), field, value);
}
- private static long getArrayElementPtr(long arrayPtr, long index, CStructs struct) {
+ public static long getArrayElementPtr(long arrayPtr, long index, CStructs struct) {
return arrayPtr + index * struct.size();
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CStructs.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CStructs.java
index a5bea19834..d95f401319 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CStructs.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CStructs.java
@@ -53,6 +53,7 @@ public enum CStructs {
PyModuleDef,
PyModuleDef_Slot,
PyMethodDef,
+ PySlot,
PyObject,
GraalPyObject,
PyBytesObject,
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.java
index a41da7f09d..a10346a095 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.java
@@ -38,7 +38,7 @@
import com.oracle.graal.python.builtins.PythonBuiltins;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtContext;
+import com.oracle.graal.python.builtins.objects.cext.capi.PyMethodFlags;
import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
import com.oracle.graal.python.builtins.objects.str.PString;
import com.oracle.graal.python.builtins.objects.str.StringUtils;
@@ -156,11 +156,11 @@ public int getFlags() {
}
public boolean isStatic() {
- return (flags & CExtContext.METH_STATIC) != 0;
+ return (flags & PyMethodFlags.METH_STATIC) != 0;
}
public boolean needsDeclaringType() {
- return (flags & CExtContext.METH_METHOD) != 0;
+ return (flags & PyMethodFlags.METH_METHOD) != 0;
}
@TruffleBoundary
@@ -170,23 +170,23 @@ public static int getFlags(Builtin builtin, Signature signature) {
}
int flags = 0;
if (builtin.isClassmethod()) {
- flags |= CExtContext.METH_CLASS;
+ flags |= PyMethodFlags.METH_CLASS;
}
if (builtin.isStaticmethod()) {
- flags |= CExtContext.METH_STATIC;
+ flags |= PyMethodFlags.METH_STATIC;
}
if (!signature.takesKeywordArgs() && !signature.takesVarArgs() && PythonBuiltins.numDefaults(builtin) == 0) {
int params = signature.getParameterIds().length;
if (params == 1) {
// only 'self'
- return flags | CExtContext.METH_NOARGS;
+ return flags | PyMethodFlags.METH_NOARGS;
} else if (params == 2) {
- return flags | CExtContext.METH_O;
+ return flags | PyMethodFlags.METH_O;
}
}
- flags |= CExtContext.METH_VARARGS;
+ flags |= PyMethodFlags.METH_VARARGS;
if (signature.takesKeywordArgs()) {
- flags |= CExtContext.METH_KEYWORDS;
+ flags |= PyMethodFlags.METH_KEYWORDS;
}
return flags;
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/NativeModuleData.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/NativeModuleData.java
new file mode 100644
index 0000000000..0728e599f7
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/NativeModuleData.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2026, 2026, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.oracle.graal.python.builtins.objects.module;
+
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+
+final class NativeModuleData {
+ /**
+ * Stores the native {@code PyModuleDef *} structure if this module was created via the
+ * multiphase extension module initialization mechanism. Will be non-null only for legacy definitions.
+ */
+ long def = NULLPTR;
+ /** {@code void *md_state} */
+ long state = NULLPTR;
+
+ /** {@code Py_ssize_t md_state_size} */
+ long stateSize;
+
+ /** {@code traverseproc md_state_traverse} */
+ long stateTraverse = NULLPTR;
+ /** {@code inquiry md_state_clear} */
+ long stateClear = NULLPTR;
+ /** {@code freefunc md_state_free} */
+ long stateFree = NULLPTR;
+
+ /** {@code _Py_modexecfunc md_exec} */
+ long exec = NULLPTR; // direct PySlot[] modules
+ /** {@code void *md_token} */
+ long token = NULLPTR;
+ /** {@code bool md_token_is_def} */
+ boolean tokenIsDef;
+ /** {@code bool md_requires_gil} */
+ boolean requiresGil;
+
+ /**
+ * Replicates the native references of this module's native state in Java.
+ *
+ * Since a module can have a native module state where it is valid to store native references to
+ * other objects, we need this field to replicate those references in Java if we make the handle
+ * table reference weak in order to break possible reference cycles. This field will ever only
+ * be set if the module's native definition provides a traverse function (see
+ * {@code moduleobject.c: module_traverse}). The condition for this is:
+ *
+ *
+ * {@code
+ * if (m -> md_def && m -> md_def -> m_traverse && (m -> md_def -> m_size <= 0 || m -> md_state != NULL)) {
+ * // ...
+ * }
+ * }
+ *
+ *
+ */
+ Object[] replicatedNativeReferences;
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java
index 962a97f1c8..3ccf91308f 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java
@@ -32,6 +32,7 @@
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___NAME__;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___PACKAGE__;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___SPEC__;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
@@ -49,32 +50,7 @@
public final class PythonModule extends PythonObject {
- /**
- * Stores the native {@code PyModuleDef *} structure if this module was created via the
- * multiphase extension module initialization mechanism.
- */
- private long nativeModuleDef;
- private long nativeModuleState;
-
- /**
- * Replicates the native references of this module's native state in Java.
- *
- * Since a module can have a native module state where it is valid to store native references to
- * other objects, we need this field to replicate those references in Java if we make the handle
- * table reference weak in order to break possible reference cycles. This field will ever only
- * be set if the module's native definition provides a traverse function (see
- * {@code moduleobject.c: module_traverse}). The condition for this is:
- *
- *
- * {@code
- * if (m -> md_def && m -> md_def -> m_traverse && (m -> md_def -> m_size <= 0 || m -> md_state != NULL)) {
- * // ...
- * }
- * }
- *
- *
- */
- private Object[] replicatedNativeReferences;
+ private NativeModuleData nativeModuleData;
private PythonBuiltins builtins;
private Object moduleState;
@@ -139,31 +115,102 @@ public String toString() {
return "";
}
+ private NativeModuleData ensureNativeModuleData() {
+ if (nativeModuleData == null) {
+ nativeModuleData = new NativeModuleData();
+ }
+ return nativeModuleData;
+ }
+
public long getNativeModuleDef() {
- return nativeModuleDef;
+ return nativeModuleData != null ? nativeModuleData.def : NULLPTR;
}
public void setNativeModuleDef(long nativeModuleDef) {
- this.nativeModuleDef = nativeModuleDef;
+ ensureNativeModuleData().def = nativeModuleDef;
}
public long getNativeModuleState() {
- return nativeModuleState;
+ return nativeModuleData != null ? nativeModuleData.state : NULLPTR;
}
public void setNativeModuleState(long nativeModuleState) {
- this.nativeModuleState = nativeModuleState;
+ ensureNativeModuleData().state = nativeModuleState;
+ }
+
+ public long getNativeModuleStateSize() {
+ return nativeModuleData != null ? nativeModuleData.stateSize : 0;
+ }
+
+ public void setNativeModuleStateSize(long nativeModuleStateSize) {
+ ensureNativeModuleData().stateSize = nativeModuleStateSize;
+ }
+
+ public long getNativeModuleTraverse() {
+ return nativeModuleData != null ? nativeModuleData.stateTraverse : NULLPTR;
+ }
+
+ public void setNativeModuleTraverse(long nativeModuleTraverse) {
+ ensureNativeModuleData().stateTraverse = nativeModuleTraverse;
+ }
+
+ public long getNativeModuleClear() {
+ return nativeModuleData != null ? nativeModuleData.stateClear : NULLPTR;
+ }
+
+ public void setNativeModuleClear(long nativeModuleClear) {
+ ensureNativeModuleData().stateClear = nativeModuleClear;
+ }
+
+ public long getNativeModuleFree() {
+ return nativeModuleData != null ? nativeModuleData.stateFree : NULLPTR;
+ }
+
+ public void setNativeModuleFree(long nativeModuleFree) {
+ ensureNativeModuleData().stateFree = nativeModuleFree;
+ }
+
+ public long getNativeModuleExec() {
+ return nativeModuleData != null ? nativeModuleData.exec : NULLPTR;
+ }
+
+ public void setNativeModuleExec(long nativeModuleExec) {
+ ensureNativeModuleData().exec = nativeModuleExec;
+ }
+
+ public long getNativeModuleToken() {
+ return nativeModuleData != null ? nativeModuleData.token : NULLPTR;
+ }
+
+ public void setNativeModuleToken(long nativeModuleToken) {
+ ensureNativeModuleData().token = nativeModuleToken;
+ }
+
+ public boolean isNativeModuleTokenIsDef() {
+ return nativeModuleData != null && nativeModuleData.tokenIsDef;
+ }
+
+ public void setNativeModuleTokenIsDef(boolean nativeModuleTokenIsDef) {
+ ensureNativeModuleData().tokenIsDef = nativeModuleTokenIsDef;
+ }
+
+ public boolean isNativeModuleRequiresGil() {
+ return nativeModuleData != null && nativeModuleData.requiresGil;
+ }
+
+ public void setNativeModuleRequiresGil(boolean nativeModuleRequiresGil) {
+ ensureNativeModuleData().requiresGil = nativeModuleRequiresGil;
}
/**
- * For a description, see {@link #replicatedNativeReferences}.
+ * For a description, see {@link NativeModuleData#replicatedNativeReferences}.
*/
public void setReplicatedNativeReferences(Object[] replicatedNativeReferences) {
- this.replicatedNativeReferences = replicatedNativeReferences;
+ ensureNativeModuleData().replicatedNativeReferences = replicatedNativeReferences;
}
public Object[] getReplicatedNativeReferences() {
- return this.replicatedNativeReferences;
+ return nativeModuleData != null ? nativeModuleData.replicatedNativeReferences : null;
}
public PDict getDict() {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java
index 2e73793ff5..348084cdef 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java
@@ -1031,6 +1031,8 @@ public abstract class ErrorMessages {
public static final TruffleString INPUT_LOST_SYS_S = tsLiteral("input(): lost sys.%s");
public static final TruffleString EOF_WHEN_READING_A_LINE = tsLiteral("EOF when reading a line");
public static final TruffleString OBJECT_READLINE_RETURNED_NON_STRING = tsLiteral("object.readline() returned non-string");
+ public static final TruffleString MODULE_EXPORT_HOOK_FAILED = tsLiteral("module export hook for module %s failed without setting an exception");
+ public static final TruffleString MODULE_EXPORT_HOOK_RAISED_EXCEPTION = tsLiteral("module export hook for module %s raised unreported exception");
// SSL errors
public static final TruffleString SSL_SESSION_CLOSED = tsLiteral("SSL/TLS session closed cleanly.");
@@ -1317,12 +1319,34 @@ public abstract class ErrorMessages {
public static final TruffleString CREATION_RAISED_EXCEPTION = tsLiteral("creation of module %s raised unreported exception");
public static final TruffleString NOT_A_MODULE_OBJECT_BUT_REQUESTS_MODULE_STATE = tsLiteral("module %s is not a module object, but requests module state");
public static final TruffleString MODULE_SPECIFIES_EXEC_SLOTS_BUT_DIDNT_CREATE_INSTANCE = tsLiteral("module %s specifies execution slots, but did not create a ModuleType instance");
+ public static final TruffleString MODULE_SPECIFIES_TOKEN_BUT_DIDNT_CREATE_INSTANCE = tsLiteral("module %s specifies token, but did not create a ModuleType instance");
+ public static final TruffleString MODULE_S_DOES_NOT_SUPPORT_LOADING_IN_SUBINTERPRETERS = tsLiteral("module %s does not support loading in subinterpreters");
public static final TruffleString EXECUTION_FAILED_WITHOUT_EXCEPTION = tsLiteral("execution of module %s failed without setting an exception");
public static final TruffleString EXECUTION_RAISED_EXCEPTION = tsLiteral("execution of module %s raised unreported exception");
public static final TruffleString MODULE_INITIALIZED_WITH_UNKNOWN_SLOT = tsLiteral("module %s initialized with unknown slot %i");
public static final TruffleString M_SIZE_CANNOT_BE_NEGATIVE = tsLiteral("module %s: m_size may not be negative for multi-phase initialization");
public static final TruffleString MODULE_HAS_MULTIPLE_CREATE_SLOTS = tsLiteral("module %s has multiple create slots");
+ public static final TruffleString MODULE_HAS_MULTIPLE_EXEC_SLOTS = tsLiteral("module %s has multiple Py_mod_exec slots");
+ public static final TruffleString MODULE_HAS_MULTIPLE_SLOT = tsLiteral("module %s has multiple slot %i entries");
public static final TruffleString MODULE_USES_UNKNOW_SLOT_ID = tsLiteral("module %s uses unknown slot ID %i");
+ public static final TruffleString MODULE_DOES_NOT_DEFINE_ABI = tsLiteral("module %s does not define Py_mod_abi, which is mandatory for modules defined from slots only");
+ public static final TruffleString MODULE_SLOTS_TOO_DEEPLY_NESTED = tsLiteral("module %s has too many levels of nested slots");
+ public static final TruffleString INVALID_FLAGS_FOR_SLOT_END = tsLiteral("invalid flags for Py_slot_end: 0x%x");
+ public static final TruffleString MODULE_METHODS_SLOT_REQUIRES_STATIC = tsLiteral("module %s: Py_mod_methods requires PySlot_STATIC");
+ public static final TruffleString NULL_NOT_ALLOWED_FOR_MODULE_SLOT = tsLiteral("module %s: NULL not allowed for slot %i");
+ public static final TruffleString NULL_VALUE_IN_SLOT_DEPRECATED = tsLiteral("NULL value in slot %i is deprecated");
+ public static final TruffleString SLOTS_INCLUDED_FROM_MODULE_DEF_MUST_BE_STATIC = tsLiteral("slots included from PyModuleDef must be static");
+ public static final TruffleString MODULE_ARBITRARY_TOKEN_WITH_MODULE_DEF = tsLiteral("module %s: arbitrary Py_mod_token not allowed with PyModuleDef");
+ public static final TruffleString MODULE_SLOT_CONFLICTS_WITH_MODULE_DEF = tsLiteral("module %s: slot conflicts with PyModuleDef.%s");
+ public static final TruffleString MODULE_NULL_ABI_INFO = tsLiteral("%s: NULL PyABIInfo");
+ public static final TruffleString MODULE_ABI_INFO_VERSION_TOO_HIGH = tsLiteral("%s: PyABIInfo version too high");
+ public static final TruffleString MODULE_INTERNAL_ABI_UNSUPPORTED = tsLiteral("%s: internal CPython ABI is not supported by GraalPy");
+ public static final TruffleString MODULE_NON_STABLE_ABI_UNSUPPORTED = tsLiteral("%s: non-stable CPython ABI is not supported by GraalPy");
+ public static final TruffleString MODULE_FUTURE_STABLE_ABI = tsLiteral("%s: incompatible future stable ABI version (%d.%d)");
+ public static final TruffleString MODULE_INVALID_STABLE_ABI = tsLiteral("%s: invalid stable ABI version (%d.%d)");
+ public static final TruffleString MODULE_ONLY_FREETHREADED = tsLiteral("%s: only compatible with free-threaded CPython");
+ public static final TruffleString PYMODULE_FROMSLOTS_NULL_SLOTS = tsLiteral("PyModule_FromSlotsAndSpec called with NULL slots");
+ public static final TruffleString EXPECTED_MODULE_GOT_T = tsLiteral("expected module, got %T");
public static final TruffleString CANNOT_DELETE = tsLiteral("cannot delete attribute");
public static final TruffleString MV_UNDERLYING_BUF_ISNT_WRITABLE = tsLiteral("memoryview: underlying buffer is not writable");
public static final TruffleString MV_UNDERLYING_BUF_ISNT_C_CONTIGUOUS = tsLiteral("memoryview: underlying buffer is not C-contiguous");
@@ -1636,4 +1660,5 @@ public abstract class ErrorMessages {
public static final TruffleString S_MUST_BE_CALLABLE = tsLiteral("%s must be callable");
public static final TruffleString METHOD_CANNOT_BE_BOTH_CLASS_AND_STATIC = tsLiteral("method cannot be both class and static");
+ public static final TruffleString CANNOT_BE_USED_IN_MAIN_INTERP = tsLiteral("_imp._override_multi_interp_extensions_check() cannot be used in the main interpreter");
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/StringLiterals.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/StringLiterals.java
index a13d0082df..e30f53eafa 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/StringLiterals.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/StringLiterals.java
@@ -114,6 +114,10 @@ public abstract class StringLiterals {
public static final String J_TYPE_ID = "_typeid";
public static final TruffleString T_TYPE_ID = tsLiteral(J_TYPE_ID);
public static final String J_LIB_PREFIX = "lib";
+ public static final String J_ABI3T_MULTIARCH = ".abi3t-";
+ public static final TruffleString T_ABI3T_MULTIARCH = tsLiteral(J_ABI3T_MULTIARCH);
+ public static final String J_ABI3T_EXT_SO = ".abi3t.so";
+ public static final TruffleString T_ABI3T_EXT_SO = tsLiteral(J_ABI3T_EXT_SO);
public static final String J_EXT_SO = ".so";
public static final TruffleString T_EXT_SO = tsLiteral(J_EXT_SO);
public static final String J_EXT_PYD = ".pyd";
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java
index df0b4c4c53..5dda431df8 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java
@@ -837,6 +837,13 @@ public Thread getOwner() {
// Used by CPython tests to selectively enable or disable frozen modules.
private TriState overrideFrozenModules = TriState.UNDEFINED;
+ /**
+ * Per-interpreter override for checking legacy extension modules.
+ * Subinterpreters are not supported yet, but keeping this state on the context makes the
+ * eventual implementation independent for each interpreter.
+ */
+ private int overrideMultiInterpExtensionsCheck;
+
// the full module name for package imports
private TruffleString pyPackageContext;
@@ -907,6 +914,19 @@ public void setOverrideFrozenModules(TriState overrideFrozenModules) {
this.overrideFrozenModules = overrideFrozenModules;
}
+ public int getOverrideMultiInterpExtensionsCheck() {
+ return overrideMultiInterpExtensionsCheck;
+ }
+
+ public void setOverrideMultiInterpExtensionsCheck(int override) {
+ this.overrideMultiInterpExtensionsCheck = override;
+ }
+
+ /** GraalPy does not support subinterpreters yet, so every context is currently the main one. */
+ public boolean isMainInterpreter() {
+ return true;
+ }
+
@TruffleBoundary
private static int computeMinIntBitLengthOverLimit(int limit) {
/*
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java
index f65751169a..962f9298e7 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java
@@ -385,6 +385,9 @@ private PythonOptions() {
@EngineOption @Option(category = OptionCategory.USER, usageSyntax = "true|false", help = "Emulate some Jython features that can cause performance degradation", stability = OptionStability.STABLE) //
public static final OptionKey EmulateJython = new OptionKey<>(false);
+ @Option(category = OptionCategory.EXPERT, usageSyntax = "true|false", help = "Enable Python abi3t compatibility") //
+ public static final OptionKey EnableAbi3t = new OptionKey<>(false);
+
@EngineOption @Option(category = OptionCategory.EXPERT, usageSyntax = "true|false", help = "Enable tracing of native memory (ATTENTION: this will have significant impact on CExt execution performance).") //
public static final OptionKey TraceNativeMemory = new OptionKey<>(false);
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java
index 4565d027cc..7a453e8dbd 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java
@@ -92,8 +92,8 @@
import com.oracle.graal.python.builtins.objects.capsule.PyCapsule;
import com.oracle.graal.python.builtins.objects.cell.PCell;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.PyMethodFlags;
import com.oracle.graal.python.builtins.objects.cext.capi.TruffleObjectNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtContext;
import com.oracle.graal.python.builtins.objects.code.PCode;
import com.oracle.graal.python.builtins.objects.common.DynamicObjectStorage;
import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage;
@@ -541,7 +541,7 @@ public static PBuiltinFunction createWrapperDescriptor(PythonLanguage language,
}
public static PBuiltinMethod createNewWrapper(PythonLanguage language, Object type, Object[] defaults, PKeyword[] kwdefaults, PRootNode rootNode, TpSlot slot) {
- PBuiltinFunction func = createWrapperDescriptor(language, T___NEW__, type, defaults, kwdefaults, CExtContext.METH_VARARGS | CExtContext.METH_KEYWORDS, rootNode, slot,
+ PBuiltinFunction func = createWrapperDescriptor(language, T___NEW__, type, defaults, kwdefaults, PyMethodFlags.METH_VARARGS | PyMethodFlags.METH_KEYWORDS, rootNode, slot,
PExternalFunctionWrapper.NEW);
return createBuiltinMethod(language, type, func);
}
diff --git a/graalpython/lib-graalpython/patches/pip-26.1.2.patch b/graalpython/lib-graalpython/patches/pip-26.1.2.patch
index 379ee0600d..dc352e9e53 100644
--- a/graalpython/lib-graalpython/patches/pip-26.1.2.patch
+++ b/graalpython/lib-graalpython/patches/pip-26.1.2.patch
@@ -222,3 +222,44 @@ index 251600e..2c1b1a7 100644
if self.version:
params.append(self.version)
path = os.path.join(base[0], *params) # noqa: PTH118
+
+diff --git a/pip/_vendor/packaging/tags.py b/pip/_vendor/packaging/tags.py
+--- a/pip/_vendor/packaging/tags.py
++++ b/pip/_vendor/packaging/tags.py
+@@ -844,0 +845,21 @@
++def _graalpy_abi3t_tags(
++ python_version: PythonVersion | None = None,
++ platforms: Iterable[str] | None = None,
++) -> Iterator[Tag]:
++ try:
++ import __graalpython__
++ except ImportError:
++ return
++ if not getattr(__graalpython__, "abi3t_enabled", False):
++ return
++ if python_version is None:
++ python_version = sys.version_info[:2]
++ if len(python_version) < 2 or tuple(python_version) < (3, 2):
++ return
++ platforms = list(platforms or platform_tags())
++ for minor_version in range(python_version[1], 1, -1):
++ interpreter = f"cp{_version_nodot((python_version[0], minor_version))}"
++ for platform_ in platforms:
++ yield Tag(interpreter, "abi3t", platform_)
++
++
+@@ -878,0 +900 @@
++ yield from _graalpy_abi3t_tags()
+diff --git a/pip/_internal/utils/compatibility_tags.py b/pip/_internal/utils/compatibility_tags.py
+--- a/pip/_internal/utils/compatibility_tags.py
++++ b/pip/_internal/utils/compatibility_tags.py
+@@ -9,0 +10 @@
++ _graalpy_abi3t_tags,
+@@ -192,0 +194,7 @@
++ if impl is None and (abis is None or "abi3t" in abis):
++ supported.extend(
++ _graalpy_abi3t_tags(
++ python_version=python_version,
++ platforms=platforms,
++ )
++ )
diff --git a/graalpython/lib-python/3/ensurepip/_bundled/pip-26.1.2-py3-none-any.whl b/graalpython/lib-python/3/ensurepip/_bundled/pip-26.1.2-py3-none-any.whl
index 7e4a6c3df1..f29bce08fd 100644
Binary files a/graalpython/lib-python/3/ensurepip/_bundled/pip-26.1.2-py3-none-any.whl and b/graalpython/lib-python/3/ensurepip/_bundled/pip-26.1.2-py3-none-any.whl differ
diff --git a/mx.graalpython/copyrights/overrides b/mx.graalpython/copyrights/overrides
index 80ac8a214d..556e94fda8 100644
--- a/mx.graalpython/copyrights/overrides
+++ b/mx.graalpython/copyrights/overrides
@@ -260,6 +260,7 @@ graalpython/com.oracle.graal.python.cext/include/pytypedefs.h,python.copyright
graalpython/com.oracle.graal.python.cext/include/rangeobject.h,python.copyright
graalpython/com.oracle.graal.python.cext/include/setobject.h,python.copyright
graalpython/com.oracle.graal.python.cext/include/sliceobject.h,python.copyright
+graalpython/com.oracle.graal.python.cext/include/slots.h,python.copyright
graalpython/com.oracle.graal.python.cext/include/structmember.h,python.copyright
graalpython/com.oracle.graal.python.cext/include/structseq.h,python.copyright
graalpython/com.oracle.graal.python.cext/include/sysmodule.h,python.copyright
diff --git a/mx.graalpython/mx_graalpython.py b/mx.graalpython/mx_graalpython.py
index f7f45052ee..79fa732f32 100644
--- a/mx.graalpython/mx_graalpython.py
+++ b/mx.graalpython/mx_graalpython.py
@@ -2000,6 +2000,19 @@ def graalpython_gate_runner(_, tasks):
parallel=6,
)
+ with Task('GraalPython ABI3T unittests', tasks, tags=[GraalPythonTags.unittest]) as task:
+ if task:
+ env = os.environ.copy()
+ env['GRAALPY_TEST_ABI3T'] = 'true'
+ run_python_unittests(
+ graalpy_standalone_jvm(),
+ paths=['graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_abi3t.py'],
+ env=env,
+ nonZeroIsFatal=nonZeroIsFatal,
+ report=report(),
+ parallel=0,
+ )
+
with Task('GraalPython BouncyCastle unittests', tasks, tags=[GraalPythonTags.unittest_bouncycastle]) as task:
if task:
bc_unit_tests = [
@@ -3372,6 +3385,122 @@ def abi_check(_args):
], nonZeroIsFatal=True)
+def _read_stable_abi_symbols(path):
+ symbols = {}
+ with open(path, encoding='utf-8') as file:
+ for line_number, line in enumerate(file, 1):
+ line = line.strip()
+ if not line or line.startswith('#'):
+ continue
+ fields = line.split()
+ if len(fields) != 2 or fields[0] not in ('FUNC', 'OBJECT'):
+ mx.abort(f"Invalid stable ABI manifest entry at {path}:{line_number}: {line}")
+ kind, name = fields
+ if name in symbols:
+ mx.abort(f"Duplicate stable ABI symbol at {path}:{line_number}: {name}")
+ symbols[name] = kind
+ if list(symbols) != sorted(symbols):
+ mx.abort(f"Stable ABI symbols are not sorted by name: {path}")
+ return symbols
+
+
+def _read_elf_dynamic_exports(shared_library):
+ output = mx.OutputCapture()
+ run(['readelf', '--dyn-syms', '--wide', shared_library], out=output)
+ exports = {}
+ for line in output.data.splitlines():
+ fields = line.split()
+ if len(fields) < 8 or not fields[0].rstrip(':').isdigit():
+ continue
+ kind, binding, visibility, section, name = fields[3:8]
+ if section == 'UND' or binding not in ('GLOBAL', 'WEAK') or visibility not in ('DEFAULT', 'PROTECTED'):
+ continue
+ name = name.split('@', 1)[0]
+ # GNU indirect functions have function semantics for ABI purposes.
+ if kind == 'IFUNC':
+ kind = 'FUNC'
+ exports.setdefault(name, set()).add(kind)
+ return exports
+
+
+def stable_abi_check(raw_args, default_profile=None):
+ parser = ArgumentParser(prog='mx stable-abi-check')
+ parser.add_argument('--profile', default=default_profile, help='ABI profile from capi-abis.toml')
+ parser.add_argument('--library', help='libpython-native.so to inspect; builds a JVM standalone by default')
+ parser.add_argument('--update', action='store_true', help='update the checked-in missing-symbol report')
+ args = parser.parse_args(raw_args)
+
+ if not args.profile:
+ mx.abort("Specify --profile")
+ # Use mx's bundled TOML parser through its public proxy so this also works with Python < 3.11 without an external dependency.
+ import mx_codeowners
+ config_path = os.path.join(SUITE.dir, 'capi-abis.toml')
+ with open(config_path, encoding='utf-8') as file:
+ config = mx_codeowners.stoml_parse_rules(file.read())
+ profiles = {key.partition('.')[2]: value for key, value in config.items() if key.startswith('profiles.')}
+ profile = profiles.get(args.profile)
+ if profile is None or profile.get('mode') != 'stable':
+ mx.abort(f"{args.profile} is not a stable ABI profile in {config_path}")
+
+ if not shutil.which('readelf'):
+ mx.abort("Required tool 'readelf' was not found on PATH")
+
+ if args.library:
+ shared_library = os.path.abspath(args.library)
+ else:
+ standalone_home = graalpy_standalone_home('jvm', dev=True, build=True)
+ shared_library = os.path.join(
+ standalone_home,
+ 'lib',
+ f'graalpy{graal_version_short("major_minor")}',
+ 'libpython-native.so',
+ )
+ if not os.path.exists(shared_library):
+ mx.abort(f"Could not find shared library to check: {shared_library}")
+
+ manifest = os.path.join(SUITE.dir, profile['export_manifest'])
+ report = os.path.join(SUITE.dir, profile['missing_symbols'])
+ if not os.path.exists(manifest):
+ mx.abort(f"Could not find {args.profile} export manifest: {manifest}")
+
+ expected = _read_stable_abi_symbols(manifest)
+ exports = _read_elf_dynamic_exports(shared_library)
+ gaps = {}
+ wrong_kinds = []
+ for name, expected_kind in expected.items():
+ actual_kinds = exports.get(name)
+ if not actual_kinds or expected_kind not in actual_kinds:
+ gaps[name] = expected_kind
+ if actual_kinds:
+ wrong_kinds.append((name, expected_kind, sorted(actual_kinds)))
+
+ generated_report = ''.join(f'{kind} {name}\n' for name, kind in gaps.items())
+ if args.update:
+ with open(report, 'w', encoding='utf-8') as file:
+ file.write(generated_report)
+ mx.log(f"Updated {args.profile} missing-symbol report: {report}")
+ else:
+ if not os.path.exists(report):
+ mx.abort(f"Could not find {args.profile} missing-symbol report: {report}; run mx stable-abi-check --profile {args.profile} --update")
+ with open(report, encoding='utf-8') as file:
+ checked_in_report = file.read()
+ if checked_in_report != generated_report:
+ mx.abort(
+ f"{args.profile} missing-symbol report is out of date: {report}\n"
+ f"Run mx stable-abi-check --profile {args.profile} --update and commit the result."
+ )
+
+ for index, (name, expected_kind, actual_kinds) in enumerate(wrong_kinds, 1):
+ mx.warn(f"{index}. {name}: expected {expected_kind}, found {', '.join(actual_kinds)}")
+ satisfied = len(expected) - len(gaps)
+ mx.log(f"{args.profile} exports satisfied: {satisfied}/{len(expected)}; remaining gaps: {len(gaps)}")
+
+
+def abi3t_check(raw_args):
+ """Compatibility alias for the profile-driven stable ABI export check."""
+ stable_abi_check(raw_args, default_profile='ABI3T')
+
+
class GraalpythonBuildTask(mx.ProjectBuildTask):
class PrefixingOutput():
def __init__(self, prefix, printfunc):
@@ -3860,6 +3989,8 @@ def update_github_unittest_tags(args):
'python-src-import': [mx_graalpython_import.import_python_sources, ''],
'python-coverage': [python_coverage, ''],
'abi-check': [abi_check, ''],
+ 'stable-abi-check': [stable_abi_check, '--profile PROFILE [--library LIBPYTHON_NATIVE_SO] [--update]'],
+ 'abi3t-check': [abi3t_check, '[--library LIBPYTHON_NATIVE_SO] [--update]'],
'punittest': [punittest, ''],
'graalpytest': [graalpytest, '[-h] [--python PYTHON] [TESTS]'],
'clean': [python_clean, '[--just-pyc]'],
diff --git a/scripts/csignature.py b/scripts/csignature.py
index 44aa1a8de0..3c202f038f 100644
--- a/scripts/csignature.py
+++ b/scripts/csignature.py
@@ -1,4 +1,5 @@
-# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+#!/usr/bin/env python3
+# Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# The Universal Permissive License (UPL), Version 1.0
@@ -37,33 +38,73 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
-# This script parses gcc output on the embedded source and generates a list of
-# C API functions that can be stored in CAPIFunctions.txt to automatically
-# check the consistency of the C API implementation in GraalPy.
-#
-# Steps to update CAPI functions list:
-# 1. Install GNU Binutils (2.43.1).
-# 2. Build the desired version of CPython.
-# 3. Create and activate a venv using the CPython binary you built above.
-# 4. do `pip install pycparser pycparser-fake-libc`
-# 5. Finally:
-# Just run it in this folder e.g python csignature.py > ../graalpython/com.oracle.graal.python.cext/CAPIFunctions.txt
-#
-# Make sure to use the exact same CPython version as we are targeting
+"""Generate C API signature inventories and stable-ABI export manifests.
+
+ABI definitions live in a TOML configuration file. Local CPython builds are
+supplied on the command line because they are intentionally not checked in.
+"""
+
+from __future__ import annotations
+import argparse
+from dataclasses import dataclass
+import json
import os
+from pathlib import Path
import re
import subprocess
-import sysconfig
import tempfile
-import pycparser_fake_libc
-from pycparser import c_ast, parse_file, c_generator
+try:
+ import tomllib
+except ModuleNotFoundError:
+ from pip._vendor import tomli as tomllib # pylint: disable=no-name-in-module
+
+
+HELP = """\
+Pass the ABI profile configuration with --config. Each selected profile needs
+a local CPython source-tree build mapped in this form:
+
+ PROFILE=CPYTHON_REPOSITORY
+
+CPYTHON_REPOSITORY is a configured and built CPython Git checkout. The script
+uses its python executable and queries it for the version, include directories,
+and library location. The checkout must contain the source_ref configured for
+the profile. A full profile also requires a shared libpython; a stable profile
+reads symbol membership from Misc/stable_abi.toml at its configured source_ref.
+
+The script requires pycparser and pycparser-fake-libc. One way to install them
+without modifying the current environment is:
+
+ python3 -m venv /tmp/graalpy-csignature
+ /tmp/graalpy-csignature/bin/pip install pycparser pycparser-fake-libc
+
+Regenerate the GraalPy 3.13 and ABI3T inventories and configured manifests:
-source = """\
+ /tmp/graalpy-csignature/bin/python scripts/csignature.py \\
+ --config capi-abis.toml \\
+ --abi GRAALPY=/path/to/cpython-3.13 \\
+ --abi ABI3T=/path/to/cpython-3.15
+
+Pass only one --abi to regenerate just that profile. Pass --check to verify
+that every selected output is current without modifying files:
+
+ /tmp/graalpy-csignature/bin/python scripts/csignature.py \\
+ --config capi-abis.toml \\
+ --abi ABI3T=/path/to/cpython-3.15 \\
+ --check
+
+The script adds each CPython build directory to LD_LIBRARY_PATH while querying
+its executable, so in-tree shared libpython builds do not need manual loader
+configuration.
+
+Output paths, preprocessing definitions, CPython versions, pinned source refs,
+and platform exclusions are all defined by the selected TOML file. Relative
+output paths are resolved from the directory containing that file.
+"""
+HEADER_SOURCE = """\
#define __attribute__(x)
#define _POSIX_THREADS
-
#include
#include
#include
@@ -71,48 +112,191 @@
#include
"""
-include_path = sysconfig.get_config_var("INCLUDEPY")
-with tempfile.NamedTemporaryFile('w') as f:
- f.write(source)
- f.flush()
- cpp_args = ['-I', pycparser_fake_libc.directory, '-I', include_path]
- ast = parse_file(f.name, use_cpp=True, cpp_args=cpp_args)
-lib_path = os.path.join(sysconfig.get_config_var('LIBDIR'), sysconfig.get_config_var('LDLIBRARY'))
-out = subprocess.check_output(['nm', '--defined-only', '--dynamic', '--format=just-symbols', lib_path], text=True)
-exported_symbols = out.rstrip().splitlines()
+@dataclass(frozen=True)
+class Profile:
+ name: str
+ parent: str | None
+ python_version: str
+ source_ref: str
+ mode: str
+ defines: tuple[str, ...]
+ inventory: Path
+ manifest: Path | None
+ disabled_features: frozenset[str]
+
+
+@dataclass(frozen=True)
+class LocalCPython:
+ repository: Path
+ includes: tuple[Path, ...]
+ library: Path | None
+
+
+def load_profiles(config: Path) -> dict[str, Profile]:
+ config = config.resolve()
+ values = tomllib.loads(config.read_text(encoding="utf-8"))["profiles"]
+ root = config.parent
+ profiles = {name: Profile(name, value.get("parent"), value["python_version"], value["source_ref"], value["mode"],
+ tuple(value.get("defines", [])), root / value["inventory"],
+ root / value["export_manifest"] if "export_manifest" in value else None,
+ frozenset(value.get("disabled_features", [])))
+ for name, value in values.items()}
+ for profile in profiles.values():
+ if profile.mode not in ("full", "stable"):
+ raise ValueError(f"{profile.name}: unknown mode {profile.mode!r}")
+ if profile.parent and profile.parent not in profiles:
+ raise ValueError(f"{profile.name}: unknown parent {profile.parent!r}")
+ return profiles
+
+def parse_mapping(value: str) -> tuple[str, Path]:
+ try:
+ name, repository = value.split("=", 1)
+ except ValueError as exc:
+ raise argparse.ArgumentTypeError("expected PROFILE=CPYTHON_REPOSITORY") from exc
+ return name, Path(repository).resolve()
-def cleanup(str):
- for i in range(4):
- str = re.sub(" \\*", "*", str)
- str = re.sub("\\* ", "*", str)
- return str
+def local_cpython(repository: Path, profile: Profile) -> LocalCPython:
+ executable = repository / "python"
+ if not executable.is_file():
+ raise ValueError(f"{profile.name}: CPython checkout is not built (missing {executable})")
+ code = "import json,sys,sysconfig;print(json.dumps({'version':f'{sys.version_info.major}.{sys.version_info.minor}','include':sysconfig.get_path('include'),'confinclude':sysconfig.get_config_var('CONFINCLUDEPY'),'srcdir':sysconfig.get_config_var('srcdir'),'libdir':sysconfig.get_config_var('LIBDIR'),'library':sysconfig.get_config_var('LDLIBRARY')}))"
+ env = os.environ.copy()
+ env["LD_LIBRARY_PATH"] = os.pathsep.join(filter(None, (str(repository), env.get("LD_LIBRARY_PATH"))))
+ data = json.loads(subprocess.check_output([str(executable), "-c", code], text=True, env=env))
+ if data["version"] != profile.python_version:
+ raise ValueError(f"{profile.name}: expected CPython {profile.python_version}, got {data['version']} from {executable}")
+ library = Path(data["libdir"] or "") / (data["library"] or "")
+ # A CPython build run from its source tree can retain its installation
+ # prefix in sysconfig even though the freshly built library is beside it.
+ if not library.is_file() and data["library"]:
+ library = executable.parent / data["library"]
+ if not data["include"]:
+ raise ValueError(f"{profile.name}: CPython build lacks headers: {executable}")
+ if profile.mode == "full" and not library.is_file():
+ raise ValueError(f"{profile.name}: CPython build lacks the shared library required for full-export generation: {executable}")
+ includes = tuple(Path(path) for path in (data["include"], data.get("confinclude"), data.get("srcdir")) if path)
+ return LocalCPython(repository, includes, library if library.is_file() else None)
-class FuncDeclVisitor(c_ast.NodeVisitor):
- def __init__(self):
- self.gen = c_generator.CGenerator()
- self.results = []
- def visit_Decl(self, node):
- if isinstance(node.type, c_ast.FuncDecl):
- if node.name not in exported_symbols:
+def git_show(repository: Path, ref: str, filename: str) -> str:
+ return subprocess.check_output(["git", "-C", str(repository), "show", f"{ref}:{filename}"], text=True)
+
+
+def stable_abi(local: LocalCPython, profile: Profile) -> dict:
+ return tomllib.loads(git_show(local.repository, profile.source_ref, "Misc/stable_abi.toml"))
+
+
+def enabled(properties: dict, profile: Profile) -> bool:
+ return properties.get("ifdef") not in profile.disabled_features
+
+
+def cleanup(value: str) -> str:
+ for _ in range(4):
+ value = re.sub(" \\*", "*", value)
+ value = re.sub("\\* ", "*", value)
+ return value
+
+
+def full_members(local: LocalCPython) -> set[str]:
+ assert local.library is not None
+ output = subprocess.check_output(["nm", "--defined-only", "--dynamic", "--format=just-symbols", str(local.library)], text=True)
+ return set(output.splitlines())
+
+
+def members(profile: Profile, local: LocalCPython) -> set[str]:
+ if profile.mode == "full":
+ return full_members(local)
+ return {name for name, properties in stable_abi(local, profile)["function"].items() if enabled(properties, profile)}
+
+
+def inventory(profile: Profile, local: LocalCPython) -> str:
+ try:
+ import pycparser_fake_libc
+ from pycparser import c_ast, c_generator, parse_file
+ except ModuleNotFoundError as exc:
+ raise SystemExit("missing parser dependency; install pycparser and pycparser-fake-libc (see --help)") from exc
+
+ class FunctionVisitor(c_ast.NodeVisitor):
+ def __init__(self, function_members: set[str]):
+ self.members = function_members
+ self.generator = c_generator.CGenerator()
+ self.results: list[str] = []
+
+ def visit_Decl(self, node): # pylint: disable=invalid-name
+ if not isinstance(node.type, c_ast.FuncDecl) or node.name not in self.members:
return
- ret = cleanup(self.gen.visit(node.type.type))
- for p in node.type.args.params:
- # erase parameter names
- if not isinstance(p, c_ast.EllipsisParam):
- t = p.type
- while isinstance(t, (c_ast.PtrDecl, c_ast.FuncDecl, c_ast.ArrayDecl)):
- t = t.type
- t.declname = None
- args = [cleanup(self.gen.visit(p)) for p in node.type.args.params]
- args = "|".join(args)
- self.results.append(f"{node.name};{ret};{args}")
-
-
-v = FuncDeclVisitor()
-v.visit(ast)
-for line in sorted(v.results):
- print(line)
+ params = node.type.args.params if node.type.args else []
+ for param in params:
+ if not isinstance(param, c_ast.EllipsisParam):
+ typ = param.type
+ while isinstance(typ, (c_ast.PtrDecl, c_ast.FuncDecl, c_ast.ArrayDecl)):
+ typ = typ.type
+ typ.declname = None
+ result = f"{node.name};{cleanup(self.generator.visit(node.type.type))};"
+ self.results.append(result + "|".join(cleanup(self.generator.visit(param)) for param in params))
+
+ with tempfile.NamedTemporaryFile("w", suffix=".c") as source:
+ source.write(HEADER_SOURCE)
+ source.flush()
+ args = ["-I", pycparser_fake_libc.directory]
+ for include in local.includes:
+ args.extend(("-I", str(include)))
+ args.extend(f"-D{definition}" for definition in profile.defines)
+ ast = parse_file(source.name, use_cpp=True, cpp_args=args)
+ visitor = FunctionVisitor(members(profile, local))
+ visitor.visit(ast)
+ return "\n".join(sorted(set(visitor.results))) + "\n"
+
+
+def manifest(profile: Profile, local: LocalCPython) -> str:
+ if profile.mode != "stable" or profile.manifest is None:
+ raise ValueError(f"{profile.name} has no stable export manifest")
+ commit = subprocess.check_output(["git", "-C", str(local.repository), "rev-parse", f"{profile.source_ref}^{{commit}}"], text=True).strip()
+ symbols = []
+ for item_kind, elf_kind in (("function", "FUNC"), ("data", "OBJECT")):
+ symbols.extend((name, elf_kind) for name, properties in stable_abi(local, profile).get(item_kind, {}).items() if enabled(properties, profile))
+ lines = ["# Generated by scripts/csignature.py. Do not edit manually.",
+ f"# Profile: {profile.name}; source: CPython {profile.source_ref} ({commit})",
+ "# Source file: Misc/stable_abi.toml",
+ f"# Disabled features: {', '.join(sorted(profile.disabled_features)) or 'none'}",
+ "# Format: ", ""]
+ lines.extend(f"{kind} {name}" for name, kind in sorted(symbols))
+ return "\n".join(lines) + "\n"
+
+
+def write_or_check(path: Path, contents: str, check: bool) -> None:
+ if check:
+ if not path.is_file() or path.read_text(encoding="utf-8") != contents:
+ raise SystemExit(f"out of date: {path}")
+ else:
+ path.parent.mkdir(parents=True, exist_ok=True)
+ path.write_text(contents, encoding="utf-8")
+
+
+def main() -> None:
+ parser = argparse.ArgumentParser(description=__doc__, epilog=HELP, formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument("--config", required=True, type=Path, metavar="TOML", help="ABI profile configuration file")
+ parser.add_argument("--abi", action="append", type=parse_mapping, metavar="PROFILE=CPYTHON_REPOSITORY",
+ help="select an ABI profile and map its CPython source-tree build; repeat to process multiple profiles")
+ parser.add_argument("--check", action="store_true", help="verify selected outputs without modifying them")
+ parser.add_argument("--manifest-only", action="store_true", help="skip inventories and process only configured stable export manifests")
+ args = parser.parse_args()
+ profiles = load_profiles(args.config)
+ if not args.abi:
+ parser.error("at least one --abi mapping is required")
+ for name, repository in args.abi:
+ if name not in profiles:
+ parser.error(f"unknown profile: {name}")
+ profile = profiles[name]
+ local = local_cpython(repository, profile)
+ if not args.manifest_only:
+ write_or_check(profile.inventory, inventory(profile, local), args.check)
+ if profile.manifest:
+ write_or_check(profile.manifest, manifest(profile, local), args.check)
+
+
+if __name__ == "__main__":
+ main()