Skip to content

Commit db44e52

Browse files
committed
[GR-53626] Implement slot tp_setattro.
PullRequest: graalpython/3302
2 parents 50c8b03 + fdc4b6a commit db44e52

File tree

51 files changed

+1134
-642
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1134
-642
lines changed

graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/Slot.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ enum SlotKind {
9696
sq_length,
9797
mp_length,
9898
tp_descr_get,
99-
tp_get_attro,
100-
tp_descr_set
99+
tp_descr_set,
100+
tp_getattro,
101+
tp_setattro,
101102
}
102103
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9277,7 +9277,7 @@ static int type_ready_graalpy_slot_conv(PyTypeObject* cls) {
92779277
// NOTE: The slots may be called from managed code, i.e., we need to wrap the functions
92789278
// and convert arguments that should be C primitives.
92799279
// ADD_SLOT_CONV("__getattribute__", cls->tp_getattr, -2, JWRAPPER_GETATTR); tp_getattr does not have wrapper set in slotdefs and hence is ignored in add_operators
9280-
ADD_SLOT_CONV("__setattr__", cls->tp_setattr, -3, JWRAPPER_SETATTR);
9280+
// ADD_SLOT_CONV("__setattr__", cls->tp_setattr, -3, JWRAPPER_SETATTR); dtto for tp_setattr
92819281
ADD_SLOT_CONV("__repr__", cls->tp_repr, -1, JWRAPPER_REPR);
92829282
ADD_SLOT_CONV("__hash__", cls->tp_hash, -1, JWRAPPER_HASHFUNC);
92839283
ADD_SLOT_CONV("__call__", cls->tp_call, METH_KEYWORDS | METH_VARARGS, JWRAPPER_CALL);

graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/SlotsMapping.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,10 @@ static String getSlotBaseClass(Slot s) {
5252
return switch (s.value()) {
5353
case nb_bool -> "TpSlotInquiry.TpSlotInquiryBuiltin";
5454
case sq_length, mp_length -> "TpSlotLen.TpSlotLenBuiltin" + getSuffix(s.isComplex());
55-
case tp_get_attro -> "TpSlotGetAttr.TpSlotGetAttrBuiltin";
55+
case tp_getattro -> "TpSlotGetAttr.TpSlotGetAttrBuiltin";
5656
case tp_descr_get -> "TpSlotDescrGet.TpSlotDescrGetBuiltin" + getSuffix(s.isComplex());
5757
case tp_descr_set -> "TpSlotDescrSet.TpSlotDescrSetBuiltin";
58+
case tp_setattro -> "TpSlotSetAttr.TpSlotSetAttrBuiltin";
5859
};
5960
}
6061

@@ -63,8 +64,9 @@ static String getSlotNodeBaseClass(Slot s) {
6364
case tp_descr_get -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.DescrGetBuiltinNode";
6465
case nb_bool -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotInquiry.NbBoolBuiltinNode";
6566
case sq_length, mp_length -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotLen.LenBuiltinNode";
66-
case tp_get_attro -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttr.GetAttrBuiltinNode";
67+
case tp_getattro -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttr.GetAttrBuiltinNode";
6768
case tp_descr_set -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrSet.DescrSetBuiltinNode";
69+
case tp_setattro -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotSetAttr.SetAttrBuiltinNode";
6870
};
6971
}
7072

@@ -73,22 +75,22 @@ static String getUncachedExecuteSignature(SlotKind s) {
7375
case nb_bool -> "boolean executeUncached(Object self)";
7476
case tp_descr_get -> "Object executeUncached(Object self, Object obj, Object type)";
7577
case sq_length, mp_length -> "int executeUncached(Object self)";
76-
case tp_get_attro, tp_descr_set ->
78+
case tp_getattro, tp_descr_set, tp_setattro ->
7779
throw new AssertionError("Should not reach here: should be always complex");
7880
};
7981
}
8082

