Skip to content

Commit 6cdff55

Browse files
committed
[GR-44691] [GR-45681] Various C API fixes to avoid fatal errors.
PullRequest: graalpython/2740
2 parents b457028 + 89e4b53 commit 6cdff55

File tree

10 files changed

+136
-18
lines changed

10 files changed

+136
-18
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,12 @@ PyAPI_FUNC(RESULT) get_##NAME(RECEIVER obj) { \
432432
PyAPI_FUNC(RESULT) get_##NAME(RECEIVER obj) { \
433433
return obj->FIELD? obj->FIELD->NAME : NULL; \
434434
}
435+
436+
#define PRIMITIVE_EMBEDDED_FIELD_GETTER(RECEIVER, FIELD, RESULT, NAME) \
437+
PyAPI_FUNC(RESULT) get_##FIELD##_##NAME(RECEIVER obj) { \
438+
return obj->FIELD.NAME; \
439+
}
440+
435441
TYPE_FIELD_GETTER(PyObject*, ob_type)
436442
PRIMITIVE_FIELD_GETTER(PyObject*, Py_ssize_t, ob_refcnt)
437443
PRIMITIVE_FIELD_GETTER(PyVarObject*, Py_ssize_t, ob_size)
@@ -514,6 +520,8 @@ PRIMITIVE_FIELD_GETTER(PyTypeObject*, unsigned long, tp_flags)
514520
PRIMITIVE_FIELD_GETTER(PyModuleDef_Base*, Py_ssize_t, m_index)
515521
PRIMITIVE_FIELD_GETTER(PyModuleDef*, Py_ssize_t, m_size)
516522
PRIMITIVE_FIELD_GETTER(PyModuleDef*, const char*, m_doc)
523+
PRIMITIVE_EMBEDDED_FIELD_GETTER(PyComplexObject*, cval, double, real)
524+
PRIMITIVE_EMBEDDED_FIELD_GETTER(PyComplexObject*, cval, double, imag)
517525

