Skip to content

Commit d0f237f

Browse files
Merge pull request #382 from Distributive-Network/philippe/379-fix
Properly print/repr Promises/Futures and structural cycles at the JS level
2 parents 63be1bb + 5db54b2 commit d0f237f

File tree

1 file changed

+31
-12
lines changed

1 file changed

+31
-12
lines changed

src/JSObjectProxy.cc

+31-12
Original file line numberDiff line numberDiff line change
@@ -338,15 +338,23 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_iter_next(JSObjectProxy
338338
}
339339

340340
PyObject *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

443461
error:
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

Comments
 (0)