8183
static boolean supportsComplex(SlotKind s) {
8284
return switch (s) {
8385
case nb_bool -> false;
84-
case sq_length, mp_length, tp_get_attro, tp_descr_get, tp_descr_set -> true;
86+
case sq_length, mp_length, tp_getattro, tp_descr_get, tp_descr_set, tp_setattro -> true;
8587
};
8688
}
8789

8890
static boolean supportsSimple(SlotKind s) {
8991
return switch (s) {
9092
case nb_bool, sq_length, mp_length, tp_descr_get -> true;
91-
case tp_get_attro, tp_descr_set -> false;
93+
case tp_getattro, tp_descr_set, tp_setattro -> false;
9294
};
9395
}
9496

@@ -97,8 +99,8 @@ static String getUncachedExecuteCall(SlotKind s) {
9799
case nb_bool -> "executeBool(null, self)";
98100
case sq_length, mp_length -> "executeInt(null, self)";
99101
case tp_descr_get -> "execute(null, self, obj, type)";
100-
case tp_descr_set, tp_get_attro ->
101-
throw new AssertionError("Should not reach here since isComplexByDefault gives true");
102+
case tp_getattro, tp_descr_set, tp_setattro ->
103+
throw new AssertionError("Should not reach here: should be always complex");
102104
};
103105
}
104106
}

graalpython/com.oracle.graal.python.shell/src/com/oracle/graal/python/shell/GraalPythonMain.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,16 @@ protected List<String> preprocessArguments(List<String> givenArgs, Map<String, S
199199
inputArgs.remove("-debug-java");
200200
}
201201
continue;
202+
case "-debug-subprocess-java-port":
203+
case "-debug-subprocess-java":
204+
int subprocessDebuggerPort = 8000;
205+
if (arg.equals("-debug-subprocess-java-port")) {
206+
subprocessDebuggerPort = Integer.parseInt(argumentIterator.next());
207+
}
208+
addRelaunchArg("-debug-subprocess-java-port");
209+
addRelaunchArg(Integer.toString(subprocessDebuggerPort + 1));
210+
addRelaunchArg("--vm.agentlib:jdwp=transport=dt_socket,server=y,address=" + subprocessDebuggerPort + ",suspend=y");
211+
continue;
202212
case "-debug-perf":
203213
unrecognized.add("--engine.TraceCompilation");
204214
unrecognized.add("--engine.TraceCompilationDetails");

graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/TpSlotsTests.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,21 +65,24 @@ public void testBuilderBasic() {
6565

6666
checkSlotValue(TpSlotMeta.SQ_LENGTH, slots.combined_sq_mp_length());
6767
checkSlotValue(TpSlotMeta.MP_LENGTH, slots.combined_mp_sq_length());
68-
checkSlotValue(TpSlotMeta.TP_GET_ATTRO, slots.combined_tp_getattro_getattr());
68+
checkSlotValue(TpSlotMeta.TP_GETATTRO, slots.combined_tp_getattro_getattr());
69+
checkSlotValue(TpSlotMeta.TP_SETATTRO, slots.combined_tp_setattro_setattr());
6970
}
7071

7172
@Test
7273
public void testBuilderOptimizations1() {
7374
Builder builder = TpSlots.newBuilder();
7475
builder.set(TpSlotMeta.MP_LENGTH, new TpSlotNative(TpSlotMeta.MP_LENGTH));
75-
builder.set(TpSlotMeta.TP_GET_ATTR, new TpSlotNative(TpSlotMeta.TP_GET_ATTR));
76+
builder.set(TpSlotMeta.TP_GETATTR, new TpSlotNative(TpSlotMeta.TP_GETATTR));
77+
builder.set(TpSlotMeta.TP_SETATTR, new TpSlotNative(TpSlotMeta.TP_SETATTR));
7678

7779
TpSlots slots = builder.build();
78-
verifySlots(slots, def -> def == TpSlotMeta.MP_LENGTH || def == TpSlotMeta.TP_GET_ATTR);
80+
verifySlots(slots, def -> def == TpSlotMeta.MP_LENGTH || def == TpSlotMeta.TP_GETATTR || def == TpSlotMeta.TP_SETATTR);
7981

8082
checkSlotValue(TpSlotMeta.MP_LENGTH, slots.combined_sq_mp_length());
8183
checkSlotValue(TpSlotMeta.MP_LENGTH, slots.combined_mp_sq_length());
82-
checkSlotValue(TpSlotMeta.TP_GET_ATTR, slots.combined_tp_getattro_getattr());
84+
checkSlotValue(TpSlotMeta.TP_GETATTR, slots.combined_tp_getattro_getattr());
85+
checkSlotValue(TpSlotMeta.TP_SETATTR, slots.combined_tp_setattro_setattr());
8386
}
8487

