Skip to content
Merged
62 changes: 22 additions & 40 deletions graalpython/com.oracle.graal.python.cext/src/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,10 @@
#include <stddef.h> // offsetof

// GraalPy-specific defines
/*
* There are 4 different modes for 'PyLong_AsPrimitive:
* - MODE_COERCE_UNSIGNED
* Will coerce the object to a Python integer and returns it as unsigned primitive.
* - MODE_COERCE_SIGNED 1
* Will coerce the object to a Python integer and returns it as signed primitive.
* - MODE_PINT_UNSIGNED 2
* Requires the object to be a Python integer and returns it as unsigned primitive.
* - MODE_PINT_SIGNED 3
* Requires the object to be a Python integer and returns it as signed primitive.
* - MODE_COERCE_MASK 4
* Will coerce the object to a Python integer and does a lossy cast to an unsigned primitive.
*/
#define MODE_COERCE_UNSIGNED 0
#define MODE_COERCE_SIGNED 1
#define MODE_PINT_UNSIGNED 2
#define MODE_PINT_SIGNED 3
#define MODE_COERCE_MASK 4
/* Modes for GraalPyPrivate_Long_AsPrimitive* conversion. */
#define MODE_UNSIGNED 0
#define MODE_SIGNED 1
#define MODE_MASK 4

#if 0 // GraalPy change
#include "clinic/longobject.c.h"
Expand Down Expand Up @@ -486,12 +472,7 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow)
if (points_to_py_int_handle(vv)) {
return pointer_to_int64(vv);
}
long result = (long) GraalPyPrivate_Long_AsPrimitive(vv, MODE_COERCE_SIGNED, sizeof(long));
if (result == -1L && PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_OverflowError)) {
PyErr_Clear();
*overflow = _PyLong_Sign(vv);
}
return result;
return (long) GraalPyPrivate_Long_AsPrimitiveAndOverflow(vv, sizeof(long), overflow);
}