518526
char* get_ob_sval(PyObject* op) {
519527
return ((PyBytesObject *)(op))->ob_sval;

graalpython/com.oracle.graal.python.jni/src/capi_native.c

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -482,13 +482,18 @@ static PyObject* callBuffer[1024];
482482
PyAPI_FUNC(PyObject *) PyObject_CallFunctionObjArgs(PyObject *callable, ...) {
483483
va_list myargs;
484484
va_start(myargs, callable);
485+
PyObject *arg;
485486
int count = 0;
486487
while (count <= 1024) {
487-
PyObject *o = va_arg(myargs, PyObject *);
488-
if (o == NULL) {
488+
arg = va_arg(myargs, PyObject *);
489+
if (arg == NULL) {
489490
break;
490491
}
491-
callBuffer[count++] = o;
492+
callBuffer[count++] = arg;
493+
}
494+
// test if buffer was too small
495+
if (arg != NULL) {
496+
Py_FatalError("argument buffer for 'PyObject_CallFunctionObjArgs' is too small");
492497
}
493498
PyObject* args = PyTuple_New(count);
494499
for (int i = 0; i < count; i++) {
@@ -502,9 +507,45 @@ PyAPI_FUNC(PyObject *) PyObject_CallFunctionObjArgs(PyObject *callable, ...) {
502507
return result;
503508
}
504509

505-
PyObject * PyObject_CallMethodObjArgs(PyObject *a, PyObject *b, ...) {
506-
printf("PyObject_CallMethodObjArgs not implemented in capi_native - exiting\n");
507-
exit(-1);
510+
PyObject * PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...) {
511+
if ((obj == NULL || name == NULL) && !PyErr_Occurred()) {
512+
PyErr_SetString(PyExc_SystemError,
513+
"null argument to internal routine");
514+
return NULL;
515+
}
516+
517+
PyObject *callable = NULL;
518+
int is_method = _PyObject_GetMethod(obj, name, &callable);
519+
if (callable == NULL) {
520+
return NULL;
521+
}
522+
obj = is_method ? obj : NULL;
523+
524+
va_list myargs;
525+
va_start(myargs, name);
526+
PyObject *arg;
527+
int count = 0;
528+
while (count <= 1024) {
529+
arg = va_arg(myargs, PyObject *);
530+
if (arg == NULL) {
531+
break;
532+
}
533+
callBuffer[count++] = arg;
534+
}
535+
// test if buffer was too small
536+
if (arg != NULL) {
537+
Py_FatalError("argument buffer for 'PyObject_CallMethodObjArgs' is too small");
538+
}
539+
PyObject* args = PyTuple_New(count);
540+
for (int i = 0; i < count; i++) {
541+
Py_INCREF(callBuffer[i]);
542+
PyTuple_SetItem(args, i, callBuffer[i]);
543+
}
544+
va_end(myargs);
545+
546+
PyObject* result = PyObject_CallObject(callable, args);
547+
Py_DECREF(args);
548+
return result;
508549
}
509550

510551
PyObject * _PyObject_CallMethodIdObjArgs(PyObject *object, struct _Py_Identifier *name, ...) {

graalpython/com.oracle.graal.python.test/src/graalpytest.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
'test_unittest',
7070
'test_glob',
7171
'test_logging',
72+
# test_int contains a denial-of-service prevention test that requires to finish within a certain time.
73+
# We are actually fast enough but the parallel execution overhead can cause transient failures.
74+
'test_int',
7275
]
7376

7477

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

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
*/
4141
package com.oracle.graal.python.builtins.modules.cext;
4242

43+
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.MemoryError;
4344
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.NotImplementedError;
4445
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.RecursionError;
4546
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError;
@@ -259,6 +260,10 @@ public static PException checkThrowableBeforeNative(Throwable t, String where1,
259260
PException pe = ExceptionUtils.wrapJavaException(soe, null, newException);
260261
throw pe;
261262
}
263+
if (t instanceof OutOfMemoryError oome) {
264+
PBaseException newException = PythonContext.get(null).factory().createBaseException(MemoryError);
265+
throw ExceptionUtils.wrapJavaException(oome, null, newException);
266+
}
262267
// everything else: log and convert to PException (SystemError)
263268
CompilerDirectives.transferToInterpreter();
264269
PNodeWithContext.printStack();
@@ -1404,10 +1409,11 @@ int doCachedDomainIdx(@SuppressWarnings("unused") int domain, Object pointerObje
14041409
// this will also be called if the allocation failed
14051410
if (!lib.isNull(pointerObject)) {
14061411
CApiContext cApiContext = getCApiContext();
1407-
cApiContext.getTraceMallocDomain(cachedDomainIdx).track(pointerObject, size);
1412+
Object key = CApiContext.asPointer(pointerObject, lib);
1413+
cApiContext.getTraceMallocDomain(cachedDomainIdx).track(key, size);
14081414
cApiContext.increaseMemoryPressure(null, getThreadStateNode, this, size);
14091415
if (LOGGER.isLoggable(Level.FINE)) {
1410-
LOGGER.fine(() -> PythonUtils.formatJString("Tracking memory (size: %d): %s", size, CApiContext.asHex(pointerObject)));
1416+
LOGGER.fine(() -> PythonUtils.formatJString("Tracking memory (size: %d): %s", size, CApiContext.asHex(key)));
14111417
}
14121418
}
14131419
return 0;
@@ -1433,20 +1439,23 @@ abstract static class PyTraceMalloc_Untrack extends CApiBinaryBuiltinNode {
14331439
@Specialization(guards = {"isSingleContext()", "domain == cachedDomain"}, limit = "3")
14341440
int doCachedDomainIdx(@SuppressWarnings("unused") int domain, Object pointerObject,
14351441
@Cached("domain") @SuppressWarnings("unused") long cachedDomain,
1436-
@Cached("lookupDomain(domain)") int cachedDomainIdx) {
1442+
@Cached("lookupDomain(domain)") int cachedDomainIdx,
1443+
@CachedLibrary("pointerObject") InteropLibrary lib) {
14371444

14381445
CApiContext cApiContext = getCApiContext();
1439-
long trackedMemorySize = cApiContext.getTraceMallocDomain(cachedDomainIdx).untrack(pointerObject);
1446+
Object key = CApiContext.asPointer(pointerObject, lib);
1447+
long trackedMemorySize = cApiContext.getTraceMallocDomain(cachedDomainIdx).untrack(key);
14401448
cApiContext.reduceMemoryPressure(trackedMemorySize);
14411449
if (LOGGER.isLoggable(Level.FINE)) {
1442-
LOGGER.fine(() -> PythonUtils.formatJString("Untracking memory (size: %d): %s", trackedMemorySize, CApiContext.asHex(pointerObject)));
1450+
LOGGER.fine(() -> PythonUtils.formatJString("Untracking memory (size: %d): %s", trackedMemorySize, CApiContext.asHex(key)));
14431451
}
14441452
return 0;
14451453
}
14461454

14471455
@Specialization(replaces = "doCachedDomainIdx")
1448-
int doGeneric(int domain, Object pointerObject) {
1449-
return doCachedDomainIdx(domain, pointerObject, domain, lookupDomain(domain));
1456+
int doGeneric(int domain, Object pointerObject,
1457+
@CachedLibrary(limit = "3") InteropLibrary lib) {
1458+
return doCachedDomainIdx(domain, pointerObject, domain, lookupDomain(domain), lib);
14501459
}
14511460

14521461
int lookupDomain(int domain) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,7 @@ public static Object asPointer(Object ptr, InteropLibrary lib) {
282282
try {
283283
return lib.asPointer(ptr);
284284
} catch (UnsupportedMessageException e) {
285-
CompilerDirectives.transferToInterpreter();
286-
throw new IllegalStateException();
285+
throw CompilerDirectives.shouldNotReachHere(e);
287286
}
288287
}
289288
return ptr;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/NativeCAPISymbol.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ public enum NativeCAPISymbol implements NativeCExtSymbol {
194194
FUN_GET_MP_LENGTH("get_mp_length"),
195195
FUN_GET_MP_SUBSCRIPT("get_mp_subscript"),
196196
FUN_GET_MP_ASS_SUBSCRIPT("get_mp_ass_subscript"),
197+
FUN_GET_PY_COMPLEX_CVAL_REAL("get_cval_real"),
198+
FUN_GET_PY_COMPLEX_CVAL_IMAG("get_cval_imag"),
197199
FUN_GET_PYMODULEDEF_M_METHODS("get_PyModuleDef_m_methods"),
198200
FUN_GET_PYMODULEDEF_M_SLOTS("get_PyModuleDef_m_slots"),
199201
FUN_GET_BYTE_ARRAY_TYPE_ID("get_byte_array_typeid"),

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex/ComplexBuiltins.java

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@
8181
import com.oracle.graal.python.builtins.modules.SysModuleBuiltins;
8282
import com.oracle.graal.python.builtins.objects.PNone;
8383
import com.oracle.graal.python.builtins.objects.PNotImplemented;
84+
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
85+
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction;
86+
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.ToSulongNode;
87+
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
8488
import com.oracle.graal.python.builtins.objects.common.FormatNodeBase;
8589
import com.oracle.graal.python.builtins.objects.complex.ComplexBuiltinsClinicProviders.FormatNodeClinicProviderGen;
8690
import com.oracle.graal.python.builtins.objects.floats.FloatBuiltins;
@@ -102,6 +106,7 @@
102106
import com.oracle.graal.python.runtime.formatting.InternalFormat;
103107
import com.oracle.graal.python.runtime.formatting.InternalFormat.Spec;
104108
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
109+
import com.oracle.truffle.api.CompilerDirectives;
105110
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
106111
import com.oracle.truffle.api.dsl.Bind;
107112
import com.oracle.truffle.api.dsl.Cached;
@@ -112,6 +117,8 @@
112117
import com.oracle.truffle.api.dsl.Specialization;
113118
import com.oracle.truffle.api.dsl.TypeSystemReference;
114119
import com.oracle.truffle.api.frame.VirtualFrame;
120+
import com.oracle.truffle.api.interop.InteropLibrary;
121+
import com.oracle.truffle.api.interop.UnsupportedMessageException;
115122
import com.oracle.truffle.api.nodes.Node;
116123
import com.oracle.truffle.api.profiles.InlinedConditionProfile;
117124
import com.oracle.truffle.api.strings.TruffleString;
@@ -798,12 +805,43 @@ static double get(PComplex self) {
798805
@Builtin(name = J___HASH__, minNumOfPositionalArgs = 1)
799806
abstract static class HashNode extends PythonUnaryBuiltinNode {
800807
@Specialization
801-
static long hash(PComplex self) {
808+
static long doPComplex(PComplex self) {
809+
return complexHash(self.getReal(), self.getImag());
810+
}
811+
812+
@Specialization
813+
static long doNative(PythonAbstractNativeObject self,
814+
@Cached ToSulongNode toSulongNode,
815+
@Cached PCallCapiFunction callGetRealNode,
816+
@Cached PCallCapiFunction callGetImagNode) {
817+
Object ptr = toSulongNode.execute(self);
818+
Object realObj = callGetRealNode.call(NativeCAPISymbol.FUN_GET_PY_COMPLEX_CVAL_REAL, ptr);
819+
Object imagObj = callGetImagNode.call(NativeCAPISymbol.FUN_GET_PY_COMPLEX_CVAL_IMAG, ptr);
820+
return complexHash(expectDouble(realObj), expectDouble(imagObj));
821+
}
822+
823+
private static long complexHash(double real, double imag) {
802824
// just like CPython
803-
long realHash = PyObjectHashNode.hash(self.getReal());
804-
long imagHash = PyObjectHashNode.hash(self.getImag());
825+
long realHash = PyObjectHashNode.hash(real);
826+
long imagHash = PyObjectHashNode.hash(imag);
805827
return realHash + SysModuleBuiltins.HASH_IMAG * imagHash;
806828
}
829+
830+
private static double expectDouble(Object val) {
831+
if (val instanceof Double) {
832+
return (double) val;
833+
}
834+
InteropLibrary lib = InteropLibrary.getUncached(val);
835+
if (lib.fitsInDouble(val)) {
836+
try {
837+
return lib.asDouble(val);
838+
} catch (UnsupportedMessageException e) {
839+
// fall through
840+
}
841+
}
842+
throw CompilerDirectives.shouldNotReachHere();
843+
}
844+
807845
}
808846

809847
@GenerateNodeFactory

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2151,6 +2151,12 @@ public PythonNativeWrapper getSingletonNativeWrapper(PythonAbstractObject obj) {
21512151
return null;
21522152
}
21532153

2154+
/**
2155+
* This method is intended to be called to re-acquire the GIL after a {@link StackOverflowError}
2156+
* was catched. To reduce the probability that re-acquiring the GIL causes again a
2157+
* {@link StackOverflowError}, it is important to keep this method as simple as possible. In
2158+
* particular, do not add calls if there is a way to avoid it.
2159+
*/
21542160
public void reacquireGilAfterStackOverflow() {
21552161
while (!ownsGil()) {
21562162
try {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/exception/PException.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ private static RuntimeException createStacktraceCarrier() {
122122
return new RuntimeException();
123123
}
124124

125+
/*
126+
* Note: we use this method to convert a Java StackOverflowError into a Python RecursionError.
127+
* At the time when this is done, some Java stack frames were already unwinded but there is no
128+
* guarantee on how many. Therefore, it is important that this method is simple. In particular,
129+
* do not add calls if that can be avoided.
130+
*/
125131
public static PException fromObject(PBaseException actual, Node node, Throwable wrapped) {
126132
PException pException = new PException(actual, node, wrapped);
127133
actual.setException(pException);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PythonObjectFactory.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,12 @@ public final PBaseException createBaseException(Object cls, Object[] data, PTupl
933933
return trace(new PBaseException(cls, getShape(cls), data, args));
934934
}
935935

936+
/*
937+
* Note: we use this method to convert a Java StackOverflowError into a Python RecursionError.
938+
* At the time when this is done, some Java stack frames were already unwinded but there is no
939+
* guarantee on how many. Therefore, it is important that this method is simple. In particular,
940+
* do not add calls if that can be avoided.
941+
*/
936942
public final PBaseException createBaseException(Object cls, TruffleString format, Object[] args) {
937943
return createBaseException(cls, null, format, args);
938944
}

0 commit comments

Comments
 (0)