8588
@Test

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,9 @@ def call_set():
12421242
del obj.foo
12431243
assert obj.foo == 'unset'
12441244

1245+
obj = TestSetter()
1246+
assert TestSetter.foo.__set__(obj, 42) is None
1247+
12451248
def test_member_kind_precedence(self):
12461249
TestWithConflictingMember1 = CPyExtType(
12471250
"TestWithConflictingMember1",

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

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,53 @@ def __getattr__(self, item):
166166
assert x.foo is None
167167

168168

169+
def test_setattr_str_subclass():
170+
TestAttrSetWitStrSubclass = CPyExtType("TestAttrSetWitStrSubclass",
171+
'''
172+
int testattro_set(PyObject* self, PyObject* key, PyObject* value) {
173+
Py_XDECREF(((TestAttrSetWitStrSubclassObject*)self)->payload);
174+
if (key != NULL) {
175+
Py_INCREF(key);
176+
}
177+
((TestAttrSetWitStrSubclassObject*)self)->payload = key;
178+
return 0;
179+
}
180+
181+
PyObject* my_repr(PyObject *self) {
182+
PyObject* r = ((TestAttrSetWitStrSubclassObject*)self)->payload;
183+
if (r == NULL) Py_RETURN_NONE;
184+
Py_INCREF(r);
185+
return r;
186+
}
187+
''',
188+
cmembers='PyObject* payload;',
189+
tp_repr="my_repr",
190+
tp_setattro="(setattrofunc) testattro_set")
191+
class MyStr(str):
192+
pass
193+
194+
x = TestAttrSetWitStrSubclass()
195+
setattr(x, MyStr('hello'), 42)
196+
assert type(repr(x)) == MyStr
197+
assert repr(x) == 'hello'
198+
199+
200+
def test_setattr_wrapper():
201+
TestSetAttrWrapperReturn = CPyExtType("TestSetAttrWrapperReturn",
202+
"int myset(PyObject* self, PyObject* key, PyObject* value) { return 0; }",
203+
tp_setattro="(setattrofunc) myset")
204+
x = TestSetAttrWrapperReturn()
205+
assert x.__setattr__("bar", 42) is None
206+
207+
208+
def test_sq_ass_item_wrapper():
209+
TestSqAssItemWrapperReturn = CPyExtType("TestSqAssItemWrapperReturn",
210+
"static int myassitem(PyObject *self, Py_ssize_t i, PyObject *v) { return 0; }",
211+
sq_ass_item="myassitem")
212+
x = TestSqAssItemWrapperReturn()
213+
assert x.__setitem__(42, 42) is None
214+
215+
169216
def test_concat_vs_add():
170217
# Inheritance of the Py_sq_concat slot:
171218
SqAdd = CPyExtHeapType("SqAdd",
@@ -251,32 +298,32 @@ def test_type_not_ready():
251298
module = compile_module_from_string("""
252299
#define PY_SSIZE_T_CLEAN
253300
#include <Python.h>
254-
301+
255302
static PyObject* my_getattro(PyObject* self, PyObject* key) {
256303
return Py_NewRef(key);
257304
}
258-
305+
259306
static PyTypeObject NotReadyType = {
260307
PyVarObject_HEAD_INIT(NULL, 0)
261308
.tp_name = "NotReadyType",
262309
.tp_basicsize = sizeof(PyObject),
263310
.tp_dealloc = (destructor)PyObject_Del,
264311
.tp_getattro = my_getattro
265312
};
266-
313+
267314
static PyObject* create_not_ready(PyObject* module, PyObject* unused) {
268315
return PyObject_New(PyObject, &NotReadyType);
269316
}
270-
317+
271318
static PyMethodDef module_methods[] = {
272319
{"create_not_ready", _PyCFunction_CAST(create_not_ready), METH_NOARGS, ""},
273320
{NULL}
274321
};
275-
322+
276323
static PyModuleDef NotReadyTypeModule = {
277324
PyModuleDef_HEAD_INIT, "NotReadyType", "", -1, module_methods
278325
};
279-
326+
280327
PyMODINIT_FUNC
281328
PyInit_NotReadyType(void)
282329
{

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@
118118
import com.oracle.graal.python.builtins.modules.ctypes.PyCArrayBuiltins;
119119
import com.oracle.graal.python.builtins.modules.ctypes.PyCFuncPtrBuiltins;
120120
import com.oracle.graal.python.builtins.modules.ctypes.PyCPointerBuiltins;
121+
import com.oracle.graal.python.builtins.modules.ctypes.PyCStructTypeBuiltins;
121122
import com.oracle.graal.python.builtins.modules.ctypes.SimpleCDataBuiltins;
122123
import com.oracle.graal.python.builtins.modules.ctypes.StgDictBuiltins;
123124
import com.oracle.graal.python.builtins.modules.functools.LruCacheWrapperBuiltins;
@@ -406,8 +407,8 @@ public enum PythonBuiltinClassType implements TruffleObject {
406407
CArgObject("CArgObject", Flags.PUBLIC_BASE_WODICT),
407408
CThunkObject("CThunkObject", J__CTYPES, Flags.PUBLIC_BASE_WODICT),
408409
StgDict("StgDict", Flags.PRIVATE_DERIVED_WODICT, DICT_M_FLAGS, StgDictBuiltins.SLOTS),
409-
PyCStructType("PyCStructType", J__CTYPES, Flags.PUBLIC_BASE_WODICT, PYCSTRUCTTYPE_M_FLAGS),
410-
UnionType("UnionType", J__CTYPES, Flags.PUBLIC_BASE_WODICT, UNIONTYPE_M_FLAGS),
410+
PyCStructType("PyCStructType", J__CTYPES, Flags.PUBLIC_BASE_WODICT, PYCSTRUCTTYPE_M_FLAGS, PyCStructTypeBuiltins.SLOTS),
411+
UnionType("UnionType", J__CTYPES, Flags.PUBLIC_BASE_WODICT, UNIONTYPE_M_FLAGS, com.oracle.graal.python.builtins.modules.ctypes.UnionTypeBuiltins.SLOTS),
411412
PyCPointerType("PyCPointerType", J__CTYPES, Flags.PUBLIC_BASE_WODICT, PYCPOINTERTYPE_M_FLAGS),
412413
PyCArrayType("PyCArrayType", J__CTYPES, Flags.PUBLIC_BASE_WODICT, PYCARRAYTYPE_M_FLAGS),
413414
PyCSimpleType("PyCSimpleType", J__CTYPES, Flags.PUBLIC_BASE_WODICT, PYCSIMPLETYPE_M_FLAGS),

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@
179179
import com.oracle.graal.python.lib.PyObjectLookupAttr;
180180
import com.oracle.graal.python.lib.PyObjectLookupAttrO;
181181
import com.oracle.graal.python.lib.PyObjectReprAsObjectNode;
182+
import com.oracle.graal.python.lib.PyObjectSetAttrO;
182183
import com.oracle.graal.python.lib.PyObjectSetItem;
183184
import com.oracle.graal.python.lib.PyObjectSizeNode;
184185
import com.oracle.graal.python.lib.PyObjectStrAsObjectNode;
@@ -198,7 +199,6 @@
198199
import com.oracle.graal.python.nodes.attributes.GetAttributeNode;
199200
import com.oracle.graal.python.nodes.attributes.LookupCallableSlotInMRONode;
200201
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
201-
import com.oracle.graal.python.nodes.attributes.SetAttributeNode;
202202
import com.oracle.graal.python.nodes.builtins.ListNodes;
203203
import com.oracle.graal.python.nodes.builtins.ListNodes.ConstructListNode;
204204
import com.oracle.graal.python.nodes.bytecode.GetAIterNode;
@@ -2079,9 +2079,10 @@ static Object round(VirtualFrame frame, Object x, Object n,
20792079
@GenerateNodeFactory
20802080
public abstract static class SetAttrNode extends PythonTernaryBuiltinNode {
20812081
@Specialization
2082-
Object setAttr(VirtualFrame frame, Object object, Object key, Object value,
2083-
@Cached SetAttributeNode.Dynamic setAttrNode) {
2084-
setAttrNode.execute(frame, object, key, value);
2082+
static Object setAttr(VirtualFrame frame, Object object, Object key, Object value,
2083+
@Bind("this") Node inliningTarget,
2084+
@Cached PyObjectSetAttrO setAttrNode) {
2085+
setAttrNode.execute(frame, inliningTarget, object, key, value);
20852086
return PNone.NONE;
20862087
}
20872088
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/CodecsTruffleModuleBuiltins.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,12 @@
8484
import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetBaseClassNode;
8585
import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs;
8686
import com.oracle.graal.python.lib.PyObjectGetAttr;
87+
import com.oracle.graal.python.lib.PyObjectSetAttr;
8788
import com.oracle.graal.python.lib.PyObjectStrAsTruffleStringNode;
8889
import com.oracle.graal.python.nodes.BuiltinNames;
8990
import com.oracle.graal.python.nodes.PGuards;
9091
import com.oracle.graal.python.nodes.PNodeWithContext;
9192
import com.oracle.graal.python.nodes.PRaiseNode;
92-
import com.oracle.graal.python.nodes.attributes.SetAttributeNode;
9393
import com.oracle.graal.python.nodes.call.CallNode;
9494
import com.oracle.graal.python.nodes.call.special.CallVarargsMethodNode;
9595
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
@@ -110,7 +110,6 @@
110110
import com.oracle.truffle.api.dsl.GenerateInline;
111111
import com.oracle.truffle.api.dsl.GenerateUncached;
112112
import com.oracle.truffle.api.dsl.ImportStatic;
113-
import com.oracle.truffle.api.dsl.NeverDefault;
114113
import com.oracle.truffle.api.dsl.NodeFactory;
115114
import com.oracle.truffle.api.dsl.Specialization;
116115
import com.oracle.truffle.api.frame.Frame;
@@ -326,7 +325,7 @@ protected abstract static class CodecInitNode extends PythonVarargsBuiltinNode {
326325
Object init(VirtualFrame frame, PythonObject self, Object[] args, PKeyword[] kw,
327326
@Bind("this") Node inliningTarget,
328327
@Cached PyObjectGetAttr getAttrNode,
329-
@Cached("createSetAttr()") SetAttributeNode setAttrNode,
328+
@Cached PyObjectSetAttr setAttrNode,
330329
@Cached GetPythonObjectClassNode getClass,
331330
@Cached GetBaseClassNode getBaseClassNode,
332331
@Cached CallNode callNode) {
@@ -339,14 +338,9 @@ Object init(VirtualFrame frame, PythonObject self, Object[] args, PKeyword[] kw,
339338
PythonUtils.arraycopy(args, 1, callArgs, 1, args.length - 1);
340339
}
341340
callNode.execute(frame, superInit, callArgs, kw);
342-
setAttrNode.execute(frame, self, args[0]);
341+
setAttrNode.execute(frame, inliningTarget, self, T_ATTR_ENCODING, args[0]);
343342
return PNone.NONE;
344343
}
345-
346-
@NeverDefault
347-
protected SetAttributeNode createSetAttr() {
348-
return SetAttributeNode.create(T_ATTR_ENCODING);
349-
}
350344
}
351345

352346
@Builtin(name = J___CALL__, minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = true)

0 commit comments

Comments
 (0)