Skip to content

Commit 8b11252

Browse files
committed
[GR-76146] Accept native tuples in code constructor
1 parent d0933d0 commit 8b11252

2 files changed

Lines changed: 125 additions & 25 deletions

File tree

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

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -119,7 +119,7 @@ class TestCodeobject(CPyExtTestCase):
119119
],
120120
cmpfunc=lambda cr, pr: isinstance(cr, types.CodeType),
121121
)
122-
122+
123123
test_PyCode_NewWithPosOnlyArgs = CPyExtFunction(
124124
lambda args: args,
125125
lambda: (
@@ -148,6 +148,82 @@ class TestCodeobject(CPyExtTestCase):
148148
cmpfunc=lambda cr, pr: isinstance(cr, types.CodeType),
149149
)
150150

151+
test_PyCode_NewWithPosOnlyArgs_native_tuples = CPyExtFunction(
152+
lambda args: args,
153+
lambda: (
154+
tuple(),
155+
),
156+
code="""
157+
static PyObject* make_tuple(const char *a, const char *b, const char *c) {
158+
PyObject *tuple = PyTuple_New(3);
159+
if (tuple == NULL) {
160+
return NULL;
161+
}
162+
PyObject *item0 = PyUnicode_FromString(a);
163+
PyObject *item1 = PyUnicode_FromString(b);
164+
PyObject *item2 = PyUnicode_FromString(c);
165+
if (item0 == NULL || item1 == NULL || item2 == NULL) {
166+
Py_XDECREF(item0);
167+
Py_XDECREF(item1);
168+
Py_XDECREF(item2);
169+
Py_DECREF(tuple);
170+
return NULL;
171+
}
172+
PyTuple_SET_ITEM(tuple, 0, item0);
173+
PyTuple_SET_ITEM(tuple, 1, item1);
174+
PyTuple_SET_ITEM(tuple, 2, item2);
175+
return tuple;
176+
}
177+
178+
static PyCodeObject* wrap_PyCode_NewWithPosOnlyArgs_native_tuples(PyObject* ignored) {
179+
PyObject *code = PyBytes_FromStringAndSize("", 0);
180+
PyObject *consts = PyTuple_New(0);
181+
PyObject *names = PyTuple_New(0);
182+
PyObject *varnames = make_tuple("a", "b", "c");
183+
PyObject *freevars = PyTuple_New(0);
184+
PyObject *cellvars = PyTuple_New(0);
185+
PyObject *filename = PyUnicode_FromString("filename");
186+
PyObject *name = PyUnicode_FromString("name");
187+
PyObject *qualname = PyUnicode_FromString("module.name");
188+
PyObject *linetable = PyBytes_FromStringAndSize("", 0);
189+
PyObject *exceptiontable = PyBytes_FromStringAndSize("", 0);
190+
PyCodeObject *result = NULL;
191+
192+
if (code == NULL || consts == NULL || names == NULL || varnames == NULL ||
193+
freevars == NULL || cellvars == NULL || filename == NULL || name == NULL ||
194+
qualname == NULL || linetable == NULL || exceptiontable == NULL) {
195+
goto done;
196+
}
197+
result = PyUnstable_Code_NewWithPosOnlyArgs(
198+
1, 0, 2, 3, 4, 0,
199+
code, consts, names, varnames,
200+
freevars, cellvars,
201+
filename, name, qualname,
202+
1, linetable, exceptiontable);
203+
204+
done:
205+
Py_XDECREF(code);
206+
Py_XDECREF(consts);
207+
Py_XDECREF(names);
208+
Py_XDECREF(varnames);
209+
Py_XDECREF(freevars);
210+
Py_XDECREF(cellvars);
211+
Py_XDECREF(filename);
212+
Py_XDECREF(name);
213+
Py_XDECREF(qualname);
214+
Py_XDECREF(linetable);
215+
Py_XDECREF(exceptiontable);
216+
return result;
217+
}
218+
""",
219+
resultspec="O",
220+
resulttype="PyCodeObject*",
221+
argspec="",
222+
arguments=["PyObject* ignored"],
223+
callfunction="wrap_PyCode_NewWithPosOnlyArgs_native_tuples",
224+
cmpfunc=lambda cr, pr: isinstance(cr, types.CodeType),
225+
)
226+
151227
test_PyCode_Addr2Line = CPyExtFunction(
152228
reference_PyCode_Addr2Line,
153229
lambda: (

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeBuiltins.java

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@
4949
import com.oracle.graal.python.builtins.objects.PNone;
5050
import com.oracle.graal.python.builtins.objects.PNotImplemented;
5151
import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAccessLibrary;
52-
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
52+
import com.oracle.graal.python.builtins.objects.bytes.BytesNodes.GetBytesStorage;
5353
import com.oracle.graal.python.builtins.objects.code.CodeBuiltinsClinicProviders.CodeConstructorNodeClinicProviderGen;
54-
import com.oracle.graal.python.builtins.objects.common.SequenceNodes;
54+
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
5555
import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode;
5656
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
5757
import com.oracle.graal.python.builtins.objects.type.TpSlots;
@@ -61,12 +61,15 @@
6161
import com.oracle.graal.python.compiler.CodeUnit;
6262
import com.oracle.graal.python.compiler.OpCodes;
6363
import com.oracle.graal.python.compiler.SourceMap;
64+
import com.oracle.graal.python.lib.PyBytesCheckNode;
6465
import com.oracle.graal.python.lib.PyObjectGetIter;
6566
import com.oracle.graal.python.lib.PyObjectHashNode;
67+
import com.oracle.graal.python.lib.PyTupleCheckNode;
6668
import com.oracle.graal.python.lib.RichCmpOp;
6769
import com.oracle.graal.python.nodes.ErrorMessages;
6870
import com.oracle.graal.python.nodes.PGuards;
6971
import com.oracle.graal.python.nodes.PRaiseNode;
72+
import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage;
7073
import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode;
7174
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
7275
import com.oracle.graal.python.nodes.function.builtins.PythonClinicBuiltinNode;
@@ -124,23 +127,32 @@ public abstract static class CodeConstructorNode extends PythonClinicBuiltinNode
124127
static PCode call(VirtualFrame frame, @SuppressWarnings("unused") Object cls, int argcount,
125128
int posonlyargcount, int kwonlyargcount,
126129
int nlocals, int stacksize, int flags,
127-
PBytes codestring, PTuple constants, PTuple names, PTuple varnames,
130+
Object codestring, Object constants, Object names, Object varnames,
128131
TruffleString filename, TruffleString name, TruffleString qualname,
129-
int firstlineno, PBytes linetable, @SuppressWarnings("unused") PBytes exceptiontable,
130-
PTuple freevars, PTuple cellvars,
132+
int firstlineno, Object linetable, Object exceptiontable,
133+
Object freevars, Object cellvars,
131134
@Bind Node inliningTarget,
132135
@CachedLibrary(limit = "1") PythonBufferAccessLibrary bufferLib,
133136
@Cached CodeNodes.CreateCodeNode createCodeNode,
134-
@Cached SequenceNodes.GetObjectArrayNode getObjectArrayNode,
137+
@Cached GetBytesStorage getBytesStorage,
138+
@Cached GetTupleStorage getTupleStorage,
139+
@Cached SequenceStorageNodes.ToArrayNode toArrayNode,
140+
@Cached PyBytesCheckNode bytesCheckNode,
141+
@Cached PyTupleCheckNode tupleCheckNode,
135142
@Cached CastToTruffleStringNode castToTruffleStringNode) {
136-
byte[] codeBytes = bufferLib.getCopiedByteArray(codestring);
137-
byte[] linetableBytes = bufferLib.getCopiedByteArray(linetable);
138-
139-
Object[] constantsArr = getObjectArrayNode.execute(inliningTarget, constants);
140-
TruffleString[] namesArr = objectArrayToTruffleStringArray(inliningTarget, getObjectArrayNode.execute(inliningTarget, names), castToTruffleStringNode);
141-
TruffleString[] varnamesArr = objectArrayToTruffleStringArray(inliningTarget, getObjectArrayNode.execute(inliningTarget, varnames), castToTruffleStringNode);
142-
TruffleString[] freevarsArr = objectArrayToTruffleStringArray(inliningTarget, getObjectArrayNode.execute(inliningTarget, freevars), castToTruffleStringNode);
143-
TruffleString[] cellcarsArr = objectArrayToTruffleStringArray(inliningTarget, getObjectArrayNode.execute(inliningTarget, cellvars), castToTruffleStringNode);
143+
byte[] codeBytes = getBytes(inliningTarget, codestring, bytesCheckNode, getBytesStorage, bufferLib);
144+
byte[] linetableBytes = getBytes(inliningTarget, linetable, bytesCheckNode, getBytesStorage, bufferLib);
145+
checkBytes(inliningTarget, exceptiontable, bytesCheckNode);
146+
147+
Object[] constantsArr = getTupleArray(inliningTarget, constants, tupleCheckNode, getTupleStorage, toArrayNode);
148+
TruffleString[] namesArr = objectArrayToTruffleStringArray(inliningTarget,
149+
getTupleArray(inliningTarget, names, tupleCheckNode, getTupleStorage, toArrayNode), castToTruffleStringNode);
150+
TruffleString[] varnamesArr = objectArrayToTruffleStringArray(inliningTarget,
151+
getTupleArray(inliningTarget, varnames, tupleCheckNode, getTupleStorage, toArrayNode), castToTruffleStringNode);
152+
TruffleString[] freevarsArr = objectArrayToTruffleStringArray(inliningTarget,
153+
getTupleArray(inliningTarget, freevars, tupleCheckNode, getTupleStorage, toArrayNode), castToTruffleStringNode);
154+
TruffleString[] cellcarsArr = objectArrayToTruffleStringArray(inliningTarget,
155+
getTupleArray(inliningTarget, cellvars, tupleCheckNode, getTupleStorage, toArrayNode), castToTruffleStringNode);
144156

145157
return createCodeNode.execute(frame, argcount, posonlyargcount, kwonlyargcount,
146158
nlocals, stacksize, flags,
@@ -150,15 +162,27 @@ static PCode call(VirtualFrame frame, @SuppressWarnings("unused") Object cls, in
150162
firstlineno, linetableBytes);
151163
}
152164

153-
@Fallback
154-
@SuppressWarnings("unused")
155-
static PCode call(Object cls, Object argcount, Object kwonlyargcount, Object posonlyargcount,
156-
Object nlocals, Object stacksize, Object flags,
157-
Object codestring, Object constants, Object names, Object varnames,
158-
Object filename, Object name, Object qualname,
159-
Object firstlineno, Object linetable, Object exceptiontable,
160-
Object freevars, Object cellvars,
161-
@Bind Node inliningTarget) {
165+
private static byte[] getBytes(Node inliningTarget, Object object, PyBytesCheckNode bytesCheckNode,
166+
GetBytesStorage getBytesStorage, PythonBufferAccessLibrary bufferLib) {
167+
checkBytes(inliningTarget, object, bytesCheckNode);
168+
return bufferLib.getCopiedByteArray(getBytesStorage.execute(inliningTarget, object));
169+
}
170+
171+
private static void checkBytes(Node inliningTarget, Object object, PyBytesCheckNode bytesCheckNode) {
172+
if (!bytesCheckNode.execute(inliningTarget, object)) {
173+
throw invalidArgs(inliningTarget);
174+
}
175+
}
176+
177+
private static Object[] getTupleArray(Node inliningTarget, Object object, PyTupleCheckNode tupleCheckNode,
178+
GetTupleStorage getTupleStorage, SequenceStorageNodes.ToArrayNode toArrayNode) {
179+
if (!tupleCheckNode.execute(inliningTarget, object)) {
180+
throw invalidArgs(inliningTarget);
181+
}
182+
return toArrayNode.execute(inliningTarget, getTupleStorage.execute(inliningTarget, object));
183+
}
184+
185+
private static RuntimeException invalidArgs(Node inliningTarget) {
162186
throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.INVALID_ARGS, "code");
163187
}
164188

0 commit comments

Comments
 (0)