Skip to content

Commit f750f9b

Browse files
committed
Allow native int subclasses with custom layout
1 parent f8bb333 commit f750f9b

4 files changed

Lines changed: 86 additions & 46 deletions

File tree

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

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
# SOFTWARE.
3939
import struct
4040

41-
from . import CPyExtTestCase, CPyExtFunction, CPyExtFunctionOutVars, unhandled_error_compare
41+
from . import CPyExtTestCase, CPyExtFunction, CPyExtFunctionOutVars, CPyExtType, unhandled_error_compare
4242

4343
int_bits = struct.calcsize('i') * 8
4444
max_int = 2 ** (int_bits - 1) - 1
@@ -195,6 +195,49 @@ def _int_examples():
195195

196196
class TestPyLong(CPyExtTestCase):
197197

198+
def test_native_long_subtype_has_native_layout(self):
199+
NativeLongWithMember = CPyExtType(
200+
"NativeLongWithMember",
201+
"""
202+
static void NativeLongWithMember_dealloc(NativeLongWithMemberObject *self) {
203+
Py_XDECREF(self->member);
204+
Py_TYPE(self)->tp_free((PyObject *)self);
205+
}
206+
207+
static PyObject *set_member(PyObject *self, PyObject *value) {
208+
NativeLongWithMemberObject *obj = (NativeLongWithMemberObject *)self;
209+
Py_INCREF(value);
210+
Py_XSETREF(obj->member, value);
211+
Py_RETURN_NONE;
212+
}
213+
214+
static PyObject *get_member(PyObject *self, PyObject *Py_UNUSED(ignored)) {
215+
NativeLongWithMemberObject *obj = (NativeLongWithMemberObject *)self;
216+
if (obj->member == NULL) {
217+
PyErr_SetString(PyExc_AttributeError, "member");
218+
return NULL;
219+
}
220+
return Py_NewRef(obj->member);
221+
}
222+
""",
223+
ready_code="NativeLongWithMemberType.tp_new = PyLong_Type.tp_new;",
224+
tp_base="&PyLong_Type",
225+
struct_base="PyLongObject base;",
226+
cmembers="PyObject *member;",
227+
tp_dealloc="(destructor)NativeLongWithMember_dealloc",
228+
tp_methods='''
229+
{"set_member", (PyCFunction)set_member, METH_O, NULL},
230+
{"get_member", (PyCFunction)get_member, METH_NOARGS, NULL}
231+
''',
232+
tp_members='{"member", T_OBJECT_EX, offsetof(NativeLongWithMemberObject, member), READONLY, NULL}',
233+
)
234+
235+
obj = NativeLongWithMember(10)
236+
assert obj == 10
237+
obj.set_member("foo")
238+
assert obj.get_member() == "foo"
239+
assert obj.member == "foo"
240+
198241
test_PyLong_AsLong = CPyExtFunction(
199242
_reference_as_long,
200243
_int_examples,

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

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@
115115
import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
116116
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
117117
import com.oracle.graal.python.builtins.objects.complex.PComplex;
118-
import com.oracle.graal.python.builtins.objects.dict.PDict;
119118
import com.oracle.graal.python.builtins.objects.floats.PFloat;
120119
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
121120
import com.oracle.graal.python.builtins.objects.function.PKeyword;
@@ -271,33 +270,25 @@ static Object doGeneric(Node inliningTarget, Object object, Object arg,
271270
}
272271
}
273272

