Skip to content

Commit 06f88f4

Browse files
committed
[GR-57290][GR-61735][GR-62336] Collected February cleanups.
PullRequest: graalpython/3717
2 parents 5d827c0 + 0df925b commit 06f88f4

File tree

19 files changed

+211
-125
lines changed

19 files changed

+211
-125
lines changed

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -951,7 +951,7 @@ Py_LOCAL_SYMBOL TruffleContext* TRUFFLE_CONTEXT;
951951
* that do not work during context shutdown! (This means that it would be safe
952952
* to share this global across multiple contexts.)
953953
*/
954-
Py_LOCAL_SYMBOL int32_t graalpy_finalizing;
954+
Py_LOCAL_SYMBOL int8_t *_graalpy_finalizing = NULL;
955955

956956
PyAPI_FUNC(void) initialize_graal_capi(TruffleEnv* env, void **builtin_closures, GCState *gc) {
957957
clock_t t = clock();
@@ -1007,10 +1007,16 @@ PyAPI_FUNC(void) initialize_graal_capi(TruffleEnv* env, void **builtin_closures,
10071007

10081008
/*
10091009
* This function is called from Java during C API initialization to get the
1010-
* pointer `graalpy_finalizing`.
1010+
* pointer `_graalpy_finalizing`.
10111011
*/
1012-
PyAPI_FUNC(int32_t *) GraalPy_get_finalize_capi_pointer() {
1013-
return &graalpy_finalizing;
1012+
PyAPI_FUNC(int8_t *) GraalPy_get_finalize_capi_pointer() {
1013+
assert(!_graalpy_finalizing);
1014+
// We actually leak this memory on purpose. On the Java side, this is
1015+
// written to in a VM shutdown hook. Once such a hook is registered it
1016+
// sticks around, so we're leaking those hooks anyway. 1 byte more to leak
1017+
// does not strike me (timfel) as significant.
1018+
_graalpy_finalizing = (int8_t *)calloc(1, sizeof(int8_t));
1019+
return _graalpy_finalizing;
10141020
}
10151021

10161022
#if ((__linux__ && __GNU_LIBRARY__) || __APPLE__)

graalpython/com.oracle.graal.python.cext/src/capi.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -157,7 +157,8 @@ PyAPI_DATA(uint32_t) Py_Truffle_Options;
157157

158158
extern THREAD_LOCAL Py_LOCAL_SYMBOL PyThreadState *tstate_current;
159159

160-
extern Py_LOCAL_SYMBOL int graalpy_finalizing;
160+
extern Py_LOCAL_SYMBOL int8_t *_graalpy_finalizing;
161+
#define graalpy_finalizing (_graalpy_finalizing != NULL && *_graalpy_finalizing)
161162

162163
/* Flags definitions representing global (debug) options. */
163164
static MUST_INLINE int PyTruffle_Trace_Memory() {

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ call_traverse(traverseproc traverse, PyObject *op, visitproc visit, void *arg)
4949
Py_TYPE((op))->tp_name);
5050
return 0;
5151
} else {
52+
if (_PyObject_IsFreed(op)) {
53+
PyTruffle_Log(PY_TRUFFLE_LOG_INFO,
54+
"we tried to call tp_traverse on a freed object at %p (ctx %p)!",
55+
op, arg);
56+
return 0;
57+
}
58+
if (_PyObject_IsFreed((PyObject *)Py_TYPE(op))) {
59+
PyTruffle_Log(PY_TRUFFLE_LOG_INFO,
60+
"we tried to call tp_traverse on an object at %p with a freed type at %p (ctx %p)!",
61+
op, Py_TYPE(op), arg);
62+
return 0;
63+
}
5264
return traverse(op, (visitproc)visit, arg);
5365
}
5466

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

Lines changed: 62 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1904,98 +1904,99 @@ long_from_binary_base(const char **str, int base, PyLongObject **res)
19041904
PyObject *
19051905
PyLong_FromString(const char *str, char **pend, int base)
19061906
{
1907-
// GraalPy change: different implementation
1908-
int negative = 0, error_if_nonzero = 0;
1907+
int sign = 1, error_if_nonzero = 0;
1908+
const char *start, *orig_str = str;
1909+
PyObject *z = NULL;
1910+
PyObject *strobj;
1911+
Py_ssize_t slen;
1912+
19091913
if ((base != 0 && base < 2) || base > 36) {
19101914
PyErr_SetString(PyExc_ValueError,
19111915
"int() arg 2 must be >= 2 and <= 36");
19121916
return NULL;
19131917
}
1914-
while (*str != '\0' && Py_ISSPACE(Py_CHARMASK(*str))) {
1918+
while (*str != '\0' && Py_ISSPACE(*str)) {
19151919
str++;
19161920
}
1917-
const char *orig_str = str;
19181921
if (*str == '+') {
19191922
++str;
1920-
} else if (*str == '-') {
1923+
}
1924+
else if (*str == '-') {
19211925
++str;
1922-
negative = 1;
1926+
sign = -1;
19231927
}
19241928
if (base == 0) {
19251929
if (str[0] != '0') {
19261930
base = 10;
1927-
} else if (str[1] == 'x' || str[1] == 'X') {
1931+
}
1932+
else if (str[1] == 'x' || str[1] == 'X') {
19281933
base = 16;
1929-
str += 2;
1930-
} else if (str[1] == 'o' || str[1] == 'O') {
1934+
}
1935+
else if (str[1] == 'o' || str[1] == 'O') {
19311936
base = 8;
1932-
str += 2;
1933-
} else if (str[1] == 'b' || str[1] == 'B') {
1937+
}
1938+
else if (str[1] == 'b' || str[1] == 'B') {
19341939
base = 2;
1935-
str += 2;
1936-
} else {
1940+
}
1941+
else {
19371942
/* "old" (C-style) octal literal, now invalid.
19381943
it might still be zero though */
19391944
error_if_nonzero = 1;
19401945
base = 10;
19411946
}
19421947
}
1943-
1944-
char* numberStart = str;
1945-
int overflow = 0;
1946-
int digits = 0;
1947-
char prev;
1948-
long value;
1949-
while (1) {
1948+
if (str[0] == '0' &&
1949+
((base == 16 && (str[1] == 'x' || str[1] == 'X')) ||
1950+
(base == 8 && (str[1] == 'o' || str[1] == 'O')) ||
1951+
(base == 2 && (str[1] == 'b' || str[1] == 'B')))) {
1952+
str += 2;
1953+
/* One underscore allowed here. */
19501954
if (*str == '_') {
1951-
if (prev == '_') {
1952-
goto error;
1953-
}
1954-
} else {
1955-
unsigned char digit = _PyLong_DigitValue[Py_CHARMASK(*str)];
1956-
if (digit >= base) {
1955+
++str;
1956+
}
1957+
}
1958+
1959+
// GraalPy change: remove the CPython code below and upcall quickly. Only
1960+
// optimization we do is to check if this may be a small-ish integer. There
1961+
// is a small integer cache and if we're lucky we can just return from
1962+
// that.
1963+
// In base 2, up to 8 digits may be a small integer, in base 36 8 digits
1964+
// still fit easily in 64 bits
1965+
for (int i = 0; i < 8; i++) {
1966+
char c = str[i];
1967+
if (c == '\0') {
1968+
int errsv = errno;
1969+
char *endptr;
1970+
long long result = strtoll(str, &endptr, base);
1971+
if (error_if_nonzero && result != 0) {
1972+
// let upcall handle the error reporting
1973+
base = 0;
19571974
break;
19581975
}
1959-
long new_value = value * base - digit;
1960-
if (new_value > value) {
1961-
// overflow
1962-
overflow = 1;
1976+
// POSIX.1-2008: strtoll must not set errno on success, and set
1977+
// *endptr to str when no conversion is performed
1978+
if (errno == 0 && str != endptr) {
1979+
while (*endptr && Py_ISSPACE(*endptr)) {
1980+
endptr++;
1981+
}
1982+
if (*endptr == '\0') {
1983+
z = PyLong_FromLongLong(sign < 0 ? -result : result);
1984+
}
19631985
}
1964-
value = new_value;
1986+
errno = errsv;
1987+
break;
1988+
} else if (!(isascii(c) && isalnum(c))) {
1989+
// cannot be a base 2 to 36 digit
1990+
break;
19651991
}
1966-
prev = *str;
1967-
++str;
1968-
++digits;
1969-
}
1970-
1971-
if (prev == '_') {
1972-
/* Trailing underscore not allowed. */
1973-
goto error;
1974-
}
1975-
while (*str != '\0' && Py_ISSPACE(Py_CHARMASK(*str))) {
1976-
str++;
19771992
}
1978-
if (pend != NULL) {
1979-
*pend = str;
1980-
}
1981-
if (value == LONG_MIN && !negative) {
1982-
overflow = 1;
1983-
}
1984-
1985-
if (overflow || (error_if_nonzero && value != 0)) {
1986-
if (error_if_nonzero) {
1987-
base = 0;
1993+
if (!z) {
1994+
z = GraalPyTruffleLong_FromString(orig_str, base);
1995+
if (z) {
1996+
// TODO: we should probably set the **pend out argument
19881997
}
1989-
return GraalPyTruffleLong_FromString(orig_str, base);
1990-
} else {
1991-
return PyLong_FromLong(negative ? value : -value);
19921998
}
1993-
1994-
error:
1995-
PyErr_Format(PyExc_ValueError,
1996-
"invalid literal for int() with base %d: %.200R",
1997-
base, PyUnicode_FromString(str));
1998-
return NULL;
1999+
return z;
19992000
}
20002001

20012002
#if 0 // GraalPy change

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

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2022, 2024, Oracle and/or its affiliates.
1+
/* Copyright (c) 2022, 2025, Oracle and/or its affiliates.
22
* Copyright (C) 1996-2022 Python Software Foundation
33
*
44
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -258,7 +258,6 @@ _PyModule_CreateInitialized(PyModuleDef* module, int module_api_version)
258258
return (PyObject*)m;
259259
}
260260

261-
#if 0 // GraalPy change
262261
PyObject *
263262
PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_version)
264263
{
@@ -340,8 +339,12 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio
340339
}
341340

342341
if (PyModule_Check(m)) {
343-
((PyModuleObject*)m)->md_state = NULL;
344-
((PyModuleObject*)m)->md_def = def;
342+
// GraalPy change: avoid direct struct access
343+
GraalPy_set_PyModuleObject_md_state((PyModuleObject *)m, NULL);
344+
GraalPy_set_PyModuleObject_md_def((PyModuleObject *)m, def);
345+
// End of GraalPy change, CPython code was next two lines
346+
// ((PyModuleObject*)m)->md_state = NULL;
347+
// ((PyModuleObject*)m)->md_def = def;
345348
} else {
346349
if (def->m_size > 0 || def->m_traverse || def->m_clear || def->m_free) {
347350
PyErr_Format(
@@ -361,10 +364,16 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio
361364
}
362365

363366
if (def->m_methods != NULL) {
364-
ret = _add_methods_to_object(m, nameobj, def->m_methods);
365-
if (ret != 0) {
366-
goto error;
367+
// GraalPy change: use PyModule_AddFunctions instead of _add_methods_to_object
368+
if (PyModule_AddFunctions(m, def->m_methods) != 0) {
369+
Py_DECREF(m);
370+
return NULL;
367371
}
372+
// End of GraalPy change, original code below
373+
// ret = _add_methods_to_object(m, nameobj, def->m_methods);
374+
// if (ret != 0) {
375+
// goto error;
376+
// }
368377
}
369378

370379
if (def->m_doc != NULL) {
@@ -397,16 +406,27 @@ PyModule_ExecDef(PyObject *module, PyModuleDef *def)
397406

398407
if (def->m_size >= 0) {
399408
PyModuleObject *md = (PyModuleObject*)module;
400-
if (md->md_state == NULL) {
401-
/* Always set a state pointer; this serves as a marker to skip
402-
* multiple initialization (importlib.reload() is no-op) */
403-
md->md_state = PyMem_Malloc(def->m_size);
404-
if (!md->md_state) {
409+
// GraalPy change: avoid direct struct access
410+
if (GraalPy_get_PyModuleObject_md_state(md) == NULL) {
411+
void* md_state = PyMem_Malloc(def->m_size);
412+
if (!md_state) {
405413
PyErr_NoMemory();
406414
return -1;
407415
}
408-
memset(md->md_state, 0, def->m_size);
416+
memset(md_state, 0, def->m_size);
417+
GraalPy_set_PyModuleObject_md_state(md, md_state);
409418
}
419+
// End GraalPy change, original code below
420+
// if (md->md_state == NULL) {
421+
// /* Always set a state pointer; this serves as a marker to skip
422+
// * multiple initialization (importlib.reload() is no-op) */
423+
// md->md_state = PyMem_Malloc(def->m_size);
424+
// if (!md->md_state) {
425+
// PyErr_NoMemory();
426+
// return -1;
427+
// }
428+
// memset(md->md_state, 0, def->m_size);
429+
// }
410430
}
411431

412432
if (def->m_slots == NULL) {
@@ -447,7 +467,6 @@ PyModule_ExecDef(PyObject *module, PyModuleDef *def)
447467
}
448468
return 0;
449469
}
450-
#endif // GraalPy change
451470

452471
int
453472
PyModule_AddFunctions(PyObject *m, PyMethodDef *functions)

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,6 @@ PyObject_Print(PyObject *op, FILE *fp, int flags)
372372
return ret;
373373
}
374374

375-
#if 0 // GraalPy change
376375
/* For debugging convenience. Set a breakpoint here and call it from your DLL */
377376
void
378377
_Py_BreakPoint(void)
@@ -389,9 +388,14 @@ _Py_BreakPoint(void)
389388
int
390389
_PyObject_IsFreed(PyObject *op)
391390
{
391+
if (points_to_py_handle_space(op)) {
392+
return Graal_PyTruffleObject_IsFreed(op);
393+
}
394+
#if 0 // GraalPy change
392395
if (_PyMem_IsPtrFreed(op) || _PyMem_IsPtrFreed(Py_TYPE(op))) {
393396
return 1;
394397
}
398+
#endif
395399
/* ignore op->ob_ref: its value can have be modified
396400
by Py_INCREF() and Py_DECREF(). */
397401
#ifdef Py_TRACE_REFS
@@ -405,11 +409,14 @@ _PyObject_IsFreed(PyObject *op)
405409
return 0;
406410
}
407411

408-
409412
/* For debugging convenience. See Misc/gdbinit for some useful gdb hooks */
410413
void
411414
_PyObject_Dump(PyObject* op)
412415
{
416+
if (points_to_py_handle_space(op)) {
417+
Graal_PyTruffleObject_Dump(op);
418+
return;
419+
}
413420
if (_PyObject_IsFreed(op)) {
414421
/* It seems like the object memory has been freed:
415422
don't access it to prevent a segmentation fault. */
@@ -448,6 +455,7 @@ _PyObject_Dump(PyObject* op)
448455
fflush(stderr);
449456
}
450457

458+
#if 0 // GraalPy change
451459
PyObject *
452460
PyObject_Repr(PyObject *v)
453461
{

graalpython/com.oracle.graal.python.test/src/org/graalvm/python/embedding/cext/test/MultiContextCExtTest.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -243,15 +243,9 @@ public void testCreatingVenvForMulticontext() throws IOException {
243243
} catch (PolyglotException e) {
244244
assertTrue("We rely on sys.prefix", e.getMessage().contains("sys.prefix must be a str"));
245245
}
246-
// Fifth one does not work because we don't have the venv configured
247-
try {
248-
c5.eval(code);
249-
fail("should not reach here");
250-
} catch (PolyglotException e) {
251-
assertTrue("We need a venv", e.getMessage().contains("sys.prefix must point to a venv"));
252-
}
253-
// Using a context without isolation in the same process needs to use LLVM
254-
assertFalse("have not had a context use LLVM, yet", log.truffleLog.toString().contains("as bitcode"));
246+
// Fifth works even without a venv
247+
c5.eval(code);
248+
// Using a context without isolation in the same process does not work
255249
try {
256250
c0.eval(code);
257251
fail("should not reach here");

0 commit comments

Comments
 (0)