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 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()