diff --git a/graalpython/com.oracle.graal.python.cext/include/cpython/pystate.h b/graalpython/com.oracle.graal.python.cext/include/cpython/pystate.h index 3e65731bee..71d357f0cd 100644 --- a/graalpython/com.oracle.graal.python.cext/include/cpython/pystate.h +++ b/graalpython/com.oracle.graal.python.cext/include/cpython/pystate.h @@ -64,6 +64,7 @@ typedef struct { PyObject *tuple_empty; PyObject *bytes_empty; PyObject **bytes_characters; + PyObject **unicode_characters; } GraalPySingletons; typedef struct _stack_chunk { diff --git a/graalpython/com.oracle.graal.python.cext/src/capi.h b/graalpython/com.oracle.graal.python.cext/src/capi.h index 74e25b12cf..e789c315ac 100644 --- a/graalpython/com.oracle.graal.python.cext/src/capi.h +++ b/graalpython/com.oracle.graal.python.cext/src/capi.h @@ -174,7 +174,9 @@ extern Py_LOCAL_SYMBOL uint32_t Py_Truffle_Options; extern THREAD_LOCAL Py_LOCAL_SYMBOL PyThreadState *tstate_current; static inline void graalpy_initialize_thread_state_singletons(PyThreadState *tstate) { - if (tstate == NULL || GraalPyPrivate_Tuple_Empty == NULL || GraalPyPrivate_Bytes_Empty == NULL || GraalPyPrivate_Bytes_FromStringAndSize == NULL) { + if (tstate == NULL || GraalPyPrivate_Tuple_Empty == NULL || + GraalPyPrivate_Bytes_Empty == NULL || + GraalPyPrivate_InitCharacterSingletons == NULL) { return; } if (tstate->singletons.tuple_empty == NULL) { @@ -183,16 +185,12 @@ static inline void graalpy_initialize_thread_state_singletons(PyThreadState *tst if (tstate->singletons.bytes_empty == NULL) { tstate->singletons.bytes_empty = GraalPyPrivate_Bytes_Empty(); } - if (tstate->singletons.bytes_characters != NULL && tstate->singletons.bytes_characters[0] == NULL) { - for (int i = 0; i < 256; i++) { - char ch = (char)i; - tstate->singletons.bytes_characters[i] = GraalPyPrivate_Bytes_FromStringAndSize(&ch, 1); - if (tstate->singletons.bytes_characters[i] == NULL) { - Py_FatalError("failed to initialize GraalPy one-byte bytes singleton"); - } - } + if (tstate->singletons.bytes_characters == NULL || tstate->singletons.unicode_characters == NULL) { + GraalPyPrivate_InitCharacterSingletons(&tstate->singletons.bytes_characters, &tstate->singletons.unicode_characters); } - if (tstate->singletons.tuple_empty == NULL || tstate->singletons.bytes_empty == NULL || tstate->singletons.bytes_characters == NULL || tstate->singletons.bytes_characters[0] == NULL) { + if (tstate->singletons.tuple_empty == NULL || tstate->singletons.bytes_empty == NULL || + tstate->singletons.bytes_characters == NULL || tstate->singletons.bytes_characters[0] == NULL || + tstate->singletons.unicode_characters == NULL || tstate->singletons.unicode_characters[0] == NULL) { Py_FatalError("failed to initialize GraalPy thread-state singletons"); } } diff --git a/graalpython/com.oracle.graal.python.cext/src/unicodeobject.c b/graalpython/com.oracle.graal.python.cext/src/unicodeobject.c index 862a820ea2..cc2a9d05ec 100644 --- a/graalpython/com.oracle.graal.python.cext/src/unicodeobject.c +++ b/graalpython/com.oracle.graal.python.cext/src/unicodeobject.c @@ -529,10 +529,8 @@ const unsigned char _Py_ascii_whitespace[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; -#if 0 // GraalPy change /* forward */ static PyObject* get_latin1_char(unsigned char ch); -#endif // GraalPy change static int unicode_modifiable(PyObject *unicode); @@ -2020,12 +2018,12 @@ unicode_write_cstr(PyObject *unicode, Py_ssize_t index, Py_UNREACHABLE(); } } +#endif // GraalPy change static PyObject* get_latin1_char(Py_UCS1 ch) { - PyObject *o = LATIN1(ch); - return o; + return Py_NewRef(PyThreadState_Get()->singletons.unicode_characters[ch]); } static PyObject* @@ -2038,7 +2036,10 @@ unicode_char(Py_UCS4 ch) if (ch < 256) { return get_latin1_char(ch); } + // GraalPy change + return GraalPyPrivate_Unicode_FromOrdinal(ch); +#if 0 // GraalPy change unicode = PyUnicode_New(1, ch); if (unicode == NULL) return NULL; @@ -2052,8 +2053,10 @@ unicode_char(Py_UCS4 ch) } assert(_PyUnicode_CheckConsistency(unicode, 1)); return unicode; +#endif // GraalPy change } +#if 0 // GraalPy change PyObject * PyUnicode_FromWideChar(const wchar_t *u, Py_ssize_t size) { @@ -3321,6 +3324,7 @@ _PyUnicode_WideCharString_Opt_Converter(PyObject *obj, void *ptr) Py_TYPE(obj)->tp_name); return 0; } +#endif // GraalPy change PyObject * PyUnicode_FromOrdinal(int ordinal) @@ -3334,6 +3338,7 @@ PyUnicode_FromOrdinal(int ordinal) return unicode_char((Py_UCS4)ordinal); } +#if 0 // GraalPy change PyObject * PyUnicode_FromObject(PyObject *obj) { diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_unicode.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_unicode.py index 9e4c83321a..958d9c4937 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_unicode.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_unicode.py @@ -1218,6 +1218,109 @@ class TestPyUnicode(CPyExtTestCase): class TestUnicodeObject(unittest.TestCase): + def test_fromordinal_latin1_cache(self): + TestOrdinalCache = CPyExtType( + "TestOrdinalCache", + r''' + static PyObject* ordinal_value(PyObject* self, PyObject* ordinal_obj) { + long ordinal = PyLong_AsLong(ordinal_obj); + if (ordinal == -1 && PyErr_Occurred()) { + return NULL; + } + return PyUnicode_FromOrdinal((int)ordinal); + } + + static PyObject* ordinal_pointer(PyObject* self, PyObject* ordinal_obj) { + PyObject* value = ordinal_value(self, ordinal_obj); + if (value == NULL) { + return NULL; + } + PyObject* result = PyLong_FromVoidPtr(value); + Py_DECREF(value); + return result; + } + + static PyObject* interned_a_pointer(PyObject* self, PyObject* unused) { + PyObject* value = PyUnicode_InternFromString("A"); + if (value == NULL) { + return NULL; + } + PyObject* result = PyLong_FromVoidPtr(value); + Py_DECREF(value); + return result; + } + ''', + tp_methods=''' + {"ordinal_value", (PyCFunction)ordinal_value, METH_O, ""}, + {"ordinal_pointer", (PyCFunction)ordinal_pointer, METH_O, ""}, + {"interned_a_pointer", (PyCFunction)interned_a_pointer, METH_NOARGS, ""} + ''', + ) + tester = TestOrdinalCache() + pointers = [tester.ordinal_pointer(i) for i in range(256)] + assert len(set(pointers)) == 256 + assert tester.interned_a_pointer() == pointers[ord("A")] + for i, pointer in enumerate(pointers): + assert tester.ordinal_value(i) == chr(i) + assert tester.ordinal_pointer(i) == pointer + for _ in range(10): + assert tester.ordinal_pointer(i) == pointer + if GRAALPYTHON: + TestOrdinalMetadata = CPyExtType( + "TestOrdinalMetadata", + r''' + static PyObject* ordinal_metadata(PyObject* self, PyObject* ordinal_obj) { + long ordinal = PyLong_AsLong(ordinal_obj); + if (ordinal == -1 && PyErr_Occurred()) { + return NULL; + } + PyObject* value = PyUnicode_FromOrdinal((int)ordinal); + if (value == NULL) { + return NULL; + } + (void)PyUnicode_DATA(value); + Py_hash_t hash = PyObject_Hash(value); + if (hash == -1) { + Py_DECREF(value); + return NULL; + } + PyObject* result = Py_BuildValue("(nnn)", + (Py_ssize_t)Py_REFCNT(value), + (Py_ssize_t)PyUnicode_CHECK_INTERNED(value), + (Py_ssize_t)hash); + Py_DECREF(value); + return result; + } + ''', + tp_methods=''' + {"ordinal_metadata", (PyCFunction)ordinal_metadata, METH_O, ""} + ''', + ) + metadata_tester = TestOrdinalMetadata() + for i in range(256): + refcnt, interned, object_hash = metadata_tester.ordinal_metadata(i) + assert refcnt == 0xFFFFFFFF + assert interned != 0 + assert object_hash == hash(chr(i)) + + non_latin1_pointer = tester.ordinal_pointer(256) + assert non_latin1_pointer not in pointers + with self.assertRaises(ValueError): + tester.ordinal_value(-1) + with self.assertRaises(ValueError): + tester.ordinal_value(0x110000) + + import threading + worker_pointer = [] + + def get_worker_pointer(): + worker_pointer.append(tester.ordinal_pointer(42)) + + worker = threading.Thread(target=get_worker_pointer) + worker.start() + worker.join() + assert worker_pointer == [pointers[42]] + def test_intern(self): TestIntern = CPyExtType( "TestIntern", 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 971153959a..ef89e1b0d5 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 @@ -167,6 +167,7 @@ import com.oracle.graal.python.runtime.exception.ExceptionUtils; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.exception.PythonErrorType; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; import com.oracle.graal.python.runtime.nativeaccess.NativeSignature; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.runtime.sequence.storage.NativeByteSequenceStorage; @@ -1513,6 +1514,21 @@ static void GraalPyPrivate_InitBuiltinTypesAndStructs(long builtinTypesArrayPoin } } + /** + * This must be called after native {@code _PyGC_InitState}. Creating the native wrappers for + * these singletons may recursively create GC-tracked native wrappers, which are immediately + * linked into generation 0 and therefore require initialized generation list heads. + */ + @CApiBuiltin(ret = Void, args = {Pointer, Pointer}, call = Ignored) + static void GraalPyPrivate_InitCharacterSingletons(long bytesCharactersAddr, long unicodeCharactersAddr) { + CApiContext cApiContext = PythonContext.get(null).getCApiContext(); + assert cApiContext.getGCState() != NULLPTR; + long bytesCharacters = cApiContext.getOrCreateBytesCharacters(); + long unicodeCharacters = cApiContext.getOrCreateUnicodeCharacters(); + NativeMemory.writePtr(bytesCharactersAddr, bytesCharacters); + NativeMemory.writePtr(unicodeCharactersAddr, unicodeCharacters); + } + private record ClassPtrPair(PythonManagedClass clazz, long ptr, int typeLookupTableIdx) { } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextUnicodeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextUnicodeBuiltins.java index 1a37b78917..5cd0f27dc1 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextUnicodeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextUnicodeBuiltins.java @@ -220,12 +220,15 @@ static boolean isAnyString(Node inliningTarget, Object obj, GetClassNode getClas return PGuards.isString(obj) || isStringSubtype(inliningTarget, obj, getClassNode, isSubtypeNode); } - @CApiBuiltin(ret = PyObjectTransfer, args = {Int}, call = Direct) - abstract static class PyUnicode_FromOrdinal extends CApiUnaryBuiltinNode { + @CApiBuiltin(ret = PyObjectTransfer, args = {PY_UCS4}, call = Ignored) + abstract static class GraalPyPrivate_Unicode_FromOrdinal extends CApiUnaryBuiltinNode { @Specialization static Object chr(int value, - @Cached ChrNode chrNode) { - return chrNode.execute(null, value); + @Cached TruffleString.FromCodePointNode fromCodePointNode) { + // assertions are guaranteed by the native caller + assert value >= 0; + assert value <= Character.MAX_CODE_POINT; + return fromCodePointNode.execute(value, TS_ENCODING, true); } } @@ -1460,6 +1463,7 @@ static long doGeneric(PString stringObject, int charSize, boolean isAscii, Truff long taggedPointer = stringObject.getNativePointer(); assert HandlePointerConverter.pointsToPyHandleSpace(taggedPointer); long rawPointer = HandlePointerConverter.pointerToStub(taggedPointer); + long hash = CStructAccess.readLongField(rawPointer, CFields.GraalPyUnicodeObject__hash); long data = NativeMemory.malloc(dataSize); // unicode object may have been interned already @@ -1470,6 +1474,10 @@ static long doGeneric(PString stringObject, int charSize, boolean isAscii, Truff assert !GraalPyUnicodeObjectUtil.isCompact(rawPointer); GraalPyUnicodeObjectUtil.initializeGraalPyUnicodeObject(rawPointer, data, byteLength / charSize, byteLength, charSize, isAscii, interned, false); + if (hash != -1) { + // Preserve hashes precomputed for context-local immortal singletons. + writeLongField(rawPointer, CFields.GraalPyUnicodeObject__hash, hash); + } writeTruffleStringNode.write(data, unicodeString, unicodeEncoding); return data; } 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 e9389454b3..ed6689f364 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 @@ -54,6 +54,7 @@ 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.runtime.nativeaccess.NativeMemory.free; 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; @@ -87,6 +88,7 @@ import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltinExecutable; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PythonAbstractObject; +import com.oracle.graal.python.builtins.objects.bytes.PBytes; import com.oracle.graal.python.builtins.objects.capsule.PyCapsule; import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.PyObjectCheckFunctionResultNodeGen; import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming; @@ -96,6 +98,7 @@ import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandlePointerConverter; 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.GraalPyUnicodeObjectUtil; import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ReferenceQueueCoordinator; import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformExceptionFromNativeNode; import com.oracle.graal.python.builtins.objects.cext.common.LoadCExtException.ApiInitException; @@ -113,6 +116,7 @@ import com.oracle.graal.python.builtins.objects.str.PString; import com.oracle.graal.python.builtins.objects.str.StringNodes; import com.oracle.graal.python.builtins.objects.str.StringUtils; +import com.oracle.graal.python.lib.PyObjectHashNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.object.GetClassNode; @@ -203,6 +207,11 @@ public final class CApiContext { /** corresponds to {@code unicodeobject.c: interned} */ private final ConcurrentWeakSet pstringInterningCache = new ConcurrentWeakSet<>(); + private static final int CHARACTER_SINGLETON_LENGTH = 256; + /** Context-local equivalents of CPython's immortal one-byte bytes singletons. */ + private long bytesCharacters = NULLPTR; + /** Context-local equivalents of CPython's immortal Latin-1 Unicode singletons. */ + private long unicodeCharacters = NULLPTR; private final ArrayList modulesByIndex = new ArrayList<>(0); public Object timezoneType; @@ -386,6 +395,53 @@ public ConcurrentWeakSet getPstringInterningCache() { return pstringInterningCache; } + public long getOrCreateBytesCharacters() { + CompilerAsserts.neverPartOfCompilation(); + if (bytesCharacters != NULLPTR) { + return bytesCharacters; + } + + assert context.getCApiState() == CApiState.INITIALIZING; + assert context.getcApiInitializationLock().isHeldByCurrentThread(); + + long ptrArray = NativeMemory.callocPtrArray(CHARACTER_SINGLETON_LENGTH); + for (int i = 0; i < CHARACTER_SINGLETON_LENGTH; i++) { + PBytes bytes = PFactory.createBytes(getContext().getLanguage(), new byte[]{(byte) i}); + long pointer = FirstToNativeNode.executeUncached(bytes, IMMORTAL_REFCNT); + bytes.setNativePointer(pointer); + NativeMemory.writePtrArrayElement(ptrArray, i, pointer); + } + bytesCharacters = ptrArray; + return ptrArray; + } + + public long getOrCreateUnicodeCharacters() { + CompilerAsserts.neverPartOfCompilation(); + if (unicodeCharacters != NULLPTR) { + return unicodeCharacters; + } + + assert context.getCApiState() == CApiState.INITIALIZING; + assert context.getcApiInitializationLock().isHeldByCurrentThread(); + + long ptrArray = NativeMemory.callocPtrArray(CHARACTER_SINGLETON_LENGTH); + for (int i = 0; i < CHARACTER_SINGLETON_LENGTH; i++) { + TruffleString value = PythonUtils.internString(TruffleString.fromCodePointUncached(i, PythonUtils.TS_ENCODING)); + PString string = PFactory.createString(getContext().getLanguage(), value); + PString interned = pstringInterningCache.intern(string, s -> s); + assert interned == string; + long pointer = FirstToNativeNode.executeUncached(string, IMMORTAL_REFCNT); + string.setNativePointer(pointer); + long rawPointer = HandlePointerConverter.pointerToStub(pointer); + CStructAccess.writeLongField(rawPointer, CFields.GraalPyUnicodeObject__hash, PyObjectHashNode.executeUncached(string)); + // Keep kind == 0 and data == NULL so Unicode data is materialized only on demand. + GraalPyUnicodeObjectUtil.setInterned(rawPointer, GraalPyUnicodeObjectUtil.GRAALPY_UNICODE_INTERN_STATE_INTERNED); + NativeMemory.writePtrArrayElement(ptrArray, i, pointer); + } + unicodeCharacters = ptrArray; + return ptrArray; + } + @ExplodeLoop(kind = LoopExplosionKind.FULL_UNROLL_UNTIL_RETURN) static int getSingletonNativeWrapperIdx(Object obj) { for (int i = 0; i < CONTEXT_INSENSITIVE_SINGLETONS.length; i++) { @@ -451,6 +507,13 @@ private void freeSingletonNativeWrappers(HandleContext handleContext) { } } + private void freeSingletonArrays() { + free(bytesCharacters); + free(unicodeCharacters); + bytesCharacters = NULLPTR; + unicodeCharacters = NULLPTR; + } + /** * Allocates the {@code GCState} which needs to happen very early in the C API initialization * phase. Very early means it needs to happen before the first object (that takes part @@ -1299,11 +1362,12 @@ public void finalizeCApi(boolean cancelling) { // before the generic native stub cleanup below frees the stubs. context.clearNativeThreadStateSingletons(); // Now we can clear all native memory that was simply allocated from Java. This - // must be done after the the singleton wrappers were cleared because they might + // must be done after the singleton wrappers were cleared because they might // also end up in the lookup table and may otherwise be double-freed. CApiTransitions.freeNativeObjectStubs(handleContext); CApiTransitions.freeNativeReplacementStructs(context, handleContext); CApiTransitions.freeNativeStorages(handleContext); + freeSingletonArrays(); } if (pyDateTimeCAPICapsule != null) { PyDateTimeCAPIWrapper.destroyWrapper(pyDateTimeCAPICapsule); 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 b476b7c003..45cb28d0a7 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 @@ -574,6 +574,7 @@ public final class CApiFunction { @CApiBuiltin(name = "PyUnicode_FromFormat", ret = PyObject, args = {ConstCharPtrAsTruffleString, VARARGS}, call = CImpl) @CApiBuiltin(name = "PyUnicode_FromFormatV", ret = PyObject, args = {ConstCharPtrAsTruffleString, VA_LIST}, call = CImpl) @CApiBuiltin(name = "PyUnicode_FromKindAndData", ret = PyObject, args = {Int, CONST_VOID_PTR, Py_ssize_t}, call = CImpl) + @CApiBuiltin(name = "PyUnicode_FromOrdinal", ret = PyObject, args = {Int}, call = CImpl) @CApiBuiltin(name = "PyUnicode_FromStringAndSize", ret = PyObject, args = {ConstCharPtrAsTruffleString, Py_ssize_t}, call = CImpl) @CApiBuiltin(name = "PyUnicode_GetLength", ret = Py_ssize_t, args = {PyObject}, call = CImpl) @CApiBuiltin(name = "PyUnicode_InternInPlace", ret = Void, args = {PyObjectPtr}, call = CImpl) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PThreadState.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PThreadState.java index fda55ba29e..7a5037fd3b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PThreadState.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PThreadState.java @@ -146,12 +146,16 @@ private static long allocateCLayout() { CStructAccess.writePtrField(ptr, CFields.PyThreadState__dict, NULLPTR); CApiContext cApiContext = pythonContext.getCApiContext(); long smallInts = mallocPtrArray(PY_NSMALLNEGINTS + PY_NSMALLPOSINTS); - long bytesCharacters = callocPtrArray(256); long deallocatingState = CStructAccess.getFieldPtr(ptr, CFields.PyThreadState__graalpy_deallocating); long deallocating = mallocPtrArray(GRAALPY_DEALLOC_STACK_INITIAL_CAPACITY); long singletons = CStructAccess.getFieldPtr(ptr, CFields.PyThreadState__singletons); CStructAccess.writePtrField(ptr, CFields.PyThreadState__small_ints, smallInts); - CStructAccess.writePtrField(singletons, CFields.GraalPySingletons__bytes_characters, bytesCharacters); + /* + * The character singleton arrays are context-owned. Their pointers are installed when + * this thread state is made current, after native GC and type initialization are ready. + */ + CStructAccess.writePtrField(singletons, CFields.GraalPySingletons__bytes_characters, NULLPTR); + CStructAccess.writePtrField(singletons, CFields.GraalPySingletons__unicode_characters, NULLPTR); CStructAccess.writePtrField(deallocatingState, CFields.GraalPyDeallocState__items, deallocating); for (int i = -PY_NSMALLNEGINTS; i < PY_NSMALLPOSINTS; i++) { writePtrArrayElement(smallInts, i + PY_NSMALLNEGINTS, CApiTransitions.HandlePointerConverter.intToPointer(i)); @@ -176,14 +180,8 @@ public static void clearSingletons(PythonThreadState threadState) { return; } long singletons = CStructAccess.getFieldPtr(nativeCompanion, CFields.PyThreadState__singletons); - long bytesCharacters = CStructAccess.readPtrField(singletons, CFields.GraalPySingletons__bytes_characters); CStructAccess.writePtrField(singletons, CFields.GraalPySingletons__tuple_empty, NULLPTR); CStructAccess.writePtrField(singletons, CFields.GraalPySingletons__bytes_empty, NULLPTR); - assert bytesCharacters != NULLPTR; - // Drop singleton roots before the handle table frees the corresponding stubs. - for (int i = 0; i < 256; i++) { - writePtrArrayElement(bytesCharacters, i, NULLPTR); - } } public static int growDeallocatingStack(long nativeThreadState, long newCapacity) { @@ -236,9 +234,6 @@ public static void dispose(PythonThreadState threadState, boolean markShuttingDo NativeMemory.free(deallocatingItems); } long singletons = CStructAccess.getFieldPtr(nativeCompanion, CFields.PyThreadState__singletons); - long bytesCharacters = CStructAccess.readPtrField(singletons, CFields.GraalPySingletons__bytes_characters); - assert bytesCharacters != NULLPTR; - NativeMemory.free(bytesCharacters); // TODO(fa): decref PyThreadState__dict LOGGER.fine(String.format("Freeing (PyThreadState *)0x%x", nativeCompanion)); 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 a16a9e6760..bb86f66887 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 @@ -381,6 +381,7 @@ public enum CFields { GraalPySingletons__tuple_empty(PyObject), GraalPySingletons__bytes_empty(PyObject), GraalPySingletons__bytes_characters(PyObjectPtr), + GraalPySingletons__unicode_characters(PyObjectPtr), GraalPyDeallocState__items(PyObjectPtr), GraalPyDeallocState__len(Int),