Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion Include/internal/pycore_long.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,20 @@ _PyLong_IsPositive(const PyLongObject *op)
return (op->long_value.lv_tag & SIGN_MASK) == 0;
}

/* Return true if the argument is a small int */
static inline bool
_PyLong_IsSmallInt(const PyLongObject *op)
{
assert(PyLong_Check(op));
bool is_small_int = (op->long_value.lv_tag & IMMORTALITY_BIT_MASK) != 0;
assert(PyLong_CheckExact(op) || (!is_small_int));
assert(_Py_IsImmortal(op) || (!is_small_int));
assert((_PyLong_IsCompact(op)
&& _PY_IS_SMALL_INT(_PyLong_CompactValue(op)))
|| (!is_small_int));
return is_small_int;
}

static inline Py_ssize_t
_PyLong_DigitCount(const PyLongObject *op)
{
Expand Down Expand Up @@ -294,7 +308,9 @@ _PyLong_SetDigitCount(PyLongObject *op, Py_ssize_t size)
#define NON_SIZE_MASK ~(uintptr_t)((1 << NON_SIZE_BITS) - 1)

static inline void
_PyLong_FlipSign(PyLongObject *op) {
_PyLong_FlipSign(PyLongObject *op)
{
assert(!_PyLong_IsSmallInt(op));
unsigned int flipped_sign = 2 - (op->long_value.lv_tag & SIGN_MASK);
op->long_value.lv_tag &= NON_SIZE_MASK;
op->long_value.lv_tag |= flipped_sign;
Expand Down
10 changes: 10 additions & 0 deletions Lib/test/test_capi/test_long.py
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,16 @@ def to_digits(num):
self.assertEqual(pylongwriter_create(negative, digits), num,
(negative, digits))

def test_bug_143050(self):
with support.adjust_int_max_str_digits(0):
# Bug coming from using _pylong.int_from_string(), that
# currently requires > 6000 decimal digits.
int('-' + '0' * 7000, 10)
_testcapi.test_immortal_small_ints()
# Test also nonzero small int
int('-' + '0' * 7000 + '123', 10)
_testcapi.test_immortal_small_ints()


if __name__ == "__main__":
unittest.main()
4 changes: 2 additions & 2 deletions Modules/_testcapi/immortal.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ test_immortal_small_ints(PyObject *self, PyObject *Py_UNUSED(ignored))
for (int i = -5; i <= 256; i++) {
PyObject *obj = PyLong_FromLong(i);
assert(verify_immortality(obj));
int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & IMMORTALITY_BIT_MASK;
int has_int_immortal_bit = _PyLong_IsSmallInt((PyLongObject *)obj);
assert(has_int_immortal_bit);
}
for (int i = 257; i <= 260; i++) {
PyObject *obj = PyLong_FromLong(i);
assert(obj);
int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & IMMORTALITY_BIT_MASK;
int has_int_immortal_bit = _PyLong_IsSmallInt((PyLongObject *)obj);
assert(!has_int_immortal_bit);
Py_DECREF(obj);
}
Expand Down
20 changes: 5 additions & 15 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3047,11 +3047,11 @@ PyLong_FromString(const char *str, char **pend, int base)
}

/* Set sign and normalize */
if (sign < 0) {
_PyLong_FlipSign(z);
}
long_normalize(z);
z = maybe_small_long(z);
if (sign < 0) {
_PyLong_Negate(&z);
}

if (pend != NULL) {
*pend = (char *)str;
Expand Down Expand Up @@ -3551,21 +3551,11 @@ long_richcompare(PyObject *self, PyObject *other, int op)
Py_RETURN_RICHCOMPARE(result, 0, op);
}

static inline int
/// Return 1 if the object is one of the immortal small ints
_long_is_small_int(PyObject *op)
{
PyLongObject *long_object = (PyLongObject *)op;
int is_small_int = (long_object->long_value.lv_tag & IMMORTALITY_BIT_MASK) != 0;
assert((!is_small_int) || PyLong_CheckExact(op));
return is_small_int;
}

void
_PyLong_ExactDealloc(PyObject *self)
{
assert(PyLong_CheckExact(self));
if (_long_is_small_int(self)) {
if (_PyLong_IsSmallInt((PyLongObject *)self)) {
// See PEP 683, section Accidental De-Immortalizing for details
_Py_SetImmortal(self);
return;
Expand All @@ -3580,7 +3570,7 @@ _PyLong_ExactDealloc(PyObject *self)
static void
long_dealloc(PyObject *self)
{
if (_long_is_small_int(self)) {
if (_PyLong_IsSmallInt((PyLongObject *)self)) {
/* This should never get called, but we also don't want to SEGV if
* we accidentally decref small Ints out of existence. Instead,
* since small Ints are immortal, re-set the reference count.
Expand Down
Loading