diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tuple.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tuple.py index bc38569147..45f894ffda 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tuple.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tuple.py @@ -36,6 +36,15 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import _io +import codecs +import functools +import io +import itertools +import json +import os +import pickle +import tempfile import unittest from . import CPyExtTestCase, CPyExtFunction, CPyExtFunctionOutVars, unhandled_error_compare, CPyExtType, \ @@ -379,3 +388,121 @@ def test_memoryview_cast_shape_native_tuple(self): assert is_native_object(shape) view = memoryview(b"abcd").cast("B", shape) assert view.shape == (2, 2) + + def test_utime_ns_divmod_native_tuple(self): + class NativeDivmod: + def __divmod__(self, other): + assert other == 1000000000 + result = TupleSubclass(1, 234567890) + assert is_native_object(result) + return result + + with tempfile.NamedTemporaryFile() as f: + os.utime(f.name, ns=(NativeDivmod(), NativeDivmod())) + stat = os.stat(f.name) + expected = 1234567890 + # We don't care about the exact value. It's about if native tuples are accepted. + tolerance = 1_000_000_000 + assert abs(stat.st_atime_ns - expected) <= tolerance + assert abs(stat.st_mtime_ns - expected) <= tolerance + + def test_percent_format_native_tuple(self): + args = TupleSubclass("native", 7) + assert is_native_object(args) + assert "%s:%d" % args == "native:7" + + bytes_args = TupleSubclass(b"native", 7) + assert is_native_object(bytes_args) + assert b"%s:%d" % bytes_args == b"native:7" + + def test_incremental_newline_decoder_getstate_native_tuple(self): + class Decoder: + def getstate(self): + state = TupleSubclass(b"", 0) + assert is_native_object(state) + return state + + decoder = _io.IncrementalNewlineDecoder(Decoder(), translate=False) + assert decoder.getstate() == (b"", 0) + + def test_incremental_newline_decoder_setstate_native_tuple(self): + state = TupleSubclass(b"buffer", 1) + assert is_native_object(state) + + decoder = _io.IncrementalNewlineDecoder(None, translate=False) + decoder.setstate(state) + assert decoder.getstate() == (b"", 1) + + class Decoder: + def setstate(self, state): + self.state = state + + def getstate(self): + return self.state + + state = TupleSubclass(b"buffer", 3) + assert is_native_object(state) + + wrapped_decoder = Decoder() + decoder = _io.IncrementalNewlineDecoder(wrapped_decoder, translate=False) + decoder.setstate(state) + assert wrapped_decoder.state == (b"buffer", 1) + assert decoder.getstate() == (b"buffer", 3) + + def test_functools_partial_setstate_native_tuple_args(self): + args = TupleSubclass(2, 5) + assert is_native_object(args) + partial = functools.partial(int) + partial.__setstate__((pow, args, None, None)) + assert partial() == 32 + + def test_pickle_memo_accepts_native_tuple_value(self): + value = TupleSubclass(11, "memo") + assert is_native_object(value) + pickler = pickle.Pickler(io.BytesIO()) + pickler.memo = {0: value} + + def test_json_encodes_native_tuple(self): + array = TupleSubclass("native", 7) + assert is_native_object(array) + assert json.dumps(array, separators=(",", ":")) == '["native",7]' + + def test_itertools_cycle_setstate_native_tuple(self): + state = TupleSubclass([], 0) + assert is_native_object(state) + cycle = itertools.cycle([1]) + cycle.__setstate__(state) + assert next(cycle) == 1 + + def test_reversed_native_tuple(self): + values = TupleSubclass(1, 2, 3) + assert is_native_object(values) + assert list(reversed(values)) == [3, 2, 1] + + def test_base_exception_group_native_tuple(self): + exceptions = TupleSubclass(ValueError("native"), TypeError("tuple")) + assert is_native_object(exceptions) + group = BaseExceptionGroup("msg", exceptions) + assert group.message == "msg" + assert tuple(type(e) for e in group.exceptions) == (ValueError, TypeError) + + def test_base_exception_group_copies_native_tuple_notes(self): + group = BaseExceptionGroup("msg", (ValueError("native"), TypeError("tuple"))) + notes = TupleSubclass("note 1", "note 2") + assert is_native_object(notes) + group.__notes__ = notes + + match, rest = group.split(ValueError) + assert match.__notes__ == ["note 1", "note 2"] + assert rest.__notes__ == ["note 1", "note 2"] + assert match.__notes__ is not notes + assert rest.__notes__ is not notes + assert match.__notes__ is not rest.__notes__ + + def test_multibyte_stream_writer_writelines_native_tuple(self): + lines = TupleSubclass("native", " tuple") + assert is_native_object(lines) + stream = io.BytesIO() + writer = codecs.getwriter("euc_jp")(stream) + writer.writelines(lines) + assert stream.getvalue() == b"native tuple" diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java index 5e9249feb9..56772accb8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java @@ -205,6 +205,9 @@ import com.oracle.graal.python.lib.PyObjectSizeNode; import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; import com.oracle.graal.python.lib.PyObjectStrAsTruffleStringNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; +import com.oracle.graal.python.lib.PyTupleGetItem; +import com.oracle.graal.python.lib.PyTupleSizeNode; import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.lib.PyUnicodeFSDecoderNode; import com.oracle.graal.python.lib.RichCmpOp; @@ -2137,6 +2140,9 @@ static Object[] update(Object[] bases, @Bind PythonLanguage language, @Cached PyObjectLookupAttr getMroEntries, @Cached CallUnaryMethodNode callMroEntries, + @Cached PyTupleCheckNode tupleCheckNode, + @Cached PyTupleSizeNode tupleSize, + @Cached PyTupleGetItem tupleGetItem, @Cached PRaiseNode raiseNode) { CompilerAsserts.neverPartOfCompilation(); PTuple originalBases = null; @@ -2163,10 +2169,9 @@ static Object[] update(Object[] bases, originalBases = PFactory.createTuple(language, bases); } Object newBase = callMroEntries.executeObject(null, meth, originalBases); - if (!PGuards.isPTuple(newBase)) { + if (!tupleCheckNode.execute(inliningTarget, newBase)) { throw raiseNode.raise(inliningTarget, PythonErrorType.TypeError, ErrorMessages.MRO_ENTRIES_MUST_RETURN_TUPLE); } - PTuple newBaseTuple = (PTuple) newBase; if (newBases == null) { // If this is a first successful replacement, create new_bases list and copy // previously encountered bases. @@ -2175,9 +2180,9 @@ static Object[] update(Object[] bases, newBases.add(bases[j]); } } - SequenceStorage storage = newBaseTuple.getSequenceStorage(); - for (int j = 0; j < storage.length(); j++) { - newBases.add(SequenceStorageNodes.GetItemScalarNode.executeUncached(storage, j)); + int newBaseLen = tupleSize.execute(inliningTarget, newBase); + for (int j = 0; j < newBaseLen; j++) { + newBases.add(tupleGetItem.execute(inliningTarget, newBase, j)); } } if (newBases == null) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java index 6f99ff5b69..93730906d9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java @@ -119,6 +119,7 @@ import com.oracle.graal.python.builtins.objects.common.EmptyStorage; import com.oracle.graal.python.builtins.objects.common.HashingStorage; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageLen; +import com.oracle.graal.python.builtins.objects.common.SequenceNodes.GetSequenceStorageNode; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum; @@ -141,6 +142,7 @@ import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectGetItem; import com.oracle.graal.python.lib.PyObjectStrAsTruffleStringNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PConstructAndRaiseNode; import com.oracle.graal.python.nodes.PRaiseNode; @@ -178,8 +180,6 @@ import com.oracle.graal.python.runtime.exception.PythonExitException; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.runtime.sequence.PSequence; -import com.oracle.graal.python.runtime.sequence.storage.NativePrimitiveSequenceStorage; -import com.oracle.graal.python.runtime.sequence.storage.NativeSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerAsserts; @@ -193,7 +193,6 @@ import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Exclusive; -import com.oracle.truffle.api.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.NeverDefault; @@ -1021,8 +1020,14 @@ private void validate(HashingStorage dictStorage) { public abstract static class GetStorageStrategyNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - TruffleString doSet(PSequence seq) { - return PythonUtils.toTruffleStringUncached(seq.getSequenceStorage().getClass().getSimpleName()); + static TruffleString doSet(Object object, + @Bind Node inliningTarget, + @Cached PyTupleCheckNode tupleCheckNode) { + if (tupleCheckNode.isTupleOrList(inliningTarget, object)) { + SequenceStorage sequenceStorage = GetSequenceStorageNode.executeUncached(object); + return PythonUtils.toTruffleStringUncached(sequenceStorage.getClass().getSimpleName()); + } + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.BAD_ARG_TO_INTERNAL_FUNC); } } @@ -1030,22 +1035,40 @@ TruffleString doSet(PSequence seq) { @GenerateNodeFactory abstract static class StorageToNative extends PythonUnaryBuiltinNode { - @Specialization @TruffleBoundary - Object toNative(PArray array) { - CApiContext.ensureCapiWasLoaded("internal API"); - NativeSequenceStorage newStorage = ToNativeStorageNode.executeUncached(array.getSequenceStorage(), true); - array.setSequenceStorage(newStorage); - return array; + @Specialization + static Object doGeneric(Object object, + @Bind Node inliningTarget, + @Cached PyTupleCheckNode tupleCheckNode) { + if (object instanceof PArray array) { + array.setSequenceStorage(ToNativeStorageNode.executeUncached(array.getSequenceStorage(), true)); + } else if (object instanceof PSequence sequence) { + sequence.setSequenceStorage(ToNativeStorageNode.executeUncached(sequence.getSequenceStorage(), sequence instanceof PBytesLike)); + } else if (!tupleCheckNode.isTupleOrList(inliningTarget, object)) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.BAD_ARG_TO_INTERNAL_FUNC); + } + return object; } + } + + @Builtin(name = "storage_to_native_primitive", minNumOfPositionalArgs = 1) + @GenerateNodeFactory + abstract static class StorageToNativePrimitive extends PythonUnaryBuiltinNode { - @Specialization @TruffleBoundary - Object toNative(PSequence sequence) { + @Specialization + static Object doGeneric(Object object, + @Bind Node inliningTarget, + @Cached PyTupleCheckNode tupleCheckNode) { CApiContext.ensureCapiWasLoaded("internal API"); - NativeSequenceStorage newStorage = ToNativeStorageNode.executeUncached(sequence.getSequenceStorage(), sequence instanceof PBytesLike); - sequence.setSequenceStorage(newStorage); - return sequence; + if (object instanceof PArray array) { + array.setSequenceStorage(ToNativePrimitiveStorageNode.executeUncached(array.getSequenceStorage())); + } else if (object instanceof PSequence sequence) { + sequence.setSequenceStorage(ToNativePrimitiveStorageNode.executeUncached(sequence.getSequenceStorage())); + } else if (!tupleCheckNode.isTupleOrList(inliningTarget, object)) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.BAD_ARG_TO_INTERNAL_FUNC); + } + return object; } } @@ -1340,29 +1363,6 @@ TruffleString get() { } } - @Builtin(name = "storage_to_native_primitive", minNumOfPositionalArgs = 1) - @GenerateNodeFactory - abstract static class StorageToNativePrimitive extends PythonUnaryBuiltinNode { - - @Specialization - static Object doArray(PArray array, - @Shared @Cached ToNativePrimitiveStorageNode toNativePrimitiveNode, - @Bind Node inliningTarget) { - NativePrimitiveSequenceStorage newStorage = toNativePrimitiveNode.execute(inliningTarget, array.getSequenceStorage()); - array.setSequenceStorage(newStorage); - return array; - } - - @Specialization - static Object doSequence(PSequence sequence, - @Shared @Cached ToNativePrimitiveStorageNode toNativePrimitiveNode, - @Bind Node inliningTarget) { - NativePrimitiveSequenceStorage newStorage = toNativePrimitiveNode.execute(inliningTarget, sequence.getSequenceStorage()); - sequence.setSequenceStorage(newStorage); - return sequence; - } - } - @Builtin(name = "clear_interop_type_registry", maxNumOfPositionalArgs = 0) @GenerateNodeFactory public abstract static class ClearInteropTypeRegistry extends PythonBuiltinNode { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java index 67a5506dde..b54fc9dc13 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java @@ -510,8 +510,8 @@ static Object execvArgs(VirtualFrame frame, PosixPath path, Object argv, @Bind PythonContext context, @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Cached InlinedConditionProfile isPListProfile, - @Cached PyTupleCheckNode tupleCheck, @Cached TupleNodes.GetTupleStorage getTupleStorage, + @Cached PyTupleCheckNode tupleCheckNode, @Cached ToArrayNode toArrayNode, @Cached ObjectToOpaquePathNode toOpaquePathNode, @Cached SysModuleBuiltins.AuditNode auditNode, @@ -522,7 +522,7 @@ static Object execvArgs(VirtualFrame frame, PosixPath path, Object argv, SequenceStorage argvStorage; if (isPListProfile.profile(inliningTarget, argv instanceof PList)) { argvStorage = ((PList) argv).getSequenceStorage(); - } else if (tupleCheck.execute(inliningTarget, argv)) { + } else if (tupleCheckNode.execute(inliningTarget, argv)) { argvStorage = getTupleStorage.execute(inliningTarget, argv); } else { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.ARG_D_MUST_BE_S, "execv()", 2, "tuple or list"); @@ -2037,9 +2037,10 @@ static long[] now(VirtualFrame frame, PNone times, PNone ns) { return null; } - @Specialization(guards = {"isTuple(times)", "isNoValue(ns)"}) + @Specialization(guards = {"tupleCheckNode.execute(inliningTarget, times)", "isNoValue(ns)"}, limit = "1") static long[] times(VirtualFrame frame, Object times, @SuppressWarnings("unused") PNone ns, @Bind Node inliningTarget, + @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheckNode, @Exclusive @Cached TupleNodes.GetTupleStorage getTupleStorage, @Exclusive @Cached SequenceStorageNodes.GetItemScalarNode getItemNode, @Cached ObjectToTimespecNode objectToTimespecNode, @@ -2047,9 +2048,10 @@ static long[] times(VirtualFrame frame, Object times, @SuppressWarnings("unused" return convertToTimespec(frame, inliningTarget, getTupleStorage.execute(inliningTarget, times), getItemNode, objectToTimespecNode, raiseNode); } - @Specialization(guards = "isTuple(ns)") + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, ns)", limit = "1") static long[] ns(VirtualFrame frame, @SuppressWarnings("unused") PNone times, Object ns, @Bind Node inliningTarget, + @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheckNode, @Exclusive @Cached TupleNodes.GetTupleStorage getTupleStorage, @Exclusive @Cached SequenceStorageNodes.GetItemScalarNode getItemNode, @Cached SplitLongToSAndNsNode splitLongToSAndNsNode, @@ -2064,18 +2066,20 @@ static long[] bothSpecified(VirtualFrame frame, Object times, Object ns, throw PRaiseNode.raiseStatic(inliningTarget, ValueError, ErrorMessages.YOU_MAY_SPECIFY_EITHER_OR_BUT_NOT_BOTH, "utime", "times", "ns"); } - @Specialization(guards = {"!isPNone(times)", "!isTuple(times)", "isNoValue(ns)"}) + @Specialization(guards = {"!isPNone(times)", "!tupleCheckNode.execute(inliningTarget, times)", "isNoValue(ns)"}, limit = "1") @SuppressWarnings("unused") static long[] timesNotATuple(VirtualFrame frame, Object times, PNone ns, @Bind Node inliningTarget, + @Exclusive @Cached PyTupleCheckNode tupleCheckNode, @Exclusive @Cached PRaiseNode raiseNode) { throw timesTupleError(inliningTarget, raiseNode); } - @Specialization(guards = {"!isNoValue(ns)", "!isTuple(ns)"}) + @Specialization(guards = {"!isNoValue(ns)", "!tupleCheckNode.execute(inliningTarget, ns)"}, limit = "1") @SuppressWarnings("unused") static long[] nsNotATuple(VirtualFrame frame, PNone times, Object ns, - @Bind Node inliningTarget) { + @Bind Node inliningTarget, + @Exclusive @Cached PyTupleCheckNode tupleCheckNode) { // ns can actually also contain objects implementing __divmod__, but CPython produces // this error message throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.MUST_BE, "utime", "ns", "a tuple of two ints"); @@ -3265,12 +3269,14 @@ static void doLong(long value, long[] timespec, int offset) { @Specialization(guards = {"!isInteger(value)"}) static void doGeneric(VirtualFrame frame, Node inliningTarget, Object value, long[] timespec, int offset, @Cached PyNumberDivmodNode divmodNode, + @Cached TupleNodes.GetTupleStorage getTupleStorage, + @Cached PyTupleCheckNode tupleCheckNode, @Cached(value = "createNotNormalized()", inline = false) GetItemNode getItemNode, @Cached PyLongAsLongNode asLongNode, @Cached PRaiseNode raiseNode) { Object divmod = divmodNode.execute(frame, inliningTarget, value, BILLION); - if (divmod instanceof PTuple tuple) { - SequenceStorage storage = tuple.getSequenceStorage(); + if (tupleCheckNode.execute(inliningTarget, divmod)) { + SequenceStorage storage = getTupleStorage.execute(inliningTarget, divmod); if (storage.length() == 2) { timespec[offset] = asLongNode.execute(frame, inliningTarget, getItemNode.execute(storage, 0)); timespec[offset + 1] = asLongNode.execute(frame, inliningTarget, getItemNode.execute(storage, 1)); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixSubprocessModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixSubprocessModuleBuiltins.java index 3092560d74..519654fa9d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixSubprocessModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixSubprocessModuleBuiltins.java @@ -63,14 +63,15 @@ import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.GetItemNode; import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum; import com.oracle.graal.python.builtins.objects.list.PList; -import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.lib.PyObjectGetItem; import com.oracle.graal.python.lib.PyObjectSizeNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PConstructAndRaiseNode; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.builtins.ListNodes.FastConstructListNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonClinicBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentCastNode; @@ -252,6 +253,8 @@ static int forkExec(VirtualFrame frame, Object[] args, Object executableList, bo @CachedLibrary("getPosixSupport()") PosixSupportLibrary posixLib, @Bind Node inliningTarget, @Cached("createNotNormalized()") GetItemNode tupleGetItem, + @Cached TupleNodes.GetTupleStorage getTupleStorage, + @Cached PyTupleCheckNode tupleCheckNode, @Cached PyObjectGetItem getItem, @Cached CastToJavaIntExactNode castToIntNode, @Cached ObjectToOpaquePathNode objectToOpaquePathNode, @@ -266,11 +269,11 @@ static int forkExec(VirtualFrame frame, Object[] args, Object executableList, bo if (closeFds && errPipeWrite < 3) { throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.S_MUST_BE_S, "errpipe_write", ">= 3"); } - if (!(fdsToKeepObj instanceof PTuple)) { + if (!tupleCheckNode.execute(inliningTarget, fdsToKeepObj)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.ARG_D_MUST_BE_S_NOT_P, "fork_exec()", 4, "tuple", fdsToKeepObj); } Object[] processArgs = args; - int[] fdsToKeep = convertFdSequence(inliningTarget, (PTuple) fdsToKeepObj, tupleGetItem, castToIntNode, raiseNode); + int[] fdsToKeep = convertFdSequence(inliningTarget, getTupleStorage.execute(inliningTarget, fdsToKeepObj), tupleGetItem, castToIntNode, raiseNode); Object cwd = PGuards.isPNone(cwdObj) ? null : objectToOpaquePathNode.execute(frame, inliningTarget, cwdObj, false); PythonContext context = PythonContext.get(inliningTarget); @@ -319,8 +322,7 @@ static int forkExec(VirtualFrame frame, Object[] args, Object executableList, bo * Checks that the tuple contains only valid fds (positive integers fitting into an int) in * ascending order. */ - private static int[] convertFdSequence(Node inliningTarget, PTuple fdSequence, GetItemNode getItemNode, CastToJavaIntExactNode castToIntNode, PRaiseNode raiseNode) { - SequenceStorage storage = fdSequence.getSequenceStorage(); + private static int[] convertFdSequence(Node inliningTarget, SequenceStorage storage, GetItemNode getItemNode, CastToJavaIntExactNode castToIntNode, PRaiseNode raiseNode) { int len = storage.length(); int[] fds = new int[len]; int prevFd = -1; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SocketModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SocketModuleBuiltins.java index c2a3bdf724..765a7ab0e2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SocketModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SocketModuleBuiltins.java @@ -88,10 +88,12 @@ import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.lib.PyLongAsIntNode; import com.oracle.graal.python.lib.PyLongAsLongNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PConstructAndRaiseNode; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; @@ -125,6 +127,7 @@ import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateNodeFactory; +import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; @@ -544,11 +547,14 @@ protected ArgumentClinicProvider getArgumentClinic() { @Builtin(name = "getnameinfo", minNumOfPositionalArgs = 2, numOfPositionalOnlyArgs = 2, parameterNames = {"sockaddr", "flags"}) @ArgumentClinic(name = "flags", conversion = ArgumentClinic.ClinicConversion.Int) @GenerateNodeFactory + @ImportStatic(PGuards.class) public abstract static class GetNameInfoNode extends PythonBinaryClinicBuiltinNode { - @Specialization - static Object getNameInfo(VirtualFrame frame, PTuple sockaddr, int flags, + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, sockaddr)", limit = "1") + static Object getNameInfo(VirtualFrame frame, Object sockaddr, int flags, @Bind Node inliningTarget, @Bind PythonContext context, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, + @Cached GetTupleStorage getTupleStorage, @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @CachedLibrary(limit = "1") AddrInfoCursorLibrary addrInfoCursorLib, @CachedLibrary(limit = "1") UniversalSockAddrLibrary sockAddrLibrary, @@ -560,7 +566,7 @@ static Object getNameInfo(VirtualFrame frame, PTuple sockaddr, int flags, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, @Cached TruffleString.FromLongNode fromLongNode, @Cached PRaiseNode raiseNode) { - SequenceStorage addr = sockaddr.getSequenceStorage(); + SequenceStorage addr = getTupleStorage.execute(inliningTarget, sockaddr); int addrLen = addr.length(); if (addrLen < 2 || addrLen > 4) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.ILLEGAL_SOCKET_ADDR_ARG, "getnameinfo()"); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java index 7eddea3b60..bbd4d5ee00 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java @@ -2252,8 +2252,8 @@ static Object exitNoCode(PythonModule sys, PNone status, @Specialization(guards = "!isPNone(status)") static Object exit(VirtualFrame frame, @SuppressWarnings("unused") PythonModule sys, Object status, @Bind Node inliningTarget, - @Cached PyTupleCheckNode tupleCheckNode, @Cached TupleBuiltins.LenNode tupleLenNode, + @Cached PyTupleCheckNode tupleCheckNode, @Cached PyTupleGetItem getItemNode) { Object code = status; if (tupleCheckNode.execute(inliningTarget, status)) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TimeModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TimeModuleBuiltins.java index a09bafc73a..0b6665fb07 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TimeModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TimeModuleBuiltins.java @@ -62,7 +62,6 @@ import com.oracle.graal.python.builtins.modules.TimeModuleBuiltinsClinicProviders.StrfTimeNodeClinicProviderGen; import com.oracle.graal.python.builtins.modules.TimeModuleBuiltinsClinicProviders.StrptimeNodeClinicProviderGen; import com.oracle.graal.python.builtins.objects.PNone; -import com.oracle.graal.python.builtins.objects.common.SequenceNodes.GetObjectArrayNode; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.ints.PInt; import com.oracle.graal.python.builtins.objects.module.PythonModule; @@ -75,10 +74,13 @@ import com.oracle.graal.python.lib.PyLongAsLongNode; import com.oracle.graal.python.lib.PyNumberAsSizeNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToPythonObjectNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; @@ -94,6 +96,7 @@ import com.oracle.graal.python.runtime.GilNode; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.object.PFactory; +import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleOptions; @@ -106,6 +109,7 @@ import com.oracle.truffle.api.dsl.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateNodeFactory; +import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; @@ -617,6 +621,7 @@ public static SleepNode create() { @Builtin(name = "strftime", minNumOfPositionalArgs = 2, declaresExplicitSelf = true, parameterNames = {"$self", "format", "time"}) @ArgumentClinic(name = "format", conversion = ArgumentClinic.ClinicConversion.TString) @GenerateNodeFactory + @ImportStatic(PGuards.class) public abstract static class StrfTimeNode extends PythonTernaryClinicBuiltinNode { @Override protected ArgumentClinicProvider getArgumentClinic() { @@ -635,12 +640,12 @@ private static String timeFormat(int[] date) { return PythonUtils.formatJString("%02d:%02d:%02d", date[TM_HOUR], date[TM_MIN], date[TM_SEC]); } - protected static int[] checkStructtime(VirtualFrame frame, Node inliningTarget, PTuple time, + protected static int[] checkStructtime(VirtualFrame frame, Node inliningTarget, SequenceStorage timeStorage, SequenceStorageNodes.GetInternalObjectArrayNode getInternalObjectArrayNode, PyNumberAsSizeNode asSizeNode, PRaiseNode raiseNode) { - Object[] otime = getInternalObjectArrayNode.execute(inliningTarget, time.getSequenceStorage()); - if (time.getSequenceStorage().length() != 9) { + Object[] otime = getInternalObjectArrayNode.execute(inliningTarget, timeStorage); + if (timeStorage.length() != 9) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.S_ILLEGAL_TIME_TUPLE_ARG, "asctime()"); } int[] date = new int[9]; @@ -987,16 +992,18 @@ static TruffleString formatTime(PythonModule module, TruffleString format, @Supp return format(toJavaStringNode.execute(format), getIntLocalTimeStruct(moduleState.currentZoneId, (long) timeSeconds()), getTimeZone(moduleState.currentZoneId), fromJavaStringNode); } - @Specialization - static TruffleString formatTime(VirtualFrame frame, PythonModule module, TruffleString format, PTuple time, + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, time)", limit = "1") + static TruffleString formatTime(VirtualFrame frame, PythonModule module, TruffleString format, Object time, @Bind Node inliningTarget, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, + @Cached GetTupleStorage getTupleStorage, @Cached SequenceStorageNodes.GetInternalObjectArrayNode getArray, @Cached PyNumberAsSizeNode asSizeNode, @Shared("byteIndexOfCp") @Cached TruffleString.ByteIndexOfCodePointNode byteIndexOfCodePointNode, @Shared("ts2js") @Cached ToJavaStringNode toJavaStringNode, @Shared("js2ts") @Cached TruffleString.FromJavaStringNode fromJavaStringNode, @Exclusive @Cached PRaiseNode raiseNode) { - int[] date = checkStructtime(frame, inliningTarget, time, getArray, asSizeNode, raiseNode); + int[] date = checkStructtime(frame, inliningTarget, getTupleStorage.execute(inliningTarget, time), getArray, asSizeNode, raiseNode); return format(toJavaStringNode.execute(format), date, getTimeZone(module.getModuleState(ModuleState.class).currentZoneId), fromJavaStringNode); } @@ -1014,20 +1021,25 @@ static TruffleString formatTime(PythonModule module, TruffleString format, Objec "time zones; instead the returned value will either be equal to that\n" + "of the timezone or altzone attributes on the time module.") @GenerateNodeFactory + @ImportStatic(PGuards.class) abstract static class MkTimeNode extends PythonBinaryBuiltinNode { private static final int ELEMENT_COUNT = 9; - @Specialization @ExplodeLoop - static double mktime(VirtualFrame frame, PythonModule module, PTuple tuple, + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, tuple)", limit = "1") + static double mktime(VirtualFrame frame, PythonModule module, Object tuple, @Bind Node inliningTarget, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, + @Cached GetTupleStorage getTupleStorage, @Cached PyNumberAsSizeNode asSizeNode, - @Cached GetObjectArrayNode getObjectArrayNode, + @Cached SequenceStorageNodes.GetInternalObjectArrayNode getArray, @Cached PRaiseNode raiseNode) { - Object[] items = getObjectArrayNode.execute(inliningTarget, tuple); - if (items.length != ELEMENT_COUNT) { - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FUNC_TAKES_EXACTLY_D_ARGS, ELEMENT_COUNT, items.length); + SequenceStorage storage = getTupleStorage.execute(inliningTarget, tuple); + int storageLength = storage.length(); + if (storageLength != ELEMENT_COUNT) { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FUNC_TAKES_EXACTLY_D_ARGS, ELEMENT_COUNT, storageLength); } + Object[] items = getArray.execute(inliningTarget, storage); int[] integers = new int[ELEMENT_COUNT]; for (int i = 0; i < ELEMENT_COUNT; i++) { integers[i] = asSizeNode.executeExact(frame, inliningTarget, items[i]); @@ -1066,6 +1078,7 @@ protected static TruffleString format(int[] tm, TruffleString.FromJavaStringNode // time.asctime([t]) @Builtin(name = "asctime", maxNumOfPositionalArgs = 2, declaresExplicitSelf = true) @GenerateNodeFactory + @ImportStatic(PGuards.class) public abstract static class ASCTimeNode extends PythonBinaryBuiltinNode { static final String[] WDAY_NAME = new String[]{ @@ -1083,14 +1096,16 @@ static TruffleString localtime(PythonModule module, @SuppressWarnings("unused") return format(getIntLocalTimeStruct(moduleState.currentZoneId, (long) timeSeconds()), fromJavaStringNode); } - @Specialization - static TruffleString localtime(VirtualFrame frame, @SuppressWarnings("unused") PythonModule module, PTuple time, + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, time)", limit = "1") + static TruffleString localtime(VirtualFrame frame, @SuppressWarnings("unused") PythonModule module, Object time, @Bind Node inliningTarget, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, + @Cached GetTupleStorage getTupleStorage, @Cached SequenceStorageNodes.GetInternalObjectArrayNode getArray, @Cached PyNumberAsSizeNode asSizeNode, @Shared("js2ts") @Cached TruffleString.FromJavaStringNode fromJavaStringNode, @Cached PRaiseNode raiseNode) { - return format(StrfTimeNode.checkStructtime(frame, inliningTarget, time, getArray, asSizeNode, raiseNode), fromJavaStringNode); + return format(StrfTimeNode.checkStructtime(frame, inliningTarget, getTupleStorage.execute(inliningTarget, time), getArray, asSizeNode, raiseNode), fromJavaStringNode); } @Fallback diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WarningsModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WarningsModuleBuiltins.java index a303144e2f..d8ca582b2c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WarningsModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WarningsModuleBuiltins.java @@ -79,6 +79,7 @@ import com.oracle.graal.python.builtins.modules.WarningsModuleBuiltinsClinicProviders.WarnBuiltinNodeClinicProviderGen; import com.oracle.graal.python.builtins.modules.WarningsModuleBuiltinsFactory.WarnBuiltinNodeFactory; import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.common.SequenceNodes.GetSequenceStorageNode; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.frame.PFrame; @@ -100,10 +101,12 @@ import com.oracle.graal.python.lib.PyObjectRichCompareBool.CachedPyObjectRichCompareBool; import com.oracle.graal.python.lib.PyObjectSetItem; import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.SpecialAttributeNames; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromPythonObjectNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.frame.ReadFrameNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; @@ -225,6 +228,7 @@ static final class WarningsModuleNode extends Node { @Child TruffleString.RegionEqualNode regionEqualNode; @Child TruffleString.EqualNode equalNode; @Child TruffleString.SubstringNode substringNode; + @Child GetSequenceStorageNode tupleStorageNode; @NeverDefault static WarningsModuleNode create() { @@ -311,6 +315,14 @@ private SequenceStorageNodes.GetItemScalarNode getSequenceGetItemNode() { return sequenceGetItemNode; } + private GetSequenceStorageNode getTupleStorageNode() { + if (tupleStorageNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + tupleStorageNode = insert(GetSequenceStorageNode.create()); + } + return tupleStorageNode; + } + private Object getPythonClass(Object object) { if (getClassNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); @@ -571,10 +583,10 @@ private TruffleString getFilter(VirtualFrame frame, BoundaryCallData boundaryCal SequenceStorageNodes.GetItemScalarNode sequenceGetItem = getSequenceGetItemNode(); for (int i = 0; i < filtersStorage.length(); i++) { Object tmpItem = sequenceGetItem.executeCached(filtersStorage, i); - if (!(tmpItem instanceof PTuple)) { + if (!PyTupleCheckNode.executeUncached(tmpItem)) { throw PRaiseNode.raiseStatic(this, PythonBuiltinClassType.ValueError, ErrorMessages.WARN_FILTERS_IETM_ISNT_5TUPLE, i); } - SequenceStorage tmpStorage = ((PTuple) tmpItem).getSequenceStorage(); + SequenceStorage tmpStorage = getTupleStorageNode().execute(this, tmpItem); if (tmpStorage.length() != 5) { throw PRaiseNode.raiseStatic(this, PythonBuiltinClassType.ValueError, ErrorMessages.WARN_FILTERS_IETM_ISNT_5TUPLE, i); } @@ -989,12 +1001,14 @@ Object doWarn(VirtualFrame frame, PythonModule mod, Object message, Object categ @Cached("createFor($node)") BoundaryCallData boundaryCallData, @Cached SequenceStorageNodes.GetItemScalarNode getItemScalarNode, @Cached StringNodes.CastToTruffleStringChecked1Node castToStringChecked, + @Cached TupleNodes.GetTupleStorage getTupleStorage, + @Cached PyTupleCheckNode tupleCheckNode, @Cached PRaiseNode raiseNode, @Cached WarningsModuleNode moduleFunctionsNode) { // warnings_warn_impl TruffleString[] skipFilePrefixes = null; - if (skipFilePrefixesObj instanceof PTuple tuple) { - SequenceStorage storage = tuple.getSequenceStorage(); + if (tupleCheckNode.execute(inliningTarget, skipFilePrefixesObj)) { + SequenceStorage storage = getTupleStorage.execute(inliningTarget, skipFilePrefixesObj); if (storage.length() > 0) { skipFilePrefixes = new TruffleString[storage.length()]; for (int i = 0; i < storage.length(); i++) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextErrBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextErrBuiltins.java index c032befcfd..c38b70518d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextErrBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextErrBuiltins.java @@ -99,10 +99,12 @@ import com.oracle.graal.python.lib.PyObjectSetAttr; import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; import com.oracle.graal.python.lib.PyTupleCheckNode; +import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.WriteUnraisableNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode; import com.oracle.graal.python.nodes.builtins.TupleNodes.ConstructTupleNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.EnsureManagedTupleNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; import com.oracle.graal.python.nodes.object.GetClassNode; @@ -522,10 +524,16 @@ static Object get(Object exc, abstract static class PyException_SetArgs extends CApiBinaryBuiltinNode { @Specialization - static Object set(PBaseException exc, PTuple args, + static Object set(PBaseException exc, Object args, @Bind Node inliningTarget, + @Cached EnsureManagedTupleNode ensureManagedTupleNode, @Cached ExceptionNodes.SetArgsNode setArgsNode) { - setArgsNode.execute(inliningTarget, exc, args); + PTuple execute = ensureManagedTupleNode.execute(inliningTarget, args); + if (execute == null) { + // CPython doesn't do that check but they just expect that to be a tuple. We need to convert to PTuple, so we need to check eagerly. + throw PRaiseNode.raiseStatic(inliningTarget, SystemError, ErrorMessages.BAD_ARG_TO_INTERNAL_FUNC_WAS_S_P, args, args); + } + setArgsNode.execute(inliningTarget, exc, execute); return PNone.NO_VALUE; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextGenericAliasBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextGenericAliasBuiltins.java index bb8895a010..9baf679d5d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextGenericAliasBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextGenericAliasBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -47,18 +47,32 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBinaryBuiltinNode; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin; +import com.oracle.graal.python.builtins.objects.tuple.PTuple; +import com.oracle.graal.python.nodes.builtins.TupleNodes; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.dsl.Bind; +import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; public final class PythonCextGenericAliasBuiltins { + private PythonCextGenericAliasBuiltins() { + } + @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Direct) abstract static class Py_GenericAlias extends CApiBinaryBuiltinNode { @Specialization static Object genericAlias(Object origin, Object args, - @Bind PythonLanguage language) { - return PFactory.createGenericAlias(language, origin, args); + @Bind Node inliningTarget, + @Bind PythonLanguage language, + @Cached TupleNodes.EnsureManagedTupleNode ensureManagedTupleNode) { + PTuple argsTuple = ensureManagedTupleNode.execute(inliningTarget, args); + if (argsTuple == null) { + // in this case, 'args' is not a tuple object + argsTuple = PFactory.createTuple(language, new Object[]{args}); + } + return PFactory.createGenericAlias(language, origin, argsTuple); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextObjectBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextObjectBuiltins.java index 736485da88..b7541a0397 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextObjectBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextObjectBuiltins.java @@ -304,8 +304,8 @@ static Object doGeneric(@SuppressWarnings("unused") long threadState, Object cal @Cached ExpandKeywordStarargsNode castKwargsNode, @Cached SequenceStorageNodes.GetItemScalarNode getItemScalarNode, @Cached CallNode callNode, - @Cached PyTupleCheckNode tupleCheck, @Cached GetTupleStorage getTupleStorage, + @Cached PyTupleCheckNode tupleCheckNode, @Cached CastToTruffleStringNode castToTruffleStringNode) { try { @@ -315,7 +315,7 @@ static Object doGeneric(@SuppressWarnings("unused") long threadState, Object cal keywords = PKeyword.EMPTY_KEYWORDS; } else if (kwargs instanceof PDict) { keywords = castKwargsNode.execute(inliningTarget, kwargs); - } else if (tupleCheck.execute(inliningTarget, kwargs)) { + } else if (tupleCheckNode.execute(inliningTarget, kwargs)) { // We have a tuple with kw names and an array with kw values SequenceStorage storage = getTupleStorage.execute(inliningTarget, kwargs); int kwcount = storage.length(); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTupleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTupleBuiltins.java index e4409a8319..b210f92929 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTupleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTupleBuiltins.java @@ -73,7 +73,7 @@ import com.oracle.graal.python.lib.PyTupleSizeNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.builtins.TupleNodes.GetNativeTupleStorage; +import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.runtime.sequence.storage.NativeSequenceStorage; @@ -125,12 +125,11 @@ static Object doPTuple(PTuple tuple, long key, static Object doNative(PythonAbstractNativeObject tuple, long key, @Bind Node inliningTarget, @Bind PythonContext context, - @Exclusive @Cached GetNativeTupleStorage asNativeStorage, @Exclusive @Cached EnsurePythonObjectNode ensureNode, @Exclusive @Cached SetItemScalarNode setItemNode, @Exclusive @Cached GetItemScalarNode getItemNode, @Exclusive @Cached PRaiseNode raiseNode) { - SequenceStorage sequenceStorage = asNativeStorage.execute(tuple); + SequenceStorage sequenceStorage = GetTupleStorage.doNative(tuple); int index = checkIndex(inliningTarget, key, sequenceStorage, raiseNode); Object result = getItemNode.execute(inliningTarget, sequenceStorage, index); if (result == null) { @@ -185,9 +184,8 @@ static Object getSlice(PTuple tuple, Object iLow, Object iHigh, static Object doNative(PythonAbstractNativeObject tuple, Object iLow, Object iHigh, @Bind Node inliningTarget, @Shared("getItem") @Cached("createForTuple()") SequenceStorageNodes.GetItemNode getItemNode, - @Shared("newSlice") @Cached PySliceNew sliceNode, - @Cached GetNativeTupleStorage asNativeStorage) { - return doGetSlice(asNativeStorage.execute(tuple), inliningTarget, iLow, iHigh, getItemNode, sliceNode); + @Shared("newSlice") @Cached PySliceNew sliceNode) { + return doGetSlice(GetTupleStorage.doNative(tuple), inliningTarget, iLow, iHigh, getItemNode, sliceNode); } @SuppressWarnings("unused") diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextUnicodeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextUnicodeBuiltins.java index d3ff63eabb..c539855181 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextUnicodeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextUnicodeBuiltins.java @@ -919,12 +919,7 @@ static long GraalPyPrivate_Unicode_DecodeFSDefaultAndSize(long s, long lsize) { try { int size = PInt.intValueExact(lsize); TruffleString candidate = TruffleString.fromNativePointerUncached(s, 0, size, UTF_8, true); - TruffleString str; - if (candidate.isValidUncached(UTF_8)) { - str = candidate.switchEncodingUncached(TS_ENCODING); - } else { - str = candidate.switchEncodingUncached(TS_ENCODING, SURROGATE_ESCAPE_FROM_UTF8_TRANSCODING_ERROR_HANDLER); - } + TruffleString str = candidate.switchEncodingUncached(TS_ENCODING, SURROGATE_ESCAPE_FROM_UTF8_TRANSCODING_ERROR_HANDLER); // implicitly promotes TruffleString to PString return PythonToNativeInternalNode.executeNewRefUncached(str); } catch (OverflowException e) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/MultibyteCodecUtil.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/MultibyteCodecUtil.java index 93a2aa4b1e..e3971e0d70 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/MultibyteCodecUtil.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/MultibyteCodecUtil.java @@ -138,20 +138,17 @@ static TruffleString internalErrorCallback(TruffleString errors, } @GenerateInline - @GenerateCached(alwaysInlineCached = true) + @GenerateCached(false) abstract static class CallErrorCallbackNode extends Node { - abstract Object execute(VirtualFrame frame, Node inliningTarget, Object errors, Object exc); + abstract Object execute(VirtualFrame frame, Node inliningTarget, TruffleString errors, Object exc); // call_error_callback @Specialization - static Object callErrorCallback(VirtualFrame frame, Node inliningTarget, Object errors, Object exc, - @Cached CastToTruffleStringNode castToStringNode, + static Object callErrorCallback(VirtualFrame frame, Node inliningTarget, TruffleString errors, Object exc, @Cached PyCodecLookupErrorNode lookupErrorNode, @Cached(inline = false) CallNode callNode) { - assert (PyUnicodeCheckNode.executeUncached(errors)); - TruffleString str = castToStringNode.execute(inliningTarget, errors); - Object cb = lookupErrorNode.execute(frame, inliningTarget, str); + Object cb = lookupErrorNode.execute(frame, inliningTarget, errors); return callNode.execute(frame, cb, exc); } } @@ -164,17 +161,16 @@ abstract static class EncodeErrorNode extends Node { abstract int execute(VirtualFrame frame, MultibyteCodec codec, MultibyteCodecState state, MultibyteEncodeBuffer buf, - Object errors, int e); + TruffleString errors, int e); // multibytecodec_encerror @Specialization static int encerror(VirtualFrame frame, MultibyteCodec codec, MultibyteCodecState state, - MultibyteEncodeBuffer buf, Object errors, int e, + MultibyteEncodeBuffer buf, TruffleString errors, int e, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached BaseExceptionAttrNode attrNode, - @Cached PyTupleCheckNode tupleCheckNode, @Cached TupleNodes.GetTupleStorage getTupleStorage, @Cached SequenceStorageNodes.GetItemScalarNode getItemNode, @Cached PyUnicodeCheckNode unicodeCheckNode, @@ -183,7 +179,8 @@ static int encerror(VirtualFrame frame, MultibyteCodec codec, @Cached PyLongCheckNode longCheckNode, @Cached PyBytesCheckNode bytesCheckNode, @Cached PyLongAsIntNode asSizeNode, - @Cached(inline = true) CallErrorCallbackNode callErrorCallbackNode, + @Cached PyTupleCheckNode tupleCheckNode, + @Cached CallErrorCallbackNode callErrorCallbackNode, @Cached BytesNodes.ToBytesNode toBytesNode, @Cached EncodeNode encodeNode, @Cached PRaiseNode raiseNode) { @@ -329,14 +326,14 @@ static void decerror(VirtualFrame frame, MultibyteCodec codec, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached BaseExceptionAttrNode attrNode, - @Cached PyTupleCheckNode tupleCheckNode, @Cached TupleNodes.GetTupleStorage getTupleStorage, @Cached SequenceStorageNodes.GetItemScalarNode getItemNode, @Cached PyUnicodeCheckNode unicodeCheckNode, @Cached PyLongCheckNode longCheckNode, @Cached PyLongAsIntNode asSizeNode, + @Cached PyTupleCheckNode tupleCheckNode, @Cached CastToJavaStringNode toString, - @Cached(inline = true) CallErrorCallbackNode callErrorCallbackNode, + @Cached CallErrorCallbackNode callErrorCallbackNode, @Cached PRaiseNode raiseNode) { TruffleString reason = ILLEGAL_MULTIBYTE_SEQUENCE; @@ -438,11 +435,11 @@ protected static PBytes encodeEmptyInput(Node inliningTarget, int len, int flags @GenerateCached(false) abstract static class EncodeNode extends Node { - abstract PBytes execute(VirtualFrame frame, Node inliningTarget, MultibyteCodec codec, MultibyteCodecState state, MultibyteEncodeBuffer buf, Object errors, int flags); + abstract PBytes execute(VirtualFrame frame, Node inliningTarget, MultibyteCodec codec, MultibyteCodecState state, MultibyteEncodeBuffer buf, TruffleString errors, int flags); // multibytecodec_encode @Specialization - static PBytes encode(VirtualFrame frame, Node inliningTarget, MultibyteCodec codec, MultibyteCodecState state, MultibyteEncodeBuffer buf, Object errors, int flags, + static PBytes encode(VirtualFrame frame, Node inliningTarget, MultibyteCodec codec, MultibyteCodecState state, MultibyteEncodeBuffer buf, TruffleString errors, int flags, @Cached(inline = false) EncodeErrorNode encodeErrorNode, @Cached PRaiseNode raiseNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/MultibyteStreamWriterBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/MultibyteStreamWriterBuiltins.java index b5c3851b38..40317f7de0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/MultibyteStreamWriterBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/MultibyteStreamWriterBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -53,20 +53,21 @@ import java.util.List; +import com.oracle.graal.python.annotations.Builtin; import com.oracle.graal.python.annotations.Slot; import com.oracle.graal.python.annotations.Slot.SlotKind; import com.oracle.graal.python.annotations.Slot.SlotSignature; -import com.oracle.graal.python.annotations.Builtin; import com.oracle.graal.python.builtins.CoreFunctions; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.bytes.PBytes; -import com.oracle.graal.python.builtins.objects.common.SequenceNodes; -import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectGetAttr; +import com.oracle.graal.python.lib.PySequenceCheckNode; +import com.oracle.graal.python.lib.PySequenceGetItemNode; +import com.oracle.graal.python.lib.PySequenceSizeNode; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.StringLiterals; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; @@ -75,11 +76,8 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.object.PFactory; -import com.oracle.graal.python.runtime.sequence.PSequence; -import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; -import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; @@ -168,30 +166,28 @@ abstract static class WritelinesNode extends PythonBinaryBuiltinNode { // _multibytecodec_MultibyteStreamWriter_writelines @Specialization - static Object writelines(VirtualFrame frame, MultibyteStreamWriterObject self, PSequence lines, + static Object doGeneric(VirtualFrame frame, MultibyteStreamWriterObject self, Object lines, @Bind Node inliningTarget, + @Cached PySequenceCheckNode sequenceCheckNode, + @Cached PySequenceSizeNode sizeNode, + @Cached PySequenceGetItemNode getItemNode, @Cached MultibyteIncrementalEncoderBuiltins.EncodeStatefulNode encodeStatefulNode, - @Cached SequenceNodes.GetSequenceStorageNode getStorage, - @Cached SequenceStorageNodes.GetItemNode getItem, @Cached PyObjectCallMethodObjArgs callMethod) { - SequenceStorage sq = getStorage.execute(inliningTarget, lines); - for (int i = 0; i < sq.length(); i++) { + if (!sequenceCheckNode.execute(inliningTarget, lines)) { + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ARG_MUST_BE_A_SEQUENCE_OBJECT); + } + + int n = sizeNode.execute(frame, inliningTarget, lines); + for (int i = 0; i < n; i++) { /* length can be changed even within this loop */ - Object strobj = getItem.execute(sq, i); + Object strobj = getItemNode.execute(lines, i); // mbstreamwriter_iwrite Object str = encodeStatefulNode.execute(frame, self, strobj, 0); callMethod.execute(frame, inliningTarget, self.stream, WRITE, str); } return PNone.NONE; } - - // assuming !pySequenceCheck.execute(lines) - @Fallback - static Object writelines(@SuppressWarnings("unused") Object self, @SuppressWarnings("unused") Object lines, - @Bind Node inliningTarget) { - throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ARG_MUST_BE_A_SEQUENCE_OBJECT); - } } @Builtin(name = "reset", minNumOfPositionalArgs = 1, doc = "reset($self, /)\n--\n\n") diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/ErrorHandlers.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/ErrorHandlers.java index c7103d9d74..445cc11c6d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/ErrorHandlers.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/ErrorHandlers.java @@ -80,14 +80,16 @@ import com.oracle.graal.python.builtins.objects.exception.UnicodeEncodeErrorBuiltins.PyUnicodeEncodeOrTranslateErrorGetObjectNode; import com.oracle.graal.python.builtins.objects.exception.UnicodeEncodeErrorBuiltins.PyUnicodeEncodeOrTranslateErrorGetStartNode; import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked0Node; -import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.lib.PyBytesCheckNode; import com.oracle.graal.python.lib.PyNumberAsSizeNode; import com.oracle.graal.python.lib.PyObjectSizeNode; import com.oracle.graal.python.lib.PyObjectTypeCheck; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.runtime.IndirectCallData.InteropCallData; @@ -104,6 +106,7 @@ import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; @@ -845,16 +848,19 @@ public static final class DecodingErrorHandlerResult { @GenerateInline @GenerateCached(false) @GenerateUncached + @ImportStatic(PGuards.class) abstract static class ParseDecodingErrorHandlerResultNode extends Node { abstract DecodingErrorHandlerResult execute(VirtualFrame frame, Node inliningTarget, Object result); - @Specialization - static DecodingErrorHandlerResult doTuple(VirtualFrame frame, Node inliningTarget, PTuple result, + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, result)", limit = "1") + static DecodingErrorHandlerResult doTuple(VirtualFrame frame, Node inliningTarget, Object result, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, + @Cached GetTupleStorage getTupleStorage, @Cached SequenceStorageNodes.GetItemScalarNode getItemScalarNode, @Cached CastToTruffleStringChecked0Node castToTruffleStringCheckedNode, @Cached PyNumberAsSizeNode asSizeNode, @Cached PRaiseNode raiseNode) { - SequenceStorage storage = result.getSequenceStorage(); + SequenceStorage storage = getTupleStorage.execute(inliningTarget, result); if (storage.length() != 2) { throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.DECODING_ERROR_HANDLER_MUST_RETURN_STR_INT_TUPLE); } @@ -918,17 +924,20 @@ public static final class EncodingErrorHandlerResult { @GenerateInline @GenerateCached(false) @GenerateUncached + @ImportStatic(PGuards.class) abstract static class ParseEncodingErrorHandlerResultNode extends Node { abstract EncodingErrorHandlerResult execute(Frame frame, Node inliningTarget, Object result); - @Specialization - static EncodingErrorHandlerResult doTuple(VirtualFrame frame, Node inliningTarget, PTuple result, + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, result)", limit = "1") + static EncodingErrorHandlerResult doTuple(VirtualFrame frame, Node inliningTarget, Object result, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, + @Cached GetTupleStorage getTupleStorage, @Cached SequenceStorageNodes.GetItemScalarNode getItemScalarNode, @Cached PyNumberAsSizeNode asSizeNode, @Cached PyUnicodeCheckNode pyUnicodeCheckNode, @Cached PyBytesCheckNode pyBytesCheckNode, @Cached PRaiseNode raiseNode) { - SequenceStorage storage = result.getSequenceStorage(); + SequenceStorage storage = getTupleStorage.execute(inliningTarget, result); if (storage.length() != 2) { throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.ENCODING_ERROR_HANDLER_MUST_RETURN_STR_BYTES_INT_TUPLE); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaBuiltins.java index 7cf1b79071..6a2a2bc432 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaBuiltins.java @@ -86,6 +86,7 @@ import com.oracle.graal.python.lib.PyNumberTrueDivideNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectHashNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.lib.PyTupleGetItem; import com.oracle.graal.python.lib.PyTupleSizeNode; import com.oracle.graal.python.lib.RichCmpOp; @@ -721,7 +722,7 @@ private static Object toMicrosecondsUncached(TimeDeltaValue timeDelta) { * get_float_as_integer_ratio()) */ private static void validateAsIntegerRatioResult(Object object, Node inliningTarget, PRaiseNode raiseNode) { - if (!(object instanceof PTuple)) { + if (!PyTupleCheckNode.executeUncached(object)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.UNEXPECTED_RETURN_TYPE_FROM_AS_INTEGER_RATIO_EXPECTED_TUPLE_GOT_P, object); } if (PyTupleSizeNode.executeUncached(object) != 2) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/functools/PartialBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/functools/PartialBuiltins.java index 4d223f6cc3..c5972269ae 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/functools/PartialBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/functools/PartialBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -87,6 +87,7 @@ import com.oracle.graal.python.lib.PyObjectReprAsTruffleStringNode; import com.oracle.graal.python.lib.PyObjectStrAsTruffleStringNode; import com.oracle.graal.python.lib.PyTupleCheckExactNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.lib.PyTupleGetItem; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PGuards; @@ -346,21 +347,23 @@ static Object reduce(PPartial self, public abstract static class PartialSetStateNode extends PythonBinaryBuiltinNode { @Specialization - static Object setState(VirtualFrame frame, PPartial self, PTuple state, + static Object setState(VirtualFrame frame, PPartial self, Object state, @Bind Node inliningTarget, @Cached SetDictNode setDictNode, @Cached DeleteDictNode deleteDictNode, @Cached SequenceNodes.GetSequenceStorageNode storageNode, @Cached SequenceStorageNodes.ToArrayNode arrayNode, @Cached PyCallableCheckNode callableCheckNode, + @Cached PyTupleCheckNode tupleCheckNode, @Cached PyTupleCheckExactNode tupleCheckExactNode, @Cached PyDictCheckExactNode dictCheckExactNode, @Cached PyTupleGetItem getItemNode, @Cached TupleNodes.ConstructTupleNode constructTupleNode, + @Cached TupleNodes.GetTupleStorage getTupleStorage, @Cached HashingStorageCopy copyStorageNode, @Bind PythonLanguage language, @Cached PRaiseNode raiseNode) { - if (state.getSequenceStorage().length() != 4) { + if (!tupleCheckNode.execute(inliningTarget, state) || getTupleStorage.execute(inliningTarget, state).length() != 4) { throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, INVALID_PARTIAL_STATE); } @@ -370,7 +373,7 @@ static Object setState(VirtualFrame frame, PPartial self, PTuple state, final Object dict = getItemNode.execute(inliningTarget, state, 3); if (!callableCheckNode.execute(inliningTarget, function) || - !PGuards.isPTuple(fnArgs) || + !tupleCheckNode.execute(inliningTarget, fnArgs) || (fnKwargs != PNone.NONE && !PGuards.isDict(fnKwargs))) { throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, INVALID_PARTIAL_STATE); } @@ -378,10 +381,10 @@ static Object setState(VirtualFrame frame, PPartial self, PTuple state, self.setFn(function); final PTuple fnArgsTuple; - if (!tupleCheckExactNode.execute(inliningTarget, fnArgs)) { - fnArgsTuple = constructTupleNode.execute(frame, fnArgs); - } else { + if (fnArgs instanceof PTuple && tupleCheckExactNode.execute(inliningTarget, fnArgs)) { fnArgsTuple = (PTuple) fnArgs; + } else { + fnArgsTuple = constructTupleNode.execute(frame, fnArgs); } self.setArgs(inliningTarget, fnArgsTuple, storageNode, arrayNode); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BytesIOBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BytesIOBuiltins.java index 41f6370b94..5bc9da8838 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BytesIOBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BytesIOBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -97,7 +97,6 @@ import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.GetInternalObjectArrayNode; import com.oracle.graal.python.builtins.objects.dict.PDict; -import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotIterNext.TpIterNextBuiltin; @@ -107,8 +106,10 @@ import com.oracle.graal.python.lib.PyMemoryViewFromObject; import com.oracle.graal.python.lib.PyNumberAsSizeNode; import com.oracle.graal.python.lib.PyObjectGetIter; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; @@ -662,20 +663,25 @@ static Object doit(VirtualFrame frame, PBytesIO self, @GenerateNodeFactory abstract static class SetStateNode extends PythonBinaryBuiltinNode { @Specialization - static Object doit(VirtualFrame frame, PBytesIO self, PTuple state, + static Object doit(VirtualFrame frame, PBytesIO self, Object state, @Bind Node inliningTarget, + @Cached GetTupleStorage getTupleStorage, @Cached GetInternalObjectArrayNode getArray, @Cached WriteNode writeNode, @Cached PyIndexCheckNode indexCheckNode, @Cached PyNumberAsSizeNode asSizeNode, @Cached GetOrCreateDictNode getDict, @Cached HashingStorageAddAllToOther addAllToOtherNode, + @Cached PyTupleCheckNode tupleCheckNode, @Cached PRaiseNode raiseNode) { + if (!tupleCheckNode.execute(inliningTarget, state)) { + throw raiseNode.raise(inliningTarget, TypeError, P_SETSTATE_ARGUMENT_SHOULD_BE_D_TUPLE_GOT_P, self, 3, state); + } self.checkExports(inliningTarget, raiseNode); - SequenceStorage storage = state.getSequenceStorage(); + SequenceStorage storage = getTupleStorage.execute(inliningTarget, state); Object[] array = getArray.execute(inliningTarget, storage); if (storage.length() < 3) { - return notTuple(self, state, inliningTarget); + throw raiseNode.raise(inliningTarget, TypeError, P_SETSTATE_ARGUMENT_SHOULD_BE_D_TUPLE_GOT_P, self, 3, state); } /* * Reset the object to its default state. This is only needed to handle the case of @@ -716,12 +722,6 @@ static Object doit(VirtualFrame frame, PBytesIO self, PTuple state, } return PNone.NONE; } - - @Specialization(guards = "!isPTuple(state)") - static Object notTuple(PBytesIO self, Object state, - @Bind Node inliningTarget) { - throw PRaiseNode.raiseStatic(inliningTarget, TypeError, P_SETSTATE_ARGUMENT_SHOULD_BE_D_TUPLE_GOT_P, self, 3, state); - } } @Builtin(name = J_FLUSH, minNumOfPositionalArgs = 1) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/IncrementalNewlineDecoderBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/IncrementalNewlineDecoderBuiltins.java index 7231427974..3c8cb85c1c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/IncrementalNewlineDecoderBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/IncrementalNewlineDecoderBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -72,13 +72,16 @@ import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.bytes.PBytes; import com.oracle.graal.python.builtins.objects.common.SequenceNodes; +import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyIndexCheckNode; import com.oracle.graal.python.lib.PyNumberAsSizeNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; @@ -88,6 +91,7 @@ import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.object.PFactory; +import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Exclusive; @@ -320,9 +324,10 @@ static Object withDecoder(VirtualFrame frame, PNLDecoder self, @Cached PyIndexCheckNode indexCheckNode, @Cached PyNumberAsSizeNode asSizeNode, @Cached PyObjectCallMethodObjArgs callMethod, + @Cached PyTupleCheckNode tupleCheckNode, @Cached PRaiseNode raiseNode) { Object state = callMethod.execute(frame, inliningTarget, self.getDecoder(), T_GETSTATE); - if (!(state instanceof PTuple)) { + if (!tupleCheckNode.execute(inliningTarget, state)) { throw raiseNode.raise(inliningTarget, TypeError, ILLEGAL_STATE_ARGUMENT); } Object[] objects = getObjectArrayNode.execute(inliningTarget, state); @@ -343,14 +348,23 @@ static Object withDecoder(VirtualFrame frame, PNLDecoder self, abstract static class SetStateNode extends PythonBinaryBuiltinNode { @Specialization(guards = "!self.hasDecoder()") - static Object noDecoder(VirtualFrame frame, PNLDecoder self, PTuple state, + static Object noDecoder(VirtualFrame frame, PNLDecoder self, Object state, @Bind Node inliningTarget, - @Exclusive @Cached SequenceNodes.GetObjectArrayNode getObjectArrayNode, + @Exclusive @Cached GetTupleStorage getTupleStorage, + @Exclusive @Cached SequenceStorageNodes.GetInternalObjectArrayNode getArray, @Exclusive @Cached PyIndexCheckNode indexCheckNode, @Exclusive @Cached PyNumberAsSizeNode asSizeNode, + @Exclusive @Cached PyTupleCheckNode tupleCheckNode, @Exclusive @Cached PRaiseNode raiseNode) { - Object[] objects = getObjectArrayNode.execute(inliningTarget, state); - if (objects.length != 2 || !indexCheckNode.execute(inliningTarget, objects[1])) { + if (!tupleCheckNode.execute(inliningTarget, state)) { + return err(self, state, inliningTarget); + } + SequenceStorage storage = getTupleStorage.execute(inliningTarget, state); + if (storage.length() != 2) { + throw raiseNode.raise(inliningTarget, TypeError, ILLEGAL_STATE_ARGUMENT); + } + Object[] objects = getArray.execute(inliningTarget, storage); + if (!indexCheckNode.execute(inliningTarget, objects[1])) { throw raiseNode.raise(inliningTarget, TypeError, ILLEGAL_STATE_ARGUMENT); } int flag = asSizeNode.executeExact(frame, inliningTarget, objects[1]); @@ -359,16 +373,25 @@ static Object noDecoder(VirtualFrame frame, PNLDecoder self, PTuple state, } @Specialization(guards = "self.hasDecoder()") - static Object withDecoder(VirtualFrame frame, PNLDecoder self, PTuple state, + static Object withDecoder(VirtualFrame frame, PNLDecoder self, Object state, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Exclusive @Cached SequenceNodes.GetObjectArrayNode getObjectArrayNode, + @Exclusive @Cached GetTupleStorage getTupleStorage, + @Exclusive @Cached SequenceStorageNodes.GetInternalObjectArrayNode getArray, @Exclusive @Cached PyIndexCheckNode indexCheckNode, @Exclusive @Cached PyNumberAsSizeNode asSizeNode, @Cached PyObjectCallMethodObjArgs callMethod, + @Exclusive @Cached PyTupleCheckNode tupleCheckNode, @Exclusive @Cached PRaiseNode raiseNode) { - Object[] objects = getObjectArrayNode.execute(inliningTarget, state); - if (objects.length != 2 || !indexCheckNode.execute(inliningTarget, objects[1])) { + if (!tupleCheckNode.execute(inliningTarget, state)) { + return err(self, state, inliningTarget); + } + SequenceStorage storage = getTupleStorage.execute(inliningTarget, state); + if (storage.length() != 2) { + throw raiseNode.raise(inliningTarget, TypeError, ILLEGAL_STATE_ARGUMENT); + } + Object[] objects = getArray.execute(inliningTarget, storage); + if (!indexCheckNode.execute(inliningTarget, objects[1])) { throw raiseNode.raise(inliningTarget, TypeError, ILLEGAL_STATE_ARGUMENT); } int flag = asSizeNode.executeExact(frame, inliningTarget, objects[1]); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/StringIOBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/StringIOBuiltins.java index b1c2a16f1b..84b2bc626f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/StringIOBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/StringIOBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -100,7 +100,6 @@ import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.str.StringNodes.StringReplaceNode; -import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotIterNext.TpIterNextBuiltin; @@ -109,8 +108,10 @@ import com.oracle.graal.python.lib.PyNumberIndexNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectGetAttr; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; @@ -662,8 +663,9 @@ static Object doit(VirtualFrame frame, PStringIO self, abstract static class SetStateNode extends PythonBinaryBuiltinNode { @Specialization(guards = {"!self.isClosed()"}) - static Object doit(VirtualFrame frame, PStringIO self, PTuple state, + static Object doit(VirtualFrame frame, PStringIO self, Object state, @Bind Node inliningTarget, + @Cached GetTupleStorage getTupleStorage, @Cached SequenceStorageNodes.GetInternalObjectArrayNode getArray, @Cached InitNode initNode, @Cached CastToTruffleStringNode toString, @@ -673,11 +675,15 @@ static Object doit(VirtualFrame frame, PStringIO self, PTuple state, @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Cached TruffleStringBuilder.AppendStringNode appendStringNode, @Cached HashingStorageAddAllToOther addAllToOtherNode, + @Cached PyTupleCheckNode tupleCheckNode, @Cached PRaiseNode raiseNode) { - SequenceStorage storage = state.getSequenceStorage(); + if (!tupleCheckNode.execute(inliningTarget, state)) { + throw raiseNode.raise(inliningTarget, TypeError, P_SETSTATE_ARGUMENT_SHOULD_BE_D_TUPLE_GOT_P, self, 4, state); + } + SequenceStorage storage = getTupleStorage.execute(inliningTarget, state); Object[] array = getArray.execute(inliningTarget, storage); if (storage.length() < 4) { - return notTuple(self, state, inliningTarget); + throw raiseNode.raise(inliningTarget, TypeError, P_SETSTATE_ARGUMENT_SHOULD_BE_D_TUPLE_GOT_P, self, 4, state); } initNode.execute(frame, self, array[0], array[1]); /* @@ -726,12 +732,6 @@ static Object doit(VirtualFrame frame, PStringIO self, PTuple state, return PNone.NONE; } - @Specialization(guards = {"!self.isClosed()", "!isPTuple(state)"}) - static Object notTuple(PStringIO self, Object state, - @Bind Node inliningTarget) { - throw PRaiseNode.raiseStatic(inliningTarget, TypeError, P_SETSTATE_ARGUMENT_SHOULD_BE_D_TUPLE_GOT_P, self, 4, state); - } - @Specialization(guards = "self.isClosed()") static Object closedError(@SuppressWarnings("unused") PStringIO self, @SuppressWarnings("unused") Object arg, @Bind Node inliningTarget) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperBuiltins.java index f4cdabfe1d..48cdb1c5ef 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -143,7 +143,6 @@ import com.oracle.graal.python.builtins.objects.ints.PInt; import com.oracle.graal.python.builtins.objects.str.StringNodes.StringReplaceNode; import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; -import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotIterNext.TpIterNextBuiltin; @@ -158,6 +157,7 @@ import com.oracle.graal.python.lib.PyObjectReprAsTruffleStringNode; import com.oracle.graal.python.lib.PyObjectRichCompareBool; import com.oracle.graal.python.lib.PyObjectSizeNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.lib.RichCmpOp; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; @@ -926,6 +926,7 @@ static Object tell(VirtualFrame frame, PTextIO self, @Cached PyNumberAsSizeNode asSizeNode, @Exclusive @Cached PyLongAsLongNode asLongNode, @Cached PyObjectSizeNode sizeNode, + @Cached PyTupleCheckNode tupleCheckNode, @CachedLibrary(limit = "2") InteropLibrary isString, @Cached PRaiseNode raiseNode) { PTextIO.CookieType cookie = getCookie(frame, inliningTarget, self, writeFlushNode, callMethodFlush, callMethodTell, asLongNode); @@ -945,7 +946,7 @@ static Object tell(VirtualFrame frame, PTextIO self, PBytes in = PFactory.createBytes(language, snapshotNextInput, skipBytes); int charsDecoded = decoderDecode(frame, inliningTarget, self, in, callMethodDecode, toString, codePointLengthNode); if (charsDecoded <= decodedCharsUsed) { - Object[] state = decoderGetstate(frame, inliningTarget, self, savedState, getObjectArrayNode, callMethodGetState, callMethodSetState, raiseNode); + Object[] state = decoderGetstate(frame, inliningTarget, self, savedState, getObjectArrayNode, callMethodGetState, callMethodSetState, tupleCheckNode, raiseNode); int decFlags = asSizeNode.executeExact(frame, inliningTarget, state[1]); int decBufferLen = sizeNode.execute(frame, inliningTarget, state[0]); if (decBufferLen == 0) { @@ -987,7 +988,7 @@ static Object tell(VirtualFrame frame, PTextIO self, /* We got n chars for 1 byte */ charsDecoded += n; cookie.bytesToFeed += 1; - Object[] state = decoderGetstate(frame, inliningTarget, self, savedState, getObjectArrayNode, callMethodGetState, callMethodSetState, raiseNode); + Object[] state = decoderGetstate(frame, inliningTarget, self, savedState, getObjectArrayNode, callMethodGetState, callMethodSetState, tupleCheckNode, raiseNode); int decFlags = asSizeNode.executeExact(frame, inliningTarget, state[1]); int decBufferLen = sizeNode.execute(frame, inliningTarget, state[0]); @@ -1038,9 +1039,10 @@ static Object[] decoderGetstate(VirtualFrame frame, Node inliningTarget, PTextIO SequenceNodes.GetObjectArrayNode getArray, PyObjectCallMethodObjArgs callMethodGetState, PyObjectCallMethodObjArgs callMethodSetState, + PyTupleCheckNode tupleCheckNode, PRaiseNode raiseNode) { Object state = callMethodGetState.execute(frame, inliningTarget, self.getDecoder(), T_GETSTATE); - if (!(state instanceof PTuple)) { + if (!tupleCheckNode.execute(inliningTarget, state)) { fail(frame, inliningTarget, self, saved_state, callMethodSetState); throw raiseNode.raise(inliningTarget, TypeError, ILLEGAL_DECODER_STATE); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperNodes.java index 71d9832ae4..30fa8ea2c5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperNodes.java @@ -95,6 +95,7 @@ import com.oracle.graal.python.lib.PyObjectIsTrueNode; import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.lib.PyObjectRichCompareBool; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; @@ -507,6 +508,7 @@ static boolean readChunk(VirtualFrame frame, Node inliningTarget, PTextIO self, @Cached PyObjectCallMethodObjArgs callMethodGetState, @Cached PyObjectCallMethodObjArgs callMethodRead, @Cached PyNumberAsSizeNode asSizeNode, + @Cached PyTupleCheckNode tupleCheckNode, @Cached TruffleString.CodePointLengthNode codePointLengthNode, @CachedLibrary(limit = "3") PythonBufferAcquireLibrary bufferAcquireLib, @CachedLibrary(limit = "3") PythonBufferAccessLibrary bufferLib, @@ -529,7 +531,7 @@ static boolean readChunk(VirtualFrame frame, Node inliningTarget, PTextIO self, * Given this, we know there was a valid snapshot point len(decBuffer) bytes ago * with decoder state (b'', decFlags). */ - if (!(state instanceof PTuple)) { + if (!tupleCheckNode.execute(inliningTarget, state)) { throw raiseNode.raise(inliningTarget, TypeError, ILLEGAL_DECODER_STATE); } Object[] array = getArray.execute(inliningTarget, state); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONEncoderBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONEncoderBuiltins.java index 07da9493a0..f8edd3fe1e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONEncoderBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONEncoderBuiltins.java @@ -77,7 +77,6 @@ import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.formatting.FloatFormatter; import com.oracle.graal.python.runtime.object.PFactory; -import com.oracle.graal.python.runtime.sequence.PSequence; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.graal.python.util.ArrayBuilder; import com.oracle.graal.python.util.PythonUtils; @@ -190,6 +189,7 @@ PTuple call(VirtualFrame frame, PJSONEncoder self, Object obj, @SuppressWarnings @Cached InlinedBranchProfile errorProfile, @Cached GetClassNode getClassNode, @Cached IsSubtypeNode isSubtypeNode, + @Cached PyTupleCheckNode pyTupleCheckNode, @Cached PyTupleCheckExactNode pyTupleCheckExactNode, @Cached PyListCheckExactNode pyListCheckExactNode, @Cached ConstructListNode constructListNode, @@ -197,7 +197,6 @@ PTuple call(VirtualFrame frame, PJSONEncoder self, Object obj, @SuppressWarnings @Cached CallUnaryMethodNode callDefaultFn, @Cached SequenceStorageNodes.GetItemScalarNode getItemScalarNode, @Cached SequenceStorageNodes.GetItemScalarNode getItemScalarCustomNode, - @Cached PyTupleCheckNode tupleCheckNode, @Cached TupleNodes.GetTupleStorage getTupleStorage, @Cached HashingStorageGetIterator hashingStorageGetIterator, @Cached HashingStorageIteratorNext hashingStorageIteratorNext, @@ -267,11 +266,10 @@ PTuple call(VirtualFrame frame, PJSONEncoder self, Object obj, @SuppressWarnings parent = value; final Object stackIterator; final Object stackStorage; - if (value instanceof PList || value instanceof PTuple) { - PSequence list = (PSequence) value; + if (value instanceof PList list) { appendCodePointNode.execute(builder, '['); first = true; - if (pyTupleCheckExactNode.execute(inliningTarget, list) || pyListCheckExactNode.execute(inliningTarget, list)) { + if (pyListCheckExactNode.execute(inliningTarget, list)) { state = STATE_BUILTIN_LIST; builtinListStorage = list.getSequenceStorage(); stackStorage = builtinListStorage; @@ -283,6 +281,21 @@ PTuple call(VirtualFrame frame, PJSONEncoder self, Object obj, @SuppressWarnings stackStorage = null; stackIterator = genericIterator; } + } else if (pyTupleCheckNode.execute(inliningTarget, value)) { + appendCodePointNode.execute(builder, '['); + first = true; + if (pyTupleCheckExactNode.execute(inliningTarget, value)) { + state = STATE_BUILTIN_LIST; + builtinListStorage = getTupleStorage.execute(inliningTarget, value); + stackStorage = builtinListStorage; + stackIterator = null; + } else { + genericListProfile.enter(inliningTarget); + state = STATE_GENERIC_LIST; + genericIterator = callGetListIter.executeCached(frame, value); + stackStorage = null; + stackIterator = genericIterator; + } } else if (value instanceof PDict dict) { appendCodePointNode.execute(builder, '{'); first = true; @@ -342,7 +355,7 @@ PTuple call(VirtualFrame frame, PJSONEncoder self, Object obj, @SuppressWarnings Object item = pyIterNextNode.execute(frame, inliningTarget, genericIterator); if (state == STATE_GENERIC_DICT) { genericDictProfile.enter(inliningTarget); - if (!tupleCheckNode.execute(inliningTarget, item)) { + if (!pyTupleCheckNode.execute(inliningTarget, item)) { errorProfile.enter(inliningTarget); throw PRaiseNode.raiseStatic(this, ValueError, ErrorMessages.ITEMS_MUST_RETURN_2_TUPLES); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PUnpickler.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PUnpickler.java index 4c00d8ac08..305f47cede 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PUnpickler.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PUnpickler.java @@ -168,6 +168,7 @@ import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.lib.PyObjectSetAttrO; import com.oracle.graal.python.lib.PyObjectSetItem; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; @@ -691,6 +692,7 @@ public abstract static class LoadNode extends BasePickleReadNode { @Child private PyObjectSetItem setItemNode; @Child HashingStorageCopy hashCopy; @Child HashingStorageAddAllToOther addAllToOther; + @Child private PyTupleCheckNode.CachedNode tupleCheck; public abstract Object execute(VirtualFrame frame, PUnpickler self); @@ -710,6 +712,14 @@ protected HashingStorageAddAllToOther ensureHashingStorageAddAllToOther() { return addAllToOther; } + protected boolean isTuple(Object object) { + if (tupleCheck == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + tupleCheck = insert(PyTupleCheckNode.CachedNode.create()); + } + return tupleCheck.execute(object); + } + protected Object longFromBytes(byte[] data, boolean littleEndian) { if (pyLongFromByteArray == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); @@ -1761,7 +1771,7 @@ private void loadExtension(VirtualFrame frame, BoundaryCallData boundaryCallData // Since the extension registry is manipulable via Python code, confirm that pair is // really a 2-tuple of strings. - if (!(pair instanceof PTuple) || length(frame, pair) != 2) { + if (!isTuple(pair) || length(frame, pair) != 2) { throw raise(PythonBuiltinClassType.ValueError, ErrorMessages.INV_REG_NOT_2TUPLE, code); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PicklerBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PicklerBuiltins.java index 1d91c0a770..59310a5f1a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PicklerBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PicklerBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -69,18 +69,17 @@ import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIterator; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorNext; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorValue; -import com.oracle.graal.python.builtins.objects.common.SequenceNodes; -import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.function.PKeyword; -import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyCallableCheckNode; import com.oracle.graal.python.lib.PyLongAsLongNode; import com.oracle.graal.python.lib.PyNumberAsSizeNode; import com.oracle.graal.python.lib.PyObjectLookupAttr; -import com.oracle.graal.python.lib.PyObjectSizeNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; +import com.oracle.graal.python.lib.PyTupleGetItem; +import com.oracle.graal.python.lib.PyTupleSizeNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; @@ -92,7 +91,6 @@ import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.runtime.object.PFactory; -import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Exclusive; @@ -273,10 +271,10 @@ static Object get(PPickler self, @SuppressWarnings("unused") PNone none, @Specialization(guards = {"!isNoValue(obj)", "!isDeleteMarker(obj)"}) static Object set(VirtualFrame frame, PPickler self, Object obj, @Bind Node inliningTarget, - @Cached SequenceNodes.GetSequenceStorageNode getSequenceStorageNode, - @Cached SequenceStorageNodes.GetItemNode getItemNode, @Cached PyNumberAsSizeNode asSizeNode, - @Cached PyObjectSizeNode sizeNode, + @Cached PyTupleCheckNode tupleCheckNode, + @Cached PyTupleSizeNode sizeNode, + @Cached PyTupleGetItem getItemNode, @Cached HashingStorageGetIterator getIter, @Cached HashingStorageIteratorNext iterNext, @Cached HashingStorageIteratorValue iterValue, @@ -291,12 +289,11 @@ static Object set(VirtualFrame frame, PPickler self, Object obj, HashingStorageIterator it = getIter.execute(inliningTarget, dictStorage); while (iterNext.execute(inliningTarget, dictStorage, it)) { Object value = iterValue.execute(inliningTarget, dictStorage, it); - if (!(value instanceof PTuple) || sizeNode.execute(frame, inliningTarget, value) != 2) { + if (!tupleCheckNode.execute(inliningTarget, value) || sizeNode.execute(inliningTarget, value) != 2) { throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.VALUES_MUST_BE_2TUPLES, "memo"); } - SequenceStorage tupleStorage = getSequenceStorageNode.execute(inliningTarget, value); - int memoId = asSizeNode.executeExact(frame, inliningTarget, getItemNode.execute(frame, tupleStorage, 0)); - Object memoObj = getItemNode.execute(frame, tupleStorage, 1); + int memoId = asSizeNode.executeExact(frame, inliningTarget, getItemNode.execute(inliningTarget, value, 0)); + Object memoObj = getItemNode.execute(inliningTarget, value, 1); newMemo.set(memoObj, memoId); } } else { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java index ee391fe12e..c225d3c1f0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java @@ -87,7 +87,6 @@ import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.builtins.objects.str.PString; import com.oracle.graal.python.builtins.objects.str.StringNodes.StringMaterializeNode; -import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.PythonAbstractClass; import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass; import com.oracle.graal.python.builtins.objects.type.PythonClass; @@ -110,6 +109,8 @@ import com.oracle.graal.python.lib.PySequenceGetItemNode; import com.oracle.graal.python.lib.PySequenceSetItemNode; import com.oracle.graal.python.lib.PySequenceSizeNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; +import com.oracle.graal.python.lib.PyTupleGetItem; import com.oracle.graal.python.lib.PyTupleSizeNode; import com.oracle.graal.python.nodes.BuiltinNames; import com.oracle.graal.python.nodes.HiddenAttr; @@ -850,10 +851,12 @@ public LocalDate asDate( // GR-44020: make shared: @Exclusive @Cached PRaiseNode raiseNode, // GR-44020: make shared: - @Exclusive @Cached SequenceStorageNodes.GetItemDynamicNode getItemNode, + @Exclusive @Cached PyTupleCheckNode tupleCheckNode, // GR-44020: make shared: @Exclusive @Cached PyTupleSizeNode pyTupleSizeNode, // GR-44020: make shared: + @Exclusive @Cached PyTupleGetItem tupleGetItem, + // GR-44020: make shared: @Exclusive @Cached GilNode gil) throws UnsupportedMessageException { boolean mustRelease = gil.acquire(); try { @@ -861,14 +864,13 @@ public LocalDate asDate( InteropBehavior behavior = getBehavior.execute(inliningTarget, this, method); if (behavior != null) { Object value = getValue.execute(inliningTarget, behavior, method, this); - if (value instanceof PTuple tuple) { - if (pyTupleSizeNode.execute(inliningTarget, tuple) != 3) { + if (tupleCheckNode.execute(inliningTarget, value)) { + if (pyTupleSizeNode.execute(inliningTarget, value) != 3) { throw raiseNode.raise(inliningTarget, ValueError, S_MUST_BE_A_S_TUPLE, "return value", "3"); } - SequenceStorage storage = tuple.getSequenceStorage(); - int year = castToIntNode.executeWithThrowSystemError(inliningTarget, getItemNode.execute(inliningTarget, storage, 0), raiseNode); - int month = castToIntNode.executeWithThrowSystemError(inliningTarget, getItemNode.execute(inliningTarget, storage, 1), raiseNode); - int day = castToIntNode.executeWithThrowSystemError(inliningTarget, getItemNode.execute(inliningTarget, storage, 2), raiseNode); + int year = castToIntNode.executeWithThrowSystemError(inliningTarget, tupleGetItem.execute(inliningTarget, value, 0), raiseNode); + int month = castToIntNode.executeWithThrowSystemError(inliningTarget, tupleGetItem.execute(inliningTarget, value, 1), raiseNode); + int day = castToIntNode.executeWithThrowSystemError(inliningTarget, tupleGetItem.execute(inliningTarget, value, 2), raiseNode); try { return createLocalDate(year, month, day); } catch (Exception e) { @@ -923,10 +925,12 @@ public LocalTime asTime( // GR-44020: make shared: @Exclusive @Cached PRaiseNode raiseNode, // GR-44020: make shared: - @Exclusive @Cached SequenceStorageNodes.GetItemDynamicNode getItemNode, + @Exclusive @Cached PyTupleCheckNode tupleCheckNode, // GR-44020: make shared: @Exclusive @Cached PyTupleSizeNode pyTupleSizeNode, // GR-44020: make shared: + @Exclusive @Cached PyTupleGetItem tupleGetItem, + // GR-44020: make shared: @Exclusive @Cached GilNode gil) throws UnsupportedMessageException { boolean mustRelease = gil.acquire(); try { @@ -934,15 +938,14 @@ public LocalTime asTime( InteropBehavior behavior = getBehavior.execute(inliningTarget, this, method); if (behavior != null) { Object value = getValue.execute(inliningTarget, behavior, method, this); - if (value instanceof PTuple tuple) { - if (pyTupleSizeNode.execute(inliningTarget, tuple) != 4) { + if (tupleCheckNode.execute(inliningTarget, value)) { + if (pyTupleSizeNode.execute(inliningTarget, value) != 4) { throw raiseNode.raise(inliningTarget, ValueError, S_MUST_BE_A_S_TUPLE, "return value", "4"); } - SequenceStorage storage = tuple.getSequenceStorage(); - int hour = castToIntNode.executeWithThrowSystemError(inliningTarget, getItemNode.execute(inliningTarget, storage, 0), raiseNode); - int min = castToIntNode.executeWithThrowSystemError(inliningTarget, getItemNode.execute(inliningTarget, storage, 1), raiseNode); - int sec = castToIntNode.executeWithThrowSystemError(inliningTarget, getItemNode.execute(inliningTarget, storage, 2), raiseNode); - int micro = castToIntNode.executeWithThrowSystemError(inliningTarget, getItemNode.execute(inliningTarget, storage, 3), raiseNode); + int hour = castToIntNode.executeWithThrowSystemError(inliningTarget, tupleGetItem.execute(inliningTarget, value, 0), raiseNode); + int min = castToIntNode.executeWithThrowSystemError(inliningTarget, tupleGetItem.execute(inliningTarget, value, 1), raiseNode); + int sec = castToIntNode.executeWithThrowSystemError(inliningTarget, tupleGetItem.execute(inliningTarget, value, 2), raiseNode); + int micro = castToIntNode.executeWithThrowSystemError(inliningTarget, tupleGetItem.execute(inliningTarget, value, 3), raiseNode); try { return createLocalTime(hour, min, sec, micro); } catch (Exception e) { @@ -1085,10 +1088,12 @@ public Duration asDuration( // GR-44020: make shared: @Exclusive @Cached PRaiseNode raiseNode, // GR-44020: make shared: - @Exclusive @Cached SequenceStorageNodes.GetItemDynamicNode getItemNode, + @Exclusive @Cached PyTupleCheckNode tupleCheckNode, // GR-44020: make shared: @Exclusive @Cached PyTupleSizeNode pyTupleSizeNode, // GR-44020: make shared: + @Exclusive @Cached PyTupleGetItem tupleGetItem, + // GR-44020: make shared: @Exclusive @Cached GilNode gil) throws UnsupportedMessageException { boolean mustRelease = gil.acquire(); try { @@ -1096,13 +1101,12 @@ public Duration asDuration( InteropBehavior behavior = getBehavior.execute(inliningTarget, this, method); if (behavior != null) { Object value = getValue.execute(inliningTarget, behavior, method, this); - if (value instanceof PTuple tuple) { - if (pyTupleSizeNode.execute(inliningTarget, tuple) != 2) { + if (tupleCheckNode.execute(inliningTarget, value)) { + if (pyTupleSizeNode.execute(inliningTarget, value) != 2) { throw raiseNode.raise(inliningTarget, ValueError, S_MUST_BE_A_S_TUPLE, "return value", "2"); } - SequenceStorage storage = tuple.getSequenceStorage(); - long sec = castToLongNode.executeWithThrowSystemError(inliningTarget, getItemNode.execute(inliningTarget, storage, 0), raiseNode); - long nano = castToLongNode.executeWithThrowSystemError(inliningTarget, getItemNode.execute(inliningTarget, storage, 1), raiseNode); + long sec = castToLongNode.executeWithThrowSystemError(inliningTarget, tupleGetItem.execute(inliningTarget, value, 0), raiseNode); + long nano = castToLongNode.executeWithThrowSystemError(inliningTarget, tupleGetItem.execute(inliningTarget, value, 1), raiseNode); try { return createDuration(sec, nano); } catch (Exception e) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesCommonBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesCommonBuiltins.java index c1b9d1983f..f401e4aacb 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesCommonBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesCommonBuiltins.java @@ -94,6 +94,7 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSqContains.SqContainsBuiltinNode; import com.oracle.graal.python.lib.PyNumberAsSizeNode; import com.oracle.graal.python.lib.PyNumberIndexNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PGuards; @@ -437,8 +438,9 @@ private boolean doIt(VirtualFrame frame, byte[] self, int len, SequenceStorage s abstract static class PrefixSuffixDispatchNode extends Node { abstract boolean execute(VirtualFrame frame, Node inliningTarget, PrefixSuffixBaseNode parent, byte[] bytes, int len, Object substrs, int begin, int last); - @Specialization(guards = "isTuple(substrs)") + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, substrs)", limit = "1") static boolean doTuple(VirtualFrame frame, Node inliningTarget, PrefixSuffixBaseNode parent, byte[] bytes, int len, Object substrs, int begin, int last, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, @Cached(value = "createToBytesFromTuple()", inline = false) BytesNodes.ToBytesNode tobytes, @Cached SequenceNodes.GetSequenceStorageNode getSequenceStorageNode, @Cached SequenceStorageNodes.GetItemScalarNode getItemNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeBuiltins.java index 45236dca11..26ac624882 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeBuiltins.java @@ -140,15 +140,15 @@ static PCode call(VirtualFrame frame, @SuppressWarnings("unused") Object cls, in byte[] linetableBytes = getBytes(inliningTarget, linetable, bytesCheckNode, getBytesStorage, bufferLib); checkBytes(inliningTarget, exceptiontable, bytesCheckNode); - Object[] constantsArr = getTupleArray(inliningTarget, constants, tupleCheckNode, getTupleStorage, toArrayNode); + Object[] constantsArr = getTupleArray(inliningTarget, constants, getTupleStorage, toArrayNode, tupleCheckNode); TruffleString[] namesArr = objectArrayToTruffleStringArray(inliningTarget, - getTupleArray(inliningTarget, names, tupleCheckNode, getTupleStorage, toArrayNode), castToTruffleStringNode); + getTupleArray(inliningTarget, names, getTupleStorage, toArrayNode, tupleCheckNode), castToTruffleStringNode); TruffleString[] varnamesArr = objectArrayToTruffleStringArray(inliningTarget, - getTupleArray(inliningTarget, varnames, tupleCheckNode, getTupleStorage, toArrayNode), castToTruffleStringNode); + getTupleArray(inliningTarget, varnames, getTupleStorage, toArrayNode, tupleCheckNode), castToTruffleStringNode); TruffleString[] freevarsArr = objectArrayToTruffleStringArray(inliningTarget, - getTupleArray(inliningTarget, freevars, tupleCheckNode, getTupleStorage, toArrayNode), castToTruffleStringNode); + getTupleArray(inliningTarget, freevars, getTupleStorage, toArrayNode, tupleCheckNode), castToTruffleStringNode); TruffleString[] cellcarsArr = objectArrayToTruffleStringArray(inliningTarget, - getTupleArray(inliningTarget, cellvars, tupleCheckNode, getTupleStorage, toArrayNode), castToTruffleStringNode); + getTupleArray(inliningTarget, cellvars, getTupleStorage, toArrayNode, tupleCheckNode), castToTruffleStringNode); return createCodeNode.execute(frame, argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, @@ -170,8 +170,8 @@ private static void checkBytes(Node inliningTarget, Object object, PyBytesCheckN } } - private static Object[] getTupleArray(Node inliningTarget, Object object, PyTupleCheckNode tupleCheckNode, - GetTupleStorage getTupleStorage, SequenceStorageNodes.ToArrayNode toArrayNode) { + private static Object[] getTupleArray(Node inliningTarget, Object object, + GetTupleStorage getTupleStorage, SequenceStorageNodes.ToArrayNode toArrayNode, PyTupleCheckNode tupleCheckNode) { if (!tupleCheckNode.execute(inliningTarget, object)) { throw invalidArgs(inliningTarget); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceNodes.java index 31610f632e..7255a16a7b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceNodes.java @@ -56,7 +56,7 @@ import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.builtins.TupleNodes; +import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.nodes.object.IsForeignObjectNode; import com.oracle.graal.python.runtime.sequence.PSequence; import com.oracle.graal.python.runtime.sequence.storage.ForeignSequenceStorage; @@ -140,18 +140,20 @@ static SequenceStorage doSequence(Node inliningTarget, PSequence seq, return getPSequenceStorageNode.execute(inliningTarget, seq); } - @Specialization(guards = "tupleCheck.execute(inliningTarget, seq)", limit = "1") - static SequenceStorage doNativeTuple(Node inliningTarget, PythonAbstractNativeObject seq, - @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheck, - @Cached TupleNodes.GetNativeTupleStorage getNativeTupleStorage) { - return getNativeTupleStorage.execute(seq); + @Specialization(guards = "isNativeTuple(seq)") + static SequenceStorage doNativeTuple(PythonAbstractNativeObject seq) { + return GetTupleStorage.doNative(seq); + } + + static boolean isNativeTuple(PythonAbstractNativeObject seq) { + return PyTupleCheckNode.checkNative(seq); } // Note: this does not seem currently used but is good to accept foreign lists in more // places @Specialization(guards = {"isForeignObjectNode.execute(inliningTarget, seq)", "interop.hasArrayElements(seq)"}, limit = "1") static SequenceStorage doForeign(Node inliningTarget, Object seq, - @Cached IsForeignObjectNode isForeignObjectNode, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, @CachedLibrary(limit = "getCallSiteInlineCacheMaxDepth()") InteropLibrary interop, @Cached InlinedBranchProfile errorProfile) { try { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceStorageNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceStorageNodes.java index a9c404d528..0851c13bdf 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceStorageNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceStorageNodes.java @@ -26,6 +26,11 @@ package com.oracle.graal.python.builtins.objects.common; import static com.oracle.graal.python.builtins.objects.common.IndexNodes.checkBounds; +import static com.oracle.graal.python.runtime.exception.PythonErrorType.IndexError; +import static com.oracle.graal.python.runtime.exception.PythonErrorType.MemoryError; +import static com.oracle.graal.python.runtime.exception.PythonErrorType.OverflowError; +import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; +import static com.oracle.graal.python.runtime.exception.PythonErrorType.ValueError; import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.callocByteArray; import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.callocPtrArray; @@ -37,11 +42,6 @@ import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.writeByteArrayElement; import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.writeByteArrayElements; import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.writePtrArrayElement; -import static com.oracle.graal.python.runtime.exception.PythonErrorType.IndexError; -import static com.oracle.graal.python.runtime.exception.PythonErrorType.MemoryError; -import static com.oracle.graal.python.runtime.exception.PythonErrorType.OverflowError; -import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; -import static com.oracle.graal.python.runtime.exception.PythonErrorType.ValueError; import static com.oracle.graal.python.runtime.sequence.storage.SequenceStorage.StorageType.Boolean; import static com.oracle.graal.python.runtime.sequence.storage.SequenceStorage.StorageType.Byte; import static com.oracle.graal.python.runtime.sequence.storage.SequenceStorage.StorageType.Double; @@ -663,7 +663,7 @@ protected static Object doNativeObject(NativeObjectSequenceStorage storage, int } @Specialization - protected static int doNativeByte(NativeByteSequenceStorage storage, int idx) { + public static int doNativeByte(NativeByteSequenceStorage storage, int idx) { return readByteArrayElement(storage.getPtr(), idx) & 0xff; } } @@ -3319,18 +3319,17 @@ static SequenceStorage doNativeInt(Node inliningTarget, NativeIntSequenceStorage } @Specialization - static SequenceStorage doNativeBytes(NativeByteSequenceStorage s, - @Shared @Cached(inline = false) GetNativeItemScalarNode getItem) { + static SequenceStorage doNativeBytes(NativeByteSequenceStorage s) { byte[] bytes = new byte[s.length()]; for (int i = 0; i < bytes.length; i++) { - bytes[i] = (byte) (int) getItem.execute(s, i); + bytes[i] = (byte) GetNativeItemScalarNode.doNativeByte(s, i); } return new ByteSequenceStorage(bytes); } @Specialization static SequenceStorage doNativeObjects(NativeObjectSequenceStorage s, - @Shared @Cached(inline = false) GetNativeItemScalarNode getItem) { + @Cached(inline = false) GetNativeItemScalarNode getItem) { Object[] objects = new Object[s.length()]; for (int i = 0; i < objects.length; i++) { objects[i] = getItem.execute(s, i); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictViewBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictViewBuiltins.java index c97b4631f2..a203480f5d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictViewBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictViewBuiltins.java @@ -97,6 +97,7 @@ import com.oracle.graal.python.lib.PyObjectRichCompareBool; import com.oracle.graal.python.lib.PyObjectSizeNode; import com.oracle.graal.python.lib.PySequenceContainsNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.lib.RichCmpOp; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; @@ -225,9 +226,10 @@ static boolean contains(VirtualFrame frame, PDictKeysView self, Object key, return getItem.hasKey(frame, inliningTarget, self.getWrappedStorage(), key); } - @Specialization(guards = "isTuple(key)") + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, key)", limit = "1") static boolean contains(VirtualFrame frame, PDictItemsView self, Object key, @Bind Node inliningTarget, + @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheckNode, @Exclusive @Cached HashingStorageGetItem getItem, @Cached TupleNodes.GetTupleStorage getTupleStorage, @Cached PyObjectRichCompareBool eqNode, @@ -246,14 +248,15 @@ static boolean contains(VirtualFrame frame, PDictItemsView self, Object key, } } - protected static boolean isFallback(Node inliningTarget, Object self, Object key) { - return !(self instanceof PDictView) || self instanceof PDictItemsView && !PGuards.isTuple(key); + protected static boolean isFallback(Node inliningTarget, Object self, Object key, PyTupleCheckNode tupleCheckNode) { + return !(self instanceof PDictView) || self instanceof PDictItemsView && !tupleCheckNode.execute(inliningTarget, key); } @SuppressWarnings("unused") - @Specialization(guards = "isFallback(inliningTarget, self, key)") + @Specialization(guards = "isFallback(inliningTarget, self, key, tupleCheckNode)", limit = "1") static boolean contains(Object self, Object key, - @Bind Node inliningTarget) { + @Bind Node inliningTarget, + @Exclusive @Cached PyTupleCheckNode tupleCheckNode) { return false; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionGroupBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionGroupBuiltins.java index ca874691b0..ac16acd8a4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionGroupBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionGroupBuiltins.java @@ -89,6 +89,7 @@ import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.builtins.ListNodes; import com.oracle.graal.python.nodes.builtins.TupleNodes; +import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; @@ -374,7 +375,7 @@ private static SequenceStorage getExactTupleStorage(Object value) { return tuple.getSequenceStorage(); } if (value instanceof PythonAbstractNativeObject nativeTuple && PyTupleCheckExactNode.executeUncached(nativeTuple)) { - return TupleNodes.GetNativeTupleStorage.getUncached().execute(nativeTuple); + return GetTupleStorage.doNative(nativeTuple); } return null; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/PrepareExceptionNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/PrepareExceptionNode.java index dd31b7aca9..66b209c7ff 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/PrepareExceptionNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/PrepareExceptionNode.java @@ -97,12 +97,12 @@ static Object doException(@SuppressWarnings("unused") PBaseException exc, @Suppr throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.INSTANCE_EX_MAY_NOT_HAVE_SEP_VALUE); } - @Specialization(guards = {"isTypeNode.execute(inliningTarget, type)", "!isPNone(value)", "!tupleCheck.execute(inliningTarget, value)"}, limit = "1") + @Specialization(guards = {"isTypeNode.execute(inliningTarget, type)", "!isPNone(value)", "!tupleCheckNode.execute(inliningTarget, value)"}, limit = "1") static Object doExceptionOrCreate(VirtualFrame frame, Object type, Object value, @Bind Node inliningTarget, @SuppressWarnings("unused") @Exclusive @Cached IsTypeNode isTypeNode, + @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheckNode, @Exclusive @Cached PyExceptionInstanceCheckNode check, - @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheck, @Cached PyObjectIsInstanceNode isInstanceNode, @Cached InlinedConditionProfile isInstanceProfile, @Shared @Cached IsSubtypeNode isSubtypeNode, @@ -138,12 +138,12 @@ static Object doCreate(VirtualFrame frame, Object type, @SuppressWarnings("unuse } } - @Specialization(guards = {"isTypeNode.execute(inliningTarget, type)", "tupleCheck.execute(inliningTarget, value)"}, limit = "1") + @Specialization(guards = {"isTypeNode.execute(inliningTarget, type)", "tupleCheckNode.execute(inliningTarget, value)"}, limit = "1") static Object doCreateTuple(VirtualFrame frame, Object type, Object value, @Bind Node inliningTarget, @SuppressWarnings("unused") @Exclusive @Cached IsTypeNode isTypeNode, + @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheckNode, @Exclusive @Cached PyExceptionInstanceCheckNode check, - @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheck, @Exclusive @Cached GetTupleStorage getTupleStorage, @Exclusive @Cached SequenceStorageNodes.ToArrayNode toArrayNode, @Shared @Cached IsSubtypeNode isSubtypeNode, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/AbstractFunctionBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/AbstractFunctionBuiltins.java index 44e6836809..cf21985bf3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/AbstractFunctionBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/AbstractFunctionBuiltins.java @@ -62,14 +62,16 @@ import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.builtins.objects.object.PythonObject; -import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.lib.PyObjectGetItem; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.StringLiterals; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.EnsureManagedTupleNode; import com.oracle.graal.python.nodes.call.CallDispatchers; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; @@ -82,9 +84,11 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.Cached.Exclusive; import com.oracle.truffle.api.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateNodeFactory; +import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; @@ -257,6 +261,7 @@ static Object getModule(PBuiltinFunction self, Object value, @Builtin(name = J___TYPE_PARAMS__, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) @GenerateNodeFactory + @ImportStatic(PGuards.class) abstract static class GetTypeParamsNode extends PythonBuiltinNode { @Specialization(guards = {"!isBuiltinFunction(self)", "isNoValue(none)"}) static Object getTypeParams(PFunction self, @SuppressWarnings("unused") PNone none, @@ -269,17 +274,21 @@ static Object getTypeParams(PFunction self, @SuppressWarnings("unused") PNone no return typeParams; } - @Specialization(guards = {"!isBuiltinFunction(self)"}) - static Object setTypeParams(PFunction self, PTuple value, + @Specialization(guards = {"!isBuiltinFunction(self)", "tupleCheckNode.execute(inliningTarget, value)"}, limit = "1") + static Object setTypeParams(PFunction self, Object value, + @Bind Node inliningTarget, + @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheckNode, + @Cached EnsureManagedTupleNode ensureManagedTupleNode, @Cached WriteAttributeToObjectNode writeObject) { - writeObject.execute(self, T___TYPE_PARAMS__, value); + writeObject.execute(self, T___TYPE_PARAMS__, ensureManagedTupleNode.execute(inliningTarget, value)); return PNone.NONE; } - @Specialization(guards = {"!isBuiltinFunction(self)", "!isNoValue(value)", "!isPTuple(value)"}) + @Specialization(guards = {"!isBuiltinFunction(self)", "!isNoValue(value)", "!tupleCheckNode.execute(inliningTarget, value)"}, limit = "1") @SuppressWarnings("unused") static Object setNotTuple(PFunction self, Object value, - @Bind Node inliningTarget) { + @Bind Node inliningTarget, + @Exclusive @Cached PyTupleCheckNode tupleCheckNode) { throw PRaiseNode.raiseStatic(inliningTarget, AttributeError, ErrorMessages.MUST_BE_SET_TO_S, J___TYPE_PARAMS__, "tuple"); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java index 6b5af3e289..eb0bc2bac3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java @@ -67,20 +67,24 @@ import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageLen; import com.oracle.graal.python.builtins.objects.common.KeywordsStorage; import com.oracle.graal.python.builtins.objects.common.SequenceNodes.GetObjectArrayNode; +import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.ToArrayNode; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorDeleteMarker; import com.oracle.graal.python.builtins.objects.method.PMethod; import com.oracle.graal.python.builtins.objects.str.PString; import com.oracle.graal.python.builtins.objects.str.StringNodes; import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; -import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.DescrGetBuiltinNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode; import com.oracle.graal.python.nodes.builtins.FunctionNodes.GetFunctionCodeNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.EnsureManagedTupleNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; @@ -94,6 +98,7 @@ import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.Node; @@ -111,6 +116,7 @@ protected List> getNodeFa @Slot(value = SlotKind.tp_new, isComplex = true) @SlotSignature(name = "function", minNumOfPositionalArgs = 3, parameterNames = {"$cls", "code", "globals", "name", "argdefs", "closure"}) @GenerateNodeFactory + @ImportStatic(PGuards.class) public abstract static class FunctionNode extends PythonBuiltinNode { @Specialization @@ -120,13 +126,16 @@ static PFunction function(@SuppressWarnings("unused") Object cls, PCode code, PD return PFactory.createFunction(language, name, code, globals, null); } - @Specialization + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, closure)") static PFunction function(@SuppressWarnings("unused") Object cls, PCode code, PDict globals, @SuppressWarnings("unused") PNone name, @SuppressWarnings("unused") PNone defaultArgs, - PTuple closure, + Object closure, @Bind Node inliningTarget, - @Shared("getObjectArrayNode") @Cached GetObjectArrayNode getObjectArrayNode, + @SuppressWarnings("unused") @Shared("tupleCheck") @Cached PyTupleCheckNode tupleCheckNode, + @Shared("getTupleStorage") @Cached GetTupleStorage getTupleStorage, + @Shared("toArray") @Cached ToArrayNode toArray, @Bind PythonLanguage language) { - return PFactory.createFunction(language, T_LAMBDA_NAME, code, globals, PCell.toCellArray(getObjectArrayNode.execute(inliningTarget, closure))); + Object[] closureArray = toArray.execute(inliningTarget, getTupleStorage.execute(inliningTarget, closure)); + return PFactory.createFunction(language, T_LAMBDA_NAME, code, globals, PCell.toCellArray(closureArray)); } @Specialization @@ -137,41 +146,53 @@ static PFunction function(@SuppressWarnings("unused") Object cls, PCode code, PD return PFactory.createFunction(language, T_LAMBDA_NAME, code, globals, null); } - @Specialization - static PFunction function(@SuppressWarnings("unused") Object cls, PCode code, PDict globals, TruffleString name, @SuppressWarnings("unused") PNone defaultArgs, PTuple closure, + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, closure)") + static PFunction function(@SuppressWarnings("unused") Object cls, PCode code, PDict globals, TruffleString name, @SuppressWarnings("unused") PNone defaultArgs, Object closure, @Bind Node inliningTarget, - @Shared("getObjectArrayNode") @Cached GetObjectArrayNode getObjectArrayNode, + @SuppressWarnings("unused") @Shared("tupleCheck") @Cached PyTupleCheckNode tupleCheckNode, + @Shared("getTupleStorage") @Cached GetTupleStorage getTupleStorage, + @Shared("toArray") @Cached ToArrayNode toArray, @Bind PythonLanguage language) { - return PFactory.createFunction(language, name, code, globals, PCell.toCellArray(getObjectArrayNode.execute(inliningTarget, closure))); + Object[] closureArray = toArray.execute(inliningTarget, getTupleStorage.execute(inliningTarget, closure)); + return PFactory.createFunction(language, name, code, globals, PCell.toCellArray(closureArray)); } - @Specialization - static PFunction function(@SuppressWarnings("unused") Object cls, PCode code, PDict globals, @SuppressWarnings("unused") PNone name, PTuple defaultArgs, + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, defaultArgs)") + static PFunction function(@SuppressWarnings("unused") Object cls, PCode code, PDict globals, @SuppressWarnings("unused") PNone name, Object defaultArgs, @SuppressWarnings("unused") PNone closure, @Bind Node inliningTarget, - @Shared("getObjectArrayNode") @Cached GetObjectArrayNode getObjectArrayNode, + @SuppressWarnings("unused") @Shared("tupleCheck") @Cached PyTupleCheckNode tupleCheckNode, + @Shared("getTupleStorage") @Cached GetTupleStorage getTupleStorage, + @Shared("toArray") @Cached ToArrayNode toArray, @Bind PythonLanguage language) { // TODO split defaults of positional args from kwDefaults - return PFactory.createFunction(language, code.getName(), code, globals, getObjectArrayNode.execute(inliningTarget, defaultArgs), null, null); + Object[] defaultArgsArray = toArray.execute(inliningTarget, getTupleStorage.execute(inliningTarget, defaultArgs)); + return PFactory.createFunction(language, code.getName(), code, globals, defaultArgsArray, null, null); } - @Specialization - static PFunction function(@SuppressWarnings("unused") Object cls, PCode code, PDict globals, TruffleString name, PTuple defaultArgs, @SuppressWarnings("unused") PNone closure, + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, defaultArgs)") + static PFunction function(@SuppressWarnings("unused") Object cls, PCode code, PDict globals, TruffleString name, Object defaultArgs, @SuppressWarnings("unused") PNone closure, @Bind Node inliningTarget, - @Shared("getObjectArrayNode") @Cached GetObjectArrayNode getObjectArrayNode, + @SuppressWarnings("unused") @Shared("tupleCheck") @Cached PyTupleCheckNode tupleCheckNode, + @Shared("getTupleStorage") @Cached GetTupleStorage getTupleStorage, + @Shared("toArray") @Cached ToArrayNode toArray, @Bind PythonLanguage language) { // TODO split defaults of positional args from kwDefaults - return PFactory.createFunction(language, name, code, globals, getObjectArrayNode.execute(inliningTarget, defaultArgs), null, null); + Object[] defaultArgsArray = toArray.execute(inliningTarget, getTupleStorage.execute(inliningTarget, defaultArgs)); + return PFactory.createFunction(language, name, code, globals, defaultArgsArray, null, null); } - @Specialization - static PFunction function(@SuppressWarnings("unused") Object cls, PCode code, PDict globals, TruffleString name, PTuple defaultArgs, PTuple closure, + @Specialization(guards = {"tupleCheckNode.execute(inliningTarget, defaultArgs)", "tupleCheckNode.execute(inliningTarget, closure)"}) + static PFunction function(@SuppressWarnings("unused") Object cls, PCode code, PDict globals, TruffleString name, Object defaultArgs, Object closure, @Bind Node inliningTarget, - @Shared("getObjectArrayNode") @Cached GetObjectArrayNode getObjectArrayNode, + @SuppressWarnings("unused") @Shared("tupleCheck") @Cached PyTupleCheckNode tupleCheckNode, + @Shared("getTupleStorage") @Cached GetTupleStorage getTupleStorage, + @Shared("toArray") @Cached ToArrayNode toArray, @Bind PythonLanguage language) { // TODO split defaults of positional args from kwDefaults - return PFactory.createFunction(language, name, code, globals, getObjectArrayNode.execute(inliningTarget, defaultArgs), null, - PCell.toCellArray(getObjectArrayNode.execute(inliningTarget, closure))); + Object[] closureArray = toArray.execute(inliningTarget, getTupleStorage.execute(inliningTarget, closure)); + Object[] defaultArgsArray = toArray.execute(inliningTarget, getTupleStorage.execute(inliningTarget, defaultArgs)); + return PFactory.createFunction(language, name, code, globals, defaultArgsArray, null, PCell.toCellArray(closureArray)); } @Fallback @@ -254,6 +275,7 @@ static Object setQualname(PFunction self, Object value, @Builtin(name = J___DEFAULTS__, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true) @GenerateNodeFactory + @ImportStatic(PGuards.class) public abstract static class GetDefaultsNode extends PythonBinaryBuiltinNode { @Specialization(guards = "isNoValue(defaults)") static Object defaults(PFunction self, @SuppressWarnings("unused") PNone defaults, @@ -263,11 +285,13 @@ static Object defaults(PFunction self, @SuppressWarnings("unused") PNone default return (argDefaults.length == 0) ? PNone.NONE : PFactory.createTuple(language, argDefaults); } - @Specialization - static Object setDefaults(PFunction self, PTuple defaults, + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, defaults)", limit = "1") + static Object setDefaults(PFunction self, Object defaults, @Bind Node inliningTarget, - @Cached GetObjectArrayNode getObjectArrayNode) { - self.setDefaults(getObjectArrayNode.execute(inliningTarget, defaults)); + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, + @Cached GetTupleStorage getTupleStorage, + @Cached ToArrayNode toArray) { + self.setDefaults(toArray.execute(inliningTarget, getTupleStorage.execute(inliningTarget, defaults))); return PNone.NONE; } @@ -412,6 +436,7 @@ Object delete(PFunction self, @SuppressWarnings("unused") DescriptorDeleteMarker @Builtin(name = J___TYPE_PARAMS__, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) @GenerateNodeFactory + @ImportStatic(PGuards.class) public abstract static class GetTypeParamsNode extends PythonBinaryBuiltinNode { @Specialization(guards = "isNoValue(value)") static Object get(PFunction self, @SuppressWarnings("unused") PNone value, @@ -424,10 +449,13 @@ static Object get(PFunction self, @SuppressWarnings("unused") PNone value, return typeParams; } - @Specialization - static Object set(PFunction self, PTuple typeParams, + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, typeParams)", limit = "1") + static Object set(PFunction self, Object typeParams, + @Bind Node inliningTarget, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, + @Cached EnsureManagedTupleNode ensureManagedTupleNode, @Cached WriteAttributeToObjectNode writeObject) { - writeObject.execute(self, T___TYPE_PARAMS__, typeParams); + writeObject.execute(self, T___TYPE_PARAMS__, ensureManagedTupleNode.execute(inliningTarget, typeParams)); return PNone.NONE; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/iterator/IteratorNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/iterator/IteratorNodes.java index ea20fa3462..921d1ec4ab 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/iterator/IteratorNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/iterator/IteratorNodes.java @@ -47,7 +47,6 @@ import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import java.util.ArrayList; -import java.util.List; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; @@ -71,6 +70,7 @@ import com.oracle.graal.python.lib.PyIterNextNode; import com.oracle.graal.python.lib.PyNumberAsSizeNode; import com.oracle.graal.python.lib.PyObjectGetIter; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; @@ -83,8 +83,8 @@ import com.oracle.graal.python.nodes.object.GetClassNode.GetPythonObjectClassNode; import com.oracle.graal.python.nodes.util.CastBuiltinStringToTruffleStringNode; import com.oracle.graal.python.runtime.exception.PException; -import com.oracle.graal.python.runtime.sequence.PSequence; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; +import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; @@ -341,7 +341,7 @@ public abstract static class ToArrayNode extends Node { public abstract Object[] execute(VirtualFrame frame, Object iterable); @Specialization(guards = "isString(iterableObj)") - public static Object[] doIt(Object iterableObj, + static Object[] doString(Object iterableObj, @Bind Node inliningTarget, @Cached CastBuiltinStringToTruffleStringNode castToStringNode, @Cached InlinedLoopConditionProfile loopProfile, @@ -361,22 +361,23 @@ public static Object[] doIt(Object iterableObj, return result; } - @Specialization - public static Object[] doIt(PSequence iterable, + @Specialization(guards = "isPSequence(seq) || tupleCheckNode.execute(inliningTarget, seq)", limit = "1") + static Object[] doSequenceWithStorage(Object seq, @Bind Node inliningTarget, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, @Cached GetSequenceStorageNode getStorageNode, @Cached SequenceStorageNodes.ToArrayNode toArrayNode) { - SequenceStorage storage = getStorageNode.execute(inliningTarget, iterable); + SequenceStorage storage = getStorageNode.execute(inliningTarget, seq); return toArrayNode.execute(inliningTarget, storage); } @Fallback - public static Object[] doIt(VirtualFrame frame, Object iterable, + static Object[] doGeneric(VirtualFrame frame, Object iterable, @Bind Node inliningTarget, @Cached PyObjectGetIter getIter, @Cached PyIterNextNode nextNode) { Object it = getIter.execute(frame, inliningTarget, iterable); - List result = createlist(); + ArrayList result = new ArrayList<>(); while (true) { try { Object next = nextNode.execute(frame, inliningTarget, it); @@ -385,12 +386,7 @@ public static Object[] doIt(VirtualFrame frame, Object iterable, break; } } - return result.toArray(new Object[result.size()]); - } - - @TruffleBoundary - private static List createlist() { - return new ArrayList<>(); + return PythonUtils.toArray(result); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/ChainBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/ChainBuiltins.java index c8beab0279..cec3c67921 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/ChainBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/ChainBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -69,6 +69,7 @@ import com.oracle.graal.python.lib.PyIterCheckNode; import com.oracle.graal.python.lib.PyIterNextNode; import com.oracle.graal.python.lib.PyObjectGetIter; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.lib.PyTupleGetItem; import com.oracle.graal.python.lib.PyTupleSizeNode; import com.oracle.graal.python.nodes.ErrorMessages; @@ -221,7 +222,7 @@ public abstract static class SetStateNode extends DeprecatedSetStateBuiltin { @Specialization static Object setState(PChain self, Object state, @Bind Node node) { - if (!(state instanceof PTuple)) { + if (!PyTupleCheckNode.executeUncached(state)) { throw PRaiseNode.raiseStatic(node, TypeError, IS_NOT_A, "state", "a length 1 or 2 tuple"); } int len = PyTupleSizeNode.executeUncached(state); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/CombinationsBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/CombinationsBuiltins.java index 236a291b5d..d42d9186d9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/CombinationsBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/CombinationsBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -61,13 +61,15 @@ import com.oracle.graal.python.builtins.modules.ItertoolsModuleBuiltins.DeprecatedReduceBuiltin; import com.oracle.graal.python.builtins.modules.ItertoolsModuleBuiltins.DeprecatedSetStateBuiltin; import com.oracle.graal.python.builtins.objects.PNone; -import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.iterator.IteratorNodes; import com.oracle.graal.python.builtins.objects.itertools.CombinationsBuiltinsClinicProviders.CombinationsNodeClinicProviderGen; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotIterNext.TpIterNextBuiltin; +import com.oracle.graal.python.lib.PyTupleCheckNode; +import com.oracle.graal.python.lib.PyTupleGetItem; +import com.oracle.graal.python.lib.PyTupleSizeNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; @@ -78,9 +80,9 @@ import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode; import com.oracle.graal.python.runtime.object.PFactory; -import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Exclusive; @@ -272,14 +274,14 @@ static Object reduce(PAbstractCombinations self) { public abstract static class SetStateNode extends DeprecatedSetStateBuiltin { @Specialization + @TruffleBoundary static Object setState(PAbstractCombinations self, Object stateObj, @Bind Node inliningTarget) { int n = self.getPool().length; - if (stateObj instanceof PTuple state && state.getSequenceStorage().length() == self.getR()) { - SequenceStorage storage = state.getSequenceStorage(); + if (PyTupleCheckNode.executeUncached(stateObj) && PyTupleSizeNode.executeUncached(stateObj) == self.getR()) { try { for (int i = 0; i < self.getR(); i++) { - int index = CastToJavaIntExactNode.executeUncached(SequenceStorageNodes.GetItemScalarNode.executeUncached(storage, i)); + int index = CastToJavaIntExactNode.executeUncached(PyTupleGetItem.executeUncached(stateObj, i)); int max = i + n - self.getR(); if (index > max) { index = max; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/CycleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/CycleBuiltins.java index 3abd88468f..37927f7db6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/CycleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/CycleBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -74,6 +74,7 @@ import com.oracle.graal.python.lib.PyNumberAsSizeNode; import com.oracle.graal.python.lib.PyObjectGetIter; import com.oracle.graal.python.lib.PyObjectLookupAttr; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.lib.PyTupleGetItem; import com.oracle.graal.python.lib.PyTupleSizeNode; import com.oracle.graal.python.nodes.ErrorMessages; @@ -241,9 +242,10 @@ protected boolean hasIterable(PCycle self) { @GenerateNodeFactory public abstract static class SetStateNode extends DeprecatedSetStateBuiltin { @Specialization + @TruffleBoundary static Object setState(PCycle self, Object state, @Bind Node inliningTarget) { - if (!((state instanceof PTuple) && (PyTupleSizeNode.executeUncached(state) == 2))) { + if (!(PyTupleCheckNode.executeUncached(state) && PyTupleSizeNode.executeUncached(state) == 2)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, IS_NOT_A, "state", "2-tuple"); } Object obj = PyTupleGetItem.executeUncached(state, 0); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/GroupByBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/GroupByBuiltins.java index 17c121e634..e7acde6d75 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/GroupByBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/GroupByBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -67,6 +67,7 @@ import com.oracle.graal.python.lib.PyIterNextNode; import com.oracle.graal.python.lib.PyObjectGetIter; import com.oracle.graal.python.lib.PyObjectRichCompareBool; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.lib.PyTupleGetItem; import com.oracle.graal.python.lib.PyTupleSizeNode; import com.oracle.graal.python.nodes.ErrorMessages; @@ -79,6 +80,7 @@ import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.runtime.object.PFactory; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateNodeFactory; @@ -203,9 +205,10 @@ private static boolean valuesSet(PGroupBy self) { @GenerateNodeFactory public abstract static class SetStateNode extends DeprecatedSetStateBuiltin { @Specialization + @TruffleBoundary static Object setState(PGroupBy self, Object state, @Bind Node inliningTarget) { - if (!(state instanceof PTuple) || PyTupleSizeNode.executeUncached(state) != 3) { + if (!PyTupleCheckNode.executeUncached(state) || PyTupleSizeNode.executeUncached(state) != 3) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, IS_NOT_A, "state", "3-tuple"); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/TeeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/TeeBuiltins.java index 639669155b..1bbad4bb4f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/TeeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/TeeBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -69,6 +69,7 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotIterNext.TpIterNextBuiltin; import com.oracle.graal.python.lib.PyIterNextNode; import com.oracle.graal.python.lib.PyObjectGetIter; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.lib.PyTupleGetItem; import com.oracle.graal.python.lib.PyTupleSizeNode; import com.oracle.graal.python.nodes.PRaiseNode; @@ -80,6 +81,7 @@ import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToJavaIntLossyNode; import com.oracle.graal.python.runtime.object.PFactory; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateNodeFactory; @@ -186,10 +188,11 @@ static Object reduce(PTee self) { public abstract static class SetStateNode extends DeprecatedSetStateBuiltin { @Specialization + @TruffleBoundary static Object setState(PTee self, Object state, @Bind Node inliningTarget) { - if (!(state instanceof PTuple) || PyTupleSizeNode.executeUncached(state) != 2) { + if (!PyTupleCheckNode.executeUncached(state) || PyTupleSizeNode.executeUncached(state) != 2) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, IS_NOT_A, "state", "2-tuple"); } Object dataObject = PyTupleGetItem.executeUncached(state, 0); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/mappingproxy/MappingproxyBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/mappingproxy/MappingproxyBuiltins.java index 803851cab2..ea68575aa2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/mappingproxy/MappingproxyBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/mappingproxy/MappingproxyBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. * Copyright (c) 2013, Regents of the University of California * * All rights reserved. @@ -51,9 +51,7 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.objects.PNone; -import com.oracle.graal.python.builtins.objects.list.PList; import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; -import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryFunc.MpSubscriptBuiltinNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.BinaryOpBuiltinNode; @@ -70,6 +68,7 @@ import com.oracle.graal.python.lib.PyObjectSizeNode; import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; import com.oracle.graal.python.lib.PySequenceContainsNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.lib.RichCmpOp; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; @@ -106,10 +105,11 @@ public abstract static class MappingproxyNode extends PythonBinaryBuiltinNode { static Object doMapping(@SuppressWarnings("unused") Object cls, Object obj, @Bind Node inliningTarget, @Cached PyMappingCheckNode mappingCheckNode, + @Cached PyTupleCheckNode tupleCheckNode, @Bind PythonLanguage language, @Cached PRaiseNode raiseNode) { // descrobject.c mappingproxy_check_mapping() - if (!(obj instanceof PList || obj instanceof PTuple) && mappingCheckNode.execute(inliningTarget, obj)) { + if (!tupleCheckNode.isTupleOrList(inliningTarget, obj) && mappingCheckNode.execute(inliningTarget, obj)) { return PFactory.createMappingproxy(language, obj); } throw raiseNode.raise(inliningTarget, PythonErrorType.TypeError, ErrorMessages.S_ARG_MUST_BE_S_NOT_P, "mappingproxy()", "mapping", obj); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewBuiltins.java index 7970c7d41d..b68a8c7486 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewBuiltins.java @@ -654,9 +654,9 @@ static PMemoryView doGeneric(VirtualFrame frame, PMemoryView self, TruffleString @Cached InlinedBranchProfile isPListProfile, @Cached SequenceStorageNodes.GetItemScalarNode getItemScalarNode, @Cached PyNumberAsSizeNode asSizeNode, + @Cached PyTupleCheckNode tupleCheckNode, @Cached TruffleString.CodePointLengthNode lengthNode, @Cached TruffleString.CodePointAtIndexUTF32Node atIndexNode, - @Cached PyTupleCheckNode tupleCheck, @Exclusive @Cached TupleNodes.GetTupleStorage getTupleStorage, @Cached PRaiseNode raiseNode) { self.checkReleased(inliningTarget, raiseNode); @@ -667,7 +667,7 @@ static PMemoryView doGeneric(VirtualFrame frame, PMemoryView self, TruffleString } else if (shapeObj instanceof PList) { isPListProfile.enter(inliningTarget); shape = shapeFromStorage(frame, inliningTarget, ((PList) shapeObj).getSequenceStorage(), getItemScalarNode, asSizeNode, raiseNode); - } else if (tupleCheck.execute(inliningTarget, shapeObj)) { + } else if (tupleCheckNode.execute(inliningTarget, shapeObj)) { SequenceStorage storage = getTupleStorage.execute(inliningTarget, shapeObj); shape = shapeFromStorage(frame, inliningTarget, storage, getItemScalarNode, asSizeNode, raiseNode); } else { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewNodes.java index 1bf17b91fb..2fcbda6741 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewNodes.java @@ -57,10 +57,12 @@ import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.lib.PyIndexCheckNode; import com.oracle.graal.python.lib.PyNumberAsSizeNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; import com.oracle.graal.python.nodes.util.CastToByteNode; @@ -391,15 +393,16 @@ MemoryPointer resolveTupleCached(VirtualFrame frame, PMemoryView self, PTuple in return ptr; } - @Specialization(replaces = "resolveTupleCached") - MemoryPointer resolveTupleGeneric(VirtualFrame frame, PMemoryView self, PTuple indices, + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, indices)", replaces = "resolveTupleCached") + MemoryPointer resolveTupleGeneric(VirtualFrame frame, PMemoryView self, Object indices, @Bind Node inliningTarget, + @SuppressWarnings("unused") @Shared @Cached PyTupleCheckNode tupleCheckNode, @Shared @Cached InlinedConditionProfile hasSuboffsetsProfile, @Shared @Cached PyIndexCheckNode indexCheckNode, - @Shared @Cached SequenceNodes.GetSequenceStorageNode getSequenceStorageNode, + @Shared @Cached GetTupleStorage getTupleStorage, @Shared @Cached SequenceStorageNodes.GetItemScalarNode getItemNode, @Shared @Cached PRaiseNode raiseNode) { - SequenceStorage indicesStorage = getSequenceStorageNode.execute(inliningTarget, indices); + SequenceStorage indicesStorage = getTupleStorage.execute(inliningTarget, indices); int ndim = self.getDimensions(); checkTupleLength(inliningTarget, indicesStorage, ndim, raiseNode); MemoryPointer ptr = new MemoryPointer(self.getBufferPointer(), self.getOffset()); @@ -411,9 +414,10 @@ MemoryPointer resolveTupleGeneric(VirtualFrame frame, PMemoryView self, PTuple i return ptr; } - @Specialization(guards = "!isPTuple(indexObj)") + @Specialization(guards = "!tupleCheckNode.execute(inliningTarget, indexObj)") MemoryPointer resolveIntObj(VirtualFrame frame, PMemoryView self, Object indexObj, @Bind Node inliningTarget, + @SuppressWarnings("unused") @Shared @Cached PyTupleCheckNode tupleCheckNode, @Shared @Cached InlinedConditionProfile hasOneDimensionProfile, @Shared @Cached InlinedConditionProfile hasSuboffsetsProfile, @Shared @Cached PyIndexCheckNode indexCheckNode, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java index 93207ee1d0..a5237a9456 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java @@ -452,10 +452,10 @@ abstract static class GetNewArgsInternalNode extends Node { static Pair doNewArgsEx(VirtualFrame frame, Object getNewArgsExAttr, @SuppressWarnings("unused") Object getNewArgsAttr, @Bind Node inliningTarget, @Exclusive @Cached CallNode callNode, - @Exclusive @Cached PyTupleCheckNode tupleCheckNode, @Cached PyDictCheckNode isDictSubClassNode, @Cached PyObjectGetItem getItemNode, @Cached PyObjectSizeNode sizeNode, + @Exclusive @Cached PyTupleCheckNode tupleCheckNode, @Exclusive @Cached PRaiseNode raiseNode) { Object newargs = callNode.execute(frame, getNewArgsExAttr); if (!tupleCheckNode.execute(inliningTarget, newargs)) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/random/RandomBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/random/RandomBuiltins.java index 33b12a6040..1b4cac834f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/random/RandomBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/random/RandomBuiltins.java @@ -60,14 +60,16 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.objects.PNone; -import com.oracle.graal.python.builtins.objects.common.SequenceNodes.GetObjectArrayNode; +import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.GetInternalObjectArrayNode; import com.oracle.graal.python.builtins.objects.ints.PInt; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyObjectHashNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode; import com.oracle.graal.python.nodes.call.special.SpecialMethodNotFound; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; @@ -78,6 +80,7 @@ import com.oracle.graal.python.nodes.util.CastToJavaUnsignedLongNode; import com.oracle.graal.python.runtime.exception.PythonErrorType; import com.oracle.graal.python.runtime.object.PFactory; +import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Bind; @@ -190,15 +193,21 @@ static PNone seedGeneric(VirtualFrame frame, PRandom random, Object inputSeed, public abstract static class SetStateNode extends PythonBuiltinNode { @Specialization - static PNone setstate(PRandom random, PTuple tuple, + static PNone setstate(PRandom random, Object tuple, @Bind Node inliningTarget, - @Cached GetObjectArrayNode getObjectArrayNode, + @Cached GetTupleStorage getTupleStorage, + @Cached GetInternalObjectArrayNode getArray, @Cached CastToJavaUnsignedLongNode castNode, + @Cached PyTupleCheckNode tupleCheckNode, @Cached PRaiseNode raiseNode) { - Object[] arr = getObjectArrayNode.execute(inliningTarget, tuple); - if (arr.length != PRandom.N + 1) { + if (!tupleCheckNode.execute(inliningTarget, tuple)) { + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.STATE_VECTOR_MUST_BE_A_TUPLE); + } + SequenceStorage storage = getTupleStorage.execute(inliningTarget, tuple); + if (storage.length() != PRandom.N + 1) { throw raiseNode.raise(inliningTarget, PythonErrorType.ValueError, ErrorMessages.STATE_VECTOR_INVALID); } + Object[] arr = getArray.execute(inliningTarget, storage); int[] state = new int[PRandom.N]; for (int i = 0; i < PRandom.N; ++i) { long l = castNode.execute(inliningTarget, arr[i]); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketNodes.java index 72b3c64866..3db5196ae5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketNodes.java @@ -128,7 +128,7 @@ static UniversalSockAddr doInet(VirtualFrame frame, @SuppressWarnings("unused") @Bind Node inliningTarget, @CachedLibrary(limit = "1") @Shared("posixLib") PosixSupportLibrary posixLib, @CachedLibrary(limit = "1") @Shared("sockAddrLib") UniversalSockAddrLibrary sockAddrLib, - @Cached @Shared("tupleCheck") PyTupleCheckNode tupleCheck, + @Shared("tupleCheck") @Cached PyTupleCheckNode tupleCheckNode, @Cached @Shared("tupleSize") PyTupleSizeNode tupleSize, @Cached @Shared("tupleGetItem") PyTupleGetItem tupleGetItem, @Cached @Shared("asInt") PyLongAsIntNode asIntNode, @@ -137,7 +137,7 @@ static UniversalSockAddr doInet(VirtualFrame frame, @SuppressWarnings("unused") @Cached @Shared("setIpAddr") SetIpAddrNode setIpAddrNode, @Cached @Shared PRaiseNode raiseNode) { PythonContext context = PythonContext.get(inliningTarget); - if (!tupleCheck.execute(inliningTarget, address)) { + if (!tupleCheckNode.execute(inliningTarget, address)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.S_AF_INET_VALUES_MUST_BE_TUPLE_NOT_P, caller, address); } int length = tupleSize.execute(inliningTarget, address); @@ -156,7 +156,7 @@ static UniversalSockAddr doInet6(VirtualFrame frame, @SuppressWarnings("unused") @Bind Node inliningTarget, @CachedLibrary(limit = "1") @Shared("posixLib") PosixSupportLibrary posixLib, @CachedLibrary(limit = "1") @Shared("sockAddrLib") UniversalSockAddrLibrary sockAddrLib, - @Cached @Shared("tupleCheck") PyTupleCheckNode tupleCheck, + @Shared("tupleCheck") @Cached PyTupleCheckNode tupleCheckNode, @Cached @Shared("tupleSize") PyTupleSizeNode tupleSize, @Cached @Shared("tupleGetItem") PyTupleGetItem tupleGetItem, @Cached @Shared("asInt") PyLongAsIntNode asIntNode, @@ -165,7 +165,7 @@ static UniversalSockAddr doInet6(VirtualFrame frame, @SuppressWarnings("unused") @Cached @Shared("setIpAddr") SetIpAddrNode setIpAddrNode, @Cached @Shared PRaiseNode raiseNode) { PythonContext context = PythonContext.get(inliningTarget); - if (!tupleCheck.execute(inliningTarget, address)) { + if (!tupleCheckNode.execute(inliningTarget, address)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.S_AF_INET_VALUES_MUST_BE_TUPLE_NOT_S, caller, address); } int length = tupleSize.execute(inliningTarget, address); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java index 978671eb31..5c5ffad650 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java @@ -651,9 +651,10 @@ public final boolean endsWith(Object self, Object subStr, int start, int end) { return execute(self, subStr, start, end, Op.SUFFIX); } - @Specialization(guards = "!isTuple(subStrObj)") + @Specialization(guards = "!tupleCheckNode.execute(inliningTarget, subStrObj)", limit = "1") static boolean doString(Object selfObj, Object subStrObj, int start, int end, Op op, @Bind Node inliningTarget, + @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheckNode, @Exclusive @Cached CastToTruffleStringChecked2Node castSelfNode, @Exclusive @Cached CastToTruffleStringChecked3Node castPrefixNode, @Shared @Cached TruffleString.CodePointLengthNode codePointLengthNode, @@ -665,9 +666,10 @@ static boolean doString(Object selfObj, Object subStrObj, int start, int end, Op return doIt(self, subStr, adjustStartIndex(start, selfLen), adjustEndIndex(end, selfLen), selfLen, subStrLen, regionEqualNode, op); } - @Specialization(guards = "isTuple(subStrs)") + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, subStrs)", limit = "1") static boolean doTuple(Object selfObj, Object subStrs, int start, int end, Op op, @Bind Node inliningTarget, + @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheckNode, @Exclusive @Cached TupleNodes.GetTupleStorage getTupleStorage, @Exclusive @Cached SequenceStorageNodes.GetItemScalarNode getItemNode, @Exclusive @Cached CastToTruffleStringChecked2Node castSelfNode, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/RLockBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/RLockBuiltins.java index 19a35cad51..7e39fd9078 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/RLockBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/RLockBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -55,8 +55,10 @@ import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; @@ -106,11 +108,16 @@ boolean isOwned(PRLock self) { @GenerateNodeFactory abstract static class AcquireRestoreRLockNode extends PythonBinaryBuiltinNode { @Specialization - Object acquireRestore(PRLock self, PTuple state, + Object acquireRestore(PRLock self, Object state, @Bind Node inliningTarget, + @Cached GetTupleStorage getTupleStorage, @Cached GilNode gil, @Cached CastToJavaUnsignedLongNode castLong, + @Cached PyTupleCheckNode tupleCheckNode, @Cached SequenceStorageNodes.GetItemDynamicNode getItemNode) { + if (!tupleCheckNode.execute(inliningTarget, state)) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.STATE_ARGUMENT_MUST_BE_A_TUPLE); + } if (!self.acquireNonBlocking()) { gil.release(true); try { @@ -120,7 +127,7 @@ Object acquireRestore(PRLock self, PTuple state, } } // ignore owner, we use the Java lock and cannot set it - long count = castLong.execute(inliningTarget, getItemNode.execute(inliningTarget, state.getSequenceStorage(), 0)); + long count = castLong.execute(inliningTarget, getItemNode.execute(inliningTarget, getTupleStorage.execute(inliningTarget, state), 0)); long actualCount = self.getCount(); while (count > actualCount) { self.acquireBlocking(this); // we already own it at this point diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/TupleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/TupleBuiltins.java index f37cabf139..b91de2043f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/TupleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/TupleBuiltins.java @@ -81,7 +81,6 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.builtins.TupleNodes; -import com.oracle.graal.python.nodes.builtins.TupleNodes.GetNativeTupleStorage; import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.nodes.classes.IsSubtypeNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; @@ -468,9 +467,8 @@ long computeHash(VirtualFrame frame, PTuple self, long computeHash(VirtualFrame frame, PythonAbstractNativeObject self, @Bind Node inliningTarget, @Shared("getItem") @Cached("createNotNormalized()") SequenceStorageNodes.GetItemNode getItemNode, - @Shared("hash") @Cached PyObjectHashNode hashNode, - @Cached GetNativeTupleStorage getStorage) { - return doComputeHash(frame, inliningTarget, getItemNode, hashNode, getStorage.execute(self)); + @Shared("hash") @Cached PyObjectHashNode hashNode) { + return doComputeHash(frame, inliningTarget, getItemNode, hashNode, GetTupleStorage.doNative(self)); } private static long doComputeHash(VirtualFrame frame, Node inliningTarget, SequenceStorageNodes.GetItemNode getItemNode, PyObjectHashNode hashNode, SequenceStorage tupleStore) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java index a7ffa2ac9f..d5e9ef293e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java @@ -344,35 +344,46 @@ public final Object execute(VirtualFrame frame, Object self, Object[] arguments, @Specialization(guards = "isString(wName)") @SuppressWarnings("truffle-static-method") - Object typeNew(VirtualFrame frame, Object cls, Object wName, PTuple bases, PDict namespaceOrig, PKeyword[] kwds, + Object typeNew(VirtualFrame frame, Object cls, Object wName, Object bases, PDict namespaceOrig, PKeyword[] kwds, @Bind Node inliningTarget, @Cached GetClassNode getClassNode, @Cached GetCachedTpSlotsNode getSlots, @Cached CallSlotTpNewNode callNew, @Cached @Exclusive IsTypeNode isTypeNode, @Cached PyObjectLookupAttr lookupMroEntriesNode, + @Exclusive @Cached PyTupleCheckNode tupleCheckNode, @Cached CastToTruffleStringNode castStr, @Cached TypeNodes.CreateTypeNode createType, - @Cached GetObjectArrayNode getObjectArrayNode) { + @Cached GetObjectArrayNode getObjectArrayNode, + @Exclusive @Cached ConstructTupleNode constructTupleNode) { + PTuple basesTuple; + if (bases instanceof PTuple) { + basesTuple = (PTuple) bases; + } else if (tupleCheckNode.execute(inliningTarget, bases)) { + basesTuple = constructTupleNode.execute(frame, bases); + } else { + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.ARG_D_MUST_BE_S_NOT_P, "type.__new__()", 2, "tuple", bases); + } + // Determine the proper metatype to deal with this TruffleString name = castStr.execute(inliningTarget, wName); Object metaclass = cls; - Object winner = calculateMetaclass(frame, inliningTarget, metaclass, bases, getClassNode, isTypeNode, lookupMroEntriesNode, getObjectArrayNode); + Object winner = calculateMetaclass(frame, inliningTarget, metaclass, basesTuple, getClassNode, isTypeNode, lookupMroEntriesNode, getObjectArrayNode); if (winner != metaclass) { TpSlot winnerNew = getSlots.execute(inliningTarget, winner).tp_new(); if (winnerNew != SLOTS.tp_new()) { // Pass it to the winner - return callNew.execute(frame, inliningTarget, winnerNew, winner, new Object[]{name, bases, namespaceOrig}, kwds); + return callNew.execute(frame, inliningTarget, winnerNew, winner, new Object[]{name, basesTuple, namespaceOrig}, kwds); } metaclass = winner; } - return createType.execute(frame, namespaceOrig, name, bases, metaclass, kwds); + return createType.execute(frame, namespaceOrig, name, basesTuple, metaclass, kwds); } @Fallback Object generic(@SuppressWarnings("unused") Object cls, @SuppressWarnings("unused") Object name, Object bases, Object namespace, @SuppressWarnings("unused") PKeyword[] kwds) { - if (!(bases instanceof PTuple)) { + if (!PyTupleCheckNode.executeUncached(bases)) { throw PRaiseNode.raiseStatic(this, TypeError, ErrorMessages.ARG_D_MUST_BE_S_NOT_P, "type.__new__()", 2, "tuple", bases); } else if (!(namespace instanceof PDict)) { throw PRaiseNode.raiseStatic(this, TypeError, ErrorMessages.ARG_D_MUST_BE_S_NOT_P, "type.__new__()", 3, "dict", bases); @@ -421,15 +432,15 @@ static Object typeGeneric(VirtualFrame frame, Object cls, Object name, Object ba @Bind Node inliningTarget, @Cached TypeNode nextTypeNode, @Cached PRaiseNode raiseNode, - @Cached PyTupleCheckNode tupleCheck, - @Cached ConstructTupleNode constructTupleNode, + @Exclusive @Cached PyTupleCheckNode tupleCheckNode, + @Exclusive @Cached ConstructTupleNode constructTupleNode, @Exclusive @Cached IsTypeNode isTypeNode) { Object basesTuple; if (!(name instanceof TruffleString || name instanceof PString)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.MUST_BE_STRINGS_NOT_P, "type() argument 1", name); } else if (bases instanceof PTuple) { basesTuple = bases; - } else if (tupleCheck.execute(inliningTarget, bases)) { + } else if (tupleCheckNode.execute(inliningTarget, bases)) { basesTuple = constructTupleNode.execute(frame, bases); } else { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.MUST_BE_STRINGS_NOT_P, "type() argument 2", bases); @@ -686,10 +697,10 @@ static Object getBases(Object self, @SuppressWarnings("unused") PNone value, return PFactory.createTuple(language, getBaseClassesNode.execute(inliningTarget, self)); } - @Specialization(guards = "tupleCheck.execute(inliningTarget, value)", limit = "1") + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, value)", limit = "1") static Object setBases(VirtualFrame frame, PythonClass cls, Object value, @Bind Node inliningTarget, - @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheck, + @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheckNode, @Cached ConstructTupleNode constructTupleNode, @Cached GetObjectArrayNode getArray, @Cached GetBaseClassNode getBase, @@ -759,10 +770,10 @@ private static boolean typeIsSubtypeBaseChain(Node inliningTarget, Object a, Obj return (isSameTypeNode.execute(inliningTarget, b, PythonBuiltinClassType.PythonObject)); } - @Specialization(guards = "!tupleCheck.execute(inliningTarget, value)", limit = "1") + @Specialization(guards = "!tupleCheckNode.execute(inliningTarget, value)", limit = "1") static Object setObject(@SuppressWarnings("unused") PythonClass cls, @SuppressWarnings("unused") Object value, @Bind Node inliningTarget, - @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheck) { + @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheckNode) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.CAN_ONLY_ASSIGN_S_TO_S_S_NOT_P, "tuple", GetNameNode.executeUncached(cls), "__bases__", value); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java index 960f28b93f..df7e624ce4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java @@ -183,6 +183,7 @@ import com.oracle.graal.python.lib.PyEvalGetGlobals; import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.lib.PyObjectSizeNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.HiddenAttr; @@ -2156,6 +2157,8 @@ static PythonClass typeMetaclass(VirtualFrame frame, TruffleString name, PTuple @Cached GetInstanceShape getInstanceShape, @Cached CastToListNode castToListNode, @Cached PyUnicodeCheckNode stringCheck, + @Cached PyTupleCheckNode tupleCheckNode, + @Cached TupleNodes.GetTupleStorage getTupleStorage, @Cached TruffleString.IsValidNode isValidNode, @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Cached TruffleString.IndexOfCodePointNode indexOfCodePointNode, @@ -2234,8 +2237,8 @@ static PythonClass typeMetaclass(VirtualFrame frame, TruffleString name, PTuple Object slotsObject = ctx.slotsObject; if (stringCheck.execute(inliningTarget, ctx.slotsObject)) { slotsStorage = new ObjectSequenceStorage(new Object[]{castToStringNode.execute(inliningTarget, ctx.slotsObject)}); - } else if (ctx.slotsObject instanceof PTuple slotsTuple) { - slotsStorage = slotsTuple.getSequenceStorage(); + } else if (tupleCheckNode.execute(inliningTarget, ctx.slotsObject)) { + slotsStorage = getTupleStorage.execute(inliningTarget, ctx.slotsObject); } else if (ctx.slotsObject instanceof PList slotsList) { slotsStorage = slotsList.getSequenceStorage(); } else { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/types/GenericAliasBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/types/GenericAliasBuiltins.java index 792357d470..00eec6a44c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/types/GenericAliasBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/types/GenericAliasBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -105,6 +105,7 @@ import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.StringLiterals; import com.oracle.graal.python.nodes.builtins.ListNodes; +import com.oracle.graal.python.nodes.builtins.TupleNodes; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; @@ -165,9 +166,16 @@ protected List> getNodeFa public abstract static class GenericAliasNode extends PythonTernaryBuiltinNode { @Specialization static PGenericAlias doit(Object cls, Object origin, Object arguments, + @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TypeNodes.GetInstanceShape getInstanceShape) { - return PFactory.createGenericAlias(language, cls, getInstanceShape.execute(cls), origin, arguments, false); + @Cached TypeNodes.GetInstanceShape getInstanceShape, + @Cached TupleNodes.EnsureManagedTupleNode ensureManagedTupleNode) { + PTuple argumentsTuple = ensureManagedTupleNode.execute(inliningTarget, arguments); + if (argumentsTuple == null) { + // in this case, 'args' is not a tuple object + argumentsTuple = PFactory.createTuple(language, new Object[]{arguments}); + } + return PFactory.createGenericAlias(language, cls, getInstanceShape.execute(cls), origin, argumentsTuple, false); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/types/GenericTypeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/types/GenericTypeNodes.java index b1fb359a91..89d7061d7a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/types/GenericTypeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/types/GenericTypeNodes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -59,7 +59,6 @@ import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; -import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.GetInternalObjectArrayNode; import com.oracle.graal.python.builtins.objects.ellipsis.PEllipsis; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode; @@ -71,6 +70,9 @@ import com.oracle.graal.python.lib.PyObjectRichCompareBool; import com.oracle.graal.python.lib.PyObjectStrAsTruffleStringNode; import com.oracle.graal.python.lib.PyObjectTypeCheck; +import com.oracle.graal.python.lib.PyTupleCheckNode; +import com.oracle.graal.python.lib.PyTupleGetItem; +import com.oracle.graal.python.lib.PyTupleSizeNode; import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.nodes.BuiltinNames; import com.oracle.graal.python.nodes.ErrorMessages; @@ -166,10 +168,10 @@ static Object[] makeParametersUncached(PTuple args) { listAdd(parameters, t); } else { Object subparams = lookup.execute(null, null, t, T___PARAMETERS__); - if (subparams instanceof PTuple subparamsTuple) { - SequenceStorage subparamsStorage = subparamsTuple.getSequenceStorage(); - for (int j = 0; j < subparamsStorage.length(); j++) { - listAdd(parameters, getItemUncached(subparamsStorage, j)); + if (PyTupleCheckNode.executeUncached(subparams)) { + int subparamsLen = PyTupleSizeNode.executeUncached(subparams); + for (int j = 0; j < subparamsLen; j++) { + listAdd(parameters, PyTupleGetItem.executeUncached(subparams, j)); } } } @@ -209,10 +211,10 @@ private static int tupleIndex(PTuple tuple, Object obj) { // Equivalent of tuple_extend, but we use list @TruffleBoundary - private static void listExtend(List list, PTuple tuple) { - SequenceStorage storage = tuple.getSequenceStorage(); - for (int i = 0; i < storage.length(); i++) { - list.add(SequenceStorageNodes.GetItemScalarNode.executeUncached(storage, i)); + private static void listExtend(List list, Object tuple) { + int len = PyTupleSizeNode.executeUncached(tuple); + for (int i = 0; i < len; i++) { + list.add(PyTupleGetItem.executeUncached(tuple, i)); } } @@ -242,10 +244,10 @@ private static Object unpackedTupleArgs(Object item) { @TruffleBoundary private static PTuple unpackArgs(Object item) { List newargs = new ArrayList<>(); - if (item instanceof PTuple tuple) { - SequenceStorage storage = tuple.getSequenceStorage(); - for (int i = 0; i < storage.length(); i++) { - unpackArgsInner(newargs, getItemUncached(storage, i)); + if (PyTupleCheckNode.executeUncached(item)) { + int len = PyTupleSizeNode.executeUncached(item); + for (int i = 0; i < len; i++) { + unpackArgsInner(newargs, PyTupleGetItem.executeUncached(item, i)); } } else { unpackArgsInner(newargs, item); @@ -256,11 +258,11 @@ private static PTuple unpackArgs(Object item) { private static void unpackArgsInner(List newargs, Object item) { if (!TypeNodes.IsTypeNode.executeUncached(item)) { Object subargs = unpackedTupleArgs(item); - if (subargs instanceof PTuple tuple) { - SequenceStorage storage = tuple.getSequenceStorage(); - if (!(storage.length() > 0 && getItemUncached(storage, storage.length() - 1) == PEllipsis.INSTANCE)) { - for (int i = 0; i < storage.length(); i++) { - newargs.add(getItemUncached(storage, i)); + if (PyTupleCheckNode.executeUncached(subargs)) { + int len = PyTupleSizeNode.executeUncached(subargs); + if (!(len > 0 && PyTupleGetItem.executeUncached(subargs, len - 1) == PEllipsis.INSTANCE)) { + for (int i = 0; i < len; i++) { + newargs.add(PyTupleGetItem.executeUncached(subargs, i)); } return; } @@ -283,16 +285,19 @@ static Object[] subsParametersUncached(Node node, Object self, PTuple args, PTup Object param = getItemUncached(paramsStorage, i); Object prepare = PyObjectLookupAttr.executeUncached(param, T___TYPING_PREPARE_SUBST__); if (!(prepare instanceof PNone)) { - Object itemarg = item instanceof PTuple ? item : PFactory.createTuple(language, new Object[]{item}); + Object itemarg = PyTupleCheckNode.executeUncached(item) ? item : PFactory.createTuple(language, new Object[]{item}); item = CallNode.executeUncached(prepare, self, itemarg); } } int nitems; Object[] argitems; - if (item instanceof PTuple t) { - argitems = GetInternalObjectArrayNode.executeUncached(t.getSequenceStorage()); - nitems = t.getSequenceStorage().length(); + if (PyTupleCheckNode.executeUncached(item)) { + nitems = PyTupleSizeNode.executeUncached(item); + argitems = new Object[nitems]; + for (int i = 0; i < nitems; i++) { + argitems[i] = PyTupleGetItem.executeUncached(item, i); + } } else { argitems = new Object[]{item}; nitems = 1; @@ -318,8 +323,8 @@ static Object[] subsParametersUncached(Node node, Object self, PTuple args, PTup } else { arg = subsTvars(arg, parameters, argitems); } - if (unpack && arg instanceof PTuple tuple /* CPython doesn't check the cast?! */) { - listExtend(newargs, tuple); + if (unpack && PyTupleCheckNode.executeUncached(arg) /* CPython doesn't check the cast?! */) { + listExtend(newargs, arg); } else { newargs.add(arg); } @@ -330,19 +335,19 @@ static Object[] subsParametersUncached(Node node, Object self, PTuple args, PTup @TruffleBoundary private static Object subsTvars(Object obj, PTuple parameters, Object[] argitems) { Object subparams = PyObjectLookupAttr.executeUncached(obj, T___PARAMETERS__); - if (subparams instanceof PTuple tuple && tuple.getSequenceStorage().length() > 0) { - SequenceStorage subparamsStorage = tuple.getSequenceStorage(); - List subargs = new ArrayList<>(subparamsStorage.length()); - for (int i = 0; i < subparamsStorage.length(); i++) { - Object arg = getItemUncached(subparamsStorage, i); + if (PyTupleCheckNode.executeUncached(subparams) && PyTupleSizeNode.executeUncached(subparams) > 0) { + int subparamsLen = PyTupleSizeNode.executeUncached(subparams); + List subargs = new ArrayList<>(subparamsLen); + for (int i = 0; i < subparamsLen; i++) { + Object arg = PyTupleGetItem.executeUncached(subparams, i); int foundIndex = tupleIndex(parameters, arg); if (foundIndex >= 0) { Object param = getItemUncached(parameters.getSequenceStorage(), foundIndex); arg = argitems[foundIndex]; // TypeVarTuple - if (arg instanceof PTuple tuple1) { + if (PyTupleCheckNode.executeUncached(arg)) { if (GetObjectSlotsNode.executeUncached(param).tp_iter() != null) { - listExtend(subargs, tuple1); + listExtend(subargs, arg); continue; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/typing/TypeAliasTypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/typing/TypeAliasTypeBuiltins.java index f473cde726..214a07368f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/typing/TypeAliasTypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/typing/TypeAliasTypeBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -74,8 +74,10 @@ import com.oracle.graal.python.builtins.objects.types.GenericTypeNodes.UnionTypeOrNode; import com.oracle.graal.python.builtins.objects.typing.TypeAliasTypeBuiltinsClinicProviders.TypeAliasTypeNodeClinicProviderGen; import com.oracle.graal.python.lib.PyObjectGetItem; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.EnsureManagedTupleNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonClinicBuiltinNode; @@ -140,9 +142,12 @@ static PTuple doDefault(@SuppressWarnings("unused") Object o) { return null; } - @Specialization - static PTuple doTuple(PTuple o) { - return o.getSequenceStorage().length() == 0 ? null : o; + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, o)", limit = "1") + static PTuple doTuple(Node inliningTarget, Object o, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, + @Cached EnsureManagedTupleNode ensureManagedTupleNode) { + PTuple tuple = ensureManagedTupleNode.execute(inliningTarget, o); + return tuple.getSequenceStorage().length() == 0 ? null : tuple; } @Fallback diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyErrExceptionMatchesNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyErrExceptionMatchesNode.java index a93679e963..1c4951a910 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyErrExceptionMatchesNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyErrExceptionMatchesNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -43,6 +43,7 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.type.TypeNodes; +import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.builtins.TupleNodes; import com.oracle.graal.python.nodes.classes.IsSubtypeNode; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles; @@ -53,6 +54,7 @@ import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.InlinedLoopConditionProfile; @@ -64,6 +66,7 @@ */ @GenerateInline(inlineByDefault = true) @GenerateUncached +@ImportStatic(PGuards.class) public abstract class PyErrExceptionMatchesNode extends Node { public abstract boolean execute(Node inliningTarget, Object exception, Object typeOrTuple); @@ -89,9 +92,9 @@ static boolean doException(@SuppressWarnings("unused") Node inliningTarget, Obje } } - @Specialization(guards = "tupleCheck.execute(inliningTarget, tuple)", limit = "1") + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, tuple)", limit = "1") static boolean doTuple(Node inliningTarget, Object exception, Object tuple, - @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheck, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, @Cached TupleNodes.GetTupleStorage getTupleStorage, @Cached SequenceStorageNodes.GetItemScalarNode getItemScalarNode, @Cached InlinedLoopConditionProfile loopConditionProfile, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyMappingCheckNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyMappingCheckNode.java index f1b1a9c8a5..4a954a6536 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyMappingCheckNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyMappingCheckNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -40,6 +40,7 @@ */ package com.oracle.graal.python.lib; +import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.str.PString; import com.oracle.graal.python.builtins.objects.type.TpSlots.GetCachedTpSlotsNode; @@ -82,6 +83,15 @@ static boolean doSequence(@SuppressWarnings("unused") PSequence object) { return true; } + @Specialization(guards = "isNativeTuple(object)") + static boolean doSequence(@SuppressWarnings("unused") PythonAbstractNativeObject object) { + return true; + } + + static boolean isNativeTuple(PythonAbstractNativeObject object) { + return PyTupleCheckNode.checkNative(object); + } + @Specialization static boolean doString(@SuppressWarnings("unused") PString object) { return true; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsInstanceNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsInstanceNode.java index 84b729f2d8..b6156c4407 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsInstanceNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsInstanceNode.java @@ -87,10 +87,10 @@ public static PyObjectIsInstanceNode getUncached() { return PyObjectIsInstanceNodeGen.getUncached(); } - @Specialization(guards = "!tupleCheck.execute(inliningTarget, cls)", insertBefore = "doRecursiveWithNode", limit = "1") + @Specialization(guards = "!tupleCheckNode.execute(inliningTarget, cls)", insertBefore = "doRecursiveWithNode", limit = "1") static boolean isInstance(VirtualFrame frame, Object instance, Object cls, @SuppressWarnings("unused") int depth, @Bind Node inliningTarget, - @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheck, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, @Cached GetClassNode getClsClassNode, @Cached IsBuiltinClassExactProfile classProfile, @Cached GetClassNode getInstanceClassNode, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsSubclassNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsSubclassNode.java index 4ba85c6c47..c43b30fcc6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsSubclassNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsSubclassNode.java @@ -87,10 +87,10 @@ public static PyObjectIsSubclassNode getUncached() { return PyObjectIsSubclassNodeGen.getUncached(); } - @Specialization(guards = "!tupleCheck.execute(inliningTarget, cls)", insertBefore = "doRecursiveWithNode", limit = "1") + @Specialization(guards = "!tupleCheckNode.execute(inliningTarget, cls)", insertBefore = "doRecursiveWithNode", limit = "1") static boolean isSubclass(VirtualFrame frame, Object derived, Object cls, @SuppressWarnings("unused") int depth, @Bind Node inliningTarget, - @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheck, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, @Cached GetClassNode getClsClassNode, @Cached IsBuiltinClassExactProfile classProfile, @Cached LookupSpecialMethodNode.Dynamic subclassCheckLookup, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectRecursiveBinaryCheckNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectRecursiveBinaryCheckNode.java index 6d182dfd60..58c19a75d5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectRecursiveBinaryCheckNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectRecursiveBinaryCheckNode.java @@ -82,11 +82,11 @@ public final boolean execute(Frame frame, Object arg, Object classinfo) { abstract PyObjectRecursiveBinaryCheckNode getUncachedRecursive(); - @Specialization(guards = {"depth < getNodeRecursionLimit(language)", "tupleCheck.execute(inliningTarget, clsTuple)"}, limit = "1", excludeForUncached = true) + @Specialization(guards = {"depth < getNodeRecursionLimit(language)", "tupleCheckNode.execute(inliningTarget, clsTuple)"}, excludeForUncached = true, limit = "1") static boolean doRecursiveWithNode(VirtualFrame frame, Object arg, Object clsTuple, int depth, @Bind Node inliningTarget, @SuppressWarnings("unused") @Bind PythonLanguage language, - @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheck, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, @Cached TupleNodes.GetTupleStorage getTupleStorage, @Cached SequenceStorageNodes.ToArrayNode toArrayNode, @Cached("createRecursive()") PyObjectRecursiveBinaryCheckNode recursiveNode) { @@ -94,12 +94,12 @@ static boolean doRecursiveWithNode(VirtualFrame frame, Object arg, Object clsTup } @SuppressWarnings("truffle-static-method") - @Specialization(guards = {"depth >= getNodeRecursionLimit(language)", "tupleCheck.execute(inliningTarget, clsTuple)"}, limit = "1", excludeForUncached = true) + @Specialization(guards = {"depth >= getNodeRecursionLimit(language)", "tupleCheckNode.execute(inliningTarget, clsTuple)"}, excludeForUncached = true, limit = "1") boolean doRecursiveTransition(VirtualFrame frame, Object arg, Object clsTuple, @SuppressWarnings("unused") int depth, @Bind Node inliningTarget, @SuppressWarnings("unused") @Bind PythonLanguage language, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, @Cached("createFor($node)") BoundaryCallData boundaryCallData, - @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheck, @Cached TupleNodes.GetTupleStorage getTupleStorage, @Cached SequenceStorageNodes.ToArrayNode toArrayNode) { Object state = BoundaryCallContext.enter(frame, boundaryCallData); @@ -112,10 +112,10 @@ boolean doRecursiveTransition(VirtualFrame frame, Object arg, Object clsTuple, @ } @SuppressWarnings("truffle-static-method") - @Specialization(guards = "tupleCheck.execute(inliningTarget, clsTuple)", limit = "1") + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, clsTuple)", limit = "1") boolean doRecursiveUncached(VirtualFrame frame, Object arg, Object clsTuple, @SuppressWarnings("unused") int depth, @Bind Node inliningTarget, - @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheck, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheckNode, @Cached TupleNodes.GetTupleStorage getTupleStorage, @Cached SequenceStorageNodes.ToArrayNode toArrayNode) { assert this instanceof UnadoptableNode; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceCheckNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceCheckNode.java index 2624cc5bc1..a9906bdd09 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceCheckNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceCheckNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -40,6 +40,7 @@ */ package com.oracle.graal.python.lib; +import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.str.PString; import com.oracle.graal.python.builtins.objects.type.TpSlots.GetCachedTpSlotsNode; @@ -88,6 +89,15 @@ static boolean doDict(@SuppressWarnings("unused") PDict object) { return false; } + @Specialization(guards = "isNativeTuple(object)") + static boolean doNativeTuple(@SuppressWarnings("unused") PythonAbstractNativeObject object) { + return true; + } + + static boolean isNativeTuple(PythonAbstractNativeObject object) { + return PyTupleCheckNode.checkNative(object); + } + @Fallback static boolean doGeneric(Node inliningTarget, Object object, @Cached PyDictCheckNode dictCheckNode, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleCheckExactNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleCheckExactNode.java index 464979834a..8b7e80633f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleCheckExactNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleCheckExactNode.java @@ -40,20 +40,25 @@ */ package com.oracle.graal.python.lib; +import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyObject__ob_type; +import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readPtrField; + import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectExactProfile; +import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; -import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.InlinedBranchProfile; /** * Equivalent of CPython's {@code PyTuple_CheckExact}. @@ -63,25 +68,32 @@ @GenerateInline @ImportStatic(PGuards.class) public abstract class PyTupleCheckExactNode extends PNodeWithContext { + @TruffleBoundary public static boolean executeUncached(Object object) { return PyTupleCheckExactNodeGen.getUncached().execute(null, object); } public abstract boolean execute(Node inliningTarget, Object object); - @Specialization(guards = "isBuiltinTuple(tuple)") - static boolean doBuiltinTuple(@SuppressWarnings("unused") PTuple tuple) { - return true; - } - @Specialization - static boolean doNativeTuple(PythonAbstractNativeObject tuple, - @Cached(inline = false) IsBuiltinObjectExactProfile check) { - return check.profileObject(check, tuple, PythonBuiltinClassType.PTuple); + public static boolean doGeneric(Node inliningTarget, Object object, + @Cached InlinedBranchProfile isPTupleProfile, + @Cached InlinedBranchProfile isNativeProfile) { + if (object instanceof PTuple tuple) { + isPTupleProfile.enter(inliningTarget); + return PGuards.isBuiltinTuple(tuple); + } + if (object instanceof PythonAbstractNativeObject nativeObject) { + isNativeProfile.enter(inliningTarget); + return checkNative(PythonContext.get(inliningTarget), nativeObject); + } + return false; } - @Fallback - static boolean doOther(@SuppressWarnings("unused") Object object) { - return false; + public static boolean checkNative(PythonContext context, PythonAbstractNativeObject nativeObject) { + long obType = readPtrField(nativeObject.pointer, PyObject__ob_type); + boolean isTupleExact = obType == context.lookupType(PythonBuiltinClassType.PTuple).getNativePointer(); + assert IsBuiltinObjectExactProfile.profileObjectUncached(nativeObject, PythonBuiltinClassType.PTuple) == isTupleExact; + return isTupleExact; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleCheckNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleCheckNode.java index 7414e329ec..5b1f264234 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleCheckNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleCheckNode.java @@ -47,13 +47,16 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; import com.oracle.graal.python.builtins.objects.cext.structs.CFields; +import com.oracle.graal.python.builtins.objects.list.PList; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TypeFlags; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.InlinedBranchProfile; @@ -65,6 +68,10 @@ public abstract class PyTupleCheckNode extends Node { public abstract boolean execute(Node inliningTarget, Object object); + public final boolean isTupleOrList(Node inliningTarget, Object object) { + return object instanceof PList || execute(inliningTarget, object); + } + public static boolean executeUncached(Object object) { return PyTupleCheckNodeGen.getUncached().execute(null, object); } @@ -90,4 +97,26 @@ public static boolean checkNative(PythonAbstractNativeObject nativeObject) { assert IsBuiltinObjectProfile.profileObjectUncached(nativeObject, PythonBuiltinClassType.PTuple) == isTupleSubclass; return isTupleSubclass; } + + @GenerateInline(false) + public abstract static class CachedNode extends Node { + public abstract boolean execute(Object object); + + public final boolean isTupleOrList(Object object) { + return object instanceof PList || execute(object); + } + + @Specialization + static boolean doGeneric(Object object, + @Bind Node inliningTarget, + @Cached InlinedBranchProfile isPTupleProfile, + @Cached InlinedBranchProfile isNativeProfile) { + return PyTupleCheckNode.doGeneric(inliningTarget, object, isPTupleProfile, isNativeProfile); + } + + @NeverDefault + public static CachedNode create() { + return PyTupleCheckNodeGen.CachedNodeGen.create(); + } + } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java index 74bd1f80a9..05cb1cb880 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java @@ -90,7 +90,6 @@ import com.oracle.graal.python.builtins.objects.type.TpSlots.GetCachedTpSlotsNode; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyIndexCheckNode; -import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.object.GetClassNode.GetPythonObjectClassNode; import com.oracle.graal.python.nodes.object.IsForeignObjectNode; import com.oracle.graal.python.runtime.exception.PException; @@ -109,7 +108,6 @@ import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.UnexpectedResultException; -import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.api.strings.TruffleString; import com.oracle.truffle.api.strings.TruffleString.CodeRange; @@ -327,10 +325,6 @@ public static boolean isPTuple(Object obj) { return obj instanceof PTuple; } - public static boolean isTuple(Object obj) { - return PyTupleCheckNode.doGeneric(null, obj, InlinedBranchProfile.getUncached(), InlinedBranchProfile.getUncached()); - } - public static boolean isPSequence(Object obj) { return obj instanceof PSequence; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/ListNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/ListNodes.java index 914d3e2ccb..132725a9d4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/ListNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/ListNodes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -346,7 +346,7 @@ NativeSequenceStorage getNative(PythonAbstractNativeObject list) { long array = readPtrField(listRawPtr, PyListObject__ob_item); int size = (int) readLongField(listRawPtr, PyVarObject__ob_size); int allocated = (int) readLongField(listRawPtr, PyListObject__allocated); - return NativeObjectSequenceStorage.create(array, size, allocated, false); + return NativeObjectSequenceStorage.create(array, size, allocated, list); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/TupleNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/TupleNodes.java index 16ac0db8d9..5e5ba28be1 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/TupleNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/TupleNodes.java @@ -55,6 +55,7 @@ import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.lib.PyObjectGetIter; import com.oracle.graal.python.lib.PyTupleCheckNode; +import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.runtime.sequence.storage.NativeObjectSequenceStorage; @@ -66,6 +67,7 @@ import com.oracle.truffle.api.dsl.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.Frame; @@ -76,6 +78,7 @@ public abstract class TupleNodes { @GenerateUncached @GenerateInline(false) // footprint reduction 40 -> 21 + @ImportStatic({PGuards.class, PyTupleCheckNode.class}) public abstract static class ConstructTupleNode extends PNodeWithContext { public abstract PTuple execute(Frame frame, Object value); @@ -98,14 +101,12 @@ static PTuple list(PList iterable, return PFactory.createTuple(language, copyNode.execute(inliningTarget, iterable.getSequenceStorage())); } - @Specialization(guards = "tupleCheck.execute(inliningTarget, iterable)", limit = "1") + @Specialization(guards = "checkNative(iterable)") static PTuple nativeTuple(PythonAbstractNativeObject iterable, @Bind Node inliningTarget, @Bind PythonLanguage language, - @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheck, - @Cached GetNativeTupleStorage getNativeTupleStorage, @Cached SequenceStorageNodes.ToArrayNode toArrayNode) { - return PFactory.createTuple(language, toArrayNode.execute(inliningTarget, getNativeTupleStorage.execute(iterable))); + return PFactory.createTuple(language, toArrayNode.execute(inliningTarget, GetTupleStorage.doNative(iterable))); } @Fallback @@ -138,33 +139,46 @@ public abstract static class GetTupleStorage extends Node { public abstract SequenceStorage execute(Node inliningTarget, Object tuple); @Specialization - SequenceStorage getManaged(PTuple tuple) { + static SequenceStorage doManaged(PTuple tuple) { return tuple.getSequenceStorage(); } @Specialization - SequenceStorage getNative(PythonAbstractNativeObject tuple, - @Cached(inline = false) GetNativeTupleStorage getNativeTupleStorage) { - return getNativeTupleStorage.execute(tuple); + public static NativeObjectSequenceStorage doNative(PythonAbstractNativeObject tuple) { + assert PyTupleCheckNode.executeUncached(tuple); + long tupleRawPtr = tuple.getPtr(); + long array = CStructAccess.getFieldPtr(tupleRawPtr, CFields.PyTupleObject__ob_item); + int size = (int) readLongField(tupleRawPtr, PyVarObject__ob_size); + // The storage borrows the native tuple's ob_item memory, so keep the native tuple owner + // alive for as long as the storage may read from it. + return NativeObjectSequenceStorage.create(array, size, size, tuple); } } - @GenerateInline(false) @GenerateUncached - public abstract static class GetNativeTupleStorage extends Node { - public abstract NativeObjectSequenceStorage execute(PythonAbstractNativeObject tuple); + @GenerateInline + @GenerateCached(false) + public abstract static class EnsureManagedTupleNode extends Node { - public static GetNativeTupleStorage getUncached() { - return TupleNodesFactory.GetNativeTupleStorageNodeGen.getUncached(); - } + /** + * Ensures that the given tuple object is a managed tuple (i.e. {@link PTuple}). If the + * object is not a tuple at all, this node returns {@code null}. + */ + public abstract PTuple execute(Node inliningTarget, Object object); @Specialization - NativeObjectSequenceStorage getNative(PythonAbstractNativeObject tuple) { - assert PyTupleCheckNode.executeUncached(tuple); - long tupleRawPtr = tuple.getPtr(); - long array = CStructAccess.getFieldPtr(tupleRawPtr, CFields.PyTupleObject__ob_item); - int size = (int) readLongField(tupleRawPtr, PyVarObject__ob_size); - return NativeObjectSequenceStorage.create(array, size, size, false); + static PTuple doGeneric(Node inliningTarget, Object object, + @Cached SequenceStorageNodes.CopyNode copyNode) { + if (object instanceof PTuple managedTuple) { + return managedTuple; + } + if (object instanceof PythonAbstractNativeObject nativeTuple && PyTupleCheckNode.checkNative(nativeTuple)) { + // 'GetTupleStorageNode.doNative' will just "wrap" the 'ob_item' pointer. The memory is then still + // owned by the native tuple object. Therefore, we need to copy the storage to a managed storage. + NativeObjectSequenceStorage nativeObjectSequenceStorage = GetTupleStorage.doNative(nativeTuple); + return PFactory.createTuple(PythonLanguage.get(inliningTarget), copyNode.execute(inliningTarget, nativeObjectSequenceStorage)); + } + return null; } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes/AbstractObjectGetBasesNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes/AbstractObjectGetBasesNode.java index 2a80ba9b32..7e171aff33 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes/AbstractObjectGetBasesNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes/AbstractObjectGetBasesNode.java @@ -70,12 +70,12 @@ public static AbstractObjectGetBasesNode getUncached() { @Specialization static PTuple getBasesCached(VirtualFrame frame, Node inliningTarget, Object cls, @Cached PyObjectLookupAttr lookupAttr, - @Cached PyTupleCheckNode tupleCheck, + @Cached PyTupleCheckNode tupleCheckNode, @Cached ConstructTupleNode constructTupleNode) { Object bases = lookupAttr.execute(frame, inliningTarget, cls, T___BASES__); if (bases instanceof PTuple) { return (PTuple) bases; - } else if (tupleCheck.execute(inliningTarget, bases)) { + } else if (tupleCheckNode.execute(inliningTarget, bases)) { return constructTupleNode.execute(frame, bases); } return null; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/ExceptMatchNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/ExceptMatchNode.java index bcfbaa552b..44593245aa 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/ExceptMatchNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/ExceptMatchNode.java @@ -82,10 +82,10 @@ private static void raiseIfNoException(Node inliningTarget, Object clause, Valid } } - @Specialization(guards = "!tupleCheck.execute(inliningTarget, clause)") + @Specialization(guards = "!tupleCheckNode.execute(inliningTarget, clause)") public static boolean matchPythonSingle(PException e, Object clause, @Bind Node inliningTarget, - @SuppressWarnings("unused") @Shared("tupleCheck") @Cached PyTupleCheckNode tupleCheck, + @SuppressWarnings("unused") @Shared @Cached PyTupleCheckNode tupleCheckNode, @Shared @Cached ValidExceptionNode isValidException, @Shared @Cached GetClassNode getClassNode, @Shared @Cached IsSubtypeNode isSubtype) { @@ -93,10 +93,10 @@ public static boolean matchPythonSingle(PException e, Object clause, return isSubtype.execute(getClassNode.execute(inliningTarget, e.getUnreifiedException()), clause); } - @Specialization(guards = "!tupleCheck.execute(inliningTarget, clause)") + @Specialization(guards = "!tupleCheckNode.execute(inliningTarget, clause)") public static boolean matchPythonBaseSingle(PBaseException e, Object clause, @Bind Node inliningTarget, - @SuppressWarnings("unused") @Shared("tupleCheck") @Cached PyTupleCheckNode tupleCheck, + @SuppressWarnings("unused") @Shared @Cached PyTupleCheckNode tupleCheckNode, @Shared @Cached ValidExceptionNode isValidException, @Shared @Cached GetClassNode getClassNode, @Shared @Cached IsSubtypeNode isSubtype) { @@ -104,10 +104,10 @@ public static boolean matchPythonBaseSingle(PBaseException e, Object clause, return isSubtype.execute(getClassNode.execute(inliningTarget, e), clause); } - @Specialization(guards = {"!tupleCheck.execute(inliningTarget, clause)", "!isPException(e)"}, limit = "1") + @Specialization(guards = {"!tupleCheckNode.execute(inliningTarget, clause)", "!isPException(e)"}, limit = "1") public static boolean matchJava(AbstractTruffleException e, Object clause, @Bind Node inliningTarget, - @SuppressWarnings("unused") @Shared("tupleCheck") @Cached PyTupleCheckNode tupleCheck, + @SuppressWarnings("unused") @Shared @Cached PyTupleCheckNode tupleCheckNode, @Shared @Cached ValidExceptionNode isValidException, @CachedLibrary("clause") InteropLibrary clauseLib) { // n.b.: we can only allow Java exceptions in clauses, because we cannot tell for other @@ -125,10 +125,10 @@ public static boolean matchJava(AbstractTruffleException e, Object clause, } } - @Specialization(guards = "tupleCheck.execute(inliningTarget, clause)", limit = "1") + @Specialization(guards = "tupleCheckNode.execute(inliningTarget, clause)", limit = "1") public static boolean matchTuple(Object e, Object clause, @Bind Node inliningTarget, - @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheck, + @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheckNode, @Exclusive @Cached ExceptMatchNode recursiveNode, @Exclusive @Cached TupleNodes.GetTupleStorage getTupleStorage, @Exclusive @Cached SequenceStorageNodes.GetItemScalarNode getItemNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/function/builtins/clinic/TupleConversionNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/function/builtins/clinic/TupleConversionNode.java index 7c74719936..63fab02265 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/function/builtins/clinic/TupleConversionNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/function/builtins/clinic/TupleConversionNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -45,17 +45,23 @@ import com.oracle.graal.python.annotations.ClinicConverterFactory; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.tuple.PTuple; +import com.oracle.graal.python.lib.PyTupleCheckNode; +import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.Node; +@ImportStatic({PGuards.class, PyTupleCheckNode.class}) public abstract class TupleConversionNode extends ArgumentCastNode { @Specialization static Object[] doNone(@SuppressWarnings("unused") PNone none) { @@ -69,6 +75,14 @@ static Object[] doTuple(PTuple t, return getInternalArrayNode.execute(inliningTarget, t.getSequenceStorage()); } + @Specialization(guards = "checkNative(t)") + static Object[] doNativeTuple(PythonAbstractNativeObject t, + @Bind Node inliningTarget, + @Cached TupleNodes.GetTupleStorage getTupleStorage, + @Cached SequenceStorageNodes.ToArrayNode toArrayNode) { + return toArrayNode.execute(inliningTarget, getTupleStorage.execute(inliningTarget, t)); + } + @Fallback static Object doOthers(Object value, @Bind Node inliningTarget) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/formatting/BytesFormatProcessor.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/formatting/BytesFormatProcessor.java index ac8d0b4579..4f61904be5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/formatting/BytesFormatProcessor.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/formatting/BytesFormatProcessor.java @@ -60,9 +60,9 @@ import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.ToByteArrayNode; import com.oracle.graal.python.builtins.objects.ints.PInt; import com.oracle.graal.python.builtins.objects.str.PString; -import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.lib.PyMappingCheckNode; import com.oracle.graal.python.lib.PyObjectAsciiAsTruffleStringNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.runtime.exception.PException; @@ -122,7 +122,8 @@ Object parseMappingKey(int start, int end) { @Override protected boolean isMapping(Object obj) { // bytesobject.c _PyBytes_FormatEx() - return !(obj instanceof PTuple || obj instanceof PBytesLike || obj instanceof PString || obj instanceof TruffleString || isJavaString(obj)) && PyMappingCheckNode.executeUncached(obj); + return !(PyTupleCheckNode.executeUncached(obj) || obj instanceof PBytesLike || obj instanceof PString || obj instanceof TruffleString || isJavaString(obj)) && + PyMappingCheckNode.executeUncached(obj); } @Override diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/formatting/FormatProcessor.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/formatting/FormatProcessor.java index e03f41ba72..921e286a94 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/formatting/FormatProcessor.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/formatting/FormatProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. * Copyright (c) -2016 Jython Developers * * Licensed under PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 @@ -25,6 +25,7 @@ import com.oracle.graal.python.lib.PyNumberIndexNode; import com.oracle.graal.python.lib.PyNumberLongNode; import com.oracle.graal.python.lib.PyObjectGetItem; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.lib.PyTupleGetItem; import com.oracle.graal.python.lib.PyTupleSizeNode; import com.oracle.graal.python.nodes.ErrorMessages; @@ -249,10 +250,7 @@ private T formatImpl(Object args1) { Object mapping = null; this.args = args1; - // We need to do a full subtype-check because native objects may inherit from tuple but have - // Java type 'PythonNativeObject' (e.g. 'namedtuple' alias 'structseq'). - final Object args1LazyClass = GetClassNode.executeUncached(args1); - boolean tupleArgs = PGuards.isPTuple(args1) || isSubtype(args1LazyClass, PythonBuiltinClassType.PTuple); + boolean tupleArgs = PyTupleCheckNode.executeUncached(args1); if (tupleArgs) { // We will simply work through the tuple elements argIndex = 0; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/formatting/StringFormatProcessor.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/formatting/StringFormatProcessor.java index 9ec500f1a5..fbb71888dd 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/formatting/StringFormatProcessor.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/formatting/StringFormatProcessor.java @@ -14,11 +14,11 @@ import com.oracle.graal.python.builtins.Python3Core; import com.oracle.graal.python.builtins.objects.str.PString; -import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.lib.PyMappingCheckNode; import com.oracle.graal.python.lib.PyObjectAsciiAsTruffleStringNode; import com.oracle.graal.python.lib.PyObjectReprAsTruffleStringNode; import com.oracle.graal.python.lib.PyObjectStrAsTruffleStringNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.runtime.formatting.InternalFormat.Spec; @@ -66,7 +66,7 @@ Object parseMappingKey(int start, int end) { @Override protected boolean isMapping(Object obj) { // unicodeobject.c PyUnicode_Format() - return !(obj instanceof PTuple || obj instanceof PString || obj instanceof TruffleString || isJavaString(obj)) && PyMappingCheckNode.executeUncached(obj); + return !(PyTupleCheckNode.executeUncached(obj) || obj instanceof PString || obj instanceof TruffleString || isJavaString(obj)) && PyMappingCheckNode.executeUncached(obj); } private static boolean isOneCharacter(String str) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java index 62ce9d3a9e..7fd6d55a66 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java @@ -231,6 +231,7 @@ import com.oracle.graal.python.builtins.objects.typing.PTypeAliasType; import com.oracle.graal.python.builtins.objects.typing.PTypeVar; import com.oracle.graal.python.builtins.objects.typing.PTypeVarTuple; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.PRootNode; import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLCodeUnit; import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; @@ -1485,22 +1486,19 @@ public static PContextVarsToken createContextVarsToken(PythonLanguage language, return new PContextVarsToken(var, oldValue, PythonBuiltinClassType.ContextVarsToken, PythonBuiltinClassType.ContextVarsToken.getInstanceShape(language)); } - public static PGenericAlias createGenericAlias(PythonLanguage language, Object cls, Shape shape, Object origin, Object arguments, boolean starred) { - PTuple argumentsTuple; - if (arguments instanceof PTuple) { - argumentsTuple = (PTuple) arguments; - } else { - argumentsTuple = createTuple(language, new Object[]{arguments}); - } - return new PGenericAlias(cls, shape, origin, argumentsTuple, starred); + public static PGenericAlias createGenericAlias(PythonLanguage language, Object cls, Shape shape, Object origin, PTuple arguments, boolean starred) { + return new PGenericAlias(cls, shape, origin, arguments, starred); } - public static PGenericAlias createGenericAlias(PythonLanguage language, Object origin, Object arguments, boolean starred) { - return createGenericAlias(language, PythonBuiltinClassType.PGenericAlias, PythonBuiltinClassType.PGenericAlias.getInstanceShape(language), origin, arguments, starred); + public static PGenericAlias createGenericAlias(PythonLanguage language, Object origin, PTuple argumentsTuple, boolean starred) { + return createGenericAlias(language, PythonBuiltinClassType.PGenericAlias, PythonBuiltinClassType.PGenericAlias.getInstanceShape(language), origin, argumentsTuple, starred); } public static PGenericAlias createGenericAlias(PythonLanguage language, Object origin, Object arguments) { - return createGenericAlias(language, origin, arguments, false); + // native tuples need to be converted by the caller + assert arguments instanceof PTuple || !PyTupleCheckNode.executeUncached(arguments); + PTuple argumentsTuple = arguments instanceof PTuple ? (PTuple) arguments : createTuple(language, new Object[]{arguments}); + return createGenericAlias(language, origin, argumentsTuple, false); } public static PGenericAliasIterator createGenericAliasIterator(PythonLanguage language, PGenericAlias object) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeObjectSequenceStorage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeObjectSequenceStorage.java index c66dbad689..d095f21cc4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeObjectSequenceStorage.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeObjectSequenceStorage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -60,6 +60,12 @@ public static NativeObjectSequenceStorage create(long ptr, int length, int capac return storage; } + public static NativeObjectSequenceStorage create(long ptr, int length, int capacity, Object owner) { + NativeObjectSequenceStorage storage = new NativeObjectSequenceStorage(ptr, length, capacity); + storage.setBorrowedMemoryOwner(owner); + return storage; + } + @Override public StorageType getElementType() { return StorageType.Generic; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeSequenceStorage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeSequenceStorage.java index 76399fb323..4728a56fe3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeSequenceStorage.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeSequenceStorage.java @@ -42,6 +42,7 @@ import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; +import java.util.Arrays; import java.util.logging.Level; import com.oracle.graal.python.PythonLanguage; @@ -53,20 +54,57 @@ public abstract class NativeSequenceStorage extends SequenceStorage { private static final TruffleLogger LOGGER = PythonLanguage.getLogger(NativeSequenceStorage.class); + private static final Object BORROWED_OWNER_MARKER = new Object(); /* native pointer object */ private long ptr; private NativeStorageReference reference; /** - * Replicates the native references of this native sequence storage in Java. + * State for the two independent lifetime obligations of a native sequence storage: *