/* Get a C long int from an int object or any object that has an __index__
Expand All @@ -504,7 +485,7 @@ PyLong_AsLong(PyObject *obj)
if (points_to_py_int_handle(obj)) {
return pointer_to_int64(obj);
}
return (long) GraalPyPrivate_Long_AsPrimitive(obj, MODE_COERCE_SIGNED, sizeof(long));
return (long) GraalPyPrivate_Long_AsPrimitiveWithCoercion(obj, MODE_SIGNED, sizeof(long));
}

/* Get a C int from an int object or any object that has an __index__
Expand Down Expand Up @@ -533,7 +514,7 @@ PyLong_AsSsize_t(PyObject *vv) {
if (points_to_py_int_handle(vv)) {
return pointer_to_int64(vv);
}
return (Py_ssize_t) GraalPyPrivate_Long_AsPrimitive(vv, MODE_PINT_SIGNED, sizeof(Py_ssize_t));
return (Py_ssize_t) GraalPyPrivate_Long_AsPrimitive(vv, MODE_SIGNED, sizeof(Py_ssize_t));
}

/* Get a C unsigned long int from an int object.
Expand All @@ -555,7 +536,7 @@ PyLong_AsUnsignedLong(PyObject *vv)
}
return (unsigned long) value;
}
return (unsigned long) GraalPyPrivate_Long_AsPrimitive(vv, MODE_PINT_UNSIGNED, sizeof(unsigned long));
return (unsigned long) GraalPyPrivate_Long_AsPrimitive(vv, MODE_UNSIGNED, sizeof(unsigned long));
}

/* Get a C size_t from an int object. Returns (size_t)-1 and sets
Expand All @@ -573,7 +554,7 @@ PyLong_AsSize_t(PyObject *vv)
}
return (size_t) value;
}
return (size_t) GraalPyPrivate_Long_AsPrimitive(vv, MODE_PINT_UNSIGNED, sizeof(size_t));
return (size_t) GraalPyPrivate_Long_AsPrimitive(vv, MODE_UNSIGNED, sizeof(size_t));
}

#if 0 // GraalPy change
Expand Down Expand Up @@ -620,7 +601,7 @@ PyLong_AsUnsignedLongMask(PyObject *op)
if (points_to_py_int_handle(op)) {
return pointer_to_int64(op);
}
return (unsigned long) GraalPyPrivate_Long_AsPrimitive(op, MODE_COERCE_MASK, sizeof(unsigned long));
return (unsigned long) GraalPyPrivate_Long_AsPrimitiveWithCoercion(op, MODE_MASK, sizeof(unsigned long));
}

int
Expand Down Expand Up @@ -1307,7 +1288,7 @@ PyLong_AsLongLong(PyObject *vv)
if (points_to_py_int_handle(vv)) {
return pointer_to_int64(vv);
}
return (long long) GraalPyPrivate_Long_AsPrimitive(vv, MODE_COERCE_SIGNED, sizeof(long long));
return (long long) GraalPyPrivate_Long_AsPrimitiveWithCoercion(vv, MODE_SIGNED, sizeof(long long));
}

/* Get a C unsigned long long int from an int object.
Expand All @@ -1329,7 +1310,7 @@ PyLong_AsUnsignedLongLong(PyObject *vv)
}
return (unsigned long long) value;
}
return (unsigned long long) GraalPyPrivate_Long_AsPrimitive(vv, MODE_PINT_UNSIGNED, sizeof(unsigned long long));
return (unsigned long long) GraalPyPrivate_Long_AsPrimitive(vv, MODE_UNSIGNED, sizeof(unsigned long long));
}

#if 0 // GraalPy change
Expand Down Expand Up @@ -1377,7 +1358,7 @@ PyLong_AsUnsignedLongLongMask(PyObject *op)
if (points_to_py_int_handle(op)) {
return pointer_to_int64(op);
}
return (unsigned long long) GraalPyPrivate_Long_AsPrimitive(op, MODE_COERCE_MASK, sizeof(unsigned long long));
return (unsigned long long) GraalPyPrivate_Long_AsPrimitiveWithCoercion(op, MODE_MASK, sizeof(unsigned long long));
}

/* Get a C long long int from an int object or any object that has an
Expand All @@ -1394,14 +1375,15 @@ long long
PyLong_AsLongLongAndOverflow(PyObject *vv, int *overflow)
{
// GraalPy change: different implementation
long long result = PyLong_AsLongLong(vv);
if (result == -1L && PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_OverflowError)) {
PyErr_Clear();
*overflow = _PyLong_Sign(vv);
} else {
*overflow = 0;
*overflow = 0;
if (vv == NULL) {
PyErr_BadInternalCall();
return -1;
}
return result;
if (points_to_py_int_handle(vv)) {
return pointer_to_int64(vv);
}
return GraalPyPrivate_Long_AsPrimitiveAndOverflow(vv, sizeof(long long), overflow);
}

#if 0 // GraalPy change
Expand Down Expand Up @@ -6510,7 +6492,7 @@ Py_ssize_t PyUnstable_Long_CompactValue(const PyLongObject *op) {
if (points_to_py_int_handle(op)) {
return pointer_to_int64(op);
}
return GraalPyPrivate_Long_AsPrimitive((PyObject*) op, MODE_PINT_SIGNED, sizeof(Py_ssize_t));
return GraalPyPrivate_Long_AsPrimitive((PyObject*) op, MODE_SIGNED, sizeof(Py_ssize_t));
}

// GraalPy additions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
max_long = 2 ** (long_bits - 1) - 1
min_long = -2 ** (long_bits - 1)
max_ulong = 2 ** long_bits
longlong_bits = struct.calcsize('q') * 8
max_longlong = 2 ** (longlong_bits - 1) - 1
min_longlong = -2 ** (longlong_bits - 1)
ulonglong_bits = struct.calcsize('Q') * 8
max_ulonglong = 2 ** ulonglong_bits
size_t_bits = struct.calcsize('P') * 8
Expand All @@ -74,6 +77,13 @@ def _reference_as_long(args):
return n


def _reference_as_longlong(args):
n = _reference_as_index(args[0])
if n > max_longlong or n < min_longlong:
raise OverflowError("Python int too large to convert to C long")
return n


def _reference_as_int(args):
n = _reference_as_index(args[0])
if n > max_int or n < min_int:
Expand All @@ -87,11 +97,30 @@ def _reference_as_unsigned_long(args):
raise TypeError("an integer is required")
if n < 0:
raise OverflowError("can't convert negative value to unsigned int")
if n > max_ulong:
if n >= max_ulong:
raise OverflowError("Python int too large to convert to C unsigned long")
return n


def _reference_as_unsigned_longlong(args):
n = args[0]
if not isinstance(n, int):
raise TypeError("an integer is required")
if n < 0:
raise OverflowError("can't convert negative value to unsigned int")
if n >= max_ulonglong:
raise OverflowError("Python int too large to convert to C unsigned long")
return n


def _reference_as_unsigned_long_mask(args):
return _reference_as_index(args[0]) & (max_ulong - 1)


def _reference_as_unsigned_longlong_mask(args):
return _reference_as_index(args[0]) & (max_ulonglong - 1)


def _reference_as_long_and_overflow(args):
n = _reference_as_index(args[0])
if n > max_long:
Expand All @@ -101,6 +130,15 @@ def _reference_as_long_and_overflow(args):
return n, 0


def _reference_as_longlong_and_overflow(args):
n = _reference_as_index(args[0])
if n > max_longlong:
return -1, 1
elif n < min_longlong:
return -1, -1
return n, 0


def _reference_as_ssize_t(args):
n = args[0]
if not isinstance(n, int):
Expand Down Expand Up @@ -201,8 +239,11 @@ class DummyNonInt():

class DummyIndexable:

def __init__(self, value=0xBEEF):
self.value = value

def __index__(self):
return 0xBEEF
return self.value


def _int_examples():
Expand All @@ -216,6 +257,12 @@ def _int_examples():
(0x7fffffff,),
(0xffffffff,),
(-0xffffffff,),
(max_longlong,),
(max_longlong + 1,),
(min_longlong,),
(min_longlong - 1,),
(max_ulonglong - 1,),
(max_ulonglong,),
(0x7fffffffffffffffffffffffffffffff,),
(0xffffffffffffffffffffffffffffffff,),
(-0xffffffffffffffffffffffffffffffff,),
Expand All @@ -224,6 +271,8 @@ def _int_examples():
(0.3,),
(DummyNonInt(),),
(DummyIndexable(),),
(DummyIndexable(max_longlong + 1),),
(DummyIndexable(min_longlong - 1),),
]


Expand Down Expand Up @@ -267,6 +316,15 @@ def test_native_long_subtype_has_native_layout(self):
cmpfunc=unhandled_error_compare
)

test_PyLong_AsLongLong = CPyExtFunction(
_reference_as_longlong,
_int_examples,
resultspec="L",
argspec='O',
arguments=["PyObject* obj"],
cmpfunc=unhandled_error_compare
)

test_PyLong_AsInt = CPyExtFunction(
_reference_as_int,
_int_examples,
Expand All @@ -287,6 +345,17 @@ def test_native_long_subtype_has_native_layout(self):
cmpfunc=unhandled_error_compare
)

test_PyLong_AsLongLongAndOverflow = CPyExtFunctionOutVars(
_reference_as_longlong_and_overflow,
_int_examples,
resultspec="Li",
argspec='O',
arguments=["PyObject* obj"],
resulttype="long long",
resultvars=["int overflow"],
cmpfunc=unhandled_error_compare
)

test_PyLong_AsUnsignedLong = CPyExtFunction(
_reference_as_unsigned_long,
_int_examples,
Expand All @@ -296,6 +365,33 @@ def test_native_long_subtype_has_native_layout(self):
cmpfunc=unhandled_error_compare
)

test_PyLong_AsUnsignedLongLong = CPyExtFunction(
_reference_as_unsigned_longlong,
_int_examples,
resultspec="K",
argspec='O',
arguments=["PyObject* obj"],
cmpfunc=unhandled_error_compare
)

test_PyLong_AsUnsignedLongMask = CPyExtFunction(
_reference_as_unsigned_long_mask,
_int_examples,
resultspec="k",
argspec='O',
arguments=["PyObject* obj"],
cmpfunc=unhandled_error_compare
)

test_PyLong_AsUnsignedLongLongMask = CPyExtFunction(
_reference_as_unsigned_longlong_mask,
_int_examples,
resultspec="K",
argspec='O',
arguments=["PyObject* obj"],
cmpfunc=unhandled_error_compare
)

test_PyLong_AsSsize_t = CPyExtFunction(
_reference_as_ssize_t,
_int_examples,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,15 @@ def test_member(self):
import warnings
warnings.simplefilter("ignore")

def assert_overflow(member, value, message, expected_value):
try:
setattr(obj, member, value)
except OverflowError as e:
assert str(e) == message
else:
assert False, "expected OverflowError"
assert getattr(obj, member) == expected_value

# char, uchar, short, ushort, int, uint, long, ulong, Py_ssize_t
max_values = obj.get_max_values()
min_values = obj.get_min_values()
Expand All @@ -255,20 +264,34 @@ def test_member(self):
assert val != min_values[i], "was: %r" % getattr(obj, m)
assert_raises(TypeError, setattr, obj, m, "hello")
assert_raises(OverflowError, setattr, obj, m, int(-1e40))
assert_raises(OverflowError, setattr, obj, m, int(1e40))
setattr(obj, m, 42)
message = "Python int too large to convert to C unsigned long" if m == "member_uint" else "Python int too large to convert to C long"
assert_overflow(m, int(1e40), message, 42)

# T_LONG, T_ULONG, T_PYSSIZET
max_values = (0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF)
err_values = (-1, 0xFFFFFFFFFFFFFFFF, -1, -1, 0xFFFFFFFFFFFFFFFF)
err_values = (-1, 42, -1, -1, 42)
err_messages = ("Python int too large to convert to C long",
"Python int too large to convert to C unsigned long",
"Python int too large to convert to C ssize_t",
"int too big to convert",
"int too big to convert")
for i, m in enumerate(("member_long", "member_ulong", "member_pyssizet", "member_longlong", "member_ulonglong")):
assert type(getattr(obj, m)) is int
assert getattr(obj, m) == 0
assert_raises(TypeError, delattr, obj, m)
setattr(obj, m, max_values[i])
assert getattr(obj, m) == max_values[i]
assert_raises(OverflowError, setattr, obj, m, max_values[i] + 1)
val = getattr(obj, m)
assert val == err_values[i], "member: %s ;; was: %r" % (m, val)
setattr(obj, m, 42)
assert_overflow(m, max_values[i] + 1, err_messages[i], err_values[i])

class Indexable:
def __index__(self):
return 42

obj.member_pyssizet = 42
assert_raises(TypeError, setattr, obj, "member_pyssizet", Indexable())
assert obj.member_pyssizet == -1

# T_FLOAT, T_DOUBLE
for i, m in enumerate(("member_float", "member_double")):
Expand Down
Loading
Loading