Skip to content

Commit 6ae02bd

Browse files
committed
[GR-76432] Allow native int subclasses with custom layout. Fixes #595.
PullRequest: graalpython/4628
2 parents 64a8b2e + b62bcb9 commit 6ae02bd

4 files changed

Lines changed: 65 additions & 46 deletions

File tree

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

Lines changed: 23 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,28 @@ 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+
ready_code="NativeLongWithMemberType.tp_new = PyLong_Type.tp_new;",
208+
tp_base="&PyLong_Type",
209+
struct_base="PyLongObject base;",
210+
cmembers="PyObject *member;",
211+
tp_dealloc="(destructor)NativeLongWithMember_dealloc",
212+
tp_members='{"member", T_OBJECT_EX, offsetof(NativeLongWithMemberObject, member), 0, NULL}',
213+
)
214+
215+
obj = NativeLongWithMember(10)
216+
assert obj == 10
217+
obj.member = "foo"
218+
assert obj.member == "foo"
219+
198220
test_PyLong_AsLong = CPyExtFunction(
199221
_reference_as_long,
200222
_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)