274-
@GenerateInline(true)
275-
@GenerateCached(false)
276-
@GenerateUncached(false)
277-
public abstract static class DictSubtypeNew extends Node {
278-
public abstract PDict execute(Node node, Object cls, PDict managedSide);
279-
280-
@Specialization
281-
static PDict allocateNativePart(Object cls, PDict managedSide,
282-
@Bind Node inliningTarget,
283-
@Cached PythonToNativeNode toNative) {
284-
assert !managedSide.isNative();
285-
assert EnsurePythonObjectNode.doesNotNeedPromotion(cls);
286-
long clsPointer = toNative.executeLong(cls);
287-
long nativeObject;
288-
try {
289-
nativeObject = ExternalFunctionInvoker.invokePY_TYPE_GENERIC_NEW_RAW(CApiContext.getNativeSymbol(inliningTarget, NativeCAPISymbol.FUN_PY_TYPE_GENERIC_NEW_RAW).getAddress(),
290-
clsPointer, 0L, 0L);
291-
} catch (Throwable t) {
292-
throw CompilerDirectives.shouldNotReachHere(t);
293-
} finally {
294-
Reference.reachabilityFence(cls);
295-
}
296-
CApiTransitions.writeNativeRefCount(nativeObject, MANAGED_REFCNT);
297-
CApiTransitions.createReference(managedSide, nativeObject);
298-
assert managedSide.isNative();
299-
return managedSide;
300-
}
273+
public static <T extends PythonObject> T allocateNativePart(Node inliningTarget, Object cls, T managedSide) {
274+
assert !managedSide.isNative();
275+
assert EnsurePythonObjectNode.doesNotNeedPromotion(cls);
276+
long nativeObject;
277+
try {
278+
nativeObject = ExternalFunctionInvoker.invokePY_TYPE_GENERIC_NEW_RAW(CApiContext.getNativeSymbol(inliningTarget, NativeCAPISymbol.FUN_PY_TYPE_GENERIC_NEW_RAW).getAddress(),
279+
PythonToNativeNode.executeLongUncached(cls), 0L, 0L);
280+
} catch (Throwable t) {
281+
throw CompilerDirectives.shouldNotReachHere(t);
282+
} finally {
283+
Reference.reachabilityFence(cls);
284+
}
285+
PythonContext context = PythonContext.get(inliningTarget);
286+
TransformExceptionFromNativeNode.executeUncached(context.getThreadState(context.getLanguage()), NativeCAPISymbol.FUN_PY_TYPE_GENERIC_NEW_RAW.getTsName(), nativeObject == NULLPTR,
287+
true);
288+
CApiTransitions.writeNativeRefCount(nativeObject, MANAGED_REFCNT);
289+
CApiTransitions.createReference(managedSide, nativeObject);
290+
assert managedSide.isNative();
291+
return managedSide;
301292
}
302293

303294
// -----------------------------------------------------------------------------------------------------------------

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictBuiltins.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2025, Oracle and/or its affiliates.
2+
* Copyright (c) 2017, 2026, Oracle and/or its affiliates.
33
* Copyright (c) 2013, Regents of the University of California
44
*
55
* All rights reserved.
@@ -157,7 +157,6 @@ static Object dict(Object cls, Object[] args, PKeyword[] keywordArgs,
157157
@Bind Node inliningTarget,
158158
@Cached InlinedConditionProfile orderedProfile,
159159
@Cached TypeNodes.NeedsNativeAllocationNode needsNativeAllocationNode,
160-
@Cached CExtNodes.DictSubtypeNew subtypeNew,
161160
@Cached IsSubtypeNode isSubtypeNode,
162161
@Cached TypeNodes.GetInstanceShape getInstanceShape) {
163162
Shape shape = getInstanceShape.execute(cls);
@@ -166,7 +165,7 @@ static Object dict(Object cls, Object[] args, PKeyword[] keywordArgs,
166165
}
167166
PDict newDict = PFactory.createDict(cls, shape, EmptyStorage.INSTANCE);
168167
if (needsNativeAllocationNode.execute(inliningTarget, cls)) {
169-
return subtypeNew.execute(inliningTarget, cls, newDict);
168+
return CExtNodes.allocateNativePart(inliningTarget, cls, newDict);
170169
} else {
171170
return newDict;
172171
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
import com.oracle.graal.python.builtins.objects.bytes.BytesNodes.BytesFromObject;
8282
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
8383
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
84+
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
8485
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.FromNativeSubclassNode;
8586
import com.oracle.graal.python.builtins.objects.common.FormatNodeBase;
8687
import com.oracle.graal.python.builtins.objects.floats.PFloat;
@@ -167,6 +168,7 @@
167168
import com.oracle.truffle.api.library.CachedLibrary;
168169
import com.oracle.truffle.api.nodes.Node;
169170
import com.oracle.truffle.api.nodes.UnexpectedResultException;
171+
import com.oracle.truffle.api.object.Shape;
170172
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
171173
import com.oracle.truffle.api.profiles.InlinedConditionProfile;
172174
import com.oracle.truffle.api.profiles.InlinedIntValueProfile;
@@ -207,42 +209,47 @@ static Object doGeneric(VirtualFrame frame, Object cls, Object x, Object baseObj
207209
@Bind Node inliningTarget,
208210
@Cached IntNodeInnerNode innerNode,
209211
@Cached IsBuiltinClassExactProfile isPrimitiveIntProfile,
212+
@Cached TypeNodes.NeedsNativeAllocationNode needsNativeAllocationNode,
213+
@Cached TypeNodes.GetInstanceShape getInstanceShape,
210214
@Cached CreateIntSubclassNode createIntSubclassNode) {
211215
Object result = innerNode.execute(frame, inliningTarget, x, baseObj);
212216
if (isPrimitiveIntProfile.profileClass(inliningTarget, cls, PythonBuiltinClassType.PInt)) {
213217
return result;
214218
} else {
215-
return createIntSubclassNode.execute(inliningTarget, cls, result);
219+
assert IsSubtypeNode.getUncached().execute(cls, PythonBuiltinClassType.PInt);
220+
PInt managedInt = createIntSubclassNode.execute(inliningTarget, cls, result, getInstanceShape.execute(cls));
221+
if (needsNativeAllocationNode.execute(inliningTarget, cls)) {
222+
// needsNativeAllocationNode acts as branch profile
223+
return CExtNodes.allocateNativePart(inliningTarget, cls, managedInt);
224+
} else {
225+
return managedInt;
226+
}
216227
}
217228
}
218229

219230
@GenerateInline
220231
@GenerateCached(false)
221232
abstract static class CreateIntSubclassNode extends Node {
222-
public abstract Object execute(Node inliningTarget, Object cls, Object intObj);
233+
public abstract PInt execute(Node inliningTarget, Object cls, Object intObj, Shape shape);
223234

224235
@Specialization
225-
static Object doSubclass(Object cls, int value,
226-
@Shared @Cached TypeNodes.GetInstanceShape getInstanceShape) {
227-
return PFactory.createInt(cls, getInstanceShape.execute(cls), value);
236+
static PInt doManagedSubclass(Object cls, int value, Shape shape) {
237+
return PFactory.createInt(cls, shape, value);
228238
}
229239

230240
@Specialization
231-
static Object doSubclass(Object cls, long value,
232-
@Shared @Cached TypeNodes.GetInstanceShape getInstanceShape) {
233-
return PFactory.createInt(cls, getInstanceShape.execute(cls), value);
241+
static PInt doManagedSubclass(Object cls, long value, Shape shape) {
242+
return PFactory.createInt(cls, shape, value);
234243
}
235244

236245
@Specialization
237-
static Object doSubclass(Object cls, boolean value,
238-
@Shared @Cached TypeNodes.GetInstanceShape getInstanceShape) {
239-
return PFactory.createInt(cls, getInstanceShape.execute(cls), PInt.intValue(value));
246+
static PInt doManagedSubclass(Object cls, boolean value, Shape shape) {
247+
return PFactory.createInt(cls, shape, PInt.intValue(value));
240248
}
241249

242250
@Specialization
243-
static Object doSubclass(Object cls, PInt value,
244-
@Shared @Cached TypeNodes.GetInstanceShape getInstanceShape) {
245-
return PFactory.createInt(cls, getInstanceShape.execute(cls), value.getValue());
251+
static PInt doManagedSubclass(Object cls, PInt value, Shape shape) {
252+
return PFactory.createInt(cls, shape, value.getValue());
246253
}
247254
}
248255

0 commit comments

Comments
 (0)