Skip to content

Commit 5bfa6d7

Browse files
committed
Properly set pend output arg in PyLong_FromString
1 parent 90e4e52 commit 5bfa6d7

3 files changed

Lines changed: 84 additions & 10 deletions

File tree

graalpython/com.oracle.graal.python.cext/src/longobject.c

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2453,6 +2453,7 @@ PyLong_FromString(const char *str, char **pend, int base)
24532453
{
24542454
int sign = 1, error_if_nonzero = 0;
24552455
const char *start, *orig_str = str;
2456+
int orig_base = base;
24562457
PyObject *z = NULL;
24572458
PyObject *strobj;
24582459
Py_ssize_t slen;
@@ -2517,7 +2518,6 @@ PyLong_FromString(const char *str, char **pend, int base)
25172518
long long result = strtoll(str, &endptr, base);
25182519
if (error_if_nonzero && result != 0) {
25192520
// let upcall handle the error reporting
2520-
base = 0;
25212521
break;
25222522
}
25232523
// POSIX.1-2008: strtoll must not set errno on success, and set
@@ -2538,11 +2538,52 @@ PyLong_FromString(const char *str, char **pend, int base)
25382538
}
25392539
}
25402540
if (!z) {
2541-
z = GraalPyPrivate_Long_FromString((char *)orig_str, base);
2542-
if (z) {
2543-
// TODO: we should probably set the **pend out argument
2541+
z = GraalPyPrivate_Long_FromString(orig_str, orig_base);
2542+
if (!z && pend) {
2543+
/*
2544+
* We have an exception already, but we need to redo the validation
2545+
* to compute pend. Adapted from long_from_string_base
2546+
*/
2547+
*pend = str;
2548+
const char *end, *p;
2549+
char prev = 0;
2550+
start = p = str;
2551+
/* Leading underscore not allowed. */
2552+
if (*start == '_') {
2553+
return NULL;
2554+
}
2555+
/* Verify all characters are digits and underscores. */
2556+
while (_PyLong_DigitValue[Py_CHARMASK(*p)] < base || *p == '_') {
2557+
if (*p == '_') {
2558+
/* Double underscore not allowed. */
2559+
if (prev == '_') {
2560+
*pend = p - 1;
2561+
return NULL;
2562+
}
2563+
}
2564+
prev = *p;
2565+
++p;
2566+
}
2567+
/* Trailing underscore not allowed. */
2568+
if (prev == '_') {
2569+
*pend = p - 1;
2570+
return NULL;
2571+
}
2572+
*pend = end = p;
2573+
/* Reject empty strings */
2574+
if (start == end) {
2575+
return NULL;
2576+
}
2577+
/* Allow only trailing whitespace after `end` */
2578+
while (*p && Py_ISSPACE(*p)) {
2579+
p++;
2580+
}
2581+
*pend = p;
25442582
}
25452583
}
2584+
if (z && pend) {
2585+
*pend = str + strlen(str);
2586+
}
25462587
return z;
25472588
}
25482589

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_long.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040

4141
from . import CPyExtTestCase, CPyExtFunction, CPyExtFunctionOutVars, unhandled_error_compare
4242

43-
4443
int_bits = struct.calcsize('i') * 8
4544
max_int = 2 ** (int_bits - 1) - 1
4645
min_int = -2 ** (int_bits - 1)
@@ -133,6 +132,22 @@ def _reference_is_compact(args):
133132
return 1 if -2147483648 <= n <= 2147483647 else 0
134133

135134

135+
def _reference_long_from_string(args):
136+
num, base = args
137+
if not num.strip():
138+
return ValueError, len(num)
139+
try:
140+
return int(num, base), len(num)
141+
except Exception as e:
142+
for i in range(len(num)):
143+
try:
144+
int(num[:i + 1], base)
145+
except Exception:
146+
if num[0] == '0' and base == 0:
147+
i = len(num)
148+
return type(e), i
149+
150+
136151
class DummyNonInt():
137152
pass
138153

@@ -360,21 +375,39 @@ class TestPyLong(CPyExtTestCase):
360375
)
361376

362377
test_PyLong_FromString = CPyExtFunction(
363-
lambda args: int(args[0], args[1]),
378+
_reference_long_from_string,
364379
lambda: (
365380
("00", 0),
366381
("03", 0),
382+
("0003", 0),
367383
(" 12 ", 10),
368384
(" 12abg13 ", 22),
369385
("12", 0),
370386
("12321321", 0),
371387
("0x132f1", 0),
372388
("0x132132ff213213213231", 0),
373389
("13123441234123423412341234123412341234124312341234213213213213213231", 0),
390+
("1312344123412342341234123412341234123x4124312341234213213213213213231", 0),
391+
("", 0),
392+
(" ", 0),
393+
("123 ", 0),
394+
("-123 ", 0),
395+
("123 x", 0),
396+
("x", 0),
397+
("_1", 0),
398+
("1_", 0),
399+
("1_1", 0),
400+
("1__1", 0),
374401
),
375402
code='''PyObject* wrap_PyLong_FromString(const char* str, int base) {
376403
char* pend;
377-
return PyLong_FromString(str, &pend, base);
404+
PyObject* val = PyLong_FromString(str, &pend, base);
405+
if (!val) {
406+
PyObject* exc = PyErr_GetRaisedException();
407+
val = Py_NewRef(Py_TYPE(exc));
408+
Py_DECREF(exc);
409+
}
410+
return Py_BuildValue("OL", val, pend - str);
378411
}''',
379412
callfunction="wrap_PyLong_FromString",
380413
resultspec="O",

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextLongBuiltins.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Direct;
4545
import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Ignored;
4646
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.CONST_UNSIGNED_CHAR_PTR;
47-
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.CharPtrAsTruffleString;
47+
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.ConstCharPtrAsTruffleString;
4848
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.ConstPyLongObject;
4949
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Int;
5050
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.LONG_LONG;
@@ -74,8 +74,8 @@
7474
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.CastToNativeLongNode;
7575
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;
7676
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.ConvertPIntToPrimitiveNode;
77-
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformPExceptionToNativeCachedNode;
7877
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.ConvertPIntToPrimitiveNodeGen;
78+
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformPExceptionToNativeCachedNode;
7979
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
8080
import com.oracle.graal.python.builtins.objects.ints.IntBuiltins;
8181
import com.oracle.graal.python.builtins.objects.ints.IntNodes;
@@ -221,7 +221,7 @@ static Object fromDouble(double d,
221221
}
222222
}
223223

224-
@CApiBuiltin(ret = PyObjectTransfer, args = {CharPtrAsTruffleString, Int}, call = Ignored)
224+
@CApiBuiltin(ret = PyObjectTransfer, args = {ConstCharPtrAsTruffleString, Int}, call = Ignored)
225225
abstract static class GraalPyPrivate_Long_FromString extends CApiBinaryBuiltinNode {
226226

227227
@Specialization

0 commit comments

Comments
 (0)