@@ -338,15 +338,23 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_iter_next(JSObjectProxy
338338}
339339
340340PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_repr (JSObjectProxy *self) {
341- Py_ssize_t i = Py_ReprEnter ((PyObject *)self);
341+ // Detect cyclic objects
342+ PyObject *objPtr = PyLong_FromVoidPtr (self->jsObject ->get ());
343+ // For `Py_ReprEnter`, we must get a same PyObject when visiting the same JSObject.
344+ // We cannot simply use the object returned by `PyLong_FromVoidPtr` because it won't reuse the PyLongObjects for ints not between -5 and 256.
345+ // Instead, we store this PyLongObject in a global dict, using itself as the hashable key, effectively interning the PyLongObject.
346+ PyObject *tsDict = PyThreadState_GetDict ();
347+ PyObject *cyclicKey = PyDict_SetDefault (tsDict, /* key*/ objPtr, /* value*/ objPtr); // cyclicKey = (tsDict[objPtr] ??= objPtr)
348+ int i = Py_ReprEnter (cyclicKey);
342349 if (i != 0 ) {
343350 return i > 0 ? PyUnicode_FromString (" {...}" ) : NULL ;
344351 }
345352
346353 Py_ssize_t selfLength = JSObjectProxy_length (self);
347354
348355 if (selfLength == 0 ) {
349- Py_ReprLeave ((PyObject *)self);
356+ Py_ReprLeave (cyclicKey);
357+ PyDict_DelItem (tsDict, cyclicKey);
350358 return PyUnicode_FromString (" {}" );
351359 }
352360
@@ -417,15 +425,24 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_repr(JSObjectProxy *self
417425 value = pyTypeFactory (GLOBAL_CX, elementVal);
418426 }
419427
420- s = PyObject_Repr (value);
421- if (s == NULL ) {
422- goto error;
423- }
428+ if (value != NULL ) {
429+ s = PyObject_Repr (value);
430+ if (s == NULL ) {
431+ goto error;
432+ }
424433
425- res = _PyUnicodeWriter_WriteStr (&writer, s);
426- Py_DECREF (s);
427- if (res < 0 ) {
428- goto error;
434+ res = _PyUnicodeWriter_WriteStr (&writer, s);
435+ Py_DECREF (s);
436+ if (res < 0 ) {
437+ goto error;
438+ }
439+ } else {
440+ // clear any exception that was just set
441+ PyErr_Clear ();
442+
443+ if (_PyUnicodeWriter_WriteASCIIString (&writer, " <cannot repr type>" , 19 ) < 0 ) {
444+ goto error;
445+ }
429446 }
430447
431448 Py_CLEAR (key);
@@ -437,11 +454,13 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_repr(JSObjectProxy *self
437454 goto error;
438455 }
439456
440- Py_ReprLeave ((PyObject *)self);
457+ Py_ReprLeave (cyclicKey);
458+ PyDict_DelItem (tsDict, cyclicKey);
441459 return _PyUnicodeWriter_Finish (&writer);
442460
443461error:
444- Py_ReprLeave ((PyObject *)self);
462+ Py_ReprLeave (cyclicKey);
463+ PyDict_DelItem (tsDict, cyclicKey);
445464 _PyUnicodeWriter_Dealloc (&writer);
446465 Py_XDECREF (key);
447466 Py_XDECREF (value);
0 commit comments