- * Native sequence storages have references (if not empty) to other objects. Whenever the Python - * GC detects a possible reference cycle, we will replicate those native references in Java to - * give control to the Java GC when objects may die. + * Contract: + *

    + *
  • If {@link #reference} is set, the storage owns {@link #ptr}. The native memory is + * released by the {@link NativeStorageReference} when this storage dies, so the storage must not + * also keep a borrowed owner alive.
  • + *
  • If the storage only borrows {@link #ptr}, it may keep a managed owner alive here. The + * owner is the object whose native memory contains the storage pointer. The owner is never an + * {@code Object[]} so that an unmarked array can keep its old meaning of replicated native + * references.
  • + *
  • Independently of memory ownership, native sequence storages can reference other Python + * objects. When the Python GC detects a possible native reference cycle, those referents are + * replicated here as strong Java references so the Java GC controls when they may die.
  • + *
+ *

+ * Possible encoded states: + *

    + *
  • {@code null}: no borrowed owner and no replicated native references. This is the initial + * state for ordinary storages.
  • + *
  • {@code Object[] refs}: replicated native references only. This is also the only encoded + * state left when an owning {@link NativeStorageReference} is installed.
  • + *
  • {@code owner}: borrowed native memory only. {@code owner} keeps the underlying native + * object alive for as long as this storage can access {@link #ptr}.
  • + *
  • {@code Object[] {BORROWED_OWNER_MARKER, owner, refs...}}: borrowed native memory plus + * replicated native references.
  • + *
+ *

+ * Transitions: + *

    + *
  • {@link #setBorrowedMemoryOwner(Object)} transitions from no owner to borrowed ownership, + * preserving any already replicated references by creating the marker array state.
  • + *
  • {@link #setReplicatedNativeReferences(Object[])} replaces only the replicated-reference + * part. If a borrowed owner is present, it is preserved and the state is re-encoded around the + * new references.
  • + *
  • {@link #setReference(NativeStorageReference)} transitions the storage to owned memory and + * clears any borrowed owner. Replicated references, if present, are preserved because they + * describe object graph reachability rather than memory ownership.
  • + *
  • {@link #clearBorrowedMemoryOwner()} removes only the borrowed-owner part and is therefore + * the inverse of the owner-preserving marker-array encoding.
  • + *
*

*/ - private Object[] replicatedNativeReferences; + private Object nativeReferenceState; NativeSequenceStorage(long ptr, int length, int capacity) { super(length, capacity); @@ -95,6 +133,7 @@ public void setCapacity(int capacity) { public final void setReference(NativeStorageReference reference) { assert this.reference == null : "attempting to set another NativeStorageReference"; + clearBorrowedMemoryOwner(); this.reference = reference; } @@ -121,14 +160,94 @@ public String toString() { } /** - * For a description, see {@link #replicatedNativeReferences}. + * For a description, see {@link #nativeReferenceState}. */ public void setReplicatedNativeReferences(Object[] replicatedNativeReferences) { - this.replicatedNativeReferences = replicatedNativeReferences; + if (hasBorrowedMemoryOwner()) { + Object owner = getBorrowedMemoryOwner(); + if (replicatedNativeReferences == null) { + nativeReferenceState = owner; + } else { + nativeReferenceState = createBorrowedOwnerState(owner, replicatedNativeReferences); + } + } else { + nativeReferenceState = replicatedNativeReferences; + } } public Object[] getReplicatedNativeReferences() { - return replicatedNativeReferences; + if (nativeReferenceState instanceof Object[] refs) { + if (isBorrowedOwnerState(refs)) { + assert validateBorrowedOwnerState(refs); + return Arrays.copyOfRange(refs, 2, refs.length); + } + return refs; + } + return null; + } + + public void setBorrowedMemoryOwner(Object owner) { + assert isValidBorrowedMemoryOwner(owner); + assert !hasReference(); + assert !hasBorrowedMemoryOwner(); + Object[] replicatedNativeReferences = getReplicatedNativeReferences(); + if (replicatedNativeReferences == null) { + nativeReferenceState = owner; + } else { + nativeReferenceState = createBorrowedOwnerState(owner, replicatedNativeReferences); + } + } + + private void clearBorrowedMemoryOwner() { + if (nativeReferenceState instanceof Object[] refs) { + if (isBorrowedOwnerState(refs)) { + assert validateBorrowedOwnerState(refs); + nativeReferenceState = Arrays.copyOfRange(refs, 2, refs.length); + } + } else { + nativeReferenceState = null; + } + } + + private boolean hasBorrowedMemoryOwner() { + if (nativeReferenceState instanceof Object[] refs) { + return isBorrowedOwnerState(refs); + } + return nativeReferenceState != null; + } + + private Object getBorrowedMemoryOwner() { + if (nativeReferenceState instanceof Object[] refs) { + assert isBorrowedOwnerState(refs); + assert validateBorrowedOwnerState(refs); + return refs[1]; + } + assert isValidBorrowedMemoryOwner(nativeReferenceState); + return nativeReferenceState; + } + + private static Object[] createBorrowedOwnerState(Object owner, Object[] replicatedNativeReferences) { + Object[] ownerState = new Object[replicatedNativeReferences.length + 2]; + ownerState[0] = BORROWED_OWNER_MARKER; + ownerState[1] = owner; + System.arraycopy(replicatedNativeReferences, 0, ownerState, 2, replicatedNativeReferences.length); + assert validateBorrowedOwnerState(ownerState); + return ownerState; + } + + private static boolean isBorrowedOwnerState(Object[] state) { + return state.length > 0 && state[0] == BORROWED_OWNER_MARKER; + } + + private static boolean validateBorrowedOwnerState(Object[] state) { + assert isBorrowedOwnerState(state); + assert state.length >= 2; + assert isValidBorrowedMemoryOwner(state[1]); + return true; + } + + private static boolean isValidBorrowedMemoryOwner(Object owner) { + return owner != null && !(owner instanceof Object[]); } }