From ae2c4e0de4f3c594470d51e1c32650d8deadf972 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 23 Mar 2026 10:50:59 +0100 Subject: [PATCH 01/30] Add foreign temporal trait plumbing --- .../src/tests/test_interop.py | 29 ++++++++++++++++++- .../builtins/PythonBuiltinClassType.java | 4 +++ .../object/GetForeignObjectClassNode.java | 17 +++++++++-- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py index ab01a4c18d..5051bbdeab 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py @@ -114,6 +114,8 @@ def test_single_trait_classes(self): polyglot.ForeignObject, polyglot.ForeignList, polyglot.ForeignBoolean, + polyglot.ForeignDate, + polyglot.ForeignDateTime, polyglot.ForeignException, polyglot.ForeignExecutable, polyglot.ForeignDict, @@ -124,6 +126,8 @@ def test_single_trait_classes(self): polyglot.ForeignNone, polyglot.ForeignNumber, polyglot.ForeignString, + polyglot.ForeignTime, + polyglot.ForeignTimeZone, ] for c in classes: @@ -131,7 +135,16 @@ def test_single_trait_classes(self): if c is polyglot.ForeignBoolean: self.assertIs(c.__base__, polyglot.ForeignNumber) elif c is not polyglot.ForeignObject: - self.assertIs(c.__base__, polyglot.ForeignObject) + if c is polyglot.ForeignDate: + self.assertIs(c.__base__, __import__("datetime").date) + elif c is polyglot.ForeignTime: + self.assertIs(c.__base__, __import__("datetime").time) + elif c is polyglot.ForeignDateTime: + self.assertIs(c.__base__, __import__("datetime").datetime) + elif c is polyglot.ForeignTimeZone: + self.assertIs(c.__base__, __import__("datetime").tzinfo) + else: + self.assertIs(c.__base__, polyglot.ForeignObject) def test_get_class(self): def wrap(obj): @@ -155,6 +168,7 @@ def t(obj): self.assertEqual(t("abc"), polyglot.ForeignString) from java.lang import Object, Boolean, Integer, Throwable, Thread, Number, String + from java.time import LocalDate, LocalDateTime, LocalTime, ZoneId from java.util import ArrayList, HashMap, ArrayDeque from java.math import BigInteger null = Integer.getInteger("something_that_does_not_exists") @@ -172,6 +186,19 @@ def t(obj): self.assertEqual(type(null), polyglot.ForeignNone) self.assertEqual(type(BigInteger.valueOf(42)), polyglot.ForeignNumber) self.assertEqual(type(wrap(String("abc"))), polyglot.ForeignString) + local_date = LocalDate.of(2025, 3, 23) + self.assertIsInstance(local_date, polyglot.ForeignDate) + self.assertIsInstance(local_date, __import__("datetime").date) + + local_time = LocalTime.of(7, 8, 9) + self.assertIsInstance(local_time, polyglot.ForeignTime) + self.assertIsInstance(local_time, __import__("datetime").time) + + local_date_time = LocalDateTime.of(2025, 3, 23, 7, 8, 9) + self.assertIsInstance(local_date_time, polyglot.ForeignDateTime) + self.assertIsInstance(local_date_time, __import__("datetime").datetime) + + self.assertEqual(type(ZoneId.of("UTC")), polyglot.ForeignTimeZone) def test_import(self): def some_function(): diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java index f5d701d729..3939c78223 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java @@ -842,6 +842,10 @@ It can be called either on the class (e.g. C.f()) or on an instance ForeignExecutable("ForeignExecutable", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignExecutableBuiltins.SLOTS)), ForeignInstantiable("ForeignInstantiable", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().slots(ForeignInstantiableBuiltins.SLOTS)), ForeignIterable("ForeignIterable", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignIterableBuiltins.SLOTS)), + ForeignDate("ForeignDate", PDate, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), + ForeignTime("ForeignTime", PTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), + ForeignDateTime("ForeignDateTime", PDateTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), + ForeignTimeZone("ForeignTimeZone", PTzInfo, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), // bz2 BZ2Compressor("BZ2Compressor", PythonObject, newBuilder().publishInModule("_bz2").basetype().slots(BZ2CompressorBuiltins.SLOTS)), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetForeignObjectClassNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetForeignObjectClassNode.java index 36fcaa99da..0d63dfa5b9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetForeignObjectClassNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetForeignObjectClassNode.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 @@ -95,8 +95,12 @@ public enum Trait { // Interop types first as they are the most concrete/specific types NULL("None", PythonBuiltinClassType.PNone), BOOLEAN("Boolean", PythonBuiltinClassType.ForeignBoolean), + DATE("Date", PythonBuiltinClassType.ForeignDate), + DATETIME("DateTime", PythonBuiltinClassType.ForeignDateTime), NUMBER("Number", PythonBuiltinClassType.ForeignNumber), // int, float, complex STRING("String", PythonBuiltinClassType.PString), + TIME("Time", PythonBuiltinClassType.ForeignTime), + TIME_ZONE("TimeZone", PythonBuiltinClassType.ForeignTimeZone), EXCEPTION("Exception", PythonBuiltinClassType.PBaseException), META_OBJECT("AbstractClass", PythonBuiltinClassType.ForeignAbstractClass), @@ -154,9 +158,16 @@ PythonManagedClass uncached(Object object, } protected static int getTraits(Object object, InteropLibrary interop) { + // Temporal types are a bit special since some traits should exclude each other to match + // the split in Python's datetime module + boolean isDate = interop.isDate(object); + boolean isTime = interop.isTime(object); + boolean isTimeZone = interop.isTimeZone(object); // Alphabetic order here as it does not matter return (interop.hasArrayElements(object) ? Trait.ARRAY.bit : 0) + (interop.isBoolean(object) ? Trait.BOOLEAN.bit : 0) + + (isDate && isTime ? Trait.DATETIME.bit : 0) + + (isDate && !isTime ? Trait.DATE.bit : 0) + (interop.isException(object) ? Trait.EXCEPTION.bit : 0) + (interop.isExecutable(object) ? Trait.EXECUTABLE.bit : 0) + (interop.hasHashEntries(object) ? Trait.HASH.bit : 0) + @@ -166,7 +177,9 @@ protected static int getTraits(Object object, InteropLibrary interop) { (interop.isMetaObject(object) ? Trait.META_OBJECT.bit : 0) + (interop.isNull(object) ? Trait.NULL.bit : 0) + (interop.isNumber(object) ? Trait.NUMBER.bit : 0) + - (interop.isString(object) ? Trait.STRING.bit : 0); + (interop.isString(object) ? Trait.STRING.bit : 0) + + (!isDate && isTime ? Trait.TIME.bit : 0) + + (!isDate && !isTime && isTimeZone ? Trait.TIME_ZONE.bit : 0); } private PythonManagedClass classForTraits(PythonContext context, int traits) { From d1c4bc000b87951e4a24a0ea82393b57443facc5 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 23 Mar 2026 10:55:34 +0100 Subject: [PATCH 02/30] Add shared temporal projection helpers --- .../modules/datetime/DateTimeBuiltins.java | 2 +- .../modules/datetime/DateTimeNodes.java | 2 +- .../modules/datetime/TemporalNodes.java | 276 ++++++++++++++++++ .../builtins/modules/datetime/TimeNodes.java | 2 +- 4 files changed, 279 insertions(+), 3 deletions(-) create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java index 2e9ba79e03..fd60b3bab3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java @@ -3267,7 +3267,7 @@ private static LocalDateTime subtractOffsetFromDateTime(PDateTime self, PTimeDel @TruffleBoundary private static LocalDateTime toLocalDateTime(PDateTime dateTime) { - return LocalDateTime.of(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second, dateTime.microsecond * 1_000); + return TemporalNodes.DateTimeValue.of(dateTime).toLocalDateTime(); } private static Object toPDateTime(LocalDateTime local, Object tzInfo, int fold, Node inliningTarget, Object cls) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java index f6b3eddafb..56f41a70a1 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java @@ -369,7 +369,7 @@ static int getMicrosecond(PythonAbstractNativeObject self, CStructAccess.ReadByt return (b3 << 16) | (b4 << 8) | b5; } - private static Object getTzInfo(PythonAbstractNativeObject obj, CStructAccess.ReadByteNode readByteNode, CStructAccess.ReadObjectNode readObjectNode) { + static Object getTzInfo(PythonAbstractNativeObject obj, CStructAccess.ReadByteNode readByteNode, CStructAccess.ReadObjectNode readObjectNode) { Object tzInfo = null; if (readByteNode.readFromObj(obj, CFields.PyDateTime_DateTime__hastzinfo) != 0) { Object tzinfoObj = readObjectNode.readFromObj(obj, CFields.PyDateTime_DateTime__tzinfo); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java new file mode 100644 index 0000000000..3afb108c49 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java @@ -0,0 +1,276 @@ +/* + * Copyright (c) 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 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * 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. + */ +package com.oracle.graal.python.builtins.modules.datetime; + +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; + +import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; +import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; +import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.ValueType; +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.GenerateCached; +import com.oracle.truffle.api.dsl.GenerateInline; +import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; + +public final class TemporalNodes { + private TemporalNodes() { + } + + @ValueType + public static final class DateValue { + public final int year; + public final int month; + public final int day; + + public DateValue(int year, int month, int day) { + this.year = year; + this.month = month; + this.day = day; + } + + public static DateValue of(PDate date) { + return new DateValue(date.year, date.month, date.day); + } + + public LocalDate toLocalDate() { + return LocalDate.of(year, month, day); + } + } + + @ValueType + public static class TimeValue { + public final int hour; + public final int minute; + public final int second; + public final int microsecond; + public final Object tzInfo; + public final ZoneId zoneId; + public final int fold; + + public TimeValue(int hour, int minute, int second, int microsecond, Object tzInfo, ZoneId zoneId, int fold) { + this.hour = hour; + this.minute = minute; + this.second = second; + this.microsecond = microsecond; + this.tzInfo = tzInfo; + this.zoneId = zoneId; + this.fold = fold; + } + + public static TimeValue of(PTime time) { + return new TimeValue(time.hour, time.minute, time.second, time.microsecond, time.tzInfo, null, time.fold); + } + + public LocalTime toLocalTime() { + return LocalTime.of(hour, minute, second, microsecond * 1_000); + } + + public boolean hasTimeZone() { + return tzInfo != null || zoneId != null; + } + } + + @ValueType + public static final class DateTimeValue extends TimeValue { + public final int year; + public final int month; + public final int day; + + public DateTimeValue(int year, int month, int day, int hour, int minute, int second, int microsecond, Object tzInfo, ZoneId zoneId, int fold) { + super(hour, minute, second, microsecond, tzInfo, zoneId, fold); + this.year = year; + this.month = month; + this.day = day; + } + + public static DateTimeValue of(PDateTime dateTime) { + return new DateTimeValue(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second, dateTime.microsecond, dateTime.tzInfo, null, + dateTime.fold); + } + + public DateValue getDateValue() { + return new DateValue(year, month, day); + } + + public LocalDateTime toLocalDateTime() { + return LocalDateTime.of(year, month, day, hour, minute, second, microsecond * 1_000); + } + } + + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class ReadDateValueNode extends Node { + public abstract DateValue execute(Node inliningTarget, Object obj); + + @Specialization + static DateValue doManaged(PDate value) { + return DateValue.of(value); + } + + @Specialization(guards = "checkNode.execute(inliningTarget, value)", limit = "1") + static DateValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject value, + @SuppressWarnings("unused") @Cached DateNodes.DateCheckNode checkNode, + @Cached CStructAccess.ReadByteNode readNode) { + return new DateValue(DateNodes.AsManagedDateNode.getYear(value, readNode), DateNodes.AsManagedDateNode.getMonth(value, readNode), DateNodes.AsManagedDateNode.getDay(value, readNode)); + } + + @Specialization(guards = {"isForeignObjectNode.execute(inliningTarget, value)", "interop.isDate(value)"}, limit = "1") + static DateValue doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + try { + LocalDate date = interop.asDate(value); + return new DateValue(date.getYear(), date.getMonthValue(), date.getDayOfMonth()); + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @Fallback + static DateValue error(Object obj, + @Bind Node inliningTarget) { + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_EXPECTED_GOT_P, "date", obj); + } + } + + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class ReadTimeValueNode extends Node { + public abstract TimeValue execute(Node inliningTarget, Object obj); + + @Specialization + static TimeValue doManaged(PTime value) { + return TimeValue.of(value); + } + + @Specialization(guards = "checkNode.execute(inliningTarget, value)", limit = "1") + static TimeValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject value, + @SuppressWarnings("unused") @Cached TimeNodes.TimeCheckNode checkNode, + @Cached CStructAccess.ReadByteNode readByteNode, + @Cached CStructAccess.ReadObjectNode readObjectNode) { + return new TimeValue(TimeNodes.AsManagedTimeNode.getHour(value, readByteNode), TimeNodes.AsManagedTimeNode.getMinute(value, readByteNode), + TimeNodes.AsManagedTimeNode.getSecond(value, readByteNode), TimeNodes.AsManagedTimeNode.getMicrosecond(value, readByteNode), + TimeNodes.AsManagedTimeNode.getTzInfo(value, readByteNode, readObjectNode), null, TimeNodes.AsManagedTimeNode.getFold(value, readByteNode)); + } + + @Specialization(guards = {"isForeignObjectNode.execute(inliningTarget, value)", "interop.isTime(value)"}, limit = "1") + static TimeValue doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + try { + LocalTime time = interop.asTime(value); + ZoneId zoneId = interop.isTimeZone(value) ? interop.asTimeZone(value) : null; + return new TimeValue(time.getHour(), time.getMinute(), time.getSecond(), time.getNano() / 1_000, null, zoneId, 0); + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @Fallback + static TimeValue error(Object obj, + @Bind Node inliningTarget) { + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_EXPECTED_GOT_P, "time", obj); + } + } + + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class ReadDateTimeValueNode extends Node { + public abstract DateTimeValue execute(Node inliningTarget, Object obj); + + @Specialization + static DateTimeValue doManaged(PDateTime value) { + return DateTimeValue.of(value); + } + + @Specialization(guards = "checkNode.execute(inliningTarget, value)", limit = "1") + static DateTimeValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject value, + @SuppressWarnings("unused") @Cached DateTimeNodes.DateTimeCheckNode checkNode, + @Cached CStructAccess.ReadByteNode readByteNode, + @Cached CStructAccess.ReadObjectNode readObjectNode) { + return new DateTimeValue(DateTimeNodes.AsManagedDateTimeNode.getYear(value, readByteNode), DateTimeNodes.AsManagedDateTimeNode.getMonth(value, readByteNode), + DateTimeNodes.AsManagedDateTimeNode.getDay(value, readByteNode), DateTimeNodes.AsManagedDateTimeNode.getHour(value, readByteNode), + DateTimeNodes.AsManagedDateTimeNode.getMinute(value, readByteNode), DateTimeNodes.AsManagedDateTimeNode.getSecond(value, readByteNode), + DateTimeNodes.AsManagedDateTimeNode.getMicrosecond(value, readByteNode), DateTimeNodes.AsManagedDateTimeNode.getTzInfo(value, readByteNode, readObjectNode), null, + DateTimeNodes.AsManagedDateTimeNode.getFold(value, readByteNode)); + } + + @Specialization(guards = {"isForeignObjectNode.execute(inliningTarget, value)", "interop.isDate(value)", "interop.isTime(value)"}, limit = "1") + static DateTimeValue doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + try { + LocalDate date = interop.asDate(value); + LocalTime time = interop.asTime(value); + ZoneId zoneId = interop.isTimeZone(value) ? interop.asTimeZone(value) : null; + return new DateTimeValue(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), time.getHour(), time.getMinute(), time.getSecond(), time.getNano() / 1_000, null, zoneId, + 0); + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @Fallback + static DateTimeValue error(Object obj, + @Bind Node inliningTarget) { + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_EXPECTED_GOT_P, "datetime", obj); + } + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java index e68f2781e5..4df292fbba 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java @@ -282,7 +282,7 @@ static int getMicrosecond(PythonAbstractNativeObject self, CStructAccess.ReadByt return (b3 << 16) | (b4 << 8) | b5; } - private static Object getTzInfo(PythonAbstractNativeObject nativeTime, CStructAccess.ReadByteNode readByteNode, CStructAccess.ReadObjectNode readObjectNode) { + static Object getTzInfo(PythonAbstractNativeObject nativeTime, CStructAccess.ReadByteNode readByteNode, CStructAccess.ReadObjectNode readObjectNode) { Object tzinfo = null; if (readByteNode.readFromObj(nativeTime, CFields.PyDateTime_Time__hastzinfo) != 0) { Object tzinfoObj = readObjectNode.readFromObj(nativeTime, CFields.PyDateTime_Time__tzinfo); From f3bc2ad3b51e6c449cec7450cb9b7fa182438f9d Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 23 Mar 2026 11:06:41 +0100 Subject: [PATCH 03/30] Implement ForeignDate methods --- .../src/tests/test_interop.py | 29 ++ .../graal/python/builtins/Python3Core.java | 2 + .../builtins/PythonBuiltinClassType.java | 3 +- .../modules/datetime/TemporalNodes.java | 90 ++++ .../objects/foreign/ForeignDateBuiltins.java | 407 ++++++++++++++++++ 5 files changed, 530 insertions(+), 1 deletion(-) create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py index 5051bbdeab..750230b52a 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py @@ -213,6 +213,35 @@ def some_function(): assert imported_fun1 is some_function assert imported_fun1() == "hello, polyglot world!" + def test_foreign_date_behavior(self): + import datetime + import java + + LocalDate = java.type("java.time.LocalDate") + + d = LocalDate.of(2025, 3, 23) + self.assertEqual(d.year, 2025) + self.assertEqual(d.month, 3) + self.assertEqual(d.day, 23) + self.assertEqual(str(d), "2025-03-23") + self.assertEqual(d.isoformat(), "2025-03-23") + self.assertEqual(d.ctime(), datetime.date(2025, 3, 23).ctime()) + self.assertEqual(d.strftime("%Y-%m-%d"), "2025-03-23") + self.assertEqual(format(d, "%Y-%m-%d"), "2025-03-23") + self.assertEqual(d.toordinal(), datetime.date(2025, 3, 23).toordinal()) + self.assertEqual(d.weekday(), datetime.date(2025, 3, 23).weekday()) + self.assertEqual(d.isoweekday(), datetime.date(2025, 3, 23).isoweekday()) + self.assertEqual(d.isocalendar(), datetime.date(2025, 3, 23).isocalendar()) + self.assertEqual(d.timetuple(), datetime.date(2025, 3, 23).timetuple()) + self.assertEqual(hash(d), hash(datetime.date(2025, 3, 23))) + self.assertEqual(d, datetime.date(2025, 3, 23)) + self.assertEqual(d, LocalDate.of(2025, 3, 23)) + self.assertEqual(d.replace(day=24), datetime.date(2025, 3, 24)) + self.assertEqual(d + datetime.timedelta(days=1), datetime.date(2025, 3, 24)) + self.assertEqual(d - datetime.timedelta(days=1), datetime.date(2025, 3, 22)) + self.assertEqual(d - datetime.date(2025, 3, 20), datetime.timedelta(days=3)) + self.assertEqual(d - LocalDate.of(2025, 3, 20), datetime.timedelta(days=3)) + def test_read(self): o = CustomObject() assert polyglot.__read__(o, "field") == o.field diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java index 3180fe0133..1766cbce27 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java @@ -263,6 +263,7 @@ import com.oracle.graal.python.builtins.objects.floats.PFloat; import com.oracle.graal.python.builtins.objects.foreign.ForeignAbstractClassBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignBooleanBuiltins; +import com.oracle.graal.python.builtins.objects.foreign.ForeignDateBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignExecutableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignInstantiableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignIterableBuiltins; @@ -500,6 +501,7 @@ private static PythonBuiltins[] initializeBuiltins(TruffleLanguage.Env env) { new ForeignObjectBuiltins(), new ForeignNumberBuiltins(), new ForeignBooleanBuiltins(), + new ForeignDateBuiltins(), new ForeignAbstractClassBuiltins(), new ForeignExecutableBuiltins(), new ForeignInstantiableBuiltins(), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java index 3939c78223..615f734bd1 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java @@ -181,6 +181,7 @@ import com.oracle.graal.python.builtins.objects.floats.FloatBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignAbstractClassBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignBooleanBuiltins; +import com.oracle.graal.python.builtins.objects.foreign.ForeignDateBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignExecutableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignInstantiableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignIterableBuiltins; @@ -842,7 +843,7 @@ It can be called either on the class (e.g. C.f()) or on an instance ForeignExecutable("ForeignExecutable", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignExecutableBuiltins.SLOTS)), ForeignInstantiable("ForeignInstantiable", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().slots(ForeignInstantiableBuiltins.SLOTS)), ForeignIterable("ForeignIterable", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignIterableBuiltins.SLOTS)), - ForeignDate("ForeignDate", PDate, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), + ForeignDate("ForeignDate", PDate, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignDateBuiltins.SLOTS)), ForeignTime("ForeignTime", PTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), ForeignDateTime("ForeignDateTime", PDateTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), ForeignTimeZone("ForeignTimeZone", PTzInfo, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java index 3afb108c49..bf69e9ab40 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java @@ -151,6 +151,96 @@ public LocalDateTime toLocalDateTime() { } } + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class DateLikeCheckNode extends Node { + public abstract boolean execute(Node inliningTarget, Object obj); + + @Specialization + static boolean doManaged(@SuppressWarnings("unused") PDate value) { + return true; + } + + @Specialization + static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, + @Cached DateNodes.DateCheckNode checkNode) { + return checkNode.execute(inliningTarget, value); + } + + @Specialization(guards = "isForeignObjectNode.execute(inliningTarget, value)", limit = "1") + static boolean doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + return interop.isDate(value); + } + + @Fallback + static boolean doOther(@SuppressWarnings("unused") Object value) { + return false; + } + } + + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class TimeLikeCheckNode extends Node { + public abstract boolean execute(Node inliningTarget, Object obj); + + @Specialization + static boolean doManaged(@SuppressWarnings("unused") PTime value) { + return true; + } + + @Specialization + static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, + @Cached TimeNodes.TimeCheckNode checkNode) { + return checkNode.execute(inliningTarget, value); + } + + @Specialization(guards = "isForeignObjectNode.execute(inliningTarget, value)", limit = "1") + static boolean doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + return interop.isTime(value); + } + + @Fallback + static boolean doOther(@SuppressWarnings("unused") Object value) { + return false; + } + } + + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class DateTimeLikeCheckNode extends Node { + public abstract boolean execute(Node inliningTarget, Object obj); + + @Specialization + static boolean doManaged(@SuppressWarnings("unused") PDateTime value) { + return true; + } + + @Specialization + static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, + @Cached DateTimeNodes.DateTimeCheckNode checkNode) { + return checkNode.execute(inliningTarget, value); + } + + @Specialization(guards = "isForeignObjectNode.execute(inliningTarget, value)", limit = "1") + static boolean doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + return interop.isDate(value) && interop.isTime(value); + } + + @Fallback + static boolean doOther(@SuppressWarnings("unused") Object value) { + return false; + } + } + @GenerateUncached @GenerateInline @GenerateCached(false) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java new file mode 100644 index 0000000000..7e9c59ad88 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java @@ -0,0 +1,407 @@ +/* + * Copyright (c) 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 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * 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. + */ +package com.oracle.graal.python.builtins.objects.foreign; + +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.OverflowError; +import static com.oracle.graal.python.nodes.SpecialMethodNames.J___FORMAT__; +import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; +import static com.oracle.graal.python.util.PythonUtils.tsLiteral; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.List; + +import com.oracle.graal.python.PythonLanguage; +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.builtins.CoreFunctions; +import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.PythonBuiltins; +import com.oracle.graal.python.builtins.modules.TimeModuleBuiltins; +import com.oracle.graal.python.builtins.modules.datetime.DateNodes; +import com.oracle.graal.python.builtins.modules.datetime.PDate; +import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; +import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes; +import com.oracle.graal.python.builtins.modules.datetime.TimeDeltaNodes; +import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.PNotImplemented; +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.TpSlotBinaryOp.BinaryOpBuiltinNode; +import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun.HashBuiltinNode; +import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; +import com.oracle.graal.python.lib.PyLongAsLongNode; +import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; +import com.oracle.graal.python.lib.PyObjectHashNode; +import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; +import com.oracle.graal.python.lib.RichCmpOp; +import com.oracle.graal.python.nodes.ErrorMessages; +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; +import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; +import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; +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; +import com.oracle.truffle.api.dsl.NodeFactory; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.strings.TruffleString; + +@CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignDate) +public final class ForeignDateBuiltins extends PythonBuiltins { + public static final TpSlots SLOTS = ForeignDateBuiltinsSlotsGen.SLOTS; + + private static final int MAX_ORDINAL = 3_652_059; + private static final TruffleString T_ISOCALENDAR = tsLiteral("isocalendar"); + private static final TruffleString T_STRFTIME = tsLiteral("strftime"); + + @Override + protected List> getNodeFactories() { + return ForeignDateBuiltinsFactory.getFactories(); + } + + @Slot(value = SlotKind.tp_repr, isComplex = true) + @GenerateNodeFactory + abstract static class ReprNode extends PythonUnaryBuiltinNode { + @Specialization + @TruffleBoundary + static TruffleString repr(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + TemporalNodes.DateValue self = readDateValueNode.execute(inliningTarget, selfObj); + TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); + String string = String.format("%s(%d, %d, %d)", typeName, self.year, self.month, self.day); + return TruffleString.FromJavaStringNode.getUncached().execute(string, TS_ENCODING); + } + } + + @Slot(value = SlotKind.tp_str, isComplex = true) + @GenerateNodeFactory + abstract static class StrNode extends PythonUnaryBuiltinNode { + @Specialization + static TruffleString str(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + return toIsoFormat(readDateValueNode.execute(inliningTarget, selfObj)); + } + } + + @Slot(value = SlotKind.tp_richcompare, isComplex = true) + @GenerateNodeFactory + abstract static class RichCmpNode extends RichCmpBuiltinNode { + @Specialization + static Object richCmp(Object selfObj, Object otherObj, RichCmpOp op, + @Bind Node inliningTarget, + @Cached TemporalNodes.DateLikeCheckNode dateLikeCheckNode, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + if (!dateLikeCheckNode.execute(inliningTarget, otherObj)) { + return PNotImplemented.NOT_IMPLEMENTED; + } + LocalDate self = readDateValueNode.execute(inliningTarget, selfObj).toLocalDate(); + LocalDate other = readDateValueNode.execute(inliningTarget, otherObj).toLocalDate(); + return op.compareResultToBool(self.compareTo(other)); + } + } + + @Slot(value = SlotKind.tp_hash, isComplex = true) + @GenerateNodeFactory + abstract static class HashNode extends HashBuiltinNode { + @Specialization + static long hash(VirtualFrame frame, Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode, + @Cached PyObjectHashNode hashNode) { + return hashNode.execute(frame, inliningTarget, toPythonDate(readDateValueNode.execute(inliningTarget, selfObj))); + } + } + + @Slot(value = SlotKind.nb_add, isComplex = true) + @GenerateNodeFactory + abstract static class AddNode extends BinaryOpBuiltinNode { + @Specialization + @TruffleBoundary + static Object add(Object left, Object right, + @Bind Node inliningTarget, + @Cached TemporalNodes.DateLikeCheckNode dateLikeCheckNode, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + Object dateObj; + Object deltaObj; + if (dateLikeCheckNode.execute(inliningTarget, left) && TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + dateObj = left; + deltaObj = right; + } else if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(left) && dateLikeCheckNode.execute(inliningTarget, right)) { + dateObj = right; + deltaObj = left; + } else { + return PNotImplemented.NOT_IMPLEMENTED; + } + + LocalDate date = readDateValueNode.execute(inliningTarget, dateObj).toLocalDate(); + PTimeDelta delta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(deltaObj); + long days = ChronoUnit.DAYS.between(LocalDate.of(1, 1, 1), date) + 1 + delta.days; + if (days <= 0 || days > MAX_ORDINAL) { + throw com.oracle.graal.python.nodes.PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); + } + return toPythonDate(ChronoUnit.DAYS.addTo(LocalDate.of(1, 1, 1), days - 1)); + } + } + + @Slot(value = SlotKind.nb_subtract, isComplex = true) + @GenerateNodeFactory + abstract static class SubNode extends BinaryOpBuiltinNode { + @Specialization + @TruffleBoundary + static Object sub(Object left, Object right, + @Bind Node inliningTarget, + @Cached TemporalNodes.DateLikeCheckNode dateLikeCheckNode, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + if (!dateLikeCheckNode.execute(inliningTarget, left)) { + return PNotImplemented.NOT_IMPLEMENTED; + } + + LocalDate leftDate = readDateValueNode.execute(inliningTarget, left).toLocalDate(); + LocalDate from = LocalDate.of(1, 1, 1); + long leftDays = ChronoUnit.DAYS.between(from, leftDate) + 1; + if (dateLikeCheckNode.execute(inliningTarget, right)) { + LocalDate rightDate = readDateValueNode.execute(inliningTarget, right).toLocalDate(); + long rightDays = ChronoUnit.DAYS.between(from, rightDate) + 1; + return new PTimeDelta(PythonBuiltinClassType.PTimeDelta, PythonBuiltinClassType.PTimeDelta.getInstanceShape(PythonLanguage.get(null)), (int) (leftDays - rightDays), 0, 0); + } + if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + PTimeDelta delta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(right); + long days = leftDays - delta.days; + if (days <= 0 || days >= MAX_ORDINAL) { + throw com.oracle.graal.python.nodes.PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); + } + return toPythonDate(ChronoUnit.DAYS.addTo(from, days - 1)); + } + return PNotImplemented.NOT_IMPLEMENTED; + } + } + + @Builtin(name = "year", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class YearNode extends PythonUnaryBuiltinNode { + @Specialization + static int year(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + return readDateValueNode.execute(inliningTarget, selfObj).year; + } + } + + @Builtin(name = "month", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class MonthNode extends PythonUnaryBuiltinNode { + @Specialization + static int month(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + return readDateValueNode.execute(inliningTarget, selfObj).month; + } + } + + @Builtin(name = "day", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class DayNode extends PythonUnaryBuiltinNode { + @Specialization + static int day(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + return readDateValueNode.execute(inliningTarget, selfObj).day; + } + } + + @Builtin(name = "replace", minNumOfPositionalArgs = 1, parameterNames = {"self", "year", "month", "day"}) + @GenerateNodeFactory + abstract static class ReplaceNode extends PythonBuiltinNode { + @Specialization + static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Object monthObject, Object dayObject, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode, + @Cached PyLongAsLongNode longAsLongNode, + @Cached DateNodes.NewNode newNode) { + TemporalNodes.DateValue self = readDateValueNode.execute(inliningTarget, selfObj); + int year = yearObject instanceof PNone ? self.year : (int) longAsLongNode.execute(frame, inliningTarget, yearObject); + int month = monthObject instanceof PNone ? self.month : (int) longAsLongNode.execute(frame, inliningTarget, monthObject); + int day = dayObject instanceof PNone ? self.day : (int) longAsLongNode.execute(frame, inliningTarget, dayObject); + return newNode.execute(inliningTarget, PythonBuiltinClassType.PDate, year, month, day); + } + } + + @Builtin(name = "toordinal", minNumOfPositionalArgs = 1, parameterNames = {"self"}) + @GenerateNodeFactory + abstract static class ToOrdinalNode extends PythonUnaryBuiltinNode { + @Specialization + @TruffleBoundary + static long toOrdinal(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + return ChronoUnit.DAYS.between(LocalDate.of(1, 1, 1), readDateValueNode.execute(inliningTarget, selfObj).toLocalDate()) + 1; + } + } + + @Builtin(name = "weekday", minNumOfPositionalArgs = 1, parameterNames = {"self"}) + @GenerateNodeFactory + abstract static class WeekDayNode extends PythonUnaryBuiltinNode { + @Specialization + @TruffleBoundary + static int weekDay(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + return readDateValueNode.execute(inliningTarget, selfObj).toLocalDate().getDayOfWeek().getValue() - 1; + } + } + + @Builtin(name = "isoweekday", minNumOfPositionalArgs = 1, parameterNames = {"self"}) + @GenerateNodeFactory + abstract static class IsoWeekDayNode extends PythonUnaryBuiltinNode { + @Specialization + @TruffleBoundary + static int isoWeekDay(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + return readDateValueNode.execute(inliningTarget, selfObj).toLocalDate().getDayOfWeek().getValue(); + } + } + + @Builtin(name = "isocalendar", minNumOfPositionalArgs = 1, parameterNames = {"self"}) + @GenerateNodeFactory + abstract static class IsoCalendarNode extends PythonUnaryBuiltinNode { + @Specialization + static Object isoCalendar(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + return PyObjectCallMethodObjArgs.executeUncached(toPythonDate(readDateValueNode.execute(inliningTarget, selfObj)), T_ISOCALENDAR); + } + } + + @Builtin(name = "isoformat", minNumOfPositionalArgs = 1, parameterNames = {"self"}) + @GenerateNodeFactory + abstract static class IsoFormatNode extends PythonUnaryBuiltinNode { + @Specialization + static TruffleString isoFormat(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + return toIsoFormat(readDateValueNode.execute(inliningTarget, selfObj)); + } + } + + @Builtin(name = "ctime", minNumOfPositionalArgs = 1, parameterNames = {"self"}) + @GenerateNodeFactory + abstract static class CTimeNode extends PythonUnaryBuiltinNode { + @Specialization + @TruffleBoundary + static TruffleString ctime(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + String ctime = readDateValueNode.execute(inliningTarget, selfObj).toLocalDate().format(DateTimeFormatter.ofPattern("EEE LLL ppd 00:00:00 yyyy")); + return TruffleString.FromJavaStringNode.getUncached().execute(ctime, TS_ENCODING); + } + } + + @Builtin(name = "timetuple", minNumOfPositionalArgs = 1, parameterNames = {"self"}) + @GenerateNodeFactory + abstract static class TimeTupleNode extends PythonUnaryBuiltinNode { + @Specialization + @TruffleBoundary + static PTuple timeTuple(Object selfObj, + @Bind Node inliningTarget, + @Bind PythonLanguage language, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + TemporalNodes.DateValue self = readDateValueNode.execute(inliningTarget, selfObj); + LocalDate localDate = self.toLocalDate(); + Object[] fields = new Object[]{self.year, self.month, self.day, 0, 0, 0, localDate.getDayOfWeek().getValue() - 1, localDate.getDayOfYear(), -1}; + return PFactory.createStructSeq(language, TimeModuleBuiltins.STRUCT_TIME_DESC, fields); + } + } + + @Builtin(name = "strftime", minNumOfPositionalArgs = 2, parameterNames = {"self", "format"}) + @GenerateNodeFactory + abstract static class StrFTimeNode extends PythonBinaryBuiltinNode { + @Specialization + static Object strftime(Object selfObj, Object formatObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode, + @Cached CastToTruffleStringNode castToTruffleStringNode) { + TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); + return PyObjectCallMethodObjArgs.executeUncached(toPythonDate(readDateValueNode.execute(inliningTarget, selfObj)), T_STRFTIME, format); + } + } + + @Builtin(name = J___FORMAT__, minNumOfPositionalArgs = 2, parameterNames = {"$self", "format"}) + @GenerateNodeFactory + abstract static class FormatNode extends PythonBinaryBuiltinNode { + @Specialization + static Object format(VirtualFrame frame, Object selfObj, Object formatObj, + @Bind Node inliningTarget, + @Cached PyObjectStrAsObjectNode strAsObjectNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs, + @Cached CastToTruffleStringNode castToTruffleStringNode) { + TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); + if (format.isEmpty()) { + return strAsObjectNode.execute(inliningTarget, selfObj); + } + return callMethodObjArgs.execute(frame, inliningTarget, selfObj, T_STRFTIME, format); + } + } + + private static PDate toPythonDate(TemporalNodes.DateValue date) { + return (PDate) DateNodes.NewUnsafeNode.executeUncached(PythonBuiltinClassType.PDate, date.year, date.month, date.day); + } + + private static Object toPythonDate(LocalDate date) { + return DateNodes.NewUnsafeNode.executeUncached(PythonBuiltinClassType.PDate, date.getYear(), date.getMonthValue(), date.getDayOfMonth()); + } + + private static TruffleString toIsoFormat(TemporalNodes.DateValue date) { + return TruffleString.FromJavaStringNode.getUncached().execute(date.toLocalDate().toString(), TS_ENCODING); + } +} From 81ddbfe6e6ef666fb9a11b7b0fc7a70c8ae06f2e Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 23 Mar 2026 11:16:55 +0100 Subject: [PATCH 04/30] Implement ForeignTime methods --- .../src/tests/test_interop.py | 25 ++ .../graal/python/builtins/Python3Core.java | 2 + .../builtins/PythonBuiltinClassType.java | 3 +- .../objects/foreign/ForeignTimeBuiltins.java | 400 ++++++++++++++++++ 4 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py index 750230b52a..4cbf34f76c 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py @@ -242,6 +242,31 @@ def test_foreign_date_behavior(self): self.assertEqual(d - datetime.date(2025, 3, 20), datetime.timedelta(days=3)) self.assertEqual(d - LocalDate.of(2025, 3, 20), datetime.timedelta(days=3)) + def test_foreign_time_behavior(self): + import datetime + import java + + LocalTime = java.type("java.time.LocalTime") + + t = LocalTime.of(7, 8, 9) + self.assertEqual(t.hour, 7) + self.assertEqual(t.minute, 8) + self.assertEqual(t.second, 9) + self.assertEqual(t.microsecond, 0) + self.assertEqual(str(t), "07:08:09") + self.assertEqual(t.isoformat(), "07:08:09") + self.assertEqual(t.strftime("%H:%M:%S"), "07:08:09") + self.assertEqual(format(t, "%H:%M:%S"), "07:08:09") + self.assertEqual(hash(t), hash(datetime.time(7, 8, 9))) + self.assertEqual(t, datetime.time(7, 8, 9)) + self.assertEqual(t, LocalTime.of(7, 8, 9)) + self.assertEqual(t.replace(second=10), datetime.time(7, 8, 10)) + self.assertLess(t, datetime.time(7, 8, 10)) + self.assertIsNone(t.tzinfo) + self.assertIsNone(t.utcoffset()) + self.assertIsNone(t.dst()) + self.assertIsNone(t.tzname()) + def test_read(self): o = CustomObject() assert polyglot.__read__(o, "field") == o.field diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java index 1766cbce27..d9a201ca65 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java @@ -269,6 +269,7 @@ import com.oracle.graal.python.builtins.objects.foreign.ForeignIterableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignNumberBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignObjectBuiltins; +import com.oracle.graal.python.builtins.objects.foreign.ForeignTimeBuiltins; import com.oracle.graal.python.builtins.objects.frame.FrameBuiltins; import com.oracle.graal.python.builtins.objects.function.AbstractFunctionBuiltins; import com.oracle.graal.python.builtins.objects.function.BuiltinFunctionBuiltins; @@ -502,6 +503,7 @@ private static PythonBuiltins[] initializeBuiltins(TruffleLanguage.Env env) { new ForeignNumberBuiltins(), new ForeignBooleanBuiltins(), new ForeignDateBuiltins(), + new ForeignTimeBuiltins(), new ForeignAbstractClassBuiltins(), new ForeignExecutableBuiltins(), new ForeignInstantiableBuiltins(), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java index 615f734bd1..eaff420f5e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java @@ -187,6 +187,7 @@ import com.oracle.graal.python.builtins.objects.foreign.ForeignIterableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignNumberBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignObjectBuiltins; +import com.oracle.graal.python.builtins.objects.foreign.ForeignTimeBuiltins; import com.oracle.graal.python.builtins.objects.frame.FrameBuiltins; import com.oracle.graal.python.builtins.objects.function.AbstractFunctionBuiltins; import com.oracle.graal.python.builtins.objects.function.FunctionBuiltins; @@ -844,7 +845,7 @@ It can be called either on the class (e.g. C.f()) or on an instance ForeignInstantiable("ForeignInstantiable", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().slots(ForeignInstantiableBuiltins.SLOTS)), ForeignIterable("ForeignIterable", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignIterableBuiltins.SLOTS)), ForeignDate("ForeignDate", PDate, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignDateBuiltins.SLOTS)), - ForeignTime("ForeignTime", PTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), + ForeignTime("ForeignTime", PTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignTimeBuiltins.SLOTS)), ForeignDateTime("ForeignDateTime", PDateTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), ForeignTimeZone("ForeignTimeZone", PTzInfo, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java new file mode 100644 index 0000000000..c882fda751 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java @@ -0,0 +1,400 @@ +/* + * Copyright (c) 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 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * 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. + */ +package com.oracle.graal.python.builtins.objects.foreign; + +import static com.oracle.graal.python.nodes.SpecialMethodNames.J___FORMAT__; +import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; +import static com.oracle.graal.python.util.PythonUtils.tsLiteral; + +import java.time.ZoneOffset; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +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.builtins.CoreFunctions; +import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.PythonBuiltins; +import com.oracle.graal.python.builtins.modules.datetime.DatetimeModuleBuiltins; +import com.oracle.graal.python.builtins.modules.datetime.PTime; +import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; +import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes; +import com.oracle.graal.python.builtins.modules.datetime.TimeDeltaNodes; +import com.oracle.graal.python.builtins.modules.datetime.TimeNodes; +import com.oracle.graal.python.builtins.modules.datetime.TimeZoneNodes; +import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.PNotImplemented; +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.TpSlotHashFun.HashBuiltinNode; +import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; +import com.oracle.graal.python.lib.PyLongAsLongNode; +import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; +import com.oracle.graal.python.lib.PyObjectHashNode; +import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; +import com.oracle.graal.python.lib.RichCmpOp; +import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PRaiseNode; +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; +import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; +import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; +import com.oracle.graal.python.runtime.PythonContext; +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; +import com.oracle.truffle.api.dsl.NodeFactory; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.strings.TruffleString; + +@CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignTime) +public final class ForeignTimeBuiltins extends PythonBuiltins { + public static final TpSlots SLOTS = ForeignTimeBuiltinsSlotsGen.SLOTS; + + private static final TruffleString T_DST = tsLiteral("dst"); + private static final TruffleString T_ISOFORMAT = tsLiteral("isoformat"); + private static final TruffleString T_STRFTIME = tsLiteral("strftime"); + private static final TruffleString T_TZNAME = tsLiteral("tzname"); + private static final TruffleString T_UTCOFFSET = tsLiteral("utcoffset"); + + @Override + protected List> getNodeFactories() { + return ForeignTimeBuiltinsFactory.getFactories(); + } + + @Slot(value = SlotKind.tp_repr, isComplex = true) + @GenerateNodeFactory + abstract static class ReprNode extends PythonUnaryBuiltinNode { + @Specialization + @TruffleBoundary + static TruffleString repr(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + TemporalNodes.TimeValue self = readTimeValueNode.execute(inliningTarget, selfObj); + TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); + String value = self.microsecond == 0 + ? String.format("%s(%d, %d, %d)", typeName, self.hour, self.minute, self.second) + : String.format("%s(%d, %d, %d, %d)", typeName, self.hour, self.minute, self.second, self.microsecond); + return TruffleString.FromJavaStringNode.getUncached().execute(value, TS_ENCODING); + } + } + + @Slot(value = SlotKind.tp_str, isComplex = true) + @GenerateNodeFactory + abstract static class StrNode extends PythonUnaryBuiltinNode { + @Specialization + static Object str(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode, + @Cached PyObjectStrAsObjectNode strAsObjectNode) { + return strAsObjectNode.execute(inliningTarget, toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); + } + } + + @Slot(value = SlotKind.tp_richcompare, isComplex = true) + @GenerateNodeFactory + abstract static class RichCmpNode extends RichCmpBuiltinNode { + @Specialization + static Object richCmp(VirtualFrame frame, Object selfObj, Object otherObj, RichCmpOp op, + @Bind Node inliningTarget, + @Cached TemporalNodes.TimeLikeCheckNode timeLikeCheckNode, + @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs, + @Cached PRaiseNode raiseNode) { + if (!timeLikeCheckNode.execute(inliningTarget, otherObj)) { + return PNotImplemented.NOT_IMPLEMENTED; + } + PTime self = toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget); + PTime other = toPythonTime(readTimeValueNode.execute(inliningTarget, otherObj), inliningTarget); + if (self.tzInfo == other.tzInfo) { + return compareTimeComponents(self, other, op); + } + + PTimeDelta selfUtcOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, PNone.NONE, frame, inliningTarget, callMethodObjArgs, raiseNode); + PTimeDelta otherUtcOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, PNone.NONE, frame, inliningTarget, callMethodObjArgs, raiseNode); + if (Objects.equals(selfUtcOffset, otherUtcOffset)) { + return compareTimeComponents(self, other, op); + } + if ((selfUtcOffset == null) != (otherUtcOffset == null)) { + if (op == RichCmpOp.Py_EQ) { + return false; + } else if (op == RichCmpOp.Py_NE) { + return true; + } else { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.CANT_COMPARE_OFFSET_NAIVE_AND_OFFSET_AWARE_TIMES); + } + } + return op.compareResultToBool(Long.compare(toMicroseconds(self, selfUtcOffset), toMicroseconds(other, otherUtcOffset))); + } + + private static boolean compareTimeComponents(PTime self, PTime other, RichCmpOp op) { + int[] selfComponents = new int[]{self.hour, self.minute, self.second, self.microsecond}; + int[] otherComponents = new int[]{other.hour, other.minute, other.second, other.microsecond}; + return op.compareResultToBool(Arrays.compare(selfComponents, otherComponents)); + } + } + + @Slot(value = SlotKind.tp_hash, isComplex = true) + @GenerateNodeFactory + abstract static class HashNode extends HashBuiltinNode { + @Specialization + static long hash(VirtualFrame frame, Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs, + @Cached PRaiseNode raiseNode, + @Cached PyObjectHashNode hashNode) { + PTime self = toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget); + PTimeDelta utcOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, PNone.NONE, frame, inliningTarget, callMethodObjArgs, raiseNode); + if (utcOffset == null) { + return hashNode.execute(frame, inliningTarget, self); + } + return hashNode.execute(frame, inliningTarget, toMicroseconds(self, utcOffset)); + } + } + + @Builtin(name = "hour", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class HourNode extends PythonUnaryBuiltinNode { + @Specialization + static int hour(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + return readTimeValueNode.execute(inliningTarget, selfObj).hour; + } + } + + @Builtin(name = "minute", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class MinuteNode extends PythonUnaryBuiltinNode { + @Specialization + static int minute(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + return readTimeValueNode.execute(inliningTarget, selfObj).minute; + } + } + + @Builtin(name = "second", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class SecondNode extends PythonUnaryBuiltinNode { + @Specialization + static int second(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + return readTimeValueNode.execute(inliningTarget, selfObj).second; + } + } + + @Builtin(name = "microsecond", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class MicrosecondNode extends PythonUnaryBuiltinNode { + @Specialization + static int microsecond(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + return readTimeValueNode.execute(inliningTarget, selfObj).microsecond; + } + } + + @Builtin(name = "tzinfo", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class TzInfoNode extends PythonUnaryBuiltinNode { + @Specialization + static Object tzinfo(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + Object tzInfo = toPythonTzInfo(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget); + return tzInfo != null ? tzInfo : PNone.NONE; + } + } + + @Builtin(name = "fold", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class FoldNode extends PythonUnaryBuiltinNode { + @Specialization + static int fold(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + return readTimeValueNode.execute(inliningTarget, selfObj).fold; + } + } + + @Builtin(name = "replace", minNumOfPositionalArgs = 1, parameterNames = {"self", "hour", "minute", "second", "microsecond", "tzinfo"}, keywordOnlyNames = {"fold"}) + @GenerateNodeFactory + abstract static class ReplaceNode extends PythonBuiltinNode { + @Specialization + static Object replace(VirtualFrame frame, Object selfObj, Object hourObject, Object minuteObject, Object secondObject, Object microsecondObject, Object tzInfoObject, Object foldObject, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode, + @Cached PyLongAsLongNode asLongNode, + @Cached TimeNodes.NewNode newTimeNode) { + TemporalNodes.TimeValue self = readTimeValueNode.execute(inliningTarget, selfObj); + long hour = hourObject == PNone.NO_VALUE ? self.hour : asLongNode.execute(frame, inliningTarget, hourObject); + long minute = minuteObject == PNone.NO_VALUE ? self.minute : asLongNode.execute(frame, inliningTarget, minuteObject); + long second = secondObject == PNone.NO_VALUE ? self.second : asLongNode.execute(frame, inliningTarget, secondObject); + long microsecond = microsecondObject == PNone.NO_VALUE ? self.microsecond : asLongNode.execute(frame, inliningTarget, microsecondObject); + Object tzInfo; + if (tzInfoObject == PNone.NO_VALUE) { + tzInfo = toPythonTzInfo(self, inliningTarget); + } else if (tzInfoObject == PNone.NONE) { + tzInfo = null; + } else { + tzInfo = tzInfoObject; + } + long fold = foldObject == PNone.NO_VALUE ? self.fold : asLongNode.execute(frame, inliningTarget, foldObject); + return newTimeNode.execute(inliningTarget, PythonBuiltinClassType.PTime, hour, minute, second, microsecond, tzInfo != null ? tzInfo : PNone.NONE, fold); + } + } + + @Builtin(name = "isoformat", minNumOfPositionalArgs = 1, parameterNames = {"self", "timespec"}) + @GenerateNodeFactory + abstract static class IsoFormatNode extends PythonBinaryBuiltinNode { + @Specialization + static Object isoformat(Object selfObj, Object timespecObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + Object timespec = timespecObj == PNone.NO_VALUE ? PNone.NO_VALUE : timespecObj; + return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT, timespec); + } + } + + @Builtin(name = "utcoffset", minNumOfPositionalArgs = 1, parameterNames = {"$self"}) + @GenerateNodeFactory + abstract static class UtcOffsetNode extends PythonUnaryBuiltinNode { + @Specialization + static Object utcoffset(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCOFFSET); + } + } + + @Builtin(name = "dst", minNumOfPositionalArgs = 1, parameterNames = {"$self"}) + @GenerateNodeFactory + abstract static class DstNode extends PythonUnaryBuiltinNode { + @Specialization + static Object dst(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DST); + } + } + + @Builtin(name = "tzname", minNumOfPositionalArgs = 1, parameterNames = {"$self"}) + @GenerateNodeFactory + abstract static class TzNameNode extends PythonUnaryBuiltinNode { + @Specialization + static Object tzname(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TZNAME); + } + } + + @Builtin(name = "strftime", minNumOfPositionalArgs = 2, parameterNames = {"self", "format"}) + @GenerateNodeFactory + abstract static class StrFTimeNode extends PythonBinaryBuiltinNode { + @Specialization + static Object strftime(Object selfObj, Object formatObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode, + @Cached CastToTruffleStringNode castToTruffleStringNode) { + TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); + return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_STRFTIME, format); + } + } + + @Builtin(name = J___FORMAT__, minNumOfPositionalArgs = 2, parameterNames = {"$self", "format"}) + @GenerateNodeFactory + abstract static class FormatNode extends PythonBinaryBuiltinNode { + @Specialization + static Object format(VirtualFrame frame, Object selfObj, Object formatObj, + @Bind Node inliningTarget, + @Cached PyObjectStrAsObjectNode strAsObjectNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs, + @Cached CastToTruffleStringNode castToTruffleStringNode) { + TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); + if (format.isEmpty()) { + return strAsObjectNode.execute(inliningTarget, selfObj); + } + return callMethodObjArgs.execute(frame, inliningTarget, selfObj, T_STRFTIME, format); + } + } + + private static long toMicroseconds(PTime self, PTimeDelta utcOffset) { + return (long) self.hour * 3600 * 1_000_000 + + (long) self.minute * 60 * 1_000_000 + + (long) self.second * 1_000_000 + + (long) self.microsecond - + (long) utcOffset.days * 24 * 3600 * 1_000_000 - + (long) utcOffset.seconds * 1_000_000 - + (long) utcOffset.microseconds; + } + + private static PTime toPythonTime(TemporalNodes.TimeValue time, Node inliningTarget) { + Object tzInfo = toPythonTzInfo(time, inliningTarget); + return (PTime) TimeNodes.NewNode.newTimeUnchecked(PythonBuiltinClassType.PTime, time.hour, time.minute, time.second, time.microsecond, tzInfo != null ? tzInfo : PNone.NONE, time.fold); + } + + private static Object toPythonTzInfo(TemporalNodes.TimeValue time, Node inliningTarget) { + if (time.tzInfo != null) { + return time.tzInfo; + } + if (time.zoneId instanceof ZoneOffset zoneOffset) { + PTimeDelta offset = TimeDeltaNodes.NewNode.getUncached().executeBuiltin(inliningTarget, 0, zoneOffset.getTotalSeconds(), 0, 0, 0, 0, 0); + return TimeZoneNodes.NewNode.getUncached().execute(inliningTarget, PythonContext.get(inliningTarget), PythonBuiltinClassType.PTimezone, offset, PNone.NO_VALUE); + } + if (time.zoneId != null && time.zoneId.getRules().isFixedOffset()) { + ZoneOffset offset = time.zoneId.getRules().getOffset(java.time.Instant.EPOCH); + PTimeDelta delta = TimeDeltaNodes.NewNode.getUncached().executeBuiltin(inliningTarget, 0, offset.getTotalSeconds(), 0, 0, 0, 0, 0); + return TimeZoneNodes.NewNode.getUncached().execute(inliningTarget, PythonContext.get(inliningTarget), PythonBuiltinClassType.PTimezone, delta, PNone.NO_VALUE); + } + return null; + } +} From 6fbab8ae6d81adfef71aad8b00df1c30b638b656 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 23 Mar 2026 11:25:58 +0100 Subject: [PATCH 05/30] [GR-62450] Implement ForeignDateTime methods --- .../src/tests/test_interop.py | 33 + .../graal/python/builtins/Python3Core.java | 2 + .../builtins/PythonBuiltinClassType.java | 3 +- .../modules/datetime/TemporalNodes.java | 24 + .../foreign/ForeignDateTimeBuiltins.java | 587 ++++++++++++++++++ .../objects/foreign/ForeignTimeBuiltins.java | 18 +- 6 files changed, 649 insertions(+), 18 deletions(-) create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py index 4cbf34f76c..3924c28d87 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py @@ -267,6 +267,39 @@ def test_foreign_time_behavior(self): self.assertIsNone(t.dst()) self.assertIsNone(t.tzname()) + def test_foreign_datetime_behavior(self): + import datetime + import java + + LocalDateTime = java.type("java.time.LocalDateTime") + + dt = LocalDateTime.of(2025, 3, 23, 7, 8, 9) + self.assertEqual(dt.year, 2025) + self.assertEqual(dt.month, 3) + self.assertEqual(dt.day, 23) + self.assertEqual(dt.hour, 7) + self.assertEqual(dt.minute, 8) + self.assertEqual(dt.second, 9) + self.assertEqual(dt.microsecond, 0) + self.assertEqual(str(dt), "2025-03-23 07:08:09") + self.assertEqual(dt.isoformat(), "2025-03-23T07:08:09") + self.assertEqual(dt.date(), datetime.date(2025, 3, 23)) + self.assertEqual(dt.time(), datetime.time(7, 8, 9)) + self.assertEqual(dt.timetz(), datetime.time(7, 8, 9)) + self.assertEqual(dt.timetuple(), datetime.datetime(2025, 3, 23, 7, 8, 9).timetuple()) + self.assertEqual(hash(dt), hash(datetime.datetime(2025, 3, 23, 7, 8, 9))) + self.assertEqual(dt, datetime.datetime(2025, 3, 23, 7, 8, 9)) + self.assertEqual(dt, LocalDateTime.of(2025, 3, 23, 7, 8, 9)) + self.assertEqual(dt.replace(minute=9), datetime.datetime(2025, 3, 23, 7, 9, 9)) + self.assertEqual(dt + datetime.timedelta(days=1), datetime.datetime(2025, 3, 24, 7, 8, 9)) + self.assertEqual(dt - datetime.timedelta(days=1), datetime.datetime(2025, 3, 22, 7, 8, 9)) + self.assertEqual(dt - datetime.datetime(2025, 3, 20, 7, 8, 9), datetime.timedelta(days=3)) + self.assertLess(dt, datetime.datetime(2025, 3, 23, 7, 8, 10)) + self.assertIsNone(dt.tzinfo) + self.assertIsNone(dt.utcoffset()) + self.assertIsNone(dt.dst()) + self.assertIsNone(dt.tzname()) + def test_read(self): o = CustomObject() assert polyglot.__read__(o, "field") == o.field diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java index d9a201ca65..b8266893b5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java @@ -264,6 +264,7 @@ import com.oracle.graal.python.builtins.objects.foreign.ForeignAbstractClassBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignBooleanBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignDateBuiltins; +import com.oracle.graal.python.builtins.objects.foreign.ForeignDateTimeBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignExecutableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignInstantiableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignIterableBuiltins; @@ -503,6 +504,7 @@ private static PythonBuiltins[] initializeBuiltins(TruffleLanguage.Env env) { new ForeignNumberBuiltins(), new ForeignBooleanBuiltins(), new ForeignDateBuiltins(), + new ForeignDateTimeBuiltins(), new ForeignTimeBuiltins(), new ForeignAbstractClassBuiltins(), new ForeignExecutableBuiltins(), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java index eaff420f5e..98b24b578a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java @@ -182,6 +182,7 @@ import com.oracle.graal.python.builtins.objects.foreign.ForeignAbstractClassBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignBooleanBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignDateBuiltins; +import com.oracle.graal.python.builtins.objects.foreign.ForeignDateTimeBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignExecutableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignInstantiableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignIterableBuiltins; @@ -846,7 +847,7 @@ It can be called either on the class (e.g. C.f()) or on an instance ForeignIterable("ForeignIterable", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignIterableBuiltins.SLOTS)), ForeignDate("ForeignDate", PDate, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignDateBuiltins.SLOTS)), ForeignTime("ForeignTime", PTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignTimeBuiltins.SLOTS)), - ForeignDateTime("ForeignDateTime", PDateTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), + ForeignDateTime("ForeignDateTime", PDateTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignDateTimeBuiltins.SLOTS)), ForeignTimeZone("ForeignTimeZone", PTzInfo, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), // bz2 diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java index bf69e9ab40..87112c1c37 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java @@ -42,16 +42,21 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; +import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; +import java.time.ZoneOffset; +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.cext.structs.CStructAccess; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.object.IsForeignObjectNode; +import com.oracle.graal.python.runtime.PythonContext; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.ValueType; import com.oracle.truffle.api.dsl.Bind; @@ -151,6 +156,25 @@ public LocalDateTime toLocalDateTime() { } } + public static Object toPythonTzInfo(Object tzInfo, ZoneId zoneId, Node inliningTarget) { + if (tzInfo != null) { + return tzInfo; + } + if (zoneId == null) { + return null; + } + final ZoneOffset offset; + if (zoneId instanceof ZoneOffset zoneOffset) { + offset = zoneOffset; + } else if (zoneId.getRules().isFixedOffset()) { + offset = zoneId.getRules().getOffset(Instant.EPOCH); + } else { + return null; + } + PTimeDelta delta = TimeDeltaNodes.NewNode.getUncached().executeBuiltin(inliningTarget, 0, offset.getTotalSeconds(), 0, 0, 0, 0, 0); + return TimeZoneNodes.NewNode.getUncached().execute(inliningTarget, PythonContext.get(inliningTarget), PythonBuiltinClassType.PTimezone, delta, PNone.NO_VALUE); + } + @GenerateUncached @GenerateInline @GenerateCached(false) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java new file mode 100644 index 0000000000..810b9b6dc4 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java @@ -0,0 +1,587 @@ +/* + * Copyright (c) 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 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * 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. + */ +package com.oracle.graal.python.builtins.objects.foreign; + +import static com.oracle.graal.python.nodes.SpecialMethodNames.J___FORMAT__; +import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; +import static com.oracle.graal.python.util.PythonUtils.tsLiteral; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Objects; + +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.builtins.CoreFunctions; +import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.PythonBuiltins; +import com.oracle.graal.python.builtins.modules.datetime.DateTimeNodes; +import com.oracle.graal.python.builtins.modules.datetime.DatetimeModuleBuiltins; +import com.oracle.graal.python.builtins.modules.datetime.PDateTime; +import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; +import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes; +import com.oracle.graal.python.builtins.modules.datetime.TimeDeltaNodes; +import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.PNotImplemented; +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.TpSlotBinaryOp.BinaryOpBuiltinNode; +import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun.HashBuiltinNode; +import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; +import com.oracle.graal.python.lib.PyLongAsLongNode; +import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; +import com.oracle.graal.python.lib.PyObjectHashNode; +import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; +import com.oracle.graal.python.lib.RichCmpOp; +import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PRaiseNode; +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; +import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; +import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; +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; +import com.oracle.truffle.api.dsl.NodeFactory; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.strings.TruffleString; + +@CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignDateTime) +public final class ForeignDateTimeBuiltins extends PythonBuiltins { + public static final TpSlots SLOTS = ForeignDateTimeBuiltinsSlotsGen.SLOTS; + + private static final TruffleString T_ASTIMEZONE = tsLiteral("astimezone"); + private static final TruffleString T_DATE = tsLiteral("date"); + private static final TruffleString T_DST = tsLiteral("dst"); + private static final TruffleString T_ISOFORMAT = tsLiteral("isoformat"); + private static final TruffleString T_STRFTIME = tsLiteral("strftime"); + private static final TruffleString T_TIME = tsLiteral("time"); + private static final TruffleString T_TIMETZ = tsLiteral("timetz"); + private static final TruffleString T_TIMESTAMP = tsLiteral("timestamp"); + private static final TruffleString T_TIMETUPLE = tsLiteral("timetuple"); + private static final TruffleString T_TZNAME = tsLiteral("tzname"); + private static final TruffleString T_UTCOFFSET = tsLiteral("utcoffset"); + private static final TruffleString T_UTCTIMETUPLE = tsLiteral("utctimetuple"); + + @Override + protected List> getNodeFactories() { + return ForeignDateTimeBuiltinsFactory.getFactories(); + } + + @Slot(value = SlotKind.tp_repr, isComplex = true) + @GenerateNodeFactory + abstract static class ReprNode extends PythonUnaryBuiltinNode { + @Specialization + @TruffleBoundary + static TruffleString repr(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + TemporalNodes.DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); + TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); + String string = String.format("%s(%d, %d, %d, %d, %d, %d, %d)", typeName, self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond); + return TruffleString.FromJavaStringNode.getUncached().execute(string, TS_ENCODING); + } + } + + @Slot(value = SlotKind.tp_str, isComplex = true) + @GenerateNodeFactory + abstract static class StrNode extends PythonUnaryBuiltinNode { + @Specialization + static Object str(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode, + @Cached PyObjectStrAsObjectNode strAsObjectNode) { + return strAsObjectNode.execute(inliningTarget, toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); + } + } + + @Slot(value = SlotKind.tp_richcompare, isComplex = true) + @GenerateNodeFactory + abstract static class RichCmpNode extends RichCmpBuiltinNode { + @Specialization + static Object richCmp(VirtualFrame frame, Object selfObj, Object otherObj, RichCmpOp op, + @Bind Node inliningTarget, + @Cached TemporalNodes.DateTimeLikeCheckNode dateTimeLikeCheckNode, + @Cached TemporalNodes.DateLikeCheckNode dateLikeCheckNode, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs, + @Cached PRaiseNode raiseNode) { + if (!dateTimeLikeCheckNode.execute(inliningTarget, otherObj)) { + if (dateLikeCheckNode.execute(inliningTarget, otherObj)) { + if (op == RichCmpOp.Py_EQ) { + return false; + } else if (op == RichCmpOp.Py_NE) { + return true; + } else { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.CANT_COMPARE, selfObj, otherObj); + } + } + return PNotImplemented.NOT_IMPLEMENTED; + } + + PDateTime self = toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget); + PDateTime other = toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, otherObj), inliningTarget); + if (self.tzInfo == other.tzInfo) { + return op.compareResultToBool(compareDateTimeComponents(self, other)); + } + + PTimeDelta selfUtcOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, self, frame, inliningTarget, callMethodObjArgs, raiseNode); + PTimeDelta otherUtcOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, other, frame, inliningTarget, callMethodObjArgs, raiseNode); + if (Objects.equals(selfUtcOffset, otherUtcOffset)) { + return op.compareResultToBool(compareDateTimeComponents(self, other)); + } + if ((selfUtcOffset == null) != (otherUtcOffset == null)) { + if (op == RichCmpOp.Py_EQ) { + return false; + } else if (op == RichCmpOp.Py_NE) { + return true; + } else { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.CANT_COMPARE_OFFSET_NAIVE_AND_OFFSET_AWARE_DATETIMES); + } + } + LocalDateTime selfUtc = subtractOffsetFromDateTime(self, selfUtcOffset); + LocalDateTime otherUtc = subtractOffsetFromDateTime(other, otherUtcOffset); + return op.compareResultToBool(selfUtc.compareTo(otherUtc)); + } + } + + @Slot(value = SlotKind.tp_hash, isComplex = true) + @GenerateNodeFactory + abstract static class HashNode extends HashBuiltinNode { + @Specialization + static long hash(VirtualFrame frame, Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode, + @Cached PyObjectHashNode hashNode) { + return hashNode.execute(frame, inliningTarget, toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); + } + } + + @Slot(value = SlotKind.nb_add, isComplex = true) + @GenerateNodeFactory + abstract static class AddNode extends BinaryOpBuiltinNode { + @Specialization + @TruffleBoundary + static Object add(Object left, Object right, + @Bind Node inliningTarget, + @Cached TemporalNodes.DateTimeLikeCheckNode dateTimeLikeCheckNode, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + Object dateTimeObj; + Object deltaObj; + if (dateTimeLikeCheckNode.execute(inliningTarget, left) && TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + dateTimeObj = left; + deltaObj = right; + } else if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(left) && dateTimeLikeCheckNode.execute(inliningTarget, right)) { + dateTimeObj = right; + deltaObj = left; + } else { + return PNotImplemented.NOT_IMPLEMENTED; + } + PDateTime date = toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, dateTimeObj), inliningTarget); + PTimeDelta delta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(deltaObj); + LocalDateTime adjusted = toLocalDateTime(date).plusDays(delta.days).plusSeconds(delta.seconds).plusNanos(delta.microseconds * 1_000L); + return toPythonDateTime(adjusted, date.tzInfo, date.fold, inliningTarget); + } + } + + @Slot(value = SlotKind.nb_subtract, isComplex = true) + @GenerateNodeFactory + abstract static class SubNode extends BinaryOpBuiltinNode { + @Specialization + static Object sub(VirtualFrame frame, Object left, Object right, + @Bind Node inliningTarget, + @Cached TemporalNodes.DateTimeLikeCheckNode dateTimeLikeCheckNode, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs, + @Cached PRaiseNode raiseNode) { + if (!dateTimeLikeCheckNode.execute(inliningTarget, left)) { + return PNotImplemented.NOT_IMPLEMENTED; + } + PDateTime self = toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, left), inliningTarget); + if (dateTimeLikeCheckNode.execute(inliningTarget, right)) { + PDateTime other = toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, right), inliningTarget); + PTimeDelta selfOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, self, frame, inliningTarget, callMethodObjArgs, raiseNode); + PTimeDelta otherOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, other, frame, inliningTarget, callMethodObjArgs, raiseNode); + if ((selfOffset == null) != (otherOffset == null)) { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.CANNOT_SUBTRACT_OFFSET_NAIVE_AND_OFFSET_AWARE_DATETIMES); + } + LocalDateTime selfToCompare = selfOffset != null && self.tzInfo != other.tzInfo ? subtractOffsetFromDateTime(self, selfOffset) : toLocalDateTime(self); + LocalDateTime otherToCompare = otherOffset != null && self.tzInfo != other.tzInfo ? subtractOffsetFromDateTime(other, otherOffset) : toLocalDateTime(other); + long selfSeconds = selfToCompare.toEpochSecond(ZoneOffset.UTC); + long otherSeconds = otherToCompare.toEpochSecond(ZoneOffset.UTC); + return TimeDeltaNodes.NewNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PTimeDelta, 0, selfSeconds - otherSeconds, self.microsecond - other.microsecond, 0, + 0, 0, 0); + } + if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + PTimeDelta delta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(right); + LocalDateTime adjusted = toLocalDateTime(self).minusDays(delta.days).minusSeconds(delta.seconds).minusNanos(delta.microseconds * 1_000L); + return toPythonDateTime(adjusted, self.tzInfo, self.fold, inliningTarget); + } + return PNotImplemented.NOT_IMPLEMENTED; + } + } + + @Builtin(name = "year", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class YearNode extends PythonUnaryBuiltinNode { + @Specialization + static int year(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, selfObj).year; + } + } + + @Builtin(name = "month", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class MonthNode extends PythonUnaryBuiltinNode { + @Specialization + static int month(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, selfObj).month; + } + } + + @Builtin(name = "day", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class DayNode extends PythonUnaryBuiltinNode { + @Specialization + static int day(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, selfObj).day; + } + } + + @Builtin(name = "hour", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class HourNode extends PythonUnaryBuiltinNode { + @Specialization + static int hour(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, selfObj).hour; + } + } + + @Builtin(name = "minute", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class MinuteNode extends PythonUnaryBuiltinNode { + @Specialization + static int minute(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, selfObj).minute; + } + } + + @Builtin(name = "second", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class SecondNode extends PythonUnaryBuiltinNode { + @Specialization + static int second(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, selfObj).second; + } + } + + @Builtin(name = "microsecond", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class MicrosecondNode extends PythonUnaryBuiltinNode { + @Specialization + static int microsecond(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, selfObj).microsecond; + } + } + + @Builtin(name = "tzinfo", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class TzInfoNode extends PythonUnaryBuiltinNode { + @Specialization + static Object tzinfo(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + TemporalNodes.DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); + Object tzInfo = TemporalNodes.toPythonTzInfo(self.tzInfo, self.zoneId, inliningTarget); + return tzInfo != null ? tzInfo : PNone.NONE; + } + } + + @Builtin(name = "fold", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class FoldNode extends PythonUnaryBuiltinNode { + @Specialization + static int fold(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, selfObj).fold; + } + } + + @Builtin(name = "date", minNumOfPositionalArgs = 1, parameterNames = {"self"}) + @GenerateNodeFactory + abstract static class DateNode extends PythonUnaryBuiltinNode { + @Specialization + static Object date(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DATE); + } + } + + @Builtin(name = "time", minNumOfPositionalArgs = 1, parameterNames = {"self"}) + @GenerateNodeFactory + abstract static class TimeNode extends PythonUnaryBuiltinNode { + @Specialization + static Object time(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIME); + } + } + + @Builtin(name = "timetz", minNumOfPositionalArgs = 1, parameterNames = {"self"}) + @GenerateNodeFactory + abstract static class TimeTzNode extends PythonUnaryBuiltinNode { + @Specialization + static Object timetz(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMETZ); + } + } + + @Builtin(name = "replace", minNumOfPositionalArgs = 1, parameterNames = {"self", "year", "month", "day", "hour", "minute", "second", "microsecond", "tzinfo"}, keywordOnlyNames = {"fold"}) + @GenerateNodeFactory + abstract static class ReplaceNode extends PythonBuiltinNode { + @Specialization + static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Object monthObject, Object dayObject, Object hourObject, Object minuteObject, Object secondObject, + Object microsecondObject, Object tzInfoObject, Object foldObject, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode, + @Cached PyLongAsLongNode asLongNode, + @Cached DateTimeNodes.NewNode newDateTimeNode) { + TemporalNodes.DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); + long year = yearObject == PNone.NO_VALUE ? self.year : asLongNode.execute(frame, inliningTarget, yearObject); + long month = monthObject == PNone.NO_VALUE ? self.month : asLongNode.execute(frame, inliningTarget, monthObject); + long day = dayObject == PNone.NO_VALUE ? self.day : asLongNode.execute(frame, inliningTarget, dayObject); + long hour = hourObject == PNone.NO_VALUE ? self.hour : asLongNode.execute(frame, inliningTarget, hourObject); + long minute = minuteObject == PNone.NO_VALUE ? self.minute : asLongNode.execute(frame, inliningTarget, minuteObject); + long second = secondObject == PNone.NO_VALUE ? self.second : asLongNode.execute(frame, inliningTarget, secondObject); + long microsecond = microsecondObject == PNone.NO_VALUE ? self.microsecond : asLongNode.execute(frame, inliningTarget, microsecondObject); + Object tzInfo; + if (tzInfoObject == PNone.NO_VALUE) { + tzInfo = TemporalNodes.toPythonTzInfo(self.tzInfo, self.zoneId, inliningTarget); + } else if (tzInfoObject == PNone.NONE) { + tzInfo = null; + } else { + tzInfo = tzInfoObject; + } + long fold = foldObject == PNone.NO_VALUE ? self.fold : asLongNode.execute(frame, inliningTarget, foldObject); + return newDateTimeNode.execute(inliningTarget, PythonBuiltinClassType.PDateTime, year, month, day, hour, minute, second, microsecond, tzInfo != null ? tzInfo : PNone.NONE, fold); + } + } + + @Builtin(name = "isoformat", minNumOfPositionalArgs = 1, parameterNames = {"self", "sep", "timespec"}) + @GenerateNodeFactory + abstract static class IsoFormatNode extends PythonBuiltinNode { + @Specialization + static Object isoformat(Object selfObj, Object sepObj, Object timespecObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + if (sepObj == PNone.NO_VALUE && timespecObj == PNone.NO_VALUE) { + return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT); + } + if (timespecObj == PNone.NO_VALUE) { + return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT, sepObj); + } + return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT, sepObj, timespecObj); + } + } + + @Builtin(name = "utcoffset", minNumOfPositionalArgs = 1, parameterNames = {"$self"}) + @GenerateNodeFactory + abstract static class UtcOffsetNode extends PythonUnaryBuiltinNode { + @Specialization + static Object utcoffset(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCOFFSET); + } + } + + @Builtin(name = "dst", minNumOfPositionalArgs = 1, parameterNames = {"$self"}) + @GenerateNodeFactory + abstract static class DstNode extends PythonUnaryBuiltinNode { + @Specialization + static Object dst(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DST); + } + } + + @Builtin(name = "tzname", minNumOfPositionalArgs = 1, parameterNames = {"$self"}) + @GenerateNodeFactory + abstract static class TzNameNode extends PythonUnaryBuiltinNode { + @Specialization + static Object tzname(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TZNAME); + } + } + + @Builtin(name = "timetuple", minNumOfPositionalArgs = 1, parameterNames = {"self"}) + @GenerateNodeFactory + abstract static class TimeTupleNode extends PythonUnaryBuiltinNode { + @Specialization + static Object timetuple(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMETUPLE); + } + } + + @Builtin(name = "utctimetuple", minNumOfPositionalArgs = 1, parameterNames = {"self"}) + @GenerateNodeFactory + abstract static class UtcTimeTupleNode extends PythonUnaryBuiltinNode { + @Specialization + static Object utctimetuple(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCTIMETUPLE); + } + } + + @Builtin(name = "timestamp", minNumOfPositionalArgs = 1, parameterNames = {"self"}) + @GenerateNodeFactory + abstract static class TimestampNode extends PythonUnaryBuiltinNode { + @Specialization + static Object timestamp(Object selfObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMESTAMP); + } + } + + @Builtin(name = "astimezone", minNumOfPositionalArgs = 1, parameterNames = {"self", "tz"}) + @GenerateNodeFactory + abstract static class AsTimeZoneNode extends PythonBinaryBuiltinNode { + @Specialization + static Object astimezone(Object selfObj, Object tzObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + if (tzObj == PNone.NO_VALUE) { + return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ASTIMEZONE); + } + return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ASTIMEZONE, tzObj); + } + } + + @Builtin(name = "strftime", minNumOfPositionalArgs = 2, parameterNames = {"self", "format"}) + @GenerateNodeFactory + abstract static class StrFTimeNode extends PythonBinaryBuiltinNode { + @Specialization + static Object strftime(Object selfObj, Object formatObj, + @Bind Node inliningTarget, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode, + @Cached CastToTruffleStringNode castToTruffleStringNode) { + TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); + return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_STRFTIME, format); + } + } + + @Builtin(name = J___FORMAT__, minNumOfPositionalArgs = 2, parameterNames = {"$self", "format"}) + @GenerateNodeFactory + abstract static class FormatNode extends PythonBinaryBuiltinNode { + @Specialization + static Object format(VirtualFrame frame, Object selfObj, Object formatObj, + @Bind Node inliningTarget, + @Cached PyObjectStrAsObjectNode strAsObjectNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs, + @Cached CastToTruffleStringNode castToTruffleStringNode) { + TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); + if (format.isEmpty()) { + return strAsObjectNode.execute(inliningTarget, selfObj); + } + return callMethodObjArgs.execute(frame, inliningTarget, selfObj, T_STRFTIME, format); + } + } + + @TruffleBoundary + private static int compareDateTimeComponents(PDateTime self, PDateTime other) { + return java.util.Arrays.compare(new int[]{self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond}, + new int[]{other.year, other.month, other.day, other.hour, other.minute, other.second, other.microsecond}); + } + + @TruffleBoundary + private static LocalDateTime subtractOffsetFromDateTime(PDateTime self, PTimeDelta offset) { + return toLocalDateTime(self).minusDays(offset.days).minusSeconds(offset.seconds).minusNanos(offset.microseconds * 1_000L); + } + + @TruffleBoundary + private static LocalDateTime toLocalDateTime(PDateTime self) { + return LocalDateTime.of(self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond * 1_000); + } + + private static PDateTime toPythonDateTime(TemporalNodes.DateTimeValue value, Node inliningTarget) { + Object tzInfo = TemporalNodes.toPythonTzInfo(value.tzInfo, value.zoneId, inliningTarget); + return (PDateTime) DateTimeNodes.NewUnsafeNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PDateTime, value.year, value.month, value.day, value.hour, value.minute, + value.second, value.microsecond, tzInfo != null ? tzInfo : PNone.NONE, value.fold); + } + + private static Object toPythonDateTime(LocalDateTime local, Object tzInfo, int fold, Node inliningTarget) { + return DateTimeNodes.NewUnsafeNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PDateTime, local.getYear(), local.getMonthValue(), local.getDayOfMonth(), + local.getHour(), local.getMinute(), local.getSecond(), local.getNano() / 1_000, tzInfo != null ? tzInfo : PNone.NONE, fold); + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java index c882fda751..687ff8c8f6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java @@ -44,7 +44,6 @@ import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; -import java.time.ZoneOffset; import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -59,9 +58,7 @@ import com.oracle.graal.python.builtins.modules.datetime.PTime; import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes; -import com.oracle.graal.python.builtins.modules.datetime.TimeDeltaNodes; import com.oracle.graal.python.builtins.modules.datetime.TimeNodes; -import com.oracle.graal.python.builtins.modules.datetime.TimeZoneNodes; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.type.TpSlots; @@ -81,7 +78,6 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; -import com.oracle.graal.python.runtime.PythonContext; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; @@ -383,18 +379,6 @@ private static PTime toPythonTime(TemporalNodes.TimeValue time, Node inliningTar } private static Object toPythonTzInfo(TemporalNodes.TimeValue time, Node inliningTarget) { - if (time.tzInfo != null) { - return time.tzInfo; - } - if (time.zoneId instanceof ZoneOffset zoneOffset) { - PTimeDelta offset = TimeDeltaNodes.NewNode.getUncached().executeBuiltin(inliningTarget, 0, zoneOffset.getTotalSeconds(), 0, 0, 0, 0, 0); - return TimeZoneNodes.NewNode.getUncached().execute(inliningTarget, PythonContext.get(inliningTarget), PythonBuiltinClassType.PTimezone, offset, PNone.NO_VALUE); - } - if (time.zoneId != null && time.zoneId.getRules().isFixedOffset()) { - ZoneOffset offset = time.zoneId.getRules().getOffset(java.time.Instant.EPOCH); - PTimeDelta delta = TimeDeltaNodes.NewNode.getUncached().executeBuiltin(inliningTarget, 0, offset.getTotalSeconds(), 0, 0, 0, 0, 0); - return TimeZoneNodes.NewNode.getUncached().execute(inliningTarget, PythonContext.get(inliningTarget), PythonBuiltinClassType.PTimezone, delta, PNone.NO_VALUE); - } - return null; + return TemporalNodes.toPythonTzInfo(time.tzInfo, time.zoneId, inliningTarget); } } From a8e876459481f389901e4067cdd459b459be382f Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 23 Mar 2026 11:35:06 +0100 Subject: [PATCH 06/30] Support foreign timezone objects --- .../src/tests/test_interop.py | 33 +++ .../graal/python/builtins/Python3Core.java | 2 + .../builtins/PythonBuiltinClassType.java | 3 +- .../modules/datetime/TemporalNodes.java | 7 + .../modules/datetime/TzInfoNodes.java | 10 + .../foreign/ForeignTimeZoneBuiltins.java | 235 ++++++++++++++++++ 6 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py index 3924c28d87..c581ddf113 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py @@ -300,6 +300,39 @@ def test_foreign_datetime_behavior(self): self.assertIsNone(dt.dst()) self.assertIsNone(dt.tzname()) + def test_foreign_timezone_behavior(self): + import datetime + import java + + ZoneId = java.type("java.time.ZoneId") + + utc = ZoneId.of("UTC") + self.assertIsInstance(utc, datetime.tzinfo) + self.assertEqual(str(utc), "UTC") + self.assertEqual(utc.tzname(None), "UTC") + self.assertEqual(utc.utcoffset(None), datetime.timedelta()) + self.assertIsNone(utc.dst(None)) + + aware = datetime.datetime(2025, 3, 23, 7, 8, 9, tzinfo=utc) + self.assertIs(aware.tzinfo, utc) + self.assertEqual(aware.utcoffset(), datetime.timedelta()) + self.assertEqual(aware.tzname(), "UTC") + self.assertEqual(aware.isoformat(), "2025-03-23T07:08:09+00:00") + + berlin = ZoneId.of("Europe/Berlin") + self.assertIsInstance(berlin, datetime.tzinfo) + self.assertIsNone(berlin.utcoffset(None)) + self.assertIsNone(berlin.dst(None)) + self.assertIsNone(berlin.tzname(None)) + + local = datetime.datetime(2025, 3, 23, 7, 8, 9, tzinfo=berlin) + self.assertIs(local.tzinfo, berlin) + self.assertEqual(local.utcoffset(), datetime.timedelta(hours=1)) + self.assertEqual(local.dst(), datetime.timedelta()) + self.assertEqual(local.tzname(), "CET") + self.assertEqual(berlin.fromutc(datetime.datetime(2025, 3, 23, 6, 8, 9, tzinfo=berlin)), + datetime.datetime(2025, 3, 23, 7, 8, 9, tzinfo=berlin)) + def test_read(self): o = CustomObject() assert polyglot.__read__(o, "field") == o.field diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java index b8266893b5..28bdba2f63 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java @@ -271,6 +271,7 @@ import com.oracle.graal.python.builtins.objects.foreign.ForeignNumberBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignObjectBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignTimeBuiltins; +import com.oracle.graal.python.builtins.objects.foreign.ForeignTimeZoneBuiltins; import com.oracle.graal.python.builtins.objects.frame.FrameBuiltins; import com.oracle.graal.python.builtins.objects.function.AbstractFunctionBuiltins; import com.oracle.graal.python.builtins.objects.function.BuiltinFunctionBuiltins; @@ -506,6 +507,7 @@ private static PythonBuiltins[] initializeBuiltins(TruffleLanguage.Env env) { new ForeignDateBuiltins(), new ForeignDateTimeBuiltins(), new ForeignTimeBuiltins(), + new ForeignTimeZoneBuiltins(), new ForeignAbstractClassBuiltins(), new ForeignExecutableBuiltins(), new ForeignInstantiableBuiltins(), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java index 98b24b578a..9068dd8fc7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java @@ -189,6 +189,7 @@ import com.oracle.graal.python.builtins.objects.foreign.ForeignNumberBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignObjectBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignTimeBuiltins; +import com.oracle.graal.python.builtins.objects.foreign.ForeignTimeZoneBuiltins; import com.oracle.graal.python.builtins.objects.frame.FrameBuiltins; import com.oracle.graal.python.builtins.objects.function.AbstractFunctionBuiltins; import com.oracle.graal.python.builtins.objects.function.FunctionBuiltins; @@ -848,7 +849,7 @@ It can be called either on the class (e.g. C.f()) or on an instance ForeignDate("ForeignDate", PDate, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignDateBuiltins.SLOTS)), ForeignTime("ForeignTime", PTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignTimeBuiltins.SLOTS)), ForeignDateTime("ForeignDateTime", PDateTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignDateTimeBuiltins.SLOTS)), - ForeignTimeZone("ForeignTimeZone", PTzInfo, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), + ForeignTimeZone("ForeignTimeZone", PTzInfo, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignTimeZoneBuiltins.SLOTS)), // bz2 BZ2Compressor("BZ2Compressor", PythonObject, newBuilder().publishInModule("_bz2").basetype().slots(BZ2CompressorBuiltins.SLOTS)), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java index 87112c1c37..2d1e1d2920 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java @@ -160,6 +160,13 @@ public static Object toPythonTzInfo(Object tzInfo, ZoneId zoneId, Node inliningT if (tzInfo != null) { return tzInfo; } + if (zoneId == null) { + return null; + } + return zoneId; + } + + public static Object toFixedOffsetTimeZone(ZoneId zoneId, Node inliningTarget) { if (zoneId == null) { return null; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoNodes.java index fbed81342d..2549d89bae 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoNodes.java @@ -43,12 +43,15 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; 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.Specialization; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; public class TzInfoNodes { @@ -73,6 +76,13 @@ static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, return profile.profileObject(inliningTarget, value, PythonBuiltinClassType.PTzInfo); } + @Specialization(guards = "isForeignObjectNode.execute(inliningTarget, value)", limit = "1") + static boolean doForeign(Node inliningTarget, Object value, + @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + return interop.isTimeZone(value); + } + @Fallback static boolean doOther(@SuppressWarnings("unused") Object value) { return false; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java new file mode 100644 index 0000000000..a6d9b2db20 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 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 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * 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. + */ +package com.oracle.graal.python.builtins.objects.foreign; + +import static com.oracle.graal.python.nodes.SpecialMethodNames.J___REDUCE__; +import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Locale; + +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.builtins.CoreFunctions; +import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.PythonBuiltins; +import com.oracle.graal.python.builtins.modules.datetime.DateTimeNodes; +import com.oracle.graal.python.builtins.modules.datetime.DatetimeModuleBuiltins; +import com.oracle.graal.python.builtins.modules.datetime.PDateTime; +import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes; +import com.oracle.graal.python.builtins.modules.datetime.TimeDeltaNodes; +import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.type.TpSlots; +import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PRaiseNode; +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; +import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.truffle.api.CompilerDirectives; +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; +import com.oracle.truffle.api.dsl.NodeFactory; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.strings.TruffleString; + +@CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignTimeZone) +public final class ForeignTimeZoneBuiltins extends PythonBuiltins { + public static final TpSlots SLOTS = ForeignTimeZoneBuiltinsSlotsGen.SLOTS; + + @Override + protected List> getNodeFactories() { + return ForeignTimeZoneBuiltinsFactory.getFactories(); + } + + @Slot(value = SlotKind.tp_repr, isComplex = true) + @GenerateNodeFactory + abstract static class ReprNode extends PythonUnaryBuiltinNode { + @Specialization(limit = "1") + @TruffleBoundary + static TruffleString repr(Object self, + @Bind Node inliningTarget, + @CachedLibrary("self") InteropLibrary interop) { + ZoneId zoneId = asZoneId(self, interop); + TruffleString typeName = com.oracle.graal.python.builtins.objects.type.TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(self)); + String value = String.format("%s('%s')", typeName, zoneId.getId()); + return TruffleString.FromJavaStringNode.getUncached().execute(value, TS_ENCODING); + } + } + + @Slot(value = SlotKind.tp_str, isComplex = true) + @GenerateNodeFactory + abstract static class StrNode extends PythonUnaryBuiltinNode { + @Specialization(limit = "1") + static TruffleString str(Object self, + @CachedLibrary("self") InteropLibrary interop) { + return TruffleString.FromJavaStringNode.getUncached().execute(asZoneId(self, interop).getId(), TS_ENCODING); + } + } + + @Builtin(name = "utcoffset", minNumOfPositionalArgs = 1, parameterNames = {"$self", "dt"}) + @GenerateNodeFactory + abstract static class UtcOffsetNode extends PythonBinaryBuiltinNode { + @Specialization(limit = "1") + @TruffleBoundary + static Object utcoffset(Object self, Object dateTime, + @Bind Node inliningTarget, + @CachedLibrary("self") InteropLibrary interop, + @Cached TemporalNodes.DateTimeLikeCheckNode dateTimeLikeCheckNode, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + ZoneId zoneId = asZoneId(self, interop); + if (dateTime == PNone.NONE) { + Object fixed = TemporalNodes.toFixedOffsetTimeZone(zoneId, inliningTarget); + if (fixed == null) { + return PNone.NONE; + } + return DatetimeModuleBuiltins.callUtcOffset(fixed, PNone.NONE, inliningTarget); + } + if (!dateTimeLikeCheckNode.execute(inliningTarget, dateTime)) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); + } + LocalDateTime localDateTime = readDateTimeValueNode.execute(inliningTarget, dateTime).toLocalDateTime(); + ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId); + return TimeDeltaNodes.NewNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PTimeDelta, 0, zonedDateTime.getOffset().getTotalSeconds(), 0, 0, 0, 0, 0); + } + } + + @Builtin(name = "dst", minNumOfPositionalArgs = 1, parameterNames = {"$self", "dt"}) + @GenerateNodeFactory + abstract static class DstNode extends PythonBinaryBuiltinNode { + @Specialization(limit = "1") + @TruffleBoundary + static Object dst(Object self, Object dateTime, + @Bind Node inliningTarget, + @CachedLibrary("self") InteropLibrary interop, + @Cached TemporalNodes.DateTimeLikeCheckNode dateTimeLikeCheckNode, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + ZoneId zoneId = asZoneId(self, interop); + if (dateTime == PNone.NONE) { + return PNone.NONE; + } + if (!dateTimeLikeCheckNode.execute(inliningTarget, dateTime)) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); + } + LocalDateTime localDateTime = readDateTimeValueNode.execute(inliningTarget, dateTime).toLocalDateTime(); + ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId); + int dstSeconds = (int) zoneId.getRules().getDaylightSavings(zonedDateTime.toInstant()).getSeconds(); + return TimeDeltaNodes.NewNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PTimeDelta, 0, dstSeconds, 0, 0, 0, 0, 0); + } + } + + @Builtin(name = "tzname", minNumOfPositionalArgs = 1, parameterNames = {"$self", "dt"}) + @GenerateNodeFactory + abstract static class TzNameNode extends PythonBinaryBuiltinNode { + @Specialization(limit = "1") + @TruffleBoundary + static Object tzname(Object self, Object dateTime, + @Bind Node inliningTarget, + @CachedLibrary("self") InteropLibrary interop, + @Cached TemporalNodes.DateTimeLikeCheckNode dateTimeLikeCheckNode, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + ZoneId zoneId = asZoneId(self, interop); + if (dateTime == PNone.NONE) { + return zoneId.getRules().isFixedOffset() ? TruffleString.FromJavaStringNode.getUncached().execute(zoneId.getId(), TS_ENCODING) : PNone.NONE; + } + if (!dateTimeLikeCheckNode.execute(inliningTarget, dateTime)) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); + } + LocalDateTime localDateTime = readDateTimeValueNode.execute(inliningTarget, dateTime).toLocalDateTime(); + String name = DateTimeFormatter.ofPattern("z", Locale.ENGLISH).format(localDateTime.atZone(zoneId)); + return TruffleString.FromJavaStringNode.getUncached().execute(name, TS_ENCODING); + } + } + + @Builtin(name = "fromutc", minNumOfPositionalArgs = 1, parameterNames = {"$self", "dt"}) + @GenerateNodeFactory + abstract static class FromUtcNode extends PythonBinaryBuiltinNode { + @Specialization(limit = "1") + @TruffleBoundary + static Object fromutc(Object self, Object dateTime, + @Bind Node inliningTarget, + @CachedLibrary("self") InteropLibrary interop, + @Cached TemporalNodes.DateTimeLikeCheckNode dateTimeLikeCheckNode, + @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + if (!dateTimeLikeCheckNode.execute(inliningTarget, dateTime)) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); + } + PDateTime asDateTime = (PDateTime) DateTimeNodes.AsManagedDateTimeNode.executeUncached(dateTime); + if (asDateTime.tzInfo != self) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.ValueError, ErrorMessages.FROMUTC_DT_TZINFO_IS_NOT_SELF); + } + ZoneId zoneId = asZoneId(self, interop); + LocalDateTime utcDateTime = readDateTimeValueNode.execute(inliningTarget, dateTime).toLocalDateTime(); + ZonedDateTime zonedDateTime = utcDateTime.atOffset(java.time.ZoneOffset.UTC).atZoneSameInstant(zoneId); + return DateTimeNodes.NewUnsafeNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PDateTime, zonedDateTime.getYear(), zonedDateTime.getMonthValue(), + zonedDateTime.getDayOfMonth(), zonedDateTime.getHour(), zonedDateTime.getMinute(), zonedDateTime.getSecond(), zonedDateTime.getNano() / 1_000, self, 0); + } + } + + @Builtin(name = J___REDUCE__, minNumOfPositionalArgs = 1) + @GenerateNodeFactory + abstract static class ReduceNode extends PythonUnaryBuiltinNode { + @Specialization(limit = "1") + static Object reduce(Object self, + @CachedLibrary("self") InteropLibrary interop) { + return TruffleString.FromJavaStringNode.getUncached().execute(asZoneId(self, interop).getId(), TS_ENCODING); + } + } + + private static ZoneId asZoneId(Object self, InteropLibrary interop) { + try { + return interop.asTimeZone(self); + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } +} From a88184b1f8b9dd5e806ff664815d4d642da5a504 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 23 Mar 2026 20:50:40 +0100 Subject: [PATCH 07/30] [GR-62450] Add lib temporal check nodes --- .../graal/python/lib/PyDateCheckNode.java | 90 +++++++++++++++++++ .../graal/python/lib/PyDateTimeCheckNode.java | 90 +++++++++++++++++++ .../graal/python/lib/PyTZInfoCheckNode.java | 90 +++++++++++++++++++ .../graal/python/lib/PyTimeCheckNode.java | 90 +++++++++++++++++++ 4 files changed, 360 insertions(+) create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDateCheckNode.java create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDateTimeCheckNode.java create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTZInfoCheckNode.java create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTimeCheckNode.java diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDateCheckNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDateCheckNode.java new file mode 100644 index 0000000000..04943d47e1 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDateCheckNode.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 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 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * 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. + */ +package com.oracle.graal.python.lib; + +import com.oracle.graal.python.builtins.modules.datetime.PDate; +import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; +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.Specialization; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; + +/** Equivalent of CPython's {@code PyDate_Check}. */ +@GenerateUncached +@GenerateInline +@GenerateCached(false) +public abstract class PyDateCheckNode extends Node { + public static boolean executeUncached(Object object) { + return PyDateCheckNodeGen.getUncached().execute(null, object); + } + + public abstract boolean execute(Node inliningTarget, Object object); + + @Specialization + static boolean doManaged(@SuppressWarnings("unused") PDate value) { + return true; + } + + @Specialization + static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, + @Cached IsBuiltinObjectProfile profile) { + return profile.profileObject(inliningTarget, value, com.oracle.graal.python.builtins.PythonBuiltinClassType.PDate); + } + + @Specialization(guards = "isForeignObjectNode.execute(inliningTarget, value)", limit = "1") + static boolean doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + return interop.isDate(value); + } + + @Fallback + static boolean doOther(@SuppressWarnings("unused") Object value) { + return false; + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDateTimeCheckNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDateTimeCheckNode.java new file mode 100644 index 0000000000..1874d96dc0 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDateTimeCheckNode.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 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 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * 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. + */ +package com.oracle.graal.python.lib; + +import com.oracle.graal.python.builtins.modules.datetime.PDateTime; +import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; +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.Specialization; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; + +/** Equivalent of CPython's {@code PyDateTime_Check}. */ +@GenerateUncached +@GenerateInline +@GenerateCached(false) +public abstract class PyDateTimeCheckNode extends Node { + public static boolean executeUncached(Object object) { + return PyDateTimeCheckNodeGen.getUncached().execute(null, object); + } + + public abstract boolean execute(Node inliningTarget, Object object); + + @Specialization + static boolean doManaged(@SuppressWarnings("unused") PDateTime value) { + return true; + } + + @Specialization + static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, + @Cached IsBuiltinObjectProfile profile) { + return profile.profileObject(inliningTarget, value, com.oracle.graal.python.builtins.PythonBuiltinClassType.PDateTime); + } + + @Specialization(guards = "isForeignObjectNode.execute(inliningTarget, value)", limit = "1") + static boolean doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + return interop.isDate(value) && interop.isTime(value); + } + + @Fallback + static boolean doOther(@SuppressWarnings("unused") Object value) { + return false; + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTZInfoCheckNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTZInfoCheckNode.java new file mode 100644 index 0000000000..cc05e56e6a --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTZInfoCheckNode.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 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 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * 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. + */ +package com.oracle.graal.python.lib; + +import com.oracle.graal.python.builtins.modules.datetime.PTzInfo; +import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; +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.Specialization; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; + +/** Equivalent of CPython's {@code PyTZInfo_Check}. */ +@GenerateUncached +@GenerateInline +@GenerateCached(false) +public abstract class PyTZInfoCheckNode extends Node { + public static boolean executeUncached(Object object) { + return PyTZInfoCheckNodeGen.getUncached().execute(null, object); + } + + public abstract boolean execute(Node inliningTarget, Object object); + + @Specialization + static boolean doManaged(@SuppressWarnings("unused") PTzInfo value) { + return true; + } + + @Specialization + static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, + @Cached IsBuiltinObjectProfile profile) { + return profile.profileObject(inliningTarget, value, com.oracle.graal.python.builtins.PythonBuiltinClassType.PTzInfo); + } + + @Specialization(guards = "isForeignObjectNode.execute(inliningTarget, value)", limit = "1") + static boolean doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + return interop.isTimeZone(value) && !interop.isDate(value) && !interop.isTime(value); + } + + @Fallback + static boolean doOther(@SuppressWarnings("unused") Object value) { + return false; + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTimeCheckNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTimeCheckNode.java new file mode 100644 index 0000000000..a7502b49e8 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTimeCheckNode.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 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 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * 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. + */ +package com.oracle.graal.python.lib; + +import com.oracle.graal.python.builtins.modules.datetime.PTime; +import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; +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.Specialization; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; + +/** Equivalent of CPython's {@code PyTime_Check}. */ +@GenerateUncached +@GenerateInline +@GenerateCached(false) +public abstract class PyTimeCheckNode extends Node { + public static boolean executeUncached(Object object) { + return PyTimeCheckNodeGen.getUncached().execute(null, object); + } + + public abstract boolean execute(Node inliningTarget, Object object); + + @Specialization + static boolean doManaged(@SuppressWarnings("unused") PTime value) { + return true; + } + + @Specialization + static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, + @Cached IsBuiltinObjectProfile profile) { + return profile.profileObject(inliningTarget, value, com.oracle.graal.python.builtins.PythonBuiltinClassType.PTime); + } + + @Specialization(guards = "isForeignObjectNode.execute(inliningTarget, value)", limit = "1") + static boolean doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + return interop.isTime(value) && !interop.isDate(value); + } + + @Fallback + static boolean doOther(@SuppressWarnings("unused") Object value) { + return false; + } +} From b82def492e2e884f60216548f1374e39b9df455b Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 23 Mar 2026 20:59:20 +0100 Subject: [PATCH 08/30] [GR-62450] Move temporal check helpers to lib --- .../modules/datetime/DateBuiltins.java | 11 ++- .../builtins/modules/datetime/DateNodes.java | 30 +----- .../modules/datetime/DateTimeBuiltins.java | 28 +++--- .../modules/datetime/DateTimeNodes.java | 37 ++----- .../modules/datetime/TemporalNodes.java | 99 ++----------------- .../modules/datetime/TimeBuiltins.java | 6 +- .../builtins/modules/datetime/TimeNodes.java | 34 +------ .../modules/datetime/TimeZoneBuiltins.java | 5 +- .../modules/datetime/TzInfoBuiltins.java | 3 +- .../modules/datetime/TzInfoNodes.java | 91 ----------------- .../objects/foreign/ForeignDateBuiltins.java | 7 +- .../foreign/ForeignDateTimeBuiltins.java | 10 +- .../objects/foreign/ForeignTimeBuiltins.java | 3 +- .../foreign/ForeignTimeZoneBuiltins.java | 9 +- 14 files changed, 66 insertions(+), 307 deletions(-) delete mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoNodes.java diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java index 8de40f873c..8cbc635ca6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -96,6 +96,7 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.BinaryOpBuiltinNode; import com.oracle.graal.python.lib.PyFloatAsDoubleNode; import com.oracle.graal.python.lib.PyFloatCheckNode; +import com.oracle.graal.python.lib.PyDateCheckNode; import com.oracle.graal.python.lib.PyLongAsLongNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectHashNode; @@ -292,7 +293,7 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { @Specialization static Object richCmp(Object selfObj, Object otherObj, RichCmpOp op, @Bind Node inliningTarget, - @Cached DateNodes.DateCheckNode dateCheckNode, + @Cached PyDateCheckNode dateCheckNode, @Cached DateNodes.AsManagedDateNode asManagedDateNode) { if (dateCheckNode.execute(inliningTarget, selfObj) && dateCheckNode.execute(inliningTarget, otherObj)) { PDate self = asManagedDateNode.execute(inliningTarget, selfObj); @@ -341,7 +342,7 @@ static Object add(VirtualFrame frame, Object left, Object right, @TruffleBoundary private static Object addBoundary(Object left, Object right, Node inliningTarget) { Object dateObj, deltaObj; - if (DateNodes.DateCheckNode.executeUncached(left)) { + if (PyDateCheckNode.executeUncached(left)) { if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { dateObj = left; deltaObj = right; @@ -399,11 +400,11 @@ static Object sub(VirtualFrame frame, Object left, Object right, @TruffleBoundary private static Object subBoundary(Object left, Object right, Node inliningTarget) { - if (!DateNodes.DateCheckNode.executeUncached(left)) { + if (!PyDateCheckNode.executeUncached(left)) { return PNotImplemented.NOT_IMPLEMENTED; } PDate date = DateNodes.AsManagedDateNode.executeUncached(left); - if (DateNodes.DateCheckNode.executeUncached(right)) { + if (PyDateCheckNode.executeUncached(right)) { LocalDate from = LocalDate.of(1, 1, 1); LocalDate toSelf = LocalDate.of(date.year, date.month, date.day); long daysSelf = ChronoUnit.DAYS.between(from, toSelf) + 1; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateNodes.java index b13780c403..a9fc83a887 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateNodes.java @@ -57,12 +57,12 @@ import com.oracle.graal.python.builtins.objects.cext.structs.CFields; import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; import com.oracle.graal.python.builtins.objects.type.TypeNodes; +import com.oracle.graal.python.lib.PyDateCheckNode; import com.oracle.graal.python.lib.PyLongAsIntNode; 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.call.CallNode; -import com.oracle.graal.python.nodes.object.BuiltinClassProfiles; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Bind; @@ -203,7 +203,7 @@ static PDate asManaged(PDate obj) { @Specialization(guards = "checkNode.execute(inliningTarget, obj)", limit = "1") static PDate asManagedNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject obj, @Bind PythonLanguage language, - @SuppressWarnings("unused") @Cached DateCheckNode checkNode, + @SuppressWarnings("unused") @Cached PyDateCheckNode checkNode, @Cached CStructAccess.ReadByteNode readByteNode) { int year = getYear(obj, readByteNode); int month = getMonth(obj, readByteNode); @@ -233,30 +233,4 @@ static PDate error(Object obj, } } - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class DateCheckNode extends Node { - public abstract boolean execute(Node inliningTarget, Object obj); - - public static boolean executeUncached(Object obj) { - return DateNodesFactory.DateCheckNodeGen.getUncached().execute(null, obj); - } - - @Specialization - static boolean doManaged(@SuppressWarnings("unused") PDate value) { - return true; - } - - @Specialization - static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, - @Cached BuiltinClassProfiles.IsBuiltinObjectProfile profile) { - return profile.profileObject(inliningTarget, value, PythonBuiltinClassType.PDate); - } - - @Fallback - static boolean doOther(@SuppressWarnings("unused") Object value) { - return false; - } - } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java index fd60b3bab3..a81919ac71 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java @@ -117,9 +117,13 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; import com.oracle.graal.python.lib.PyFloatAsDoubleNode; import com.oracle.graal.python.lib.PyFloatCheckNode; +import com.oracle.graal.python.lib.PyDateCheckNode; +import com.oracle.graal.python.lib.PyDateTimeCheckNode; import com.oracle.graal.python.lib.PyLongAsLongNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectReprAsObjectNode; +import com.oracle.graal.python.lib.PyTZInfoCheckNode; +import com.oracle.graal.python.lib.PyTimeCheckNode; import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.lib.RichCmpOp; import com.oracle.graal.python.nodes.ErrorMessages; @@ -247,7 +251,7 @@ private static Object tryToDeserializeDateTime(Object cls, Object bytesObject, O } if (naiveBytesCheck(bytes)) { - if (tzInfo != PNone.NO_VALUE && !TzInfoNodes.TzInfoCheckNode.executeUncached(tzInfo)) { + if (tzInfo != PNone.NO_VALUE && !PyTZInfoCheckNode.executeUncached(tzInfo)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.BAD_TZINFO_STATE_ARG); } @@ -366,7 +370,7 @@ static Object nowInTimeZone(VirtualFrame frame, Object cls, Object tzInfo, @TruffleBoundary private static Object nowInTimeZoneBoundary(Object cls, Object tzInfo, Node inliningTarget) { - if (!TzInfoNodes.TzInfoCheckNode.executeUncached(tzInfo)) { + if (!PyTZInfoCheckNode.executeUncached(tzInfo)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.TZINFO_ARGUMENT_MUST_BE_NONE_OR_OF_A_TZINFO_SUBCLASS_NOT_TYPE_P, tzInfo); } // convert current time in UTC to the given time zone with tzinfo.fromutc() @@ -554,7 +558,7 @@ static Object richCmp(VirtualFrame frame, Object self, Object other, RichCmpOp o @TruffleBoundary private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp op, Node inliningTarget) { - if (!DateTimeNodes.DateTimeCheckNode.executeUncached(otherObj)) { + if (!PyDateTimeCheckNode.executeUncached(otherObj)) { /* * Prevent invocation of date_richcompare. We want to return NotImplemented here to * give the other object a chance. But since DateTime is a subclass of Date, if the @@ -562,7 +566,7 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp * alone, and we don't want that. So force unequal or uncomparable here in that * case. */ - if (DateNodes.DateCheckNode.executeUncached(otherObj)) { + if (PyDateCheckNode.executeUncached(otherObj)) { if (op == RichCmpOp.Py_EQ) { return false; } else if (op == RichCmpOp.Py_NE) { @@ -727,7 +731,7 @@ static Object add(VirtualFrame frame, Object left, Object right, @TruffleBoundary private static Object addBoundary(Object left, Object right, Node inliningTarget) { Object dateTimeObj, deltaObj; - if (DateTimeNodes.DateTimeCheckNode.executeUncached(left)) { + if (PyDateTimeCheckNode.executeUncached(left)) { if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { dateTimeObj = left; deltaObj = right; @@ -774,11 +778,11 @@ static Object sub(VirtualFrame frame, Object left, Object right, @TruffleBoundary private static Object subBoundary(Object left, Object right, Node inliningTarget) { - if (!DateTimeNodes.DateTimeCheckNode.executeUncached(left)) { + if (!PyDateTimeCheckNode.executeUncached(left)) { return PNotImplemented.NOT_IMPLEMENTED; } PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(left); - if (DateTimeNodes.DateTimeCheckNode.executeUncached(right)) { + if (PyDateTimeCheckNode.executeUncached(right)) { PDateTime other = DateTimeNodes.AsManagedDateTimeNode.executeUncached(right); final PTimeDelta selfOffset; @@ -974,7 +978,7 @@ private static Object fromTimestampBoundary(Object cls, Object timestampObject, if (tzInfoObject instanceof PNone) { tzInfo = null; } else { - if (!TzInfoNodes.TzInfoCheckNode.executeUncached(tzInfoObject)) { + if (!PyTZInfoCheckNode.executeUncached(tzInfoObject)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.TZINFO_ARGUMENT_MUST_BE_NONE_OR_OF_A_TZINFO_SUBCLASS_NOT_TYPE_P, tzInfoObject); } @@ -1148,7 +1152,7 @@ static Object combine(Object cls, Object dateObject, Object timeObject, Object t @Bind Node inliningTarget, @Cached PRaiseNode raiseNode, @Cached DateTimeNodes.SubclassNewNode newNode) { - if (!DateNodes.DateCheckNode.executeUncached(dateObject)) { + if (!PyDateCheckNode.executeUncached(dateObject)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.ARG_D_MUST_BE_S_NOT_P, @@ -1158,7 +1162,7 @@ static Object combine(Object cls, Object dateObject, Object timeObject, Object t dateObject); } - if (!TimeNodes.TimeCheckNode.executeUncached(timeObject)) { + if (!PyTimeCheckNode.executeUncached(timeObject)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.ARG_D_MUST_BE_S_NOT_P, @@ -1178,7 +1182,7 @@ static Object combine(Object cls, Object dateObject, Object timeObject, Object t tzInfo = tzInfoObject; } - if (tzInfo != null && !TzInfoNodes.TzInfoCheckNode.executeUncached(tzInfo)) { + if (tzInfo != null && !PyTZInfoCheckNode.executeUncached(tzInfo)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.TZINFO_ARGUMENT_MUST_BE_NONE_OR_OF_A_TZINFO_SUBCLASS_NOT_TYPE_P, @@ -2699,7 +2703,7 @@ private static Object inTimeZoneBoundary(Object selfObj, Object tzInfo, Node inl final Object targetTimeZone; if (tzInfo instanceof PNone) { targetTimeZone = getSystemTimeZoneAt(toLocalDateTime(self), self.fold, inliningTarget); - } else if (!TzInfoNodes.TzInfoCheckNode.executeUncached(tzInfo)) { + } else if (!PyTZInfoCheckNode.executeUncached(tzInfo)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.TZINFO_ARGUMENT_MUST_BE_NONE_OR_OF_A_TZINFO_SUBCLASS_NOT_TYPE_P, tzInfo); } else { targetTimeZone = tzInfo; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java index 56f41a70a1..7eac30a311 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java @@ -60,12 +60,13 @@ import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.type.TypeNodes; +import com.oracle.graal.python.lib.PyDateTimeCheckNode; +import com.oracle.graal.python.lib.PyTZInfoCheckNode; import com.oracle.graal.python.lib.PyLongAsIntNode; 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.call.CallNode; -import com.oracle.graal.python.nodes.object.BuiltinClassProfiles; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Bind; @@ -96,7 +97,7 @@ static Object newDateTime(VirtualFrame frame, Node inliningTarget, Object cls, O Object secondObject, Object microsecondObject, Object tzInfoObject, Object foldObject, @Cached PyLongAsIntNode asIntNode, @Cached DateTimeNodes.NewUnsafeNode newUnsafeNode, - @Cached TzInfoNodes.TzInfoCheckNode tzInfoCheckNode, + @Cached PyTZInfoCheckNode tzInfoCheckNode, @Cached PRaiseNode raiseNode) { int year = asIntNode.execute(frame, inliningTarget, yearObject); int month = asIntNode.execute(frame, inliningTarget, monthObject); @@ -174,7 +175,7 @@ private static void validateDateComponents(Node inliningTarget, PRaiseNode raise } private static void validateTimeComponents(Node inliningTarget, PRaiseNode raiseNode, long hour, long minute, long second, long microsecond, Object tzInfo, long fold, - TzInfoNodes.TzInfoCheckNode tzInfoCheckNode) { + PyTZInfoCheckNode tzInfoCheckNode) { if (hour < 0 || hour >= 24) { throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.HOUR_MUST_BE_IN); } @@ -221,7 +222,7 @@ public static NewUnsafeNode getUncached() { @Specialization static Object newDateTime(Node inliningTarget, Object cls, int year, int month, int day, int hour, int minute, int second, int microsecond, Object tzInfoObject, int fold, @Cached PRaiseNode raiseNode, - @Cached TzInfoNodes.TzInfoCheckNode tzInfoCheckNode, + @Cached PyTZInfoCheckNode tzInfoCheckNode, @Cached TypeNodes.GetInstanceShape getInstanceShape, @Cached TypeNodes.NeedsNativeAllocationNode needsNativeAllocationNode, @Cached CExtNodes.PCallCapiFunction callCapiFunction, @@ -311,7 +312,7 @@ static PDateTime asManaged(PDateTime obj) { @Specialization(guards = "checkNode.execute(inliningTarget, obj)", limit = "1") static PDateTime asManagedNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject obj, @Bind PythonLanguage language, - @SuppressWarnings("unused") @Cached DateTimeCheckNode checkNode, + @SuppressWarnings("unused") @Cached PyDateTimeCheckNode checkNode, @Cached CStructAccess.ReadByteNode readByteNode, @Cached CStructAccess.ReadObjectNode readObjectNode) { int year = getYear(obj, readByteNode); @@ -405,30 +406,4 @@ static Object getTzInfo(PythonAbstractNativeObject self, } } - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class DateTimeCheckNode extends Node { - public abstract boolean execute(Node inliningTarget, Object obj); - - public static boolean executeUncached(Object obj) { - return DateTimeNodesFactory.DateTimeCheckNodeGen.getUncached().execute(null, obj); - } - - @Specialization - static boolean doManaged(@SuppressWarnings("unused") PDateTime value) { - return true; - } - - @Specialization - static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, - @Cached BuiltinClassProfiles.IsBuiltinObjectProfile profile) { - return profile.profileObject(inliningTarget, value, PythonBuiltinClassType.PDateTime); - } - - @Fallback - static boolean doOther(@SuppressWarnings("unused") Object value) { - return false; - } - } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java index 2d1e1d2920..9faff371f6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java @@ -53,6 +53,9 @@ import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; +import com.oracle.graal.python.lib.PyDateCheckNode; +import com.oracle.graal.python.lib.PyDateTimeCheckNode; +import com.oracle.graal.python.lib.PyTimeCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.object.IsForeignObjectNode; @@ -182,96 +185,6 @@ public static Object toFixedOffsetTimeZone(ZoneId zoneId, Node inliningTarget) { return TimeZoneNodes.NewNode.getUncached().execute(inliningTarget, PythonContext.get(inliningTarget), PythonBuiltinClassType.PTimezone, delta, PNone.NO_VALUE); } - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class DateLikeCheckNode extends Node { - public abstract boolean execute(Node inliningTarget, Object obj); - - @Specialization - static boolean doManaged(@SuppressWarnings("unused") PDate value) { - return true; - } - - @Specialization - static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, - @Cached DateNodes.DateCheckNode checkNode) { - return checkNode.execute(inliningTarget, value); - } - - @Specialization(guards = "isForeignObjectNode.execute(inliningTarget, value)", limit = "1") - static boolean doForeign(Node inliningTarget, Object value, - @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, - @CachedLibrary("value") InteropLibrary interop) { - return interop.isDate(value); - } - - @Fallback - static boolean doOther(@SuppressWarnings("unused") Object value) { - return false; - } - } - - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class TimeLikeCheckNode extends Node { - public abstract boolean execute(Node inliningTarget, Object obj); - - @Specialization - static boolean doManaged(@SuppressWarnings("unused") PTime value) { - return true; - } - - @Specialization - static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, - @Cached TimeNodes.TimeCheckNode checkNode) { - return checkNode.execute(inliningTarget, value); - } - - @Specialization(guards = "isForeignObjectNode.execute(inliningTarget, value)", limit = "1") - static boolean doForeign(Node inliningTarget, Object value, - @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, - @CachedLibrary("value") InteropLibrary interop) { - return interop.isTime(value); - } - - @Fallback - static boolean doOther(@SuppressWarnings("unused") Object value) { - return false; - } - } - - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class DateTimeLikeCheckNode extends Node { - public abstract boolean execute(Node inliningTarget, Object obj); - - @Specialization - static boolean doManaged(@SuppressWarnings("unused") PDateTime value) { - return true; - } - - @Specialization - static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, - @Cached DateTimeNodes.DateTimeCheckNode checkNode) { - return checkNode.execute(inliningTarget, value); - } - - @Specialization(guards = "isForeignObjectNode.execute(inliningTarget, value)", limit = "1") - static boolean doForeign(Node inliningTarget, Object value, - @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, - @CachedLibrary("value") InteropLibrary interop) { - return interop.isDate(value) && interop.isTime(value); - } - - @Fallback - static boolean doOther(@SuppressWarnings("unused") Object value) { - return false; - } - } - @GenerateUncached @GenerateInline @GenerateCached(false) @@ -285,7 +198,7 @@ static DateValue doManaged(PDate value) { @Specialization(guards = "checkNode.execute(inliningTarget, value)", limit = "1") static DateValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject value, - @SuppressWarnings("unused") @Cached DateNodes.DateCheckNode checkNode, + @SuppressWarnings("unused") @Cached PyDateCheckNode checkNode, @Cached CStructAccess.ReadByteNode readNode) { return new DateValue(DateNodes.AsManagedDateNode.getYear(value, readNode), DateNodes.AsManagedDateNode.getMonth(value, readNode), DateNodes.AsManagedDateNode.getDay(value, readNode)); } @@ -322,7 +235,7 @@ static TimeValue doManaged(PTime value) { @Specialization(guards = "checkNode.execute(inliningTarget, value)", limit = "1") static TimeValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject value, - @SuppressWarnings("unused") @Cached TimeNodes.TimeCheckNode checkNode, + @SuppressWarnings("unused") @Cached PyTimeCheckNode checkNode, @Cached CStructAccess.ReadByteNode readByteNode, @Cached CStructAccess.ReadObjectNode readObjectNode) { return new TimeValue(TimeNodes.AsManagedTimeNode.getHour(value, readByteNode), TimeNodes.AsManagedTimeNode.getMinute(value, readByteNode), @@ -363,7 +276,7 @@ static DateTimeValue doManaged(PDateTime value) { @Specialization(guards = "checkNode.execute(inliningTarget, value)", limit = "1") static DateTimeValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject value, - @SuppressWarnings("unused") @Cached DateTimeNodes.DateTimeCheckNode checkNode, + @SuppressWarnings("unused") @Cached PyDateTimeCheckNode checkNode, @Cached CStructAccess.ReadByteNode readByteNode, @Cached CStructAccess.ReadObjectNode readObjectNode) { return new DateTimeValue(DateTimeNodes.AsManagedDateTimeNode.getYear(value, readByteNode), DateTimeNodes.AsManagedDateTimeNode.getMonth(value, readByteNode), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java index d306a9552c..a677b1aef7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java @@ -88,6 +88,8 @@ import com.oracle.graal.python.lib.PyObjectReprAsObjectNode; import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; import com.oracle.graal.python.lib.PyUnicodeCheckNode; +import com.oracle.graal.python.lib.PyTZInfoCheckNode; +import com.oracle.graal.python.lib.PyTimeCheckNode; import com.oracle.graal.python.lib.RichCmpOp; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; @@ -200,7 +202,7 @@ private static Object tryToDeserializeTime(Object cls, Object bytesObject, Objec if (naiveBytesCheck(bytes)) { // slightly different error message - if (tzInfo != PNone.NO_VALUE && !TzInfoNodes.TzInfoCheckNode.executeUncached(tzInfo)) { + if (tzInfo != PNone.NO_VALUE && !PyTZInfoCheckNode.executeUncached(tzInfo)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.BAD_TZINFO_STATE_ARG); } @@ -395,7 +397,7 @@ static Object richCmp(VirtualFrame frame, Object self, Object other, RichCmpOp o @TruffleBoundary private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp op, Node inliningTarget) { - if (!TimeNodes.TimeCheckNode.executeUncached(selfObj) || !TimeNodes.TimeCheckNode.executeUncached(otherObj)) { + if (!PyTimeCheckNode.executeUncached(selfObj) || !PyTimeCheckNode.executeUncached(otherObj)) { return PNotImplemented.NOT_IMPLEMENTED; } PTime self = TimeNodes.AsManagedTimeNode.executeUncached(selfObj); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java index 4df292fbba..7295115bea 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java @@ -57,12 +57,13 @@ import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetInstanceShape; +import com.oracle.graal.python.lib.PyTZInfoCheckNode; +import com.oracle.graal.python.lib.PyTimeCheckNode; import com.oracle.graal.python.lib.PyLongAsIntNode; 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.call.CallNode; -import com.oracle.graal.python.nodes.object.BuiltinClassProfiles; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Bind; @@ -185,7 +186,7 @@ private static void validateTimeComponents(Node inliningTarget, long hour, long throw PRaiseNode.raiseStatic(inliningTarget, ValueError, ErrorMessages.MICROSECOND_MUST_BE_IN); } - if (tzInfo != null && !TzInfoNodes.TzInfoCheckNode.executeUncached(tzInfo)) { + if (tzInfo != null && !PyTZInfoCheckNode.executeUncached(tzInfo)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.TZINFO_ARGUMENT_MUST_BE_NONE_OR_OF_A_TZINFO_SUBCLASS_NOT_TYPE_P, tzInfo); } @@ -248,7 +249,7 @@ static PTime doPTime(PTime value) { @Specialization(guards = "checkNode.execute(inliningTarget, nativeTime)", limit = "1") static PTime doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject nativeTime, @Bind PythonLanguage language, - @SuppressWarnings("unused") @Cached TimeCheckNode checkNode, + @SuppressWarnings("unused") @Cached PyTimeCheckNode checkNode, @Cached CStructAccess.ReadByteNode readByteNode, @Cached CStructAccess.ReadObjectNode readObjectNode) { int hour = getHour(nativeTime, readByteNode); @@ -304,33 +305,6 @@ static PTime error(Object obj, } } - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class TimeCheckNode extends Node { - public abstract boolean execute(Node inliningTarget, Object obj); - - public static boolean executeUncached(Object obj) { - return TimeNodesFactory.TimeCheckNodeGen.getUncached().execute(null, obj); - } - - @Specialization - static boolean doManaged(@SuppressWarnings("unused") PTime value) { - return true; - } - - @Specialization - static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, - @Cached BuiltinClassProfiles.IsBuiltinObjectProfile profile) { - return profile.profileObject(inliningTarget, value, PythonBuiltinClassType.PTime); - } - - @Fallback - static boolean doOther(@SuppressWarnings("unused") Object value) { - return false; - } - } - @GenerateInline @GenerateCached(false) @GenerateUncached diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneBuiltins.java index d0ff0aa76d..8a4e05cef2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -71,6 +71,7 @@ import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; +import com.oracle.graal.python.lib.PyDateTimeCheckNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectHashNode; import com.oracle.graal.python.lib.PyObjectReprAsObjectNode; @@ -350,7 +351,7 @@ public abstract static class FromUtcNode extends PythonBinaryBuiltinNode { @Specialization static Object fromUtc(PTimeZone self, Object dateTime, @Bind Node inliningTarget, - @Cached DateTimeNodes.DateTimeCheckNode dateTimeCheckNode, + @Cached PyDateTimeCheckNode dateTimeCheckNode, @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached PRaiseNode raiseNode, @Cached DateTimeNodes.SubclassNewNode dateTimeSubclassNewNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoBuiltins.java index 08ebb60de0..b4cb72b416 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoBuiltins.java @@ -66,6 +66,7 @@ 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.TypeNodes.GetInstanceShape; +import com.oracle.graal.python.lib.PyDateTimeCheckNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectGetStateNode; import com.oracle.graal.python.lib.PyObjectLookupAttr; @@ -174,7 +175,7 @@ public abstract static class FromUtcNode extends PythonBinaryBuiltinNode { @Specialization static Object fromUtc(VirtualFrame frame, Object self, Object dateTime, @Bind Node inliningTarget, - @Cached DateTimeNodes.DateTimeCheckNode dateTimeCheckNode, + @Cached PyDateTimeCheckNode dateTimeCheckNode, @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoNodes.java deleted file mode 100644 index 2549d89bae..0000000000 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoNodes.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 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 - * - * Subject to the condition set forth below, permission is hereby granted to any - * person obtaining a copy of this software, associated documentation and/or - * data (collectively the "Software"), free of charge and under any and all - * copyright rights in the Software, and any and all patent rights owned or - * freely licensable by each licensor hereunder covering either (i) the - * unmodified Software as contributed to or provided by such licensor, or (ii) - * the Larger Works (as defined below), to deal in both - * - * (a) the Software, and - * - * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if - * one is included with the Software each a "Larger Work" to which the Software - * is contributed by such licensors), - * - * without restriction, including without limitation the rights to copy, create - * derivative works of, display, perform, and distribute the Software and make, - * use, sell, offer for sale, import, export, have made, and have sold the - * Software and the Larger Work(s), and to sublicense the foregoing rights on - * either these or other terms. - * - * This license is subject to the following condition: - * - * The above copyright notice and either this complete permission notice or at a - * minimum a reference to the UPL must be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * 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. - */ -package com.oracle.graal.python.builtins.modules.datetime; - -import com.oracle.graal.python.builtins.PythonBuiltinClassType; -import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; -import com.oracle.graal.python.nodes.object.BuiltinClassProfiles; -import com.oracle.graal.python.nodes.object.IsForeignObjectNode; -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.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.library.CachedLibrary; -import com.oracle.truffle.api.nodes.Node; - -public class TzInfoNodes { - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class TzInfoCheckNode extends Node { - public abstract boolean execute(Node inliningTarget, Object obj); - - public static boolean executeUncached(Object obj) { - return TzInfoNodesFactory.TzInfoCheckNodeGen.getUncached().execute(null, obj); - } - - @Specialization - static boolean doManaged(@SuppressWarnings("unused") PTzInfo value) { - return true; - } - - @Specialization - static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, - @Cached BuiltinClassProfiles.IsBuiltinObjectProfile profile) { - return profile.profileObject(inliningTarget, value, PythonBuiltinClassType.PTzInfo); - } - - @Specialization(guards = "isForeignObjectNode.execute(inliningTarget, value)", limit = "1") - static boolean doForeign(Node inliningTarget, Object value, - @Cached IsForeignObjectNode isForeignObjectNode, - @CachedLibrary("value") InteropLibrary interop) { - return interop.isTimeZone(value); - } - - @Fallback - static boolean doOther(@SuppressWarnings("unused") Object value) { - return false; - } - } -} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java index 7e9c59ad88..ac0a2c27ed 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java @@ -71,6 +71,7 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.BinaryOpBuiltinNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun.HashBuiltinNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; +import com.oracle.graal.python.lib.PyDateCheckNode; import com.oracle.graal.python.lib.PyLongAsLongNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectHashNode; @@ -139,7 +140,7 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { @Specialization static Object richCmp(Object selfObj, Object otherObj, RichCmpOp op, @Bind Node inliningTarget, - @Cached TemporalNodes.DateLikeCheckNode dateLikeCheckNode, + @Cached PyDateCheckNode dateLikeCheckNode, @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { if (!dateLikeCheckNode.execute(inliningTarget, otherObj)) { return PNotImplemented.NOT_IMPLEMENTED; @@ -169,7 +170,7 @@ abstract static class AddNode extends BinaryOpBuiltinNode { @TruffleBoundary static Object add(Object left, Object right, @Bind Node inliningTarget, - @Cached TemporalNodes.DateLikeCheckNode dateLikeCheckNode, + @Cached PyDateCheckNode dateLikeCheckNode, @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { Object dateObj; Object deltaObj; @@ -200,7 +201,7 @@ abstract static class SubNode extends BinaryOpBuiltinNode { @TruffleBoundary static Object sub(Object left, Object right, @Bind Node inliningTarget, - @Cached TemporalNodes.DateLikeCheckNode dateLikeCheckNode, + @Cached PyDateCheckNode dateLikeCheckNode, @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { if (!dateLikeCheckNode.execute(inliningTarget, left)) { return PNotImplemented.NOT_IMPLEMENTED; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java index 810b9b6dc4..39e927dd3e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java @@ -68,6 +68,8 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.BinaryOpBuiltinNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun.HashBuiltinNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; +import com.oracle.graal.python.lib.PyDateCheckNode; +import com.oracle.graal.python.lib.PyDateTimeCheckNode; import com.oracle.graal.python.lib.PyLongAsLongNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectHashNode; @@ -146,8 +148,8 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { @Specialization static Object richCmp(VirtualFrame frame, Object selfObj, Object otherObj, RichCmpOp op, @Bind Node inliningTarget, - @Cached TemporalNodes.DateTimeLikeCheckNode dateTimeLikeCheckNode, - @Cached TemporalNodes.DateLikeCheckNode dateLikeCheckNode, + @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, + @Cached PyDateCheckNode dateLikeCheckNode, @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode) { @@ -209,7 +211,7 @@ abstract static class AddNode extends BinaryOpBuiltinNode { @TruffleBoundary static Object add(Object left, Object right, @Bind Node inliningTarget, - @Cached TemporalNodes.DateTimeLikeCheckNode dateTimeLikeCheckNode, + @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { Object dateTimeObj; Object deltaObj; @@ -235,7 +237,7 @@ abstract static class SubNode extends BinaryOpBuiltinNode { @Specialization static Object sub(VirtualFrame frame, Object left, Object right, @Bind Node inliningTarget, - @Cached TemporalNodes.DateTimeLikeCheckNode dateTimeLikeCheckNode, + @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java index 687ff8c8f6..ba53d1e859 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java @@ -69,6 +69,7 @@ import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectHashNode; import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; +import com.oracle.graal.python.lib.PyTimeCheckNode; import com.oracle.graal.python.lib.RichCmpOp; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; @@ -138,7 +139,7 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { @Specialization static Object richCmp(VirtualFrame frame, Object selfObj, Object otherObj, RichCmpOp op, @Bind Node inliningTarget, - @Cached TemporalNodes.TimeLikeCheckNode timeLikeCheckNode, + @Cached PyTimeCheckNode timeLikeCheckNode, @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java index a6d9b2db20..4d7b37f28c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java @@ -63,6 +63,7 @@ import com.oracle.graal.python.builtins.modules.datetime.TimeDeltaNodes; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.type.TpSlots; +import com.oracle.graal.python.lib.PyDateTimeCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; @@ -124,7 +125,7 @@ abstract static class UtcOffsetNode extends PythonBinaryBuiltinNode { static Object utcoffset(Object self, Object dateTime, @Bind Node inliningTarget, @CachedLibrary("self") InteropLibrary interop, - @Cached TemporalNodes.DateTimeLikeCheckNode dateTimeLikeCheckNode, + @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { ZoneId zoneId = asZoneId(self, interop); if (dateTime == PNone.NONE) { @@ -151,7 +152,7 @@ abstract static class DstNode extends PythonBinaryBuiltinNode { static Object dst(Object self, Object dateTime, @Bind Node inliningTarget, @CachedLibrary("self") InteropLibrary interop, - @Cached TemporalNodes.DateTimeLikeCheckNode dateTimeLikeCheckNode, + @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { ZoneId zoneId = asZoneId(self, interop); if (dateTime == PNone.NONE) { @@ -175,7 +176,7 @@ abstract static class TzNameNode extends PythonBinaryBuiltinNode { static Object tzname(Object self, Object dateTime, @Bind Node inliningTarget, @CachedLibrary("self") InteropLibrary interop, - @Cached TemporalNodes.DateTimeLikeCheckNode dateTimeLikeCheckNode, + @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { ZoneId zoneId = asZoneId(self, interop); if (dateTime == PNone.NONE) { @@ -198,7 +199,7 @@ abstract static class FromUtcNode extends PythonBinaryBuiltinNode { static Object fromutc(Object self, Object dateTime, @Bind Node inliningTarget, @CachedLibrary("self") InteropLibrary interop, - @Cached TemporalNodes.DateTimeLikeCheckNode dateTimeLikeCheckNode, + @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { if (!dateTimeLikeCheckNode.execute(inliningTarget, dateTime)) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); From fcd20b12fe12b0abf3949af2d58d013a0b07e47e Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Wed, 25 Mar 2026 17:02:21 +0100 Subject: [PATCH 09/30] Fix compilation --- .../graal/python/builtins/PythonBuiltinClassType.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java index 9068dd8fc7..d3bc0e6935 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java @@ -846,10 +846,6 @@ It can be called either on the class (e.g. C.f()) or on an instance ForeignExecutable("ForeignExecutable", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignExecutableBuiltins.SLOTS)), ForeignInstantiable("ForeignInstantiable", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().slots(ForeignInstantiableBuiltins.SLOTS)), ForeignIterable("ForeignIterable", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignIterableBuiltins.SLOTS)), - ForeignDate("ForeignDate", PDate, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignDateBuiltins.SLOTS)), - ForeignTime("ForeignTime", PTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignTimeBuiltins.SLOTS)), - ForeignDateTime("ForeignDateTime", PDateTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignDateTimeBuiltins.SLOTS)), - ForeignTimeZone("ForeignTimeZone", PTzInfo, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignTimeZoneBuiltins.SLOTS)), // bz2 BZ2Compressor("BZ2Compressor", PythonObject, newBuilder().publishInModule("_bz2").basetype().slots(BZ2CompressorBuiltins.SLOTS)), @@ -1238,6 +1234,12 @@ def takewhile(predicate, iterable): PTzInfo, newBuilder().moduleName("datetime").publishInModule("_datetime").slots(TimeZoneBuiltins.SLOTS).doc("Fixed offset from UTC implementation of tzinfo.")), + // foreign datetime + ForeignDate("ForeignDate", PDate, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignDateBuiltins.SLOTS)), + ForeignTime("ForeignTime", PTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignTimeBuiltins.SLOTS)), + ForeignDateTime("ForeignDateTime", PDateTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignDateTimeBuiltins.SLOTS)), + ForeignTimeZone("ForeignTimeZone", PTzInfo, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignTimeZoneBuiltins.SLOTS)), + // re PPattern( "Pattern", From 7897e0db37735ba631bccd31c389f4764d219244 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Wed, 25 Mar 2026 17:02:00 +0100 Subject: [PATCH 10/30] Get rid of AsManagedTimeNode for intermediate results --- .../modules/datetime/DateTimeBuiltins.java | 6 ++- .../modules/datetime/TemporalNodes.java | 18 ++++++-- .../modules/datetime/TimeBuiltins.java | 38 ++++++++-------- .../builtins/modules/datetime/TimeNodes.java | 43 +------------------ 4 files changed, 40 insertions(+), 65 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java index a81919ac71..391442b2fc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java @@ -100,6 +100,8 @@ import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.TimeModuleBuiltins; import com.oracle.graal.python.builtins.modules.WarningsModuleBuiltins.WarnNode; +import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.DateValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.TimeValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.bytes.BytesNodes; @@ -1172,8 +1174,8 @@ static Object combine(Object cls, Object dateObject, Object timeObject, Object t timeObject); } - PDate date = DateNodes.AsManagedDateNode.executeUncached(dateObject); - PTime time = TimeNodes.AsManagedTimeNode.executeUncached(timeObject); + DateValue date = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, dateObject); + TimeValue time = TemporalNodes.ReadTimeValueNode.executeUncached(inliningTarget, timeObject); final Object tzInfo; if (tzInfoObject instanceof PNone) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java index 9faff371f6..e1a911437c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java @@ -187,10 +187,14 @@ public static Object toFixedOffsetTimeZone(ZoneId zoneId, Node inliningTarget) { @GenerateUncached @GenerateInline - @GenerateCached(false) + @GenerateCached(alwaysInlineCached = true) public abstract static class ReadDateValueNode extends Node { public abstract DateValue execute(Node inliningTarget, Object obj); + public static DateValue executeUncached(Node inliningTarget, Object obj) { + return TemporalNodesFactory.ReadDateValueNodeGen.executeUncached(inliningTarget, obj); + } + @Specialization static DateValue doManaged(PDate value) { return DateValue.of(value); @@ -224,10 +228,14 @@ static DateValue error(Object obj, @GenerateUncached @GenerateInline - @GenerateCached(false) + @GenerateCached(alwaysInlineCached = true) public abstract static class ReadTimeValueNode extends Node { public abstract TimeValue execute(Node inliningTarget, Object obj); + public static TimeValue executeUncached(Node inliningTarget, Object obj) { + return TemporalNodesFactory.ReadTimeValueNodeGen.executeUncached(inliningTarget, obj); + } + @Specialization static TimeValue doManaged(PTime value) { return TimeValue.of(value); @@ -265,10 +273,14 @@ static TimeValue error(Object obj, @GenerateUncached @GenerateInline - @GenerateCached(false) + @GenerateCached(alwaysInlineCached = true) public abstract static class ReadDateTimeValueNode extends Node { public abstract DateTimeValue execute(Node inliningTarget, Object obj); + public static DateTimeValue executeUncached(Node inliningTarget, Object obj) { + return TemporalNodesFactory.ReadDateTimeValueNodeGen.executeUncached(inliningTarget, obj); + } + @Specialization static DateTimeValue doManaged(PDateTime value) { return DateTimeValue.of(value); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java index a677b1aef7..ca8739e50e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java @@ -68,6 +68,7 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.TimeModuleBuiltins; +import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.TimeValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.bytes.BytesNodes; @@ -257,10 +258,11 @@ public abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization static TruffleString repr(VirtualFrame frame, Object self, + @Bind Node inliningTarget, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return reprBoundary(self); + return reprBoundary(inliningTarget, self); } finally { // A Python method call (using PyObjectReprAsObjectNode) should be // connected to a current node. @@ -269,8 +271,8 @@ static TruffleString repr(VirtualFrame frame, Object self, } @TruffleBoundary - private static TruffleString reprBoundary(Object selfObj) { - PTime self = TimeNodes.AsManagedTimeNode.executeUncached(selfObj); + private static TruffleString reprBoundary(Node inliningTarget, Object selfObj) { + TimeValue self = TemporalNodes.ReadTimeValueNode.executeUncached(null, selfObj); var builder = new StringBuilder(); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); @@ -308,9 +310,9 @@ public abstract static class ReduceNode extends PythonUnaryBuiltinNode { static Object reduce(Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TimeNodes.AsManagedTimeNode asManagedTimeNode, + @Cached TemporalNodes.ReadTimeValueNode asManagedTimeNode, @Cached GetClassNode getClassNode) { - PTime time = asManagedTimeNode.execute(inliningTarget, self); + TimeValue time = asManagedTimeNode.execute(inliningTarget, self); // Time is serialized in the following format: // ( // bytes(hours, minutes, seconds, microseconds 1st byte, microseconds 2nd byte, @@ -348,9 +350,9 @@ public abstract static class ReduceExNode extends PythonBinaryBuiltinNode { static Object reduceEx(Object self, int protocol, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TimeNodes.AsManagedTimeNode asManagedTimeNode, + @Cached TemporalNodes.ReadTimeValueNode asManagedTimeNode, @Cached GetClassNode getClassNode) { - PTime time = asManagedTimeNode.execute(inliningTarget, self); + TimeValue time = asManagedTimeNode.execute(inliningTarget, self); byte[] baseStateBytes = new byte[6]; baseStateBytes[0] = (byte) time.hour; baseStateBytes[1] = (byte) time.minute; @@ -400,8 +402,8 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp if (!PyTimeCheckNode.executeUncached(selfObj) || !PyTimeCheckNode.executeUncached(otherObj)) { return PNotImplemented.NOT_IMPLEMENTED; } - PTime self = TimeNodes.AsManagedTimeNode.executeUncached(selfObj); - PTime other = TimeNodes.AsManagedTimeNode.executeUncached(otherObj); + TimeValue self = TemporalNodes.ReadTimeValueNode.executeUncached(inliningTarget, selfObj); + TimeValue other = TemporalNodes.ReadTimeValueNode.executeUncached(inliningTarget, otherObj); // either naive times (without timezone) or timezones are exactly the same objects if (self.tzInfo == other.tzInfo) { return compareTimeComponents(self, other, op); @@ -434,7 +436,7 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp return op.compareResultToBool(result); } - private static boolean compareTimeComponents(PTime self, PTime other, RichCmpOp op) { + private static boolean compareTimeComponents(TimeValue self, TimeValue other, RichCmpOp op) { // compare only hours, minutes, ... and ignore fold int[] selfComponents = new int[]{self.hour, self.minute, self.second, self.microsecond}; int[] otherComponents = new int[]{other.hour, other.minute, other.second, other.microsecond}; @@ -453,11 +455,11 @@ abstract static class HashNode extends HashBuiltinNode { static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TimeNodes.AsManagedTimeNode asManagedTimeNode, + @Cached TemporalNodes.ReadTimeValueNode asManagedTimeNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode, @Cached PyObjectHashNode hashNode) { - PTime self = asManagedTimeNode.execute(inliningTarget, selfObj); + TimeValue self = asManagedTimeNode.execute(inliningTarget, selfObj); PTimeDelta utcOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, PNone.NONE, frame, inliningTarget, callMethodObjArgs, raiseNode); if (utcOffset == null) { @@ -948,11 +950,11 @@ public abstract static class ReplaceNode extends PythonBuiltinNode { @Specialization static Object replace(VirtualFrame frame, Object self, Object hourObject, Object minuteObject, Object secondObject, Object microsecondObject, Object tzInfoObject, Object foldObject, @Bind Node inliningTarget, - @Cached TimeNodes.AsManagedTimeNode asManagedTimeNode, + @Cached TemporalNodes.ReadTimeValueNode asManagedTimeNode, @Cached PyLongAsLongNode asLongNode, @Cached GetClassNode getClassNode, @Cached TimeNodes.NewNode newTimeNode) { - PTime time = asManagedTimeNode.execute(inliningTarget, self); + TimeValue time = asManagedTimeNode.execute(inliningTarget, self); final long hour, minute, second, microsecond, fold; final Object tzInfo; @@ -1020,7 +1022,7 @@ static TruffleString isoFormat(VirtualFrame frame, Object self, Object timespecO @TruffleBoundary private static TruffleString isoFormatBoundary(Object selfObj, Object timespecObject, Node inliningTarget) { - PTime self = TimeNodes.AsManagedTimeNode.executeUncached(selfObj); + TimeValue self = TemporalNodes.ReadTimeValueNode.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); final String timespec; @@ -1186,7 +1188,7 @@ static TruffleString strftime(VirtualFrame frame, Object self, TruffleString for @TruffleBoundary private static TruffleString strftimeBoundary(Object selfObj, TruffleString format, Node inliningTarget) { - PTime self = TimeNodes.AsManagedTimeNode.executeUncached(selfObj); + TimeValue self = TemporalNodes.ReadTimeValueNode.executeUncached(inliningTarget, selfObj); // Reuse time.strftime(format, time_tuple) method. int[] timeTuple = new int[]{1900, 1, 1, self.hour, self.minute, self.second, 0, 1, -1}; String formatPreprocessed = preprocessFormat(format, self, inliningTarget); @@ -1197,7 +1199,7 @@ private static TruffleString strftimeBoundary(Object selfObj, TruffleString form // %Z so handle them here. // CPython: wrap_strftime() @TruffleBoundary - private static String preprocessFormat(TruffleString tsformat, PTime self, Node inliningTarget) { + private static String preprocessFormat(TruffleString tsformat, TimeValue self, Node inliningTarget) { String format = tsformat.toString(); StringBuilder builder = new StringBuilder(); int i = 0; @@ -1297,7 +1299,7 @@ static Object format(VirtualFrame frame, Object self, TruffleString format, } } - private static long toMicroseconds(PTime self, PTimeDelta utcOffset) { + private static long toMicroseconds(TimeValue self, PTimeDelta utcOffset) { return (long) self.hour * 3600 * 1_000_000 + (long) self.minute * 60 * 1_000_000 + (long) self.second * 1_000_000 + diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java index 7295115bea..6b8b822dc3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java @@ -44,7 +44,6 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; -import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; @@ -58,7 +57,6 @@ import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetInstanceShape; import com.oracle.graal.python.lib.PyTZInfoCheckNode; -import com.oracle.graal.python.lib.PyTimeCheckNode; import com.oracle.graal.python.lib.PyLongAsIntNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PGuards; @@ -66,7 +64,6 @@ import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.runtime.PythonContext; 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.Fallback; import com.oracle.truffle.api.dsl.GenerateCached; @@ -231,39 +228,7 @@ static boolean isBuiltinClass(Object cls) { } } - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class AsManagedTimeNode extends Node { - public abstract PTime execute(Node inliningTarget, Object obj); - - public static PTime executeUncached(Object obj) { - return TimeNodesFactory.AsManagedTimeNodeGen.getUncached().execute(null, obj); - } - - @Specialization - static PTime doPTime(PTime value) { - return value; - } - - @Specialization(guards = "checkNode.execute(inliningTarget, nativeTime)", limit = "1") - static PTime doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject nativeTime, - @Bind PythonLanguage language, - @SuppressWarnings("unused") @Cached PyTimeCheckNode checkNode, - @Cached CStructAccess.ReadByteNode readByteNode, - @Cached CStructAccess.ReadObjectNode readObjectNode) { - int hour = getHour(nativeTime, readByteNode); - int minute = getMinute(nativeTime, readByteNode); - int second = getSecond(nativeTime, readByteNode); - int microsecond = getMicrosecond(nativeTime, readByteNode); - - Object tzinfo = getTzInfo(nativeTime, readByteNode, readObjectNode); - int fold = getFold(nativeTime, readByteNode); - - PythonBuiltinClassType cls = PythonBuiltinClassType.PTime; - return new PTime(cls, cls.getInstanceShape(language), hour, minute, second, microsecond, tzinfo, fold); - } - + public static final class AsManagedTimeNode { static int getHour(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { return readNode.readFromObjUnsigned(self, CFields.PyDateTime_Time__data, 0); } @@ -297,12 +262,6 @@ static Object getTzInfo(PythonAbstractNativeObject nativeTime, CStructAccess.Rea static int getFold(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { return readNode.readFromObjUnsigned(self, CFields.PyDateTime_Time__fold); } - - @Fallback - static PTime error(Object obj, - @Bind Node inliningTarget) { - throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_EXPECTED_GOT_P, "time", obj); - } } @GenerateInline From fcc054479ab1d0423ea8f5bdb5737db40692bc4e Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Wed, 25 Mar 2026 22:14:37 +0100 Subject: [PATCH 11/30] Get rid of AsManagedDateNode for intermediate results --- .../modules/datetime/DateBuiltins.java | 82 +++++++++++-------- .../builtins/modules/datetime/DateNodes.java | 38 +-------- .../modules/datetime/TemporalNodes.java | 73 +++++++++++++++++ 3 files changed, 121 insertions(+), 72 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java index 8cbc635ca6..d85ef9857e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java @@ -81,6 +81,8 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.TimeModuleBuiltins; +import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.DateValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.TimeDeltaValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.bytes.BytesNodes; @@ -246,8 +248,9 @@ public abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - static TruffleString repr(Object self) { - PDate date = DateNodes.AsManagedDateNode.executeUncached(self); + static TruffleString repr(Object self, + @Bind Node inliningTarget) { + DateValue date = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, self); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(self)); var string = String.format("%s(%d, %d, %d)", typeName, date.year, date.month, date.day); return TruffleString.FromJavaStringNode.getUncached().execute(string, TS_ENCODING); @@ -260,8 +263,9 @@ public abstract static class StrNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - static TruffleString str(Object self) { - PDate date = DateNodes.AsManagedDateNode.executeUncached(self); + static TruffleString str(Object self, + @Bind Node inliningTarget) { + DateValue date = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, self); var string = String.format("%04d-%02d-%02d", date.year, date.month, date.day); return TruffleString.FromJavaStringNode.getUncached().execute(string, TS_ENCODING); } @@ -275,9 +279,9 @@ public abstract static class ReduceNode extends PythonUnaryBuiltinNode { static Object reduce(Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached DateNodes.AsManagedDateNode asManagedDateNode, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode, @Cached GetClassNode getClassNode) { - PDate date = asManagedDateNode.execute(inliningTarget, self); + DateValue date = readDateValueNode.execute(inliningTarget, self); byte[] bytes = new byte[]{(byte) (date.year / 256), (byte) (date.year % 256), (byte) date.month, (byte) date.day}; PBytes string = PFactory.createBytes(language, bytes); PTuple arguments = PFactory.createTuple(language, new Object[]{string}); @@ -294,10 +298,10 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { static Object richCmp(Object selfObj, Object otherObj, RichCmpOp op, @Bind Node inliningTarget, @Cached PyDateCheckNode dateCheckNode, - @Cached DateNodes.AsManagedDateNode asManagedDateNode) { + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { if (dateCheckNode.execute(inliningTarget, selfObj) && dateCheckNode.execute(inliningTarget, otherObj)) { - PDate self = asManagedDateNode.execute(inliningTarget, selfObj); - PDate other = asManagedDateNode.execute(inliningTarget, otherObj); + DateValue self = readDateValueNode.execute(inliningTarget, selfObj); + DateValue other = readDateValueNode.execute(inliningTarget, otherObj); int result = self.compareTo(other); return op.compareResultToBool(result); } @@ -314,8 +318,8 @@ static long hash(VirtualFrame frame, Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached PyObjectHashNode hashNode, - @Cached DateNodes.AsManagedDateNode asManaged) { - PDate d = asManaged.execute(inliningTarget, self); + @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + DateValue d = readDateValueNode.execute(inliningTarget, self); var content = new int[]{d.year, d.month, d.day}; return hashNode.execute(frame, inliningTarget, PFactory.createTuple(language, content)); } @@ -355,8 +359,8 @@ private static Object addBoundary(Object left, Object right, Node inliningTarget } else { return PNotImplemented.NOT_IMPLEMENTED; } - PDate date = DateNodes.AsManagedDateNode.executeUncached(dateObj); - PTimeDelta delta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(deltaObj); + DateValue date = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, dateObj); + TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, deltaObj); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(date.year, date.month, date.day); @@ -403,26 +407,26 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget if (!PyDateCheckNode.executeUncached(left)) { return PNotImplemented.NOT_IMPLEMENTED; } - PDate date = DateNodes.AsManagedDateNode.executeUncached(left); + DateValue date = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, left); if (PyDateCheckNode.executeUncached(right)) { LocalDate from = LocalDate.of(1, 1, 1); LocalDate toSelf = LocalDate.of(date.year, date.month, date.day); long daysSelf = ChronoUnit.DAYS.between(from, toSelf) + 1; - PDate other = DateNodes.AsManagedDateNode.executeUncached(right); + DateValue other = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, right); LocalDate toOther = LocalDate.of(other.year, other.month, other.day); long daysOther = ChronoUnit.DAYS.between(from, toOther) + 1; long days = daysSelf - daysOther; return new PTimeDelta( PythonBuiltinClassType.PTimeDelta, - PythonBuiltinClassType.PTimeDelta.getInstanceShape(PythonLanguage.get(null)), + PythonBuiltinClassType.PTimeDelta.getInstanceShape(PythonLanguage.get(inliningTarget)), (int) days, 0, 0); } if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { - PTimeDelta timeDelta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(right); + TimeDeltaValue timeDelta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(date.year, date.month, date.day); long days = ChronoUnit.DAYS.between(from, to) + 1; @@ -739,11 +743,11 @@ public abstract static class ReplaceNode extends PythonBuiltinNode { @Specialization static Object replace(VirtualFrame frame, Object self, Object yearObject, Object monthObject, Object dayObject, @Bind Node inliningTarget, - @Cached DateNodes.AsManagedDateNode asManagedDateNode, + @Cached TemporalNodes.ReadDateValueNode readDateValueNode, @Cached PyLongAsLongNode longAsLongNode, @Cached GetClassNode getClassNode, @Cached DateNodes.NewNode newNode) { - PDate date = asManagedDateNode.execute(inliningTarget, self); + DateValue date = readDateValueNode.execute(inliningTarget, self); final int year, month, day; if (yearObject instanceof PNone) { @@ -774,8 +778,9 @@ public abstract static class ToOrdinalNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - static long toOrdinal(Object selfObj) { - PDate self = DateNodes.AsManagedDateNode.executeUncached(selfObj); + static long toOrdinal(Object selfObj, + @Bind Node inliningTarget) { + DateValue self = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, selfObj); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(self.year, self.month, self.day); return ChronoUnit.DAYS.between(from, to) + 1; @@ -825,8 +830,9 @@ public abstract static class WeekDayNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - static int weekDay(Object selfObj) { - PDate self = DateNodes.AsManagedDateNode.executeUncached(selfObj); + static int weekDay(Object selfObj, + @Bind Node inliningTarget) { + DateValue self = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); DayOfWeek dayOfWeek = localDate.getDayOfWeek(); @@ -841,8 +847,9 @@ public abstract static class IsoWeekDayNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - static int weekDay(Object selfObj) { - PDate self = DateNodes.AsManagedDateNode.executeUncached(selfObj); + static int weekDay(Object selfObj, + @Bind Node inliningTarget) { + DateValue self = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); DayOfWeek dayOfWeek = localDate.getDayOfWeek(); return dayOfWeek.getValue(); @@ -856,8 +863,9 @@ public abstract static class IsoCalendarNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static PTuple isoCalendar(Object selfObj, - @Bind PythonLanguage language) { - PDate self = DateNodes.AsManagedDateNode.executeUncached(selfObj); + @Bind PythonLanguage language, + @Bind Node inliningTarget) { + DateValue self = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); // use week based year ISO-8601 calendar @@ -875,8 +883,9 @@ public abstract static class IsoFormatNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - static TruffleString isoFormat(Object selfObj) { - PDate self = DateNodes.AsManagedDateNode.executeUncached(selfObj); + static TruffleString isoFormat(Object selfObj, + @Bind Node inliningTarget) { + DateValue self = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, selfObj); LocalDate locaDate = LocalDate.of(self.year, self.month, self.day); var isoString = locaDate.toString(); return TruffleString.FromJavaStringNode.getUncached().execute(isoString, TS_ENCODING); @@ -889,8 +898,9 @@ public abstract static class CTimeNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - static TruffleString cTime(Object selfObj) { - PDate self = DateNodes.AsManagedDateNode.executeUncached(selfObj); + static TruffleString cTime(Object selfObj, + @Bind Node inliningTarget) { + DateValue self = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE LLL ppd 00:00:00 yyyy"); String ctime = localDate.format(formatter); @@ -905,8 +915,9 @@ public abstract static class TimeTupleNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static PTuple timeTuple(Object selfObj, - @Bind PythonLanguage language) { - PDate self = DateNodes.AsManagedDateNode.executeUncached(selfObj); + @Bind PythonLanguage language, + @Bind Node inliningTarget) { + DateValue self = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); // Python's day of week is in range 0-6 @@ -930,8 +941,9 @@ protected ArgumentClinicProvider getArgumentClinic() { @Specialization @TruffleBoundary - static TruffleString strftime(Object selfObj, TruffleString format) { - PDate self = DateNodes.AsManagedDateNode.executeUncached(selfObj); + static TruffleString strftime(Object selfObj, TruffleString format, + @Bind Node inliningTarget) { + DateValue self = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, selfObj); // Reuse time.strftime(format, time_tuple) method. // construct time_tuple diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateNodes.java index a9fc83a887..dd6621b8a0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateNodes.java @@ -40,14 +40,12 @@ */ package com.oracle.graal.python.builtins.modules.datetime; -import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError; import static com.oracle.graal.python.builtins.modules.datetime.DatetimeModuleBuiltins.MAX_YEAR; import static com.oracle.graal.python.builtins.modules.datetime.DatetimeModuleBuiltins.MIN_YEAR; import java.time.YearMonth; -import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes; @@ -57,7 +55,6 @@ import com.oracle.graal.python.builtins.objects.cext.structs.CFields; import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; import com.oracle.graal.python.builtins.objects.type.TypeNodes; -import com.oracle.graal.python.lib.PyDateCheckNode; import com.oracle.graal.python.lib.PyLongAsIntNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PGuards; @@ -65,7 +62,6 @@ import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.runtime.PythonContext; 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.Fallback; import com.oracle.truffle.api.dsl.GenerateCached; @@ -185,33 +181,7 @@ static boolean isBuiltinClass(Object cls) { } } - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class AsManagedDateNode extends Node { - public abstract PDate execute(Node inliningTarget, Object obj); - - public static PDate executeUncached(Object obj) { - return DateNodesFactory.AsManagedDateNodeGen.getUncached().execute(null, obj); - } - - @Specialization - static PDate asManaged(PDate obj) { - return obj; - } - - @Specialization(guards = "checkNode.execute(inliningTarget, obj)", limit = "1") - static PDate asManagedNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject obj, - @Bind PythonLanguage language, - @SuppressWarnings("unused") @Cached PyDateCheckNode checkNode, - @Cached CStructAccess.ReadByteNode readByteNode) { - int year = getYear(obj, readByteNode); - int month = getMonth(obj, readByteNode); - int day = getDay(obj, readByteNode); - PythonBuiltinClassType cls = PythonBuiltinClassType.PDate; - return new PDate(cls, cls.getInstanceShape(language), year, month, day); - } - + public static final class AsManagedDateNode { static int getYear(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { int b0 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_Date__data, 0); int b1 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_Date__data, 1); @@ -225,12 +195,6 @@ static int getMonth(PythonAbstractNativeObject self, CStructAccess.ReadByteNode static int getDay(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { return readNode.readFromObjUnsigned(self, CFields.PyDateTime_Date__data, 3); } - - @Fallback - static PDate error(Object obj, - @Bind Node inliningTarget) { - throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_EXPECTED_GOT_P, "date", obj); - } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java index e1a911437c..7335552dbc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java @@ -97,6 +97,49 @@ public static DateValue of(PDate date) { public LocalDate toLocalDate() { return LocalDate.of(year, month, day); } + + public int compareTo(DateValue other) { + if (year < other.year) { + return -1; + } + if (year > other.year) { + return 1; + } + if (month < other.month) { + return -1; + } + if (month > other.month) { + return 1; + } + if (day < other.day) { + return -1; + } + if (day > other.day) { + return 1; + } + return 0; + } + } + + @ValueType + public static final class TimeDeltaValue { + public final int days; + public final int seconds; + public final int microseconds; + + public TimeDeltaValue(int days, int seconds, int microseconds) { + this.days = days; + this.seconds = seconds; + this.microseconds = microseconds; + } + + public static TimeDeltaValue of(PTimeDelta delta) { + return new TimeDeltaValue(delta.days, delta.seconds, delta.microseconds); + } + + public boolean isZero() { + return days == 0 && seconds == 0 && microseconds == 0; + } } @ValueType @@ -185,6 +228,36 @@ public static Object toFixedOffsetTimeZone(ZoneId zoneId, Node inliningTarget) { return TimeZoneNodes.NewNode.getUncached().execute(inliningTarget, PythonContext.get(inliningTarget), PythonBuiltinClassType.PTimezone, delta, PNone.NO_VALUE); } + @GenerateUncached + @GenerateInline + @GenerateCached(alwaysInlineCached = true) + public abstract static class ReadTimeDeltaValueNode extends Node { + public abstract TimeDeltaValue execute(Node inliningTarget, Object obj); + + public static TimeDeltaValue executeUncached(Node inliningTarget, Object obj) { + return TemporalNodesFactory.ReadTimeDeltaValueNodeGen.executeUncached(inliningTarget, obj); + } + + @Specialization + static TimeDeltaValue doManaged(PTimeDelta value) { + return TimeDeltaValue.of(value); + } + + @Specialization(guards = "checkNode.execute(inliningTarget, value)", limit = "1") + static TimeDeltaValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject value, + @SuppressWarnings("unused") @Cached TimeDeltaNodes.TimeDeltaCheckNode checkNode, + @Cached CStructAccess.ReadI32Node readIntNode) { + return new TimeDeltaValue(TimeDeltaNodes.AsManagedTimeDeltaNode.getDays(value, readIntNode), TimeDeltaNodes.AsManagedTimeDeltaNode.getSeconds(value, readIntNode), + TimeDeltaNodes.AsManagedTimeDeltaNode.getMicroseconds(value, readIntNode)); + } + + @Fallback + static TimeDeltaValue error(Object obj, + @Bind Node inliningTarget) { + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_EXPECTED_GOT_P, "timedelta", obj); + } + } + @GenerateUncached @GenerateInline @GenerateCached(alwaysInlineCached = true) From 75a3e553f12cd46f15622da3da40588645bfc287 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Thu, 26 Mar 2026 08:47:43 +0100 Subject: [PATCH 12/30] Refactor all other trivial temp conversions to managed datetime objects --- .../modules/datetime/DateTimeBuiltins.java | 34 +++++++------- .../datetime/DatetimeModuleBuiltins.java | 6 +-- .../modules/datetime/TemporalNodes.java | 8 ++-- .../modules/datetime/TimeDeltaBuiltins.java | 45 ++++++++++--------- .../objects/foreign/ForeignDateBuiltins.java | 4 +- .../foreign/ForeignDateTimeBuiltins.java | 4 +- 6 files changed, 52 insertions(+), 49 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java index 391442b2fc..02dbb63690 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java @@ -101,6 +101,8 @@ import com.oracle.graal.python.builtins.modules.TimeModuleBuiltins; import com.oracle.graal.python.builtins.modules.WarningsModuleBuiltins.WarnNode; import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.DateValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.DateTimeValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.TimeDeltaValue; import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.TimeValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; @@ -417,7 +419,7 @@ static TruffleString repr(Object selfObj, EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent(); Node encapsulatingNode = encapsulating.set(inliningTarget); try { - return reprBoundary(selfObj); + return reprBoundary(inliningTarget, selfObj); } finally { // Some uncached nodes (e.g. PyFloatAsDoubleNode, PyLongAsLongNode, // PyObjectReprAsObjectNode) may raise exceptions that are not @@ -427,8 +429,8 @@ static TruffleString repr(Object selfObj, } @TruffleBoundary - private static TruffleString reprBoundary(Object selfObj) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + private static TruffleString reprBoundary(Node inliningTarget, Object selfObj) { + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); @@ -466,7 +468,7 @@ static Object reduce(Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached GetClassNode getClassNode) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); // DateTime is serialized in the following format: // ( // bytes(year 1st byte, year 2nd byte, month, day, hours, minutes, seconds, microseconds @@ -509,7 +511,7 @@ static Object reduceEx(Object selfObj, int protocol, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached GetClassNode getClassNode) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); byte[] baseStateBytes = new byte[10]; baseStateBytes[0] = (byte) (self.year / 256); baseStateBytes[1] = (byte) (self.year % 256); @@ -746,10 +748,10 @@ private static Object addBoundary(Object left, Object right, Node inliningTarget } else { return PNotImplemented.NOT_IMPLEMENTED; } - PDateTime date = DateTimeNodes.AsManagedDateTimeNode.executeUncached(dateTimeObj); - PTimeDelta delta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(deltaObj); + DateTimeValue date = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, dateTimeObj); + TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, deltaObj); - LocalDateTime local = toLocalDateTime(date); + LocalDateTime local = date.toLocalDateTime(); LocalDateTime localAdjusted = local.plusDays(delta.days).plusSeconds(delta.seconds).plusNanos(delta.microseconds * 1_000L); if (localAdjusted.getYear() < MIN_YEAR || localAdjusted.getYear() > MAX_YEAR) { @@ -2531,7 +2533,7 @@ abstract static class DateNode extends PythonUnaryBuiltinNode { static Object getDate(Object selfObj, @Bind Node inliningTarget, @Cached DateNodes.NewNode newDateNode) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); return newDateNode.execute(inliningTarget, PythonBuiltinClassType.PDate, self.year, @@ -2548,7 +2550,7 @@ abstract static class TimeNode extends PythonUnaryBuiltinNode { static Object getTime(Object selfObj, @Bind Node inliningTarget, @Cached TimeNodes.NewNode newTimeNode) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); return newTimeNode.execute(inliningTarget, PythonBuiltinClassType.PTime, self.hour, @@ -2568,7 +2570,7 @@ abstract static class TimeTzNode extends PythonUnaryBuiltinNode { static Object getTime(Object selfObj, @Bind Node inliningTarget, @Cached TimeNodes.NewNode newTimeNode) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); return newTimeNode.execute(inliningTarget, PythonBuiltinClassType.PTime, self.hour, @@ -2591,7 +2593,7 @@ static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Obj @Cached PyLongAsLongNode asLongNode, @Cached GetClassNode getClassNode, @Cached DateTimeNodes.NewNode newDateTimeNode) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); final long year, month, day; if (yearObject instanceof PNone) { @@ -2938,7 +2940,7 @@ public abstract static class ToOrdinalNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static long toOrdinal(Object selfObj) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(null, selfObj); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(self.year, self.month, self.day); return ChronoUnit.DAYS.between(from, to) + 1; @@ -3139,8 +3141,8 @@ public abstract static class CTimeNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static TruffleString cTime(Object selfObj) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); - LocalDateTime localDateTime = LocalDateTime.of(self.year, self.month, self.day, self.hour, self.minute, self.second); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(null, selfObj); + LocalDateTime localDateTime = self.toLocalDateTime(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE LLL ppd HH:mm:ss yyyy"); String ctime = localDateTime.format(formatter); return TruffleString.FromJavaStringNode.getUncached().execute(ctime, TS_ENCODING); @@ -3273,7 +3275,7 @@ private static LocalDateTime subtractOffsetFromDateTime(PDateTime self, PTimeDel @TruffleBoundary private static LocalDateTime toLocalDateTime(PDateTime dateTime) { - return TemporalNodes.DateTimeValue.of(dateTime).toLocalDateTime(); + return DateTimeValue.of(dateTime).toLocalDateTime(); } private static Object toPDateTime(LocalDateTime local, Object tzInfo, int fold, Node inliningTarget, Object cls) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DatetimeModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DatetimeModuleBuiltins.java index 3af9fa1ccc..b54f8f6999 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DatetimeModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DatetimeModuleBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -328,8 +328,8 @@ public static PTimeDelta callDst(Object tzInfo, Object dateTime, VirtualFrame fr @TruffleBoundary public static Object addOffsetToDateTime(Object dateTimeObj, PTimeDelta offset, DateTimeNodes.SubclassNewNode subclassNewNode, Node inliningTarget) { - PDateTime dateTime = DateTimeNodes.AsManagedDateTimeNode.executeUncached(dateTimeObj); - LocalDateTime utc = LocalDateTime.of(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second, dateTime.microsecond * 1_000).plusDays( + TemporalNodes.DateTimeValue dateTime = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, dateTimeObj); + LocalDateTime utc = dateTime.toLocalDateTime().plusDays( offset.days).plusSeconds(offset.seconds).plusNanos(offset.microseconds * 1_000L); return subclassNewNode.execute(inliningTarget, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java index 7335552dbc..5321718002 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java @@ -235,7 +235,7 @@ public abstract static class ReadTimeDeltaValueNode extends Node { public abstract TimeDeltaValue execute(Node inliningTarget, Object obj); public static TimeDeltaValue executeUncached(Node inliningTarget, Object obj) { - return TemporalNodesFactory.ReadTimeDeltaValueNodeGen.executeUncached(inliningTarget, obj); + return TemporalNodesFactory.ReadTimeDeltaValueNodeGen.getUncached().execute(inliningTarget, obj); } @Specialization @@ -265,7 +265,7 @@ public abstract static class ReadDateValueNode extends Node { public abstract DateValue execute(Node inliningTarget, Object obj); public static DateValue executeUncached(Node inliningTarget, Object obj) { - return TemporalNodesFactory.ReadDateValueNodeGen.executeUncached(inliningTarget, obj); + return TemporalNodesFactory.ReadDateValueNodeGen.getUncached().execute(inliningTarget, obj); } @Specialization @@ -306,7 +306,7 @@ public abstract static class ReadTimeValueNode extends Node { public abstract TimeValue execute(Node inliningTarget, Object obj); public static TimeValue executeUncached(Node inliningTarget, Object obj) { - return TemporalNodesFactory.ReadTimeValueNodeGen.executeUncached(inliningTarget, obj); + return TemporalNodesFactory.ReadTimeValueNodeGen.getUncached().execute(inliningTarget, obj); } @Specialization @@ -351,7 +351,7 @@ public abstract static class ReadDateTimeValueNode extends Node { public abstract DateTimeValue execute(Node inliningTarget, Object obj); public static DateTimeValue executeUncached(Node inliningTarget, Object obj) { - return TemporalNodesFactory.ReadDateTimeValueNodeGen.executeUncached(inliningTarget, obj); + return TemporalNodesFactory.ReadDateTimeValueNodeGen.getUncached().execute(inliningTarget, obj); } @Specialization 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 88cfccc818..13198e008c 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 @@ -163,8 +163,8 @@ abstract static class BoolNode extends TpSlotInquiry.NbBoolBuiltinNode { @Specialization static boolean bool(Object selfObj, @Bind Node inliningTarget, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, selfObj); + @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + TemporalNodes.TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); return self.days != 0 || self.seconds != 0 || self.microseconds != 0; } } @@ -176,10 +176,10 @@ public abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static TruffleString repr(Object selfObj) { - PTimeDelta self = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(selfObj); + TemporalNodes.TimeDeltaValue self = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(null, selfObj); var builder = new StringBuilder(); - builder.append(TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(self))); + builder.append(TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj))); builder.append("("); @@ -223,8 +223,9 @@ public abstract static class StrNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - static TruffleString str(Object selfObj) { - PTimeDelta self = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(selfObj); + static TruffleString str(Object selfObj, + @Bind Node inliningTarget) { + TemporalNodes.TimeDeltaValue self = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); // optional prefix with days, e.g. '1 day' or '5 days' @@ -269,8 +270,8 @@ static Object reduce(Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached GetClassNode getClassNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, selfObj); + @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + TemporalNodes.TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); Object type = getClassNode.execute(inliningTarget, selfObj); PTuple arguments = PFactory.createTuple(language, new Object[]{self.days, self.seconds, self.microseconds}); return PFactory.createTuple(language, new Object[]{type, arguments}); @@ -305,8 +306,8 @@ static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached PyObjectHashNode hashNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, selfObj); + @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + TemporalNodes.TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); var content = new int[]{self.days, self.seconds, self.microseconds}; return hashNode.execute(frame, inliningTarget, PFactory.createTuple(language, content)); } @@ -322,12 +323,12 @@ static Object add(Object left, Object right, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, @Cached TimeDeltaNodes.TimeDeltaCheckNode checkNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { + @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, right)) { return PNotImplemented.NOT_IMPLEMENTED; } - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, left); - PTimeDelta other = asManagedTimeDeltaNode.execute(inliningTarget, right); + TemporalNodes.TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, left); + TemporalNodes.TimeDeltaValue other = readTimeDeltaValueNode.execute(inliningTarget, right); return newNode.executeBuiltin(inliningTarget, self.days + other.days, self.seconds + other.seconds, self.microseconds + other.microseconds, 0, 0, 0, 0); } } @@ -342,12 +343,12 @@ static Object sub(Object left, Object rigth, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, @Cached TimeDeltaNodes.TimeDeltaCheckNode checkNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { + @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, rigth)) { return PNotImplemented.NOT_IMPLEMENTED; } - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, left); - PTimeDelta other = asManagedTimeDeltaNode.execute(inliningTarget, rigth); + TemporalNodes.TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, left); + TemporalNodes.TimeDeltaValue other = readTimeDeltaValueNode.execute(inliningTarget, rigth); return newNode.executeBuiltin(inliningTarget, self.days - other.days, self.seconds - other.seconds, self.microseconds - other.microseconds, 0, 0, 0, 0); } } @@ -603,8 +604,8 @@ abstract static class AbsNode extends PythonUnaryBuiltinNode { static PTimeDelta abs(PTimeDelta selfObj, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, selfObj); + @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + TemporalNodes.TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); if (self.days >= 0) { return newNode.executeBuiltin(inliningTarget, self.days, self.seconds, self.microseconds, 0, 0, 0, 0); } else { @@ -621,8 +622,8 @@ abstract static class PosNode extends PythonUnaryBuiltinNode { static PTimeDelta pos(PTimeDelta selfObj, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, selfObj); + @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + TemporalNodes.TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); return newNode.executeBuiltin(inliningTarget, self.days, self.seconds, self.microseconds, 0, 0, 0, 0); } } @@ -635,8 +636,8 @@ abstract static class NegNode extends PythonUnaryBuiltinNode { static PTimeDelta neg(Object selfObj, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, selfObj); + @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + TemporalNodes.TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); return newNode.executeBuiltin(inliningTarget, -self.days, -self.seconds, -self.microseconds, 0, 0, 0, 0); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java index ac0a2c27ed..c82ad6b841 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java @@ -185,7 +185,7 @@ static Object add(Object left, Object right, } LocalDate date = readDateValueNode.execute(inliningTarget, dateObj).toLocalDate(); - PTimeDelta delta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(deltaObj); + TemporalNodes.TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, deltaObj); long days = ChronoUnit.DAYS.between(LocalDate.of(1, 1, 1), date) + 1 + delta.days; if (days <= 0 || days > MAX_ORDINAL) { throw com.oracle.graal.python.nodes.PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); @@ -216,7 +216,7 @@ static Object sub(Object left, Object right, return new PTimeDelta(PythonBuiltinClassType.PTimeDelta, PythonBuiltinClassType.PTimeDelta.getInstanceShape(PythonLanguage.get(null)), (int) (leftDays - rightDays), 0, 0); } if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { - PTimeDelta delta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(right); + TemporalNodes.TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); long days = leftDays - delta.days; if (days <= 0 || days >= MAX_ORDINAL) { throw com.oracle.graal.python.nodes.PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java index 39e927dd3e..46a16bb627 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java @@ -225,7 +225,7 @@ static Object add(Object left, Object right, return PNotImplemented.NOT_IMPLEMENTED; } PDateTime date = toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, dateTimeObj), inliningTarget); - PTimeDelta delta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(deltaObj); + TemporalNodes.TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, deltaObj); LocalDateTime adjusted = toLocalDateTime(date).plusDays(delta.days).plusSeconds(delta.seconds).plusNanos(delta.microseconds * 1_000L); return toPythonDateTime(adjusted, date.tzInfo, date.fold, inliningTarget); } @@ -260,7 +260,7 @@ static Object sub(VirtualFrame frame, Object left, Object right, 0, 0, 0); } if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { - PTimeDelta delta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(right); + TemporalNodes.TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); LocalDateTime adjusted = toLocalDateTime(self).minusDays(delta.days).minusSeconds(delta.seconds).minusNanos(delta.microseconds * 1_000L); return toPythonDateTime(adjusted, self.tzInfo, self.fold, inliningTarget); } From 02aaafeeffe235c0a6cb498e176603ef9b46ecef Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Thu, 26 Mar 2026 14:47:44 +0100 Subject: [PATCH 13/30] Get rid of more PTimeDelta intermediate objects --- .../modules/datetime/TemporalNodes.java | 22 +++++++ .../modules/datetime/TimeDeltaBuiltins.java | 66 +++++++++---------- 2 files changed, 55 insertions(+), 33 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java index 5321718002..ea505b2abd 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java @@ -140,6 +140,28 @@ public static TimeDeltaValue of(PTimeDelta delta) { public boolean isZero() { return days == 0 && seconds == 0 && microseconds == 0; } + + public int compareTo(TimeDeltaValue other) { + if (days < other.days) { + return -1; + } + if (days > other.days) { + return 1; + } + if (seconds < other.seconds) { + return -1; + } + if (seconds > other.seconds) { + return 1; + } + if (microseconds < other.microseconds) { + return -1; + } + if (microseconds > other.microseconds) { + return 1; + } + return 0; + } } @ValueType 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 13198e008c..e25b479e45 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 @@ -164,7 +164,7 @@ abstract static class BoolNode extends TpSlotInquiry.NbBoolBuiltinNode { static boolean bool(Object selfObj, @Bind Node inliningTarget, @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { - TemporalNodes.TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); return self.days != 0 || self.seconds != 0 || self.microseconds != 0; } } @@ -176,7 +176,7 @@ public abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static TruffleString repr(Object selfObj) { - TemporalNodes.TimeDeltaValue self = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(null, selfObj); + TimeDeltaValue self = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(null, selfObj); var builder = new StringBuilder(); builder.append(TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj))); @@ -225,7 +225,7 @@ public abstract static class StrNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString str(Object selfObj, @Bind Node inliningTarget) { - TemporalNodes.TimeDeltaValue self = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, selfObj); + TimeDeltaValue self = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); // optional prefix with days, e.g. '1 day' or '5 days' @@ -271,7 +271,7 @@ static Object reduce(Object selfObj, @Bind PythonLanguage language, @Cached GetClassNode getClassNode, @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { - TemporalNodes.TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); Object type = getClassNode.execute(inliningTarget, selfObj); PTuple arguments = PFactory.createTuple(language, new Object[]{self.days, self.seconds, self.microseconds}); return PFactory.createTuple(language, new Object[]{type, arguments}); @@ -286,12 +286,12 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { static Object richCmp(Object left, Object right, RichCmpOp op, @Bind Node inliningTarget, @Cached TimeDeltaNodes.TimeDeltaCheckNode checkNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { + @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, right)) { return PNotImplemented.NOT_IMPLEMENTED; } - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, left); - PTimeDelta other = asManagedTimeDeltaNode.execute(inliningTarget, right); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, left); + TimeDeltaValue other = readTimeDeltaValueNode.execute(inliningTarget, right); int result = self.compareTo(other); return op.compareResultToBool(result); } @@ -307,7 +307,7 @@ static long hash(VirtualFrame frame, Object selfObj, @Bind PythonLanguage language, @Cached PyObjectHashNode hashNode, @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { - TemporalNodes.TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); var content = new int[]{self.days, self.seconds, self.microseconds}; return hashNode.execute(frame, inliningTarget, PFactory.createTuple(language, content)); } @@ -327,8 +327,8 @@ static Object add(Object left, Object right, if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, right)) { return PNotImplemented.NOT_IMPLEMENTED; } - TemporalNodes.TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, left); - TemporalNodes.TimeDeltaValue other = readTimeDeltaValueNode.execute(inliningTarget, right); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, left); + TimeDeltaValue other = readTimeDeltaValueNode.execute(inliningTarget, right); return newNode.executeBuiltin(inliningTarget, self.days + other.days, self.seconds + other.seconds, self.microseconds + other.microseconds, 0, 0, 0, 0); } } @@ -347,8 +347,8 @@ static Object sub(Object left, Object rigth, if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, rigth)) { return PNotImplemented.NOT_IMPLEMENTED; } - TemporalNodes.TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, left); - TemporalNodes.TimeDeltaValue other = readTimeDeltaValueNode.execute(inliningTarget, rigth); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, left); + TimeDeltaValue other = readTimeDeltaValueNode.execute(inliningTarget, rigth); return newNode.executeBuiltin(inliningTarget, self.days - other.days, self.seconds - other.seconds, self.microseconds - other.microseconds, 0, 0, 0, 0); } } @@ -415,14 +415,14 @@ static Object mul(VirtualFrame frame, Object left, Object right, @Cached PyNumberMultiplyNode multiplyNode, @Cached TimeDeltaNodes.NewNode newNode, @Cached TimeDeltaNodes.TimeDeltaCheckNode checkNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { - PTimeDelta date; + @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + TimeDeltaValue date; Object other; if (checkNode.execute(inliningTarget, left)) { - date = asManagedTimeDeltaNode.execute(inliningTarget, left); + date = readTimeDeltaValueNode.execute(inliningTarget, left); other = right; } else { - date = asManagedTimeDeltaNode.execute(inliningTarget, right); + date = readTimeDeltaValueNode.execute(inliningTarget, right); other = left; } if (longCheckNode.execute(inliningTarget, other)) { @@ -468,13 +468,13 @@ static Object div(VirtualFrame frame, Object left, Object right, @Cached TimeDeltaNodes.NewNode newNode, @Cached TimeDeltaNodes.TimeDeltaCheckNode checkLeft, @Cached TimeDeltaNodes.TimeDeltaCheckNode checkRight, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { + @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { if (!checkLeft.execute(inliningTarget, left)) { return PNotImplemented.NOT_IMPLEMENTED; } - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, left); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, left); if (checkRight.execute(inliningTarget, right)) { - PTimeDelta otherTimeDelta = asManagedTimeDeltaNode.execute(inliningTarget, right); + TimeDeltaValue otherTimeDelta = readTimeDeltaValueNode.execute(inliningTarget, right); Object microsecondsSelf = toMicroseconds(self, addNode, multiplyNode); Object microsecondsOther = toMicroseconds(otherTimeDelta, addNode, multiplyNode); return trueDivideNode.execute(frame, microsecondsSelf, microsecondsOther); @@ -516,13 +516,13 @@ static Object div(VirtualFrame frame, Object left, Object right, @Cached PyNumberFloorDivideNode floorDivideNode, @Cached TimeDeltaNodes.TimeDeltaCheckNode checkLeft, @Cached TimeDeltaNodes.TimeDeltaCheckNode checkRight, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { + @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { if (!checkLeft.execute(inliningTarget, left)) { return PNotImplemented.NOT_IMPLEMENTED; } - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, left); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, left); if (checkRight.execute(inliningTarget, right)) { - PTimeDelta otherTimeDelta = asManagedTimeDeltaNode.execute(inliningTarget, right); + TimeDeltaValue otherTimeDelta = readTimeDeltaValueNode.execute(inliningTarget, right); Object microsecondsSelf = toMicroseconds(self, addNode, multiplyNode); Object microsecondsOther = toMicroseconds(otherTimeDelta, addNode, multiplyNode); return floorDivideNode.execute(frame, microsecondsSelf, microsecondsOther); @@ -548,8 +548,8 @@ static Object divmod(Object left, Object right, if (!TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(left) || !TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { return PNotImplemented.NOT_IMPLEMENTED; } - PTimeDelta self = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(left); - PTimeDelta other = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(right); + TimeDeltaValue self = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, left); + TimeDeltaValue other = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent(); Node encapsulatingNode = encapsulating.set(inliningTarget); @@ -583,8 +583,8 @@ static Object mod(Object left, Object right, EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent(); Node encapsulatingNode = encapsulating.set(inliningTarget); try { - PTimeDelta self = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(left); - PTimeDelta other = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(right); + TimeDeltaValue self = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, left); + TimeDeltaValue other = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); Object microsecondsSelf = toMicrosecondsUncached(self); Object microsecondsOther = toMicrosecondsUncached(other); Object remainder = PyNumberRemainderNode.getUncached().execute(null, microsecondsSelf, microsecondsOther); @@ -605,7 +605,7 @@ static PTimeDelta abs(PTimeDelta selfObj, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { - TemporalNodes.TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); if (self.days >= 0) { return newNode.executeBuiltin(inliningTarget, self.days, self.seconds, self.microseconds, 0, 0, 0, 0); } else { @@ -623,7 +623,7 @@ static PTimeDelta pos(PTimeDelta selfObj, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { - TemporalNodes.TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); return newNode.executeBuiltin(inliningTarget, self.days, self.seconds, self.microseconds, 0, 0, 0, 0); } } @@ -637,7 +637,7 @@ static PTimeDelta neg(Object selfObj, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { - TemporalNodes.TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); return newNode.executeBuiltin(inliningTarget, -self.days, -self.seconds, -self.microseconds, 0, 0, 0, 0); } } @@ -697,24 +697,24 @@ abstract static class TotalSecondsNode extends PythonUnaryBuiltinNode { @Specialization static Object getTotalSeconds(Object selfObj, @Bind Node inliningTarget, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode, + @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode, @Cached PyNumberAddNode addNode, @Cached PyNumberMultiplyNode multiplyNode, @Cached PyNumberTrueDivideNode trueDivideNode) { - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, selfObj); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); Object microseconds = toMicroseconds(self, addNode, multiplyNode); return trueDivideNode.execute(null, microseconds, 1_000_000); } } - private static Object toMicroseconds(PTimeDelta timeDelta, PyNumberAddNode addNode, PyNumberMultiplyNode multiplyNode) { + private static Object toMicroseconds(TimeDeltaValue timeDelta, PyNumberAddNode addNode, PyNumberMultiplyNode multiplyNode) { Object x = multiplyNode.execute(null, timeDelta.days, 24 * 3600); x = addNode.execute(null, x, timeDelta.seconds); x = multiplyNode.execute(null, x, 1_000_000); return addNode.execute(null, x, timeDelta.microseconds); } - private static Object toMicrosecondsUncached(PTimeDelta timeDelta) { + private static Object toMicrosecondsUncached(TimeDeltaValue timeDelta) { return toMicroseconds(timeDelta, PyNumberAddNode.getUncached(), PyNumberMultiplyNode.getUncached()); } From fa5e116301b4b510301ebb7b689488d7b360eda6 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Thu, 26 Mar 2026 14:59:58 +0100 Subject: [PATCH 14/30] Introduce PyDeltaCheckNode --- .../modules/datetime/DateBuiltins.java | 7 +- .../modules/datetime/DateTimeBuiltins.java | 7 +- .../modules/datetime/TemporalNodes.java | 3 +- .../modules/datetime/TimeDeltaBuiltins.java | 22 ++--- .../modules/datetime/TimeDeltaNodes.java | 31 +------ .../modules/datetime/TimeZoneNodes.java | 5 +- .../objects/foreign/ForeignDateBuiltins.java | 8 +- .../foreign/ForeignDateTimeBuiltins.java | 7 +- .../graal/python/lib/PyDeltaCheckNode.java | 81 +++++++++++++++++++ 9 files changed, 116 insertions(+), 55 deletions(-) create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDeltaCheckNode.java diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java index d85ef9857e..292f8e847c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java @@ -99,6 +99,7 @@ import com.oracle.graal.python.lib.PyFloatAsDoubleNode; import com.oracle.graal.python.lib.PyFloatCheckNode; import com.oracle.graal.python.lib.PyDateCheckNode; +import com.oracle.graal.python.lib.PyDeltaCheckNode; import com.oracle.graal.python.lib.PyLongAsLongNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectHashNode; @@ -347,13 +348,13 @@ static Object add(VirtualFrame frame, Object left, Object right, private static Object addBoundary(Object left, Object right, Node inliningTarget) { Object dateObj, deltaObj; if (PyDateCheckNode.executeUncached(left)) { - if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + if (PyDeltaCheckNode.executeUncached(right)) { dateObj = left; deltaObj = right; } else { return PNotImplemented.NOT_IMPLEMENTED; } - } else if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(left)) { + } else if (PyDeltaCheckNode.executeUncached(left)) { dateObj = right; deltaObj = left; } else { @@ -425,7 +426,7 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget 0, 0); } - if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + if (PyDeltaCheckNode.executeUncached(right)) { TimeDeltaValue timeDelta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(date.year, date.month, date.day); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java index 02dbb63690..83a576c894 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java @@ -123,6 +123,7 @@ import com.oracle.graal.python.lib.PyFloatCheckNode; import com.oracle.graal.python.lib.PyDateCheckNode; import com.oracle.graal.python.lib.PyDateTimeCheckNode; +import com.oracle.graal.python.lib.PyDeltaCheckNode; import com.oracle.graal.python.lib.PyLongAsLongNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectReprAsObjectNode; @@ -736,13 +737,13 @@ static Object add(VirtualFrame frame, Object left, Object right, private static Object addBoundary(Object left, Object right, Node inliningTarget) { Object dateTimeObj, deltaObj; if (PyDateTimeCheckNode.executeUncached(left)) { - if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + if (PyDeltaCheckNode.executeUncached(right)) { dateTimeObj = left; deltaObj = right; } else { return PNotImplemented.NOT_IMPLEMENTED; } - } else if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(left)) { + } else if (PyDeltaCheckNode.executeUncached(left)) { dateTimeObj = right; deltaObj = left; } else { @@ -822,7 +823,7 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget 0, 0, 0); - } else if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + } else if (PyDeltaCheckNode.executeUncached(right)) { PTimeDelta timeDelta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(right); LocalDateTime local = toLocalDateTime(self); LocalDateTime localAdjusted = local.minusDays(timeDelta.days).minusSeconds(timeDelta.seconds).minusNanos(timeDelta.microseconds * 1_000L); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java index ea505b2abd..d4826d19a6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java @@ -55,6 +55,7 @@ import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; import com.oracle.graal.python.lib.PyDateCheckNode; import com.oracle.graal.python.lib.PyDateTimeCheckNode; +import com.oracle.graal.python.lib.PyDeltaCheckNode; import com.oracle.graal.python.lib.PyTimeCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; @@ -267,7 +268,7 @@ static TimeDeltaValue doManaged(PTimeDelta value) { @Specialization(guards = "checkNode.execute(inliningTarget, value)", limit = "1") static TimeDeltaValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject value, - @SuppressWarnings("unused") @Cached TimeDeltaNodes.TimeDeltaCheckNode checkNode, + @SuppressWarnings("unused") @Cached PyDeltaCheckNode checkNode, @Cached CStructAccess.ReadI32Node readIntNode) { return new TimeDeltaValue(TimeDeltaNodes.AsManagedTimeDeltaNode.getDays(value, readIntNode), TimeDeltaNodes.AsManagedTimeDeltaNode.getSeconds(value, readIntNode), TimeDeltaNodes.AsManagedTimeDeltaNode.getMicroseconds(value, readIntNode)); 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 e25b479e45..3d9eca050f 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 @@ -76,7 +76,9 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotInquiry; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; +import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.TimeDeltaValue; import com.oracle.graal.python.lib.PyFloatCheckNode; +import com.oracle.graal.python.lib.PyDeltaCheckNode; import com.oracle.graal.python.lib.PyLongCheckNode; import com.oracle.graal.python.lib.PyNumberAddNode; import com.oracle.graal.python.lib.PyNumberFloorDivideNode; @@ -285,7 +287,7 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { @Specialization static Object richCmp(Object left, Object right, RichCmpOp op, @Bind Node inliningTarget, - @Cached TimeDeltaNodes.TimeDeltaCheckNode checkNode, + @Cached PyDeltaCheckNode checkNode, @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, right)) { return PNotImplemented.NOT_IMPLEMENTED; @@ -322,7 +324,7 @@ abstract static class AddNode extends BinaryOpBuiltinNode { static Object add(Object left, Object right, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TimeDeltaNodes.TimeDeltaCheckNode checkNode, + @Cached PyDeltaCheckNode checkNode, @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, right)) { return PNotImplemented.NOT_IMPLEMENTED; @@ -342,7 +344,7 @@ abstract static class SubNode extends BinaryOpBuiltinNode { static Object sub(Object left, Object rigth, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TimeDeltaNodes.TimeDeltaCheckNode checkNode, + @Cached PyDeltaCheckNode checkNode, @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, rigth)) { return PNotImplemented.NOT_IMPLEMENTED; @@ -414,7 +416,7 @@ static Object mul(VirtualFrame frame, Object left, Object right, @Cached PyNumberAddNode addNode, @Cached PyNumberMultiplyNode multiplyNode, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TimeDeltaNodes.TimeDeltaCheckNode checkNode, + @Cached PyDeltaCheckNode checkNode, @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { TimeDeltaValue date; Object other; @@ -466,8 +468,8 @@ static Object div(VirtualFrame frame, Object left, Object right, @Cached PyNumberMultiplyNode multiplyNode, @Cached PyNumberTrueDivideNode trueDivideNode, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TimeDeltaNodes.TimeDeltaCheckNode checkLeft, - @Cached TimeDeltaNodes.TimeDeltaCheckNode checkRight, + @Cached PyDeltaCheckNode checkLeft, + @Cached PyDeltaCheckNode checkRight, @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { if (!checkLeft.execute(inliningTarget, left)) { return PNotImplemented.NOT_IMPLEMENTED; @@ -514,8 +516,8 @@ static Object div(VirtualFrame frame, Object left, Object right, @Cached PyNumberAddNode addNode, @Cached PyNumberMultiplyNode multiplyNode, @Cached PyNumberFloorDivideNode floorDivideNode, - @Cached TimeDeltaNodes.TimeDeltaCheckNode checkLeft, - @Cached TimeDeltaNodes.TimeDeltaCheckNode checkRight, + @Cached PyDeltaCheckNode checkLeft, + @Cached PyDeltaCheckNode checkRight, @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { if (!checkLeft.execute(inliningTarget, left)) { return PNotImplemented.NOT_IMPLEMENTED; @@ -545,7 +547,7 @@ abstract static class DivModNode extends BinaryOpBuiltinNode { static Object divmod(Object left, Object right, @Bind Node inliningTarget, @Bind PythonLanguage language) { - if (!TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(left) || !TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + if (!PyDeltaCheckNode.executeUncached(left) || !PyDeltaCheckNode.executeUncached(right)) { return PNotImplemented.NOT_IMPLEMENTED; } TimeDeltaValue self = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, left); @@ -577,7 +579,7 @@ abstract static class ModNode extends BinaryOpBuiltinNode { @TruffleBoundary static Object mod(Object left, Object right, @Bind Node inliningTarget) { - if (!TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(left) || !TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + if (!PyDeltaCheckNode.executeUncached(left) || !PyDeltaCheckNode.executeUncached(right)) { return PNotImplemented.NOT_IMPLEMENTED; } EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent(); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaNodes.java index 6dcd2390f9..444fcb5e19 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaNodes.java @@ -58,13 +58,13 @@ import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyFloatAsDoubleNode; import com.oracle.graal.python.lib.PyFloatCheckNode; +import com.oracle.graal.python.lib.PyDeltaCheckNode; import com.oracle.graal.python.lib.PyLongAsDoubleNode; import com.oracle.graal.python.lib.PyLongCheckNode; import com.oracle.graal.python.lib.PyLongFromDoubleNode; import com.oracle.graal.python.lib.PyNumberMultiplyNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.object.BuiltinClassProfiles; import com.oracle.graal.python.nodes.util.CastToJavaBigIntegerNode; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -239,33 +239,6 @@ public BigInteger getTotalMicroseconds() { } } - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class TimeDeltaCheckNode extends Node { - public abstract boolean execute(Node inliningTarget, Object obj); - - public static boolean executeUncached(Object obj) { - return TimeDeltaNodesFactory.TimeDeltaCheckNodeGen.getUncached().execute(null, obj); - } - - @Specialization - static boolean doManaged(@SuppressWarnings("unused") PTimeDelta value) { - return true; - } - - @Specialization - static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, - @Cached BuiltinClassProfiles.IsBuiltinObjectProfile profile) { - return profile.profileObject(inliningTarget, value, PythonBuiltinClassType.PTimeDelta); - } - - @Fallback - static boolean doOther(@SuppressWarnings("unused") Object value) { - return false; - } - } - @GenerateUncached @GenerateInline @GenerateCached(false) @@ -284,7 +257,7 @@ static PTimeDelta doPTimeDelta(PTimeDelta value) { @Specialization(guards = "checkNode.execute(inliningTarget, nativeDelta)", limit = "1") static PTimeDelta doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject nativeDelta, @Bind PythonLanguage language, - @SuppressWarnings("unused") @Cached TimeDeltaCheckNode checkNode, + @SuppressWarnings("unused") @Cached PyDeltaCheckNode checkNode, @Cached CStructAccess.ReadI32Node readIntNode) { int days = getDays(nativeDelta, readIntNode); int seconds = getSeconds(nativeDelta, readIntNode); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java index 792e4de29b..98edbcaac0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -44,6 +44,7 @@ import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.type.TypeNodes; +import com.oracle.graal.python.lib.PyDeltaCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.util.CannotCastException; @@ -72,7 +73,7 @@ public static NewNode getUncached() { @Specialization static PTimeZone newTimezone(Node inliningTarget, PythonContext context, Object cls, Object offsetObj, Object nameObject, - @Cached TimeDeltaNodes.TimeDeltaCheckNode timeDeltaCheckNode, + @Cached PyDeltaCheckNode timeDeltaCheckNode, @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode, @Cached CastToTruffleStringNode castToTruffleStringNode, @Cached PRaiseNode raiseNode, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java index c82ad6b841..210277a5a7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java @@ -62,7 +62,6 @@ import com.oracle.graal.python.builtins.modules.datetime.PDate; import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes; -import com.oracle.graal.python.builtins.modules.datetime.TimeDeltaNodes; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.tuple.PTuple; @@ -72,6 +71,7 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun.HashBuiltinNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; import com.oracle.graal.python.lib.PyDateCheckNode; +import com.oracle.graal.python.lib.PyDeltaCheckNode; import com.oracle.graal.python.lib.PyLongAsLongNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectHashNode; @@ -174,10 +174,10 @@ static Object add(Object left, Object right, @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { Object dateObj; Object deltaObj; - if (dateLikeCheckNode.execute(inliningTarget, left) && TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + if (dateLikeCheckNode.execute(inliningTarget, left) && PyDeltaCheckNode.executeUncached(right)) { dateObj = left; deltaObj = right; - } else if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(left) && dateLikeCheckNode.execute(inliningTarget, right)) { + } else if (PyDeltaCheckNode.executeUncached(left) && dateLikeCheckNode.execute(inliningTarget, right)) { dateObj = right; deltaObj = left; } else { @@ -215,7 +215,7 @@ static Object sub(Object left, Object right, long rightDays = ChronoUnit.DAYS.between(from, rightDate) + 1; return new PTimeDelta(PythonBuiltinClassType.PTimeDelta, PythonBuiltinClassType.PTimeDelta.getInstanceShape(PythonLanguage.get(null)), (int) (leftDays - rightDays), 0, 0); } - if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + if (PyDeltaCheckNode.executeUncached(right)) { TemporalNodes.TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); long days = leftDays - delta.days; if (days <= 0 || days >= MAX_ORDINAL) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java index 46a16bb627..dc1fea8308 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java @@ -70,6 +70,7 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; import com.oracle.graal.python.lib.PyDateCheckNode; import com.oracle.graal.python.lib.PyDateTimeCheckNode; +import com.oracle.graal.python.lib.PyDeltaCheckNode; import com.oracle.graal.python.lib.PyLongAsLongNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectHashNode; @@ -215,10 +216,10 @@ static Object add(Object left, Object right, @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { Object dateTimeObj; Object deltaObj; - if (dateTimeLikeCheckNode.execute(inliningTarget, left) && TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + if (dateTimeLikeCheckNode.execute(inliningTarget, left) && PyDeltaCheckNode.executeUncached(right)) { dateTimeObj = left; deltaObj = right; - } else if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(left) && dateTimeLikeCheckNode.execute(inliningTarget, right)) { + } else if (PyDeltaCheckNode.executeUncached(left) && dateTimeLikeCheckNode.execute(inliningTarget, right)) { dateTimeObj = right; deltaObj = left; } else { @@ -259,7 +260,7 @@ static Object sub(VirtualFrame frame, Object left, Object right, return TimeDeltaNodes.NewNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PTimeDelta, 0, selfSeconds - otherSeconds, self.microsecond - other.microsecond, 0, 0, 0, 0); } - if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + if (PyDeltaCheckNode.executeUncached(right)) { TemporalNodes.TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); LocalDateTime adjusted = toLocalDateTime(self).minusDays(delta.days).minusSeconds(delta.seconds).minusNanos(delta.microseconds * 1_000L); return toPythonDateTime(adjusted, self.tzInfo, self.fold, inliningTarget); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDeltaCheckNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDeltaCheckNode.java new file mode 100644 index 0000000000..9a03066472 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDeltaCheckNode.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 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 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * 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. + */ +package com.oracle.graal.python.lib; + +import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; +import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +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.Specialization; +import com.oracle.truffle.api.nodes.Node; + +/** Equivalent of CPython's {@code PyDelta_Check}. */ +@GenerateUncached +@GenerateInline +@GenerateCached(false) +public abstract class PyDeltaCheckNode extends Node { + public static boolean executeUncached(Object object) { + return PyDeltaCheckNodeGen.getUncached().execute(null, object); + } + + public abstract boolean execute(Node inliningTarget, Object object); + + @Specialization + static boolean doManaged(@SuppressWarnings("unused") PTimeDelta value) { + return true; + } + + @Specialization + static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, + @Cached IsBuiltinObjectProfile profile) { + return profile.profileObject(inliningTarget, value, PythonBuiltinClassType.PTimeDelta); + } + + @Fallback + static boolean doOther(@SuppressWarnings("unused") Object value) { + return false; + } +} From d55e6ef870b07dfae5b7898d3e61997b532050d1 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Thu, 26 Mar 2026 17:53:15 +0100 Subject: [PATCH 15/30] Get rid of AsManaged*Nodes for datetime classes as nodes --- .../modules/datetime/DateTimeBuiltins.java | 95 +++++++++---------- .../modules/datetime/DateTimeNodes.java | 48 +--------- .../modules/datetime/TimeDeltaNodes.java | 39 +------- .../modules/datetime/TimeZoneNodes.java | 15 ++- .../objects/foreign/ForeignDateBuiltins.java | 5 +- .../foreign/ForeignTimeZoneBuiltins.java | 6 +- 6 files changed, 67 insertions(+), 141 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java index 83a576c894..0eece93116 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java @@ -582,8 +582,8 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp } return PNotImplemented.NOT_IMPLEMENTED; } - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); - PDateTime other = DateTimeNodes.AsManagedDateTimeNode.executeUncached(otherObj); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); + DateTimeValue other = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, otherObj); // either naive datetimes (without timezone) or timezones are exactly the same objects if (self.tzInfo == other.tzInfo) { int result = compareDateTimeComponents(self, other); @@ -636,7 +636,7 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp } @TruffleBoundary - private static int compareDateTimeComponents(PDateTime self, PDateTime other) { + private static int compareDateTimeComponents(DateTimeValue self, DateTimeValue other) { // compare only year, month, day, hours, minutes, ... and ignore fold int[] selfComponents = new int[]{self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond}; int[] otherComponents = new int[]{other.year, other.month, other.day, other.hour, other.minute, other.second, other.microsecond}; @@ -649,12 +649,12 @@ private static int compareDateTimeComponents(PDateTime self, PDateTime other) { * 495 – Local Time Disambiguation". See PEP 495 * – Local Time Disambiguation */ - private static boolean isExceptionInPep495(Object selfObj, PDateTime self, PTimeDelta selfUtcOffset, PDateTime other, PTimeDelta otherUtcOffset, Node inliningTarget) { + private static boolean isExceptionInPep495(Object selfObj, DateTimeValue self, PTimeDelta selfUtcOffset, DateTimeValue other, PTimeDelta otherUtcOffset, Node inliningTarget) { return isExceptionInPep495(selfObj, self, selfUtcOffset, inliningTarget) || isExceptionInPep495(selfObj, other, otherUtcOffset, inliningTarget); } @TruffleBoundary - private static boolean isExceptionInPep495(Object dateTimeObj, PDateTime dateTime, PTimeDelta utcOffset, Node inliningTarget) { + private static boolean isExceptionInPep495(Object dateTimeObj, DateTimeValue dateTime, PTimeDelta utcOffset, Node inliningTarget) { Object cls = GetClassNode.executeUncached(dateTimeObj); Shape shape = TypeNodes.GetInstanceShape.getUncached().execute(cls); int fold = dateTime.fold == 1 ? 0 : 1; @@ -677,20 +677,20 @@ static long hash(VirtualFrame frame, Object selfObj, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode, @Cached TypeNodes.GetInstanceShape getInstanceShape) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); final PTimeDelta offset; if (self.tzInfo == null) { offset = null; } else { // ignore fold in calculating utc offset - final PDateTime getUtcOffsetFrom; + final Object getUtcOffsetFrom; if (self.fold == 1) { // reset fold Object cls = GetClassNode.executeUncached(selfObj); Shape shape = getInstanceShape.execute(cls); getUtcOffsetFrom = new PDateTime(cls, shape, self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond, self.tzInfo, 0); } else { - getUtcOffsetFrom = self; + getUtcOffsetFrom = selfObj; } offset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, getUtcOffsetFrom, frame, inliningTarget, callMethodObjArgs, raiseNode); @@ -704,12 +704,12 @@ static long hash(VirtualFrame frame, Object selfObj, } @TruffleBoundary - private static long getHashForDateTime(PDateTime self) { + private static long getHashForDateTime(DateTimeValue self) { return Objects.hash(self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond); } @TruffleBoundary - private static long getHashForDateTimeWithOffset(PDateTime self, PTimeDelta offset) { + private static long getHashForDateTimeWithOffset(DateTimeValue self, PTimeDelta offset) { LocalDateTime utc = subtractOffsetFromDateTime(self, offset); return Objects.hash(utc.getYear(), utc.getMonthValue(), utc.getDayOfMonth(), utc.getHour(), utc.getMinute(), utc.getSecond(), utc.getNano() / 1_000); } @@ -786,15 +786,12 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget if (!PyDateTimeCheckNode.executeUncached(left)) { return PNotImplemented.NOT_IMPLEMENTED; } - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(left); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, left); if (PyDateTimeCheckNode.executeUncached(right)) { - PDateTime other = DateTimeNodes.AsManagedDateTimeNode.executeUncached(right); - - final PTimeDelta selfOffset; - final PTimeDelta otherOffset; + DateTimeValue other = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, right); - selfOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, self, inliningTarget); - otherOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, other, inliningTarget); + final PTimeDelta selfOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, left, inliningTarget); + final PTimeDelta otherOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, right, inliningTarget); if ((selfOffset == null) != (otherOffset == null)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.CANNOT_SUBTRACT_OFFSET_NAIVE_AND_OFFSET_AWARE_DATETIMES); @@ -807,8 +804,8 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget selfToCompare = subtractOffsetFromDateTime(self, selfOffset); otherToCompare = subtractOffsetFromDateTime(other, otherOffset); } else { - selfToCompare = toLocalDateTime(self); - otherToCompare = toLocalDateTime(other); + selfToCompare = self.toLocalDateTime(); + otherToCompare = other.toLocalDateTime(); } long selfSeconds = selfToCompare.toEpochSecond(ZoneOffset.UTC); @@ -824,8 +821,8 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget 0, 0); } else if (PyDeltaCheckNode.executeUncached(right)) { - PTimeDelta timeDelta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(right); - LocalDateTime local = toLocalDateTime(self); + TimeDeltaValue timeDelta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); + LocalDateTime local = self.toLocalDateTime(); LocalDateTime localAdjusted = local.minusDays(timeDelta.days).minusSeconds(timeDelta.seconds).minusNanos(timeDelta.microseconds * 1_000L); if (localAdjusted.getYear() < MIN_YEAR || localAdjusted.getYear() > MAX_YEAR) { @@ -2681,23 +2678,23 @@ static Object inTimeZone(VirtualFrame frame, Object self, Object tzInfo, @TruffleBoundary private static Object inTimeZoneBoundary(Object selfObj, Object tzInfo, Node inliningTarget) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); if (tzInfo == self.tzInfo) { - return self; + return selfObj; } Object sourceTimeZone; if (self.tzInfo != null) { sourceTimeZone = self.tzInfo; } else { - sourceTimeZone = getSystemTimeZoneAt(toLocalDateTime(self), self.fold, inliningTarget); + sourceTimeZone = getSystemTimeZoneAt(self.toLocalDateTime(), self.fold, inliningTarget); } - PTimeDelta sourceOffset = DatetimeModuleBuiltins.callUtcOffset(sourceTimeZone, self, inliningTarget); + PTimeDelta sourceOffset = DatetimeModuleBuiltins.callUtcOffset(sourceTimeZone, selfObj, inliningTarget); if (sourceOffset == null) { - sourceTimeZone = getSystemTimeZoneAt(toLocalDateTime(self), self.fold, inliningTarget); - sourceOffset = DatetimeModuleBuiltins.callUtcOffset(sourceTimeZone, self, inliningTarget); + sourceTimeZone = getSystemTimeZoneAt(self.toLocalDateTime(), self.fold, inliningTarget); + sourceOffset = DatetimeModuleBuiltins.callUtcOffset(sourceTimeZone, selfObj, inliningTarget); } LocalDateTime selfAsLocalDateTimeInUtc = subtractOffsetFromDateTime(self, sourceOffset); @@ -2707,7 +2704,7 @@ private static Object inTimeZoneBoundary(Object selfObj, Object tzInfo, Node inl final Object targetTimeZone; if (tzInfo instanceof PNone) { - targetTimeZone = getSystemTimeZoneAt(toLocalDateTime(self), self.fold, inliningTarget); + targetTimeZone = getSystemTimeZoneAt(self.toLocalDateTime(), self.fold, inliningTarget); } else if (!PyTZInfoCheckNode.executeUncached(tzInfo)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.TZINFO_ARGUMENT_MUST_BE_NONE_OR_OF_A_TZINFO_SUBCLASS_NOT_TYPE_P, tzInfo); } else { @@ -2858,20 +2855,20 @@ static PTuple composeTimeTuple(VirtualFrame frame, Object self, @TruffleBoundary private static PTuple composeTimeTupleBoundary(Object selfObj, Node inliningTarget, PythonLanguage language) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); int dayOfWeek = localDate.getDayOfWeek().getValue() - 1; // Python's day of week range // is 0-6 int dayOfYear = localDate.getDayOfYear(); - int isDst = getIsDst(self, inliningTarget); + int isDst = getIsDst(self.tzInfo, selfObj, inliningTarget); Object[] fields = new Object[]{self.year, self.month, self.day, self.hour, self.minute, self.second, dayOfWeek, dayOfYear, isDst}; return PFactory.createStructSeq(language, TimeModuleBuiltins.STRUCT_TIME_DESC, fields); } - private static int getIsDst(PDateTime self, Node inliningTarget) { + private static int getIsDst(Object tzInfo, Object selfObj, Node inliningTarget) { int isDst; - PTimeDelta offset = DatetimeModuleBuiltins.callDst(self.tzInfo, self, inliningTarget); + PTimeDelta offset = DatetimeModuleBuiltins.callDst(tzInfo, selfObj, inliningTarget); if (offset == null) { isDst = -1; @@ -2908,12 +2905,12 @@ static PTuple composeTimeTuple(VirtualFrame frame, Object self, @TruffleBoundary private static PTuple composeTimeTupleBoundary(Object selfObj, Node inliningTarget, PythonLanguage language) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); final LocalDateTime localDateTime; - PTimeDelta offset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, self, inliningTarget); + PTimeDelta offset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, selfObj, inliningTarget); if (offset == null) { - localDateTime = toLocalDateTime(self); + localDateTime = self.toLocalDateTime(); } else { // convert self to UTC localDateTime = subtractOffsetFromDateTime(self, offset); @@ -2968,13 +2965,13 @@ static double toTimestamp(VirtualFrame frame, Object self, @TruffleBoundary private static double toTimestampBoundary(Object selfObj, Node inliningTarget) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); if (self.tzInfo == null) { // CPython: local_to_seconds() TimeZone timeZone = TimeModuleBuiltins.getGlobalTimeZone(getContext(inliningTarget)); ZoneId zoneId = timeZone.toZoneId(); - LocalDateTime localDateTime = toLocalDateTime(self); + LocalDateTime localDateTime = self.toLocalDateTime(); ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, zoneId); if (localDateTime.equals(zonedDateTime.toLocalDateTime())) { @@ -3001,10 +2998,10 @@ private static double toTimestampBoundary(Object selfObj, Node inliningTarget) { } } else { final LocalDateTime localDateTime; - PTimeDelta offset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, self, inliningTarget); + PTimeDelta offset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, selfObj, inliningTarget); if (offset == null) { - localDateTime = toLocalDateTime(self); + localDateTime = self.toLocalDateTime(); } else { // convert self to UTC localDateTime = subtractOffsetFromDateTime(self, offset); @@ -3038,7 +3035,7 @@ static TruffleString isoFormat(VirtualFrame frame, Object self, Object separator @TruffleBoundary private static TruffleString isoFormatBoundary(Object selfObj, Object separatorObject, Object timespecObject, Node inliningTarget) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); String dateSection = PythonUtils.formatJString("%04d-%02d-%02d", self.year, self.month, self.day); @@ -3128,7 +3125,7 @@ private static TruffleString isoFormatBoundary(Object selfObj, Object separatorO ErrorMessages.UNKNOWN_TIMESPEC_VALUE); } - Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, self, true, inliningTarget); + Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, selfObj, true, inliningTarget); builder.append(utcOffsetString); return TruffleString.FromJavaStringNode.getUncached().execute(builder.toString(), TS_ENCODING); @@ -3177,7 +3174,7 @@ static TruffleString strftime(VirtualFrame frame, Object self, TruffleString for @TruffleBoundary private static TruffleString strftimeBoundary(Object selfObj, TruffleString format, Node inliningTarget) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); // Reuse time.strftime(format, time_tuple) method. // construct time_tuple @@ -3186,7 +3183,7 @@ private static TruffleString strftimeBoundary(Object selfObj, TruffleString form int dayOfYear = localDate.getDayOfYear(); int[] timeTuple = new int[]{self.year, self.month, self.day, self.hour, self.minute, self.second, dayOfWeek, dayOfYear, -1}; - String formatPreprocessed = preprocessFormat(format, self, inliningTarget); + String formatPreprocessed = preprocessFormat(format, self, selfObj, inliningTarget); return TimeModuleBuiltins.StrfTimeNode.format(formatPreprocessed, timeTuple, TruffleString.FromJavaStringNode.getUncached()); } @@ -3194,7 +3191,7 @@ private static TruffleString strftimeBoundary(Object selfObj, TruffleString form // The datetime.datetime.strftime() method supports some extra formatters - %f, %z, %:z, // and %Z so handle them here. // CPython: wrap_strftime() - private static String preprocessFormat(TruffleString tsformat, PDateTime self, Node inliningTarget) { + private static String preprocessFormat(TruffleString tsformat, DateTimeValue self, Object selfObj, Node inliningTarget) { String format = tsformat.toString(); StringBuilder builder = new StringBuilder(); int i = 0; @@ -3217,13 +3214,13 @@ private static String preprocessFormat(TruffleString tsformat, PDateTime self, N char c = format.charAt(p + 1); if (c == 'z') { - Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, self, false, inliningTarget); + Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, selfObj, false, inliningTarget); builder.append(utcOffsetString); i = p + 2; } else if (c == 'Z') { if (self.tzInfo != null) { // call tzname() - Object tzNameObject = PyObjectCallMethodObjArgs.executeUncached(self.tzInfo, T_TZNAME, self); + Object tzNameObject = PyObjectCallMethodObjArgs.executeUncached(self.tzInfo, T_TZNAME, selfObj); // ignore None value if (tzNameObject != PNone.NONE) { @@ -3254,7 +3251,7 @@ private static String preprocessFormat(TruffleString tsformat, PDateTime self, N char d = format.charAt(p + 2); if (d == 'z') { - Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, self, true, inliningTarget); + Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, selfObj, true, inliningTarget); builder.append(utcOffsetString); i = p + 3; @@ -3270,8 +3267,8 @@ private static String preprocessFormat(TruffleString tsformat, PDateTime self, N } @TruffleBoundary - private static LocalDateTime subtractOffsetFromDateTime(PDateTime self, PTimeDelta offset) { - return toLocalDateTime(self).minusDays(offset.days).minusSeconds(offset.seconds).minusNanos(offset.microseconds * 1_000L); + private static LocalDateTime subtractOffsetFromDateTime(DateTimeValue self, PTimeDelta offset) { + return self.toLocalDateTime().minusDays(offset.days).minusSeconds(offset.seconds).minusNanos(offset.microseconds * 1_000L); } @TruffleBoundary diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java index 7eac30a311..61a03e00b7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java @@ -48,7 +48,6 @@ import java.time.YearMonth; -import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; @@ -60,7 +59,6 @@ import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.type.TypeNodes; -import com.oracle.graal.python.lib.PyDateTimeCheckNode; import com.oracle.graal.python.lib.PyTZInfoCheckNode; import com.oracle.graal.python.lib.PyLongAsIntNode; import com.oracle.graal.python.nodes.ErrorMessages; @@ -69,7 +67,6 @@ import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.runtime.PythonContext; 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.Fallback; import com.oracle.truffle.api.dsl.GenerateCached; @@ -293,50 +290,7 @@ static boolean isBuiltinClass(Object cls) { } } - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class AsManagedDateTimeNode extends Node { - - public abstract PDateTime execute(Node inliningTarget, Object obj); - - public static PDateTime executeUncached(Object obj) { - return DateTimeNodesFactory.AsManagedDateTimeNodeGen.getUncached().execute(null, obj); - } - - @Specialization - static PDateTime asManaged(PDateTime obj) { - return obj; - } - - @Specialization(guards = "checkNode.execute(inliningTarget, obj)", limit = "1") - static PDateTime asManagedNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject obj, - @Bind PythonLanguage language, - @SuppressWarnings("unused") @Cached PyDateTimeCheckNode checkNode, - @Cached CStructAccess.ReadByteNode readByteNode, - @Cached CStructAccess.ReadObjectNode readObjectNode) { - int year = getYear(obj, readByteNode); - int month = getMonth(obj, readByteNode); - int day = getDay(obj, readByteNode); - - int hour = getHour(obj, readByteNode); - int minute = getMinute(obj, readByteNode); - int second = getSecond(obj, readByteNode); - int microsecond = getMicrosecond(obj, readByteNode); - - Object tzInfo = getTzInfo(obj, readByteNode, readObjectNode); - int fold = getFold(obj, readByteNode); - - PythonBuiltinClassType cls = PythonBuiltinClassType.PDateTime; - return new PDateTime(cls, cls.getInstanceShape(language), year, month, day, hour, minute, second, microsecond, tzInfo, fold); - } - - @Fallback - static PDateTime error(Object obj, - @Bind Node inliningTarget) { - throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_EXPECTED_GOT_P, "datetime", obj); - } - + public static final class AsManagedDateTimeNode { static int getYear(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { int b0 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_DateTime__data, 0); int b1 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_DateTime__data, 1); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaNodes.java index 444fcb5e19..d2ea52f2bf 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaNodes.java @@ -45,7 +45,6 @@ import java.math.BigInteger; -import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; @@ -58,7 +57,6 @@ import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyFloatAsDoubleNode; import com.oracle.graal.python.lib.PyFloatCheckNode; -import com.oracle.graal.python.lib.PyDeltaCheckNode; import com.oracle.graal.python.lib.PyLongAsDoubleNode; import com.oracle.graal.python.lib.PyLongCheckNode; import com.oracle.graal.python.lib.PyLongFromDoubleNode; @@ -68,9 +66,7 @@ import com.oracle.graal.python.nodes.util.CastToJavaBigIntegerNode; import com.oracle.graal.python.runtime.PythonContext; 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.Fallback; import com.oracle.truffle.api.dsl.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; @@ -239,34 +235,7 @@ public BigInteger getTotalMicroseconds() { } } - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class AsManagedTimeDeltaNode extends Node { - public abstract PTimeDelta execute(Node inliningTarget, Object obj); - - public static PTimeDelta executeUncached(Object obj) { - return TimeDeltaNodesFactory.AsManagedTimeDeltaNodeGen.getUncached().execute(null, obj); - } - - @Specialization - static PTimeDelta doPTimeDelta(PTimeDelta value) { - return value; - } - - @Specialization(guards = "checkNode.execute(inliningTarget, nativeDelta)", limit = "1") - static PTimeDelta doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject nativeDelta, - @Bind PythonLanguage language, - @SuppressWarnings("unused") @Cached PyDeltaCheckNode checkNode, - @Cached CStructAccess.ReadI32Node readIntNode) { - int days = getDays(nativeDelta, readIntNode); - int seconds = getSeconds(nativeDelta, readIntNode); - int microseconds = getMicroseconds(nativeDelta, readIntNode); - - PythonBuiltinClassType cls = PythonBuiltinClassType.PTimeDelta; - return new PTimeDelta(cls, cls.getInstanceShape(language), days, seconds, microseconds); - } - + public static final class AsManagedTimeDeltaNode { static int getDays(PythonAbstractNativeObject self, CStructAccess.ReadI32Node readNode) { return readNode.readFromObj(self, CFields.PyDateTime_Delta__days); } @@ -278,11 +247,5 @@ static int getSeconds(PythonAbstractNativeObject self, CStructAccess.ReadI32Node static int getMicroseconds(PythonAbstractNativeObject self, CStructAccess.ReadI32Node readNode) { return readNode.readFromObj(self, CFields.PyDateTime_Delta__microseconds); } - - @Fallback - static PTimeDelta error(Object obj, - @Bind Node inliningTarget) { - throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_EXPECTED_GOT_P, "timedelta", obj); - } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java index 98edbcaac0..23cec3277f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java @@ -42,6 +42,9 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; +import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.TimeDeltaValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyDeltaCheckNode; @@ -50,6 +53,7 @@ import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.PythonContext; +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; @@ -73,8 +77,8 @@ public static NewNode getUncached() { @Specialization static PTimeZone newTimezone(Node inliningTarget, PythonContext context, Object cls, Object offsetObj, Object nameObject, + @Bind PythonLanguage language, @Cached PyDeltaCheckNode timeDeltaCheckNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode, @Cached CastToTruffleStringNode castToTruffleStringNode, @Cached PRaiseNode raiseNode, @Cached TypeNodes.GetInstanceShape getInstanceShape) { @@ -87,7 +91,14 @@ static PTimeZone newTimezone(Node inliningTarget, PythonContext context, Object "datetime.timedelta", offsetObj); } - PTimeDelta offset = asManagedTimeDeltaNode.execute(inliningTarget, offsetObj); + PTimeDelta offset; + if (offsetObj instanceof PTimeDelta value) { + offset = value; + } else { + TimeDeltaValue offsetValue = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, offsetObj); + PythonBuiltinClassType tdcls = PythonBuiltinClassType.PTimeDelta; + offset = new PTimeDelta(tdcls, tdcls.getInstanceShape(language), offsetValue.days, offsetValue.seconds, offsetValue.microseconds); + } final TruffleString name; if (nameObject == PNone.NO_VALUE) { name = null; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java index 210277a5a7..f75d4207d4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java @@ -62,6 +62,7 @@ import com.oracle.graal.python.builtins.modules.datetime.PDate; import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes; +import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.TimeDeltaValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.tuple.PTuple; @@ -185,7 +186,7 @@ static Object add(Object left, Object right, } LocalDate date = readDateValueNode.execute(inliningTarget, dateObj).toLocalDate(); - TemporalNodes.TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, deltaObj); + TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, deltaObj); long days = ChronoUnit.DAYS.between(LocalDate.of(1, 1, 1), date) + 1 + delta.days; if (days <= 0 || days > MAX_ORDINAL) { throw com.oracle.graal.python.nodes.PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); @@ -216,7 +217,7 @@ static Object sub(Object left, Object right, return new PTimeDelta(PythonBuiltinClassType.PTimeDelta, PythonBuiltinClassType.PTimeDelta.getInstanceShape(PythonLanguage.get(null)), (int) (leftDays - rightDays), 0, 0); } if (PyDeltaCheckNode.executeUncached(right)) { - TemporalNodes.TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); + TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); long days = leftDays - delta.days; if (days <= 0 || days >= MAX_ORDINAL) { throw com.oracle.graal.python.nodes.PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java index 4d7b37f28c..9081eb4260 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java @@ -58,9 +58,9 @@ import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.datetime.DateTimeNodes; import com.oracle.graal.python.builtins.modules.datetime.DatetimeModuleBuiltins; -import com.oracle.graal.python.builtins.modules.datetime.PDateTime; import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes; import com.oracle.graal.python.builtins.modules.datetime.TimeDeltaNodes; +import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.DateTimeValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.lib.PyDateTimeCheckNode; @@ -204,12 +204,12 @@ static Object fromutc(Object self, Object dateTime, if (!dateTimeLikeCheckNode.execute(inliningTarget, dateTime)) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); } - PDateTime asDateTime = (PDateTime) DateTimeNodes.AsManagedDateTimeNode.executeUncached(dateTime); + DateTimeValue asDateTime = readDateTimeValueNode.execute(inliningTarget, dateTime); if (asDateTime.tzInfo != self) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.ValueError, ErrorMessages.FROMUTC_DT_TZINFO_IS_NOT_SELF); } ZoneId zoneId = asZoneId(self, interop); - LocalDateTime utcDateTime = readDateTimeValueNode.execute(inliningTarget, dateTime).toLocalDateTime(); + LocalDateTime utcDateTime = asDateTime.toLocalDateTime(); ZonedDateTime zonedDateTime = utcDateTime.atOffset(java.time.ZoneOffset.UTC).atZoneSameInstant(zoneId); return DateTimeNodes.NewUnsafeNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PDateTime, zonedDateTime.getYear(), zonedDateTime.getMonthValue(), zonedDateTime.getDayOfMonth(), zonedDateTime.getHour(), zonedDateTime.getMinute(), zonedDateTime.getSecond(), zonedDateTime.getNano() / 1_000, self, 0); From 964c637e5317f551a2057d592a68795e918dc5e7 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Fri, 27 Mar 2026 09:18:26 +0100 Subject: [PATCH 16/30] Rename AsManaged*Nodes for datetime classes now they are just from-native helpers --- .../modules/datetime/DateBuiltins.java | 6 ++--- .../builtins/modules/datetime/DateNodes.java | 2 +- .../modules/datetime/DateTimeBuiltins.java | 10 ++++----- .../modules/datetime/DateTimeNodes.java | 4 ++-- .../modules/datetime/TemporalNodes.java | 22 +++++++++---------- .../modules/datetime/TimeBuiltins.java | 10 ++++----- .../modules/datetime/TimeDeltaBuiltins.java | 6 ++--- .../modules/datetime/TimeDeltaNodes.java | 2 +- .../builtins/modules/datetime/TimeNodes.java | 4 ++-- 9 files changed, 33 insertions(+), 33 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java index 292f8e847c..ad689059f3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java @@ -464,7 +464,7 @@ static int getYear(PDate self) { @Specialization static int getYear(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { - return DateNodes.AsManagedDateNode.getYear(self, readNode); + return DateNodes.FromNative.getYear(self, readNode); } } @@ -480,7 +480,7 @@ static int getMonth(PDate self) { @Specialization static int getMonth(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { - return DateNodes.AsManagedDateNode.getMonth(self, readNode); + return DateNodes.FromNative.getMonth(self, readNode); } } @@ -496,7 +496,7 @@ static int getDay(PDate self) { @Specialization static int getDay(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { - return DateNodes.AsManagedDateNode.getDay(self, readNode); + return DateNodes.FromNative.getDay(self, readNode); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateNodes.java index dd6621b8a0..9d5d0e43fd 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateNodes.java @@ -181,7 +181,7 @@ static boolean isBuiltinClass(Object cls) { } } - public static final class AsManagedDateNode { + public static final class FromNative { static int getYear(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { int b0 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_Date__data, 0); int b1 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_Date__data, 1); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java index 0eece93116..3eadf6949c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java @@ -847,7 +847,7 @@ static int getHour(PDateTime self) { @Specialization static int getHour(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readByteNode) { - return DateTimeNodes.AsManagedDateTimeNode.getHour(self, readByteNode); + return DateTimeNodes.FromNative.getHour(self, readByteNode); } } @@ -863,7 +863,7 @@ static int getMinute(PDateTime self) { @Specialization static int getMinute(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { - return DateTimeNodes.AsManagedDateTimeNode.getMinute(self, readNode); + return DateTimeNodes.FromNative.getMinute(self, readNode); } } @@ -879,7 +879,7 @@ static int getSecond(PDateTime self) { @Specialization static int getSecond(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { - return DateTimeNodes.AsManagedDateTimeNode.getSecond(self, readNode); + return DateTimeNodes.FromNative.getSecond(self, readNode); } } @@ -895,7 +895,7 @@ static int getMicrosecond(PDateTime self) { @Specialization static int getMicrosecond(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { - return DateTimeNodes.AsManagedDateTimeNode.getMicrosecond(self, readNode); + return DateTimeNodes.FromNative.getMicrosecond(self, readNode); } } @@ -924,7 +924,7 @@ static int getFold(PDateTime self) { @Specialization static int getFold(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { - return DateTimeNodes.AsManagedDateTimeNode.getFold(self, readNode); + return DateTimeNodes.FromNative.getFold(self, readNode); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java index 61a03e00b7..059a38a5fd 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java @@ -290,7 +290,7 @@ static boolean isBuiltinClass(Object cls) { } } - public static final class AsManagedDateTimeNode { + public static final class FromNative { static int getYear(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { int b0 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_DateTime__data, 0); int b1 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_DateTime__data, 1); @@ -356,7 +356,7 @@ static Object getTzInfo(PDateTime self) { static Object getTzInfo(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readByteNode, @Cached CStructAccess.ReadObjectNode readObjectNode) { - return AsManagedDateTimeNode.getTzInfo(self, readByteNode, readObjectNode); + return FromNative.getTzInfo(self, readByteNode, readObjectNode); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java index d4826d19a6..2db71e560e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java @@ -270,8 +270,8 @@ static TimeDeltaValue doManaged(PTimeDelta value) { static TimeDeltaValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject value, @SuppressWarnings("unused") @Cached PyDeltaCheckNode checkNode, @Cached CStructAccess.ReadI32Node readIntNode) { - return new TimeDeltaValue(TimeDeltaNodes.AsManagedTimeDeltaNode.getDays(value, readIntNode), TimeDeltaNodes.AsManagedTimeDeltaNode.getSeconds(value, readIntNode), - TimeDeltaNodes.AsManagedTimeDeltaNode.getMicroseconds(value, readIntNode)); + return new TimeDeltaValue(TimeDeltaNodes.FromNative.getDays(value, readIntNode), TimeDeltaNodes.FromNative.getSeconds(value, readIntNode), + TimeDeltaNodes.FromNative.getMicroseconds(value, readIntNode)); } @Fallback @@ -300,7 +300,7 @@ static DateValue doManaged(PDate value) { static DateValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject value, @SuppressWarnings("unused") @Cached PyDateCheckNode checkNode, @Cached CStructAccess.ReadByteNode readNode) { - return new DateValue(DateNodes.AsManagedDateNode.getYear(value, readNode), DateNodes.AsManagedDateNode.getMonth(value, readNode), DateNodes.AsManagedDateNode.getDay(value, readNode)); + return new DateValue(DateNodes.FromNative.getYear(value, readNode), DateNodes.FromNative.getMonth(value, readNode), DateNodes.FromNative.getDay(value, readNode)); } @Specialization(guards = {"isForeignObjectNode.execute(inliningTarget, value)", "interop.isDate(value)"}, limit = "1") @@ -342,9 +342,9 @@ static TimeValue doNative(@SuppressWarnings("unused") Node inliningTarget, Pytho @SuppressWarnings("unused") @Cached PyTimeCheckNode checkNode, @Cached CStructAccess.ReadByteNode readByteNode, @Cached CStructAccess.ReadObjectNode readObjectNode) { - return new TimeValue(TimeNodes.AsManagedTimeNode.getHour(value, readByteNode), TimeNodes.AsManagedTimeNode.getMinute(value, readByteNode), - TimeNodes.AsManagedTimeNode.getSecond(value, readByteNode), TimeNodes.AsManagedTimeNode.getMicrosecond(value, readByteNode), - TimeNodes.AsManagedTimeNode.getTzInfo(value, readByteNode, readObjectNode), null, TimeNodes.AsManagedTimeNode.getFold(value, readByteNode)); + return new TimeValue(TimeNodes.FromNative.getHour(value, readByteNode), TimeNodes.FromNative.getMinute(value, readByteNode), + TimeNodes.FromNative.getSecond(value, readByteNode), TimeNodes.FromNative.getMicrosecond(value, readByteNode), + TimeNodes.FromNative.getTzInfo(value, readByteNode, readObjectNode), null, TimeNodes.FromNative.getFold(value, readByteNode)); } @Specialization(guards = {"isForeignObjectNode.execute(inliningTarget, value)", "interop.isTime(value)"}, limit = "1") @@ -387,11 +387,11 @@ static DateTimeValue doNative(@SuppressWarnings("unused") Node inliningTarget, P @SuppressWarnings("unused") @Cached PyDateTimeCheckNode checkNode, @Cached CStructAccess.ReadByteNode readByteNode, @Cached CStructAccess.ReadObjectNode readObjectNode) { - return new DateTimeValue(DateTimeNodes.AsManagedDateTimeNode.getYear(value, readByteNode), DateTimeNodes.AsManagedDateTimeNode.getMonth(value, readByteNode), - DateTimeNodes.AsManagedDateTimeNode.getDay(value, readByteNode), DateTimeNodes.AsManagedDateTimeNode.getHour(value, readByteNode), - DateTimeNodes.AsManagedDateTimeNode.getMinute(value, readByteNode), DateTimeNodes.AsManagedDateTimeNode.getSecond(value, readByteNode), - DateTimeNodes.AsManagedDateTimeNode.getMicrosecond(value, readByteNode), DateTimeNodes.AsManagedDateTimeNode.getTzInfo(value, readByteNode, readObjectNode), null, - DateTimeNodes.AsManagedDateTimeNode.getFold(value, readByteNode)); + return new DateTimeValue(DateTimeNodes.FromNative.getYear(value, readByteNode), DateTimeNodes.FromNative.getMonth(value, readByteNode), + DateTimeNodes.FromNative.getDay(value, readByteNode), DateTimeNodes.FromNative.getHour(value, readByteNode), + DateTimeNodes.FromNative.getMinute(value, readByteNode), DateTimeNodes.FromNative.getSecond(value, readByteNode), + DateTimeNodes.FromNative.getMicrosecond(value, readByteNode), DateTimeNodes.FromNative.getTzInfo(value, readByteNode, readObjectNode), null, + DateTimeNodes.FromNative.getFold(value, readByteNode)); } @Specialization(guards = {"isForeignObjectNode.execute(inliningTarget, value)", "interop.isDate(value)", "interop.isTime(value)"}, limit = "1") diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java index ca8739e50e..28a6333e91 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java @@ -486,7 +486,7 @@ static int getHour(PTime self) { @Specialization static int getHour(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { - return TimeNodes.AsManagedTimeNode.getHour(self, readNode); + return TimeNodes.FromNative.getHour(self, readNode); } } @@ -502,7 +502,7 @@ static int getMinute(PTime self) { @Specialization static int getMinute(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { - return TimeNodes.AsManagedTimeNode.getMinute(self, readNode); + return TimeNodes.FromNative.getMinute(self, readNode); } } @@ -518,7 +518,7 @@ static int getSecond(PTime self) { @Specialization static int getSecond(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { - return TimeNodes.AsManagedTimeNode.getSecond(self, readNode); + return TimeNodes.FromNative.getSecond(self, readNode); } } @@ -534,7 +534,7 @@ static int getMicrosecond(PTime self) { @Specialization static int getMicrosecond(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { - return TimeNodes.AsManagedTimeNode.getMicrosecond(self, readNode); + return TimeNodes.FromNative.getMicrosecond(self, readNode); } } @@ -563,7 +563,7 @@ static int getFold(PTime self) { @Specialization static int getFold(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { - return TimeNodes.AsManagedTimeNode.getFold(self, readNode); + return TimeNodes.FromNative.getFold(self, readNode); } } 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 3d9eca050f..0d1fbbec08 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 @@ -656,7 +656,7 @@ static int getDays(PTimeDelta self) { @Specialization static int getDays(PythonAbstractNativeObject self, @Cached CStructAccess.ReadI32Node readNode) { - return TimeDeltaNodes.AsManagedTimeDeltaNode.getDays(self, readNode); + return TimeDeltaNodes.FromNative.getDays(self, readNode); } } @@ -672,7 +672,7 @@ static int getSeconds(PTimeDelta self) { @Specialization static int getSeconds(PythonAbstractNativeObject self, @Cached CStructAccess.ReadI32Node readNode) { - return TimeDeltaNodes.AsManagedTimeDeltaNode.getSeconds(self, readNode); + return TimeDeltaNodes.FromNative.getSeconds(self, readNode); } } @@ -688,7 +688,7 @@ static int getMicroseconds(PTimeDelta self) { @Specialization static int getMicroseconds(PythonAbstractNativeObject self, @Cached CStructAccess.ReadI32Node readNode) { - return TimeDeltaNodes.AsManagedTimeDeltaNode.getMicroseconds(self, readNode); + return TimeDeltaNodes.FromNative.getMicroseconds(self, readNode); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaNodes.java index d2ea52f2bf..4ee7d60e23 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaNodes.java @@ -235,7 +235,7 @@ public BigInteger getTotalMicroseconds() { } } - public static final class AsManagedTimeDeltaNode { + public static final class FromNative { static int getDays(PythonAbstractNativeObject self, CStructAccess.ReadI32Node readNode) { return readNode.readFromObj(self, CFields.PyDateTime_Delta__days); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java index 6b8b822dc3..fa00394f4d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java @@ -228,7 +228,7 @@ static boolean isBuiltinClass(Object cls) { } } - public static final class AsManagedTimeNode { + public static final class FromNative { static int getHour(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { return readNode.readFromObjUnsigned(self, CFields.PyDateTime_Time__data, 0); } @@ -280,7 +280,7 @@ static Object getTzInfo(PTime self) { static Object getTzInfo(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readByteNode, @Cached CStructAccess.ReadObjectNode readObjectNode) { - return AsManagedTimeNode.getTzInfo(self, readByteNode, readObjectNode); + return FromNative.getTzInfo(self, readByteNode, readObjectNode); } } } From cf330671fa68bd6ad058fc7d121a8372999212f4 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Fri, 27 Mar 2026 09:21:52 +0100 Subject: [PATCH 17/30] Rename nodes to get intermediate time values --- .../modules/datetime/DateBuiltins.java | 38 ++++++------ .../modules/datetime/DateTimeBuiltins.java | 50 ++++++++-------- .../datetime/DatetimeModuleBuiltins.java | 2 +- .../modules/datetime/TemporalNodes.java | 16 ++--- .../modules/datetime/TimeBuiltins.java | 18 +++--- .../modules/datetime/TimeDeltaBuiltins.java | 38 ++++++------ .../modules/datetime/TimeZoneNodes.java | 2 +- .../objects/foreign/ForeignDateBuiltins.java | 40 ++++++------- .../foreign/ForeignDateTimeBuiltins.java | 60 +++++++++---------- .../objects/foreign/ForeignTimeBuiltins.java | 32 +++++----- .../foreign/ForeignTimeZoneBuiltins.java | 8 +-- 11 files changed, 152 insertions(+), 152 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java index ad689059f3..d425c4ad1f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java @@ -251,7 +251,7 @@ public abstract static class ReprNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString repr(Object self, @Bind Node inliningTarget) { - DateValue date = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, self); + DateValue date = TemporalNodes.GetDateValue.executeUncached(inliningTarget, self); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(self)); var string = String.format("%s(%d, %d, %d)", typeName, date.year, date.month, date.day); return TruffleString.FromJavaStringNode.getUncached().execute(string, TS_ENCODING); @@ -266,7 +266,7 @@ public abstract static class StrNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString str(Object self, @Bind Node inliningTarget) { - DateValue date = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, self); + DateValue date = TemporalNodes.GetDateValue.executeUncached(inliningTarget, self); var string = String.format("%04d-%02d-%02d", date.year, date.month, date.day); return TruffleString.FromJavaStringNode.getUncached().execute(string, TS_ENCODING); } @@ -280,7 +280,7 @@ public abstract static class ReduceNode extends PythonUnaryBuiltinNode { static Object reduce(Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode, + @Cached TemporalNodes.GetDateValue readDateValueNode, @Cached GetClassNode getClassNode) { DateValue date = readDateValueNode.execute(inliningTarget, self); byte[] bytes = new byte[]{(byte) (date.year / 256), (byte) (date.year % 256), (byte) date.month, (byte) date.day}; @@ -299,7 +299,7 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { static Object richCmp(Object selfObj, Object otherObj, RichCmpOp op, @Bind Node inliningTarget, @Cached PyDateCheckNode dateCheckNode, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { if (dateCheckNode.execute(inliningTarget, selfObj) && dateCheckNode.execute(inliningTarget, otherObj)) { DateValue self = readDateValueNode.execute(inliningTarget, selfObj); DateValue other = readDateValueNode.execute(inliningTarget, otherObj); @@ -319,7 +319,7 @@ static long hash(VirtualFrame frame, Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached PyObjectHashNode hashNode, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { DateValue d = readDateValueNode.execute(inliningTarget, self); var content = new int[]{d.year, d.month, d.day}; return hashNode.execute(frame, inliningTarget, PFactory.createTuple(language, content)); @@ -360,8 +360,8 @@ private static Object addBoundary(Object left, Object right, Node inliningTarget } else { return PNotImplemented.NOT_IMPLEMENTED; } - DateValue date = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, dateObj); - TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, deltaObj); + DateValue date = TemporalNodes.GetDateValue.executeUncached(inliningTarget, dateObj); + TimeDeltaValue delta = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, deltaObj); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(date.year, date.month, date.day); @@ -408,13 +408,13 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget if (!PyDateCheckNode.executeUncached(left)) { return PNotImplemented.NOT_IMPLEMENTED; } - DateValue date = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, left); + DateValue date = TemporalNodes.GetDateValue.executeUncached(inliningTarget, left); if (PyDateCheckNode.executeUncached(right)) { LocalDate from = LocalDate.of(1, 1, 1); LocalDate toSelf = LocalDate.of(date.year, date.month, date.day); long daysSelf = ChronoUnit.DAYS.between(from, toSelf) + 1; - DateValue other = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, right); + DateValue other = TemporalNodes.GetDateValue.executeUncached(inliningTarget, right); LocalDate toOther = LocalDate.of(other.year, other.month, other.day); long daysOther = ChronoUnit.DAYS.between(from, toOther) + 1; @@ -427,7 +427,7 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget 0); } if (PyDeltaCheckNode.executeUncached(right)) { - TimeDeltaValue timeDelta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); + TimeDeltaValue timeDelta = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(date.year, date.month, date.day); long days = ChronoUnit.DAYS.between(from, to) + 1; @@ -744,7 +744,7 @@ public abstract static class ReplaceNode extends PythonBuiltinNode { @Specialization static Object replace(VirtualFrame frame, Object self, Object yearObject, Object monthObject, Object dayObject, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode, + @Cached TemporalNodes.GetDateValue readDateValueNode, @Cached PyLongAsLongNode longAsLongNode, @Cached GetClassNode getClassNode, @Cached DateNodes.NewNode newNode) { @@ -781,7 +781,7 @@ public abstract static class ToOrdinalNode extends PythonUnaryBuiltinNode { @TruffleBoundary static long toOrdinal(Object selfObj, @Bind Node inliningTarget) { - DateValue self = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(self.year, self.month, self.day); return ChronoUnit.DAYS.between(from, to) + 1; @@ -833,7 +833,7 @@ public abstract static class WeekDayNode extends PythonUnaryBuiltinNode { @TruffleBoundary static int weekDay(Object selfObj, @Bind Node inliningTarget) { - DateValue self = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); DayOfWeek dayOfWeek = localDate.getDayOfWeek(); @@ -850,7 +850,7 @@ public abstract static class IsoWeekDayNode extends PythonUnaryBuiltinNode { @TruffleBoundary static int weekDay(Object selfObj, @Bind Node inliningTarget) { - DateValue self = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); DayOfWeek dayOfWeek = localDate.getDayOfWeek(); return dayOfWeek.getValue(); @@ -866,7 +866,7 @@ public abstract static class IsoCalendarNode extends PythonUnaryBuiltinNode { static PTuple isoCalendar(Object selfObj, @Bind PythonLanguage language, @Bind Node inliningTarget) { - DateValue self = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); // use week based year ISO-8601 calendar @@ -886,7 +886,7 @@ public abstract static class IsoFormatNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString isoFormat(Object selfObj, @Bind Node inliningTarget) { - DateValue self = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate locaDate = LocalDate.of(self.year, self.month, self.day); var isoString = locaDate.toString(); return TruffleString.FromJavaStringNode.getUncached().execute(isoString, TS_ENCODING); @@ -901,7 +901,7 @@ public abstract static class CTimeNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString cTime(Object selfObj, @Bind Node inliningTarget) { - DateValue self = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE LLL ppd 00:00:00 yyyy"); String ctime = localDate.format(formatter); @@ -918,7 +918,7 @@ public abstract static class TimeTupleNode extends PythonUnaryBuiltinNode { static PTuple timeTuple(Object selfObj, @Bind PythonLanguage language, @Bind Node inliningTarget) { - DateValue self = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); // Python's day of week is in range 0-6 @@ -944,7 +944,7 @@ protected ArgumentClinicProvider getArgumentClinic() { @TruffleBoundary static TruffleString strftime(Object selfObj, TruffleString format, @Bind Node inliningTarget) { - DateValue self = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalNodes.GetDateValue.executeUncached(inliningTarget, selfObj); // Reuse time.strftime(format, time_tuple) method. // construct time_tuple diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java index 3eadf6949c..cfa51ed3a1 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java @@ -431,7 +431,7 @@ static TruffleString repr(Object selfObj, @TruffleBoundary private static TruffleString reprBoundary(Node inliningTarget, Object selfObj) { - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); @@ -469,7 +469,7 @@ static Object reduce(Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached GetClassNode getClassNode) { - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); // DateTime is serialized in the following format: // ( // bytes(year 1st byte, year 2nd byte, month, day, hours, minutes, seconds, microseconds @@ -512,7 +512,7 @@ static Object reduceEx(Object selfObj, int protocol, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached GetClassNode getClassNode) { - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); byte[] baseStateBytes = new byte[10]; baseStateBytes[0] = (byte) (self.year / 256); baseStateBytes[1] = (byte) (self.year % 256); @@ -582,8 +582,8 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp } return PNotImplemented.NOT_IMPLEMENTED; } - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); - DateTimeValue other = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, otherObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue other = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, otherObj); // either naive datetimes (without timezone) or timezones are exactly the same objects if (self.tzInfo == other.tzInfo) { int result = compareDateTimeComponents(self, other); @@ -677,7 +677,7 @@ static long hash(VirtualFrame frame, Object selfObj, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode, @Cached TypeNodes.GetInstanceShape getInstanceShape) { - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); final PTimeDelta offset; if (self.tzInfo == null) { offset = null; @@ -749,8 +749,8 @@ private static Object addBoundary(Object left, Object right, Node inliningTarget } else { return PNotImplemented.NOT_IMPLEMENTED; } - DateTimeValue date = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, dateTimeObj); - TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, deltaObj); + DateTimeValue date = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTimeObj); + TimeDeltaValue delta = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, deltaObj); LocalDateTime local = date.toLocalDateTime(); LocalDateTime localAdjusted = local.plusDays(delta.days).plusSeconds(delta.seconds).plusNanos(delta.microseconds * 1_000L); @@ -786,9 +786,9 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget if (!PyDateTimeCheckNode.executeUncached(left)) { return PNotImplemented.NOT_IMPLEMENTED; } - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, left); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, left); if (PyDateTimeCheckNode.executeUncached(right)) { - DateTimeValue other = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, right); + DateTimeValue other = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, right); final PTimeDelta selfOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, left, inliningTarget); final PTimeDelta otherOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, right, inliningTarget); @@ -821,7 +821,7 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget 0, 0); } else if (PyDeltaCheckNode.executeUncached(right)) { - TimeDeltaValue timeDelta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); + TimeDeltaValue timeDelta = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); LocalDateTime local = self.toLocalDateTime(); LocalDateTime localAdjusted = local.minusDays(timeDelta.days).minusSeconds(timeDelta.seconds).minusNanos(timeDelta.microseconds * 1_000L); @@ -1174,8 +1174,8 @@ static Object combine(Object cls, Object dateObject, Object timeObject, Object t timeObject); } - DateValue date = TemporalNodes.ReadDateValueNode.executeUncached(inliningTarget, dateObject); - TimeValue time = TemporalNodes.ReadTimeValueNode.executeUncached(inliningTarget, timeObject); + DateValue date = TemporalNodes.GetDateValue.executeUncached(inliningTarget, dateObject); + TimeValue time = TemporalNodes.GetTimeValue.executeUncached(inliningTarget, timeObject); final Object tzInfo; if (tzInfoObject instanceof PNone) { @@ -2531,7 +2531,7 @@ abstract static class DateNode extends PythonUnaryBuiltinNode { static Object getDate(Object selfObj, @Bind Node inliningTarget, @Cached DateNodes.NewNode newDateNode) { - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); return newDateNode.execute(inliningTarget, PythonBuiltinClassType.PDate, self.year, @@ -2548,7 +2548,7 @@ abstract static class TimeNode extends PythonUnaryBuiltinNode { static Object getTime(Object selfObj, @Bind Node inliningTarget, @Cached TimeNodes.NewNode newTimeNode) { - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); return newTimeNode.execute(inliningTarget, PythonBuiltinClassType.PTime, self.hour, @@ -2568,7 +2568,7 @@ abstract static class TimeTzNode extends PythonUnaryBuiltinNode { static Object getTime(Object selfObj, @Bind Node inliningTarget, @Cached TimeNodes.NewNode newTimeNode) { - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); return newTimeNode.execute(inliningTarget, PythonBuiltinClassType.PTime, self.hour, @@ -2591,7 +2591,7 @@ static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Obj @Cached PyLongAsLongNode asLongNode, @Cached GetClassNode getClassNode, @Cached DateTimeNodes.NewNode newDateTimeNode) { - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); final long year, month, day; if (yearObject instanceof PNone) { @@ -2678,7 +2678,7 @@ static Object inTimeZone(VirtualFrame frame, Object self, Object tzInfo, @TruffleBoundary private static Object inTimeZoneBoundary(Object selfObj, Object tzInfo, Node inliningTarget) { - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); if (tzInfo == self.tzInfo) { return selfObj; } @@ -2855,7 +2855,7 @@ static PTuple composeTimeTuple(VirtualFrame frame, Object self, @TruffleBoundary private static PTuple composeTimeTupleBoundary(Object selfObj, Node inliningTarget, PythonLanguage language) { - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); int dayOfWeek = localDate.getDayOfWeek().getValue() - 1; // Python's day of week range // is 0-6 @@ -2905,7 +2905,7 @@ static PTuple composeTimeTuple(VirtualFrame frame, Object self, @TruffleBoundary private static PTuple composeTimeTupleBoundary(Object selfObj, Node inliningTarget, PythonLanguage language) { - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); final LocalDateTime localDateTime; PTimeDelta offset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, selfObj, inliningTarget); @@ -2938,7 +2938,7 @@ public abstract static class ToOrdinalNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static long toOrdinal(Object selfObj) { - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(null, selfObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(null, selfObj); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(self.year, self.month, self.day); return ChronoUnit.DAYS.between(from, to) + 1; @@ -2965,7 +2965,7 @@ static double toTimestamp(VirtualFrame frame, Object self, @TruffleBoundary private static double toTimestampBoundary(Object selfObj, Node inliningTarget) { - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); if (self.tzInfo == null) { // CPython: local_to_seconds() TimeZone timeZone = TimeModuleBuiltins.getGlobalTimeZone(getContext(inliningTarget)); @@ -3035,7 +3035,7 @@ static TruffleString isoFormat(VirtualFrame frame, Object self, Object separator @TruffleBoundary private static TruffleString isoFormatBoundary(Object selfObj, Object separatorObject, Object timespecObject, Node inliningTarget) { - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); String dateSection = PythonUtils.formatJString("%04d-%02d-%02d", self.year, self.month, self.day); @@ -3139,7 +3139,7 @@ public abstract static class CTimeNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static TruffleString cTime(Object selfObj) { - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(null, selfObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(null, selfObj); LocalDateTime localDateTime = self.toLocalDateTime(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE LLL ppd HH:mm:ss yyyy"); String ctime = localDateTime.format(formatter); @@ -3174,7 +3174,7 @@ static TruffleString strftime(VirtualFrame frame, Object self, TruffleString for @TruffleBoundary private static TruffleString strftimeBoundary(Object selfObj, TruffleString format, Node inliningTarget) { - DateTimeValue self = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); // Reuse time.strftime(format, time_tuple) method. // construct time_tuple diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DatetimeModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DatetimeModuleBuiltins.java index b54f8f6999..4938a0131f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DatetimeModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DatetimeModuleBuiltins.java @@ -328,7 +328,7 @@ public static PTimeDelta callDst(Object tzInfo, Object dateTime, VirtualFrame fr @TruffleBoundary public static Object addOffsetToDateTime(Object dateTimeObj, PTimeDelta offset, DateTimeNodes.SubclassNewNode subclassNewNode, Node inliningTarget) { - TemporalNodes.DateTimeValue dateTime = TemporalNodes.ReadDateTimeValueNode.executeUncached(inliningTarget, dateTimeObj); + TemporalNodes.DateTimeValue dateTime = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTimeObj); LocalDateTime utc = dateTime.toLocalDateTime().plusDays( offset.days).plusSeconds(offset.seconds).plusNanos(offset.microseconds * 1_000L); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java index 2db71e560e..aaf0c677e8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java @@ -254,11 +254,11 @@ public static Object toFixedOffsetTimeZone(ZoneId zoneId, Node inliningTarget) { @GenerateUncached @GenerateInline @GenerateCached(alwaysInlineCached = true) - public abstract static class ReadTimeDeltaValueNode extends Node { + public abstract static class GetTimeDeltaValue extends Node { public abstract TimeDeltaValue execute(Node inliningTarget, Object obj); public static TimeDeltaValue executeUncached(Node inliningTarget, Object obj) { - return TemporalNodesFactory.ReadTimeDeltaValueNodeGen.getUncached().execute(inliningTarget, obj); + return TemporalNodesFactory.GetTimeDeltaValueNodeGen.getUncached().execute(inliningTarget, obj); } @Specialization @@ -284,11 +284,11 @@ static TimeDeltaValue error(Object obj, @GenerateUncached @GenerateInline @GenerateCached(alwaysInlineCached = true) - public abstract static class ReadDateValueNode extends Node { + public abstract static class GetDateValue extends Node { public abstract DateValue execute(Node inliningTarget, Object obj); public static DateValue executeUncached(Node inliningTarget, Object obj) { - return TemporalNodesFactory.ReadDateValueNodeGen.getUncached().execute(inliningTarget, obj); + return TemporalNodesFactory.GetDateValueNodeGen.getUncached().execute(inliningTarget, obj); } @Specialization @@ -325,11 +325,11 @@ static DateValue error(Object obj, @GenerateUncached @GenerateInline @GenerateCached(alwaysInlineCached = true) - public abstract static class ReadTimeValueNode extends Node { + public abstract static class GetTimeValue extends Node { public abstract TimeValue execute(Node inliningTarget, Object obj); public static TimeValue executeUncached(Node inliningTarget, Object obj) { - return TemporalNodesFactory.ReadTimeValueNodeGen.getUncached().execute(inliningTarget, obj); + return TemporalNodesFactory.GetTimeValueNodeGen.getUncached().execute(inliningTarget, obj); } @Specialization @@ -370,11 +370,11 @@ static TimeValue error(Object obj, @GenerateUncached @GenerateInline @GenerateCached(alwaysInlineCached = true) - public abstract static class ReadDateTimeValueNode extends Node { + public abstract static class GetDateTimeValue extends Node { public abstract DateTimeValue execute(Node inliningTarget, Object obj); public static DateTimeValue executeUncached(Node inliningTarget, Object obj) { - return TemporalNodesFactory.ReadDateTimeValueNodeGen.getUncached().execute(inliningTarget, obj); + return TemporalNodesFactory.GetDateTimeValueNodeGen.getUncached().execute(inliningTarget, obj); } @Specialization diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java index 28a6333e91..b072f1c2c9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java @@ -272,7 +272,7 @@ static TruffleString repr(VirtualFrame frame, Object self, @TruffleBoundary private static TruffleString reprBoundary(Node inliningTarget, Object selfObj) { - TimeValue self = TemporalNodes.ReadTimeValueNode.executeUncached(null, selfObj); + TimeValue self = TemporalNodes.GetTimeValue.executeUncached(null, selfObj); var builder = new StringBuilder(); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); @@ -310,7 +310,7 @@ public abstract static class ReduceNode extends PythonUnaryBuiltinNode { static Object reduce(Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TemporalNodes.ReadTimeValueNode asManagedTimeNode, + @Cached TemporalNodes.GetTimeValue asManagedTimeNode, @Cached GetClassNode getClassNode) { TimeValue time = asManagedTimeNode.execute(inliningTarget, self); // Time is serialized in the following format: @@ -350,7 +350,7 @@ public abstract static class ReduceExNode extends PythonBinaryBuiltinNode { static Object reduceEx(Object self, int protocol, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TemporalNodes.ReadTimeValueNode asManagedTimeNode, + @Cached TemporalNodes.GetTimeValue asManagedTimeNode, @Cached GetClassNode getClassNode) { TimeValue time = asManagedTimeNode.execute(inliningTarget, self); byte[] baseStateBytes = new byte[6]; @@ -402,8 +402,8 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp if (!PyTimeCheckNode.executeUncached(selfObj) || !PyTimeCheckNode.executeUncached(otherObj)) { return PNotImplemented.NOT_IMPLEMENTED; } - TimeValue self = TemporalNodes.ReadTimeValueNode.executeUncached(inliningTarget, selfObj); - TimeValue other = TemporalNodes.ReadTimeValueNode.executeUncached(inliningTarget, otherObj); + TimeValue self = TemporalNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); + TimeValue other = TemporalNodes.GetTimeValue.executeUncached(inliningTarget, otherObj); // either naive times (without timezone) or timezones are exactly the same objects if (self.tzInfo == other.tzInfo) { return compareTimeComponents(self, other, op); @@ -455,7 +455,7 @@ abstract static class HashNode extends HashBuiltinNode { static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TemporalNodes.ReadTimeValueNode asManagedTimeNode, + @Cached TemporalNodes.GetTimeValue asManagedTimeNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode, @Cached PyObjectHashNode hashNode) { @@ -950,7 +950,7 @@ public abstract static class ReplaceNode extends PythonBuiltinNode { @Specialization static Object replace(VirtualFrame frame, Object self, Object hourObject, Object minuteObject, Object secondObject, Object microsecondObject, Object tzInfoObject, Object foldObject, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeValueNode asManagedTimeNode, + @Cached TemporalNodes.GetTimeValue asManagedTimeNode, @Cached PyLongAsLongNode asLongNode, @Cached GetClassNode getClassNode, @Cached TimeNodes.NewNode newTimeNode) { @@ -1022,7 +1022,7 @@ static TruffleString isoFormat(VirtualFrame frame, Object self, Object timespecO @TruffleBoundary private static TruffleString isoFormatBoundary(Object selfObj, Object timespecObject, Node inliningTarget) { - TimeValue self = TemporalNodes.ReadTimeValueNode.executeUncached(inliningTarget, selfObj); + TimeValue self = TemporalNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); final String timespec; @@ -1188,7 +1188,7 @@ static TruffleString strftime(VirtualFrame frame, Object self, TruffleString for @TruffleBoundary private static TruffleString strftimeBoundary(Object selfObj, TruffleString format, Node inliningTarget) { - TimeValue self = TemporalNodes.ReadTimeValueNode.executeUncached(inliningTarget, selfObj); + TimeValue self = TemporalNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); // Reuse time.strftime(format, time_tuple) method. int[] timeTuple = new int[]{1900, 1, 1, self.hour, self.minute, self.second, 0, 1, -1}; String formatPreprocessed = preprocessFormat(format, self, inliningTarget); 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 0d1fbbec08..9bd34e1884 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 @@ -165,7 +165,7 @@ abstract static class BoolNode extends TpSlotInquiry.NbBoolBuiltinNode { @Specialization static boolean bool(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); return self.days != 0 || self.seconds != 0 || self.microseconds != 0; } @@ -178,7 +178,7 @@ public abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static TruffleString repr(Object selfObj) { - TimeDeltaValue self = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(null, selfObj); + TimeDeltaValue self = TemporalNodes.GetTimeDeltaValue.executeUncached(null, selfObj); var builder = new StringBuilder(); builder.append(TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj))); @@ -227,7 +227,7 @@ public abstract static class StrNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString str(Object selfObj, @Bind Node inliningTarget) { - TimeDeltaValue self = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, selfObj); + TimeDeltaValue self = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); // optional prefix with days, e.g. '1 day' or '5 days' @@ -272,7 +272,7 @@ static Object reduce(Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached GetClassNode getClassNode, - @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); Object type = getClassNode.execute(inliningTarget, selfObj); PTuple arguments = PFactory.createTuple(language, new Object[]{self.days, self.seconds, self.microseconds}); @@ -288,7 +288,7 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { static Object richCmp(Object left, Object right, RichCmpOp op, @Bind Node inliningTarget, @Cached PyDeltaCheckNode checkNode, - @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, right)) { return PNotImplemented.NOT_IMPLEMENTED; } @@ -308,7 +308,7 @@ static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached PyObjectHashNode hashNode, - @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); var content = new int[]{self.days, self.seconds, self.microseconds}; return hashNode.execute(frame, inliningTarget, PFactory.createTuple(language, content)); @@ -325,7 +325,7 @@ static Object add(Object left, Object right, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, @Cached PyDeltaCheckNode checkNode, - @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, right)) { return PNotImplemented.NOT_IMPLEMENTED; } @@ -345,7 +345,7 @@ static Object sub(Object left, Object rigth, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, @Cached PyDeltaCheckNode checkNode, - @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, rigth)) { return PNotImplemented.NOT_IMPLEMENTED; } @@ -417,7 +417,7 @@ static Object mul(VirtualFrame frame, Object left, Object right, @Cached PyNumberMultiplyNode multiplyNode, @Cached TimeDeltaNodes.NewNode newNode, @Cached PyDeltaCheckNode checkNode, - @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { TimeDeltaValue date; Object other; if (checkNode.execute(inliningTarget, left)) { @@ -470,7 +470,7 @@ static Object div(VirtualFrame frame, Object left, Object right, @Cached TimeDeltaNodes.NewNode newNode, @Cached PyDeltaCheckNode checkLeft, @Cached PyDeltaCheckNode checkRight, - @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { if (!checkLeft.execute(inliningTarget, left)) { return PNotImplemented.NOT_IMPLEMENTED; } @@ -518,7 +518,7 @@ static Object div(VirtualFrame frame, Object left, Object right, @Cached PyNumberFloorDivideNode floorDivideNode, @Cached PyDeltaCheckNode checkLeft, @Cached PyDeltaCheckNode checkRight, - @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { if (!checkLeft.execute(inliningTarget, left)) { return PNotImplemented.NOT_IMPLEMENTED; } @@ -550,8 +550,8 @@ static Object divmod(Object left, Object right, if (!PyDeltaCheckNode.executeUncached(left) || !PyDeltaCheckNode.executeUncached(right)) { return PNotImplemented.NOT_IMPLEMENTED; } - TimeDeltaValue self = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, left); - TimeDeltaValue other = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); + TimeDeltaValue self = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, left); + TimeDeltaValue other = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent(); Node encapsulatingNode = encapsulating.set(inliningTarget); @@ -585,8 +585,8 @@ static Object mod(Object left, Object right, EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent(); Node encapsulatingNode = encapsulating.set(inliningTarget); try { - TimeDeltaValue self = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, left); - TimeDeltaValue other = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); + TimeDeltaValue self = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, left); + TimeDeltaValue other = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); Object microsecondsSelf = toMicrosecondsUncached(self); Object microsecondsOther = toMicrosecondsUncached(other); Object remainder = PyNumberRemainderNode.getUncached().execute(null, microsecondsSelf, microsecondsOther); @@ -606,7 +606,7 @@ abstract static class AbsNode extends PythonUnaryBuiltinNode { static PTimeDelta abs(PTimeDelta selfObj, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); if (self.days >= 0) { return newNode.executeBuiltin(inliningTarget, self.days, self.seconds, self.microseconds, 0, 0, 0, 0); @@ -624,7 +624,7 @@ abstract static class PosNode extends PythonUnaryBuiltinNode { static PTimeDelta pos(PTimeDelta selfObj, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); return newNode.executeBuiltin(inliningTarget, self.days, self.seconds, self.microseconds, 0, 0, 0, 0); } @@ -638,7 +638,7 @@ abstract static class NegNode extends PythonUnaryBuiltinNode { static PTimeDelta neg(Object selfObj, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode) { + @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); return newNode.executeBuiltin(inliningTarget, -self.days, -self.seconds, -self.microseconds, 0, 0, 0, 0); } @@ -699,7 +699,7 @@ abstract static class TotalSecondsNode extends PythonUnaryBuiltinNode { @Specialization static Object getTotalSeconds(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeDeltaValueNode readTimeDeltaValueNode, + @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode, @Cached PyNumberAddNode addNode, @Cached PyNumberMultiplyNode multiplyNode, @Cached PyNumberTrueDivideNode trueDivideNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java index 23cec3277f..4c16005b56 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java @@ -95,7 +95,7 @@ static PTimeZone newTimezone(Node inliningTarget, PythonContext context, Object if (offsetObj instanceof PTimeDelta value) { offset = value; } else { - TimeDeltaValue offsetValue = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, offsetObj); + TimeDeltaValue offsetValue = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, offsetObj); PythonBuiltinClassType tdcls = PythonBuiltinClassType.PTimeDelta; offset = new PTimeDelta(tdcls, tdcls.getInstanceShape(language), offsetValue.days, offsetValue.seconds, offsetValue.microseconds); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java index f75d4207d4..54536948df 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java @@ -116,7 +116,7 @@ abstract static class ReprNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString repr(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { TemporalNodes.DateValue self = readDateValueNode.execute(inliningTarget, selfObj); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); String string = String.format("%s(%d, %d, %d)", typeName, self.year, self.month, self.day); @@ -130,7 +130,7 @@ abstract static class StrNode extends PythonUnaryBuiltinNode { @Specialization static TruffleString str(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { return toIsoFormat(readDateValueNode.execute(inliningTarget, selfObj)); } } @@ -142,7 +142,7 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { static Object richCmp(Object selfObj, Object otherObj, RichCmpOp op, @Bind Node inliningTarget, @Cached PyDateCheckNode dateLikeCheckNode, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { if (!dateLikeCheckNode.execute(inliningTarget, otherObj)) { return PNotImplemented.NOT_IMPLEMENTED; } @@ -158,7 +158,7 @@ abstract static class HashNode extends HashBuiltinNode { @Specialization static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode, + @Cached TemporalNodes.GetDateValue readDateValueNode, @Cached PyObjectHashNode hashNode) { return hashNode.execute(frame, inliningTarget, toPythonDate(readDateValueNode.execute(inliningTarget, selfObj))); } @@ -172,7 +172,7 @@ abstract static class AddNode extends BinaryOpBuiltinNode { static Object add(Object left, Object right, @Bind Node inliningTarget, @Cached PyDateCheckNode dateLikeCheckNode, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { Object dateObj; Object deltaObj; if (dateLikeCheckNode.execute(inliningTarget, left) && PyDeltaCheckNode.executeUncached(right)) { @@ -186,7 +186,7 @@ static Object add(Object left, Object right, } LocalDate date = readDateValueNode.execute(inliningTarget, dateObj).toLocalDate(); - TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, deltaObj); + TimeDeltaValue delta = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, deltaObj); long days = ChronoUnit.DAYS.between(LocalDate.of(1, 1, 1), date) + 1 + delta.days; if (days <= 0 || days > MAX_ORDINAL) { throw com.oracle.graal.python.nodes.PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); @@ -203,7 +203,7 @@ abstract static class SubNode extends BinaryOpBuiltinNode { static Object sub(Object left, Object right, @Bind Node inliningTarget, @Cached PyDateCheckNode dateLikeCheckNode, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { if (!dateLikeCheckNode.execute(inliningTarget, left)) { return PNotImplemented.NOT_IMPLEMENTED; } @@ -217,7 +217,7 @@ static Object sub(Object left, Object right, return new PTimeDelta(PythonBuiltinClassType.PTimeDelta, PythonBuiltinClassType.PTimeDelta.getInstanceShape(PythonLanguage.get(null)), (int) (leftDays - rightDays), 0, 0); } if (PyDeltaCheckNode.executeUncached(right)) { - TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); + TimeDeltaValue delta = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); long days = leftDays - delta.days; if (days <= 0 || days >= MAX_ORDINAL) { throw com.oracle.graal.python.nodes.PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); @@ -234,7 +234,7 @@ abstract static class YearNode extends PythonUnaryBuiltinNode { @Specialization static int year(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { return readDateValueNode.execute(inliningTarget, selfObj).year; } } @@ -245,7 +245,7 @@ abstract static class MonthNode extends PythonUnaryBuiltinNode { @Specialization static int month(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { return readDateValueNode.execute(inliningTarget, selfObj).month; } } @@ -256,7 +256,7 @@ abstract static class DayNode extends PythonUnaryBuiltinNode { @Specialization static int day(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { return readDateValueNode.execute(inliningTarget, selfObj).day; } } @@ -267,7 +267,7 @@ abstract static class ReplaceNode extends PythonBuiltinNode { @Specialization static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Object monthObject, Object dayObject, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode, + @Cached TemporalNodes.GetDateValue readDateValueNode, @Cached PyLongAsLongNode longAsLongNode, @Cached DateNodes.NewNode newNode) { TemporalNodes.DateValue self = readDateValueNode.execute(inliningTarget, selfObj); @@ -285,7 +285,7 @@ abstract static class ToOrdinalNode extends PythonUnaryBuiltinNode { @TruffleBoundary static long toOrdinal(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { return ChronoUnit.DAYS.between(LocalDate.of(1, 1, 1), readDateValueNode.execute(inliningTarget, selfObj).toLocalDate()) + 1; } } @@ -297,7 +297,7 @@ abstract static class WeekDayNode extends PythonUnaryBuiltinNode { @TruffleBoundary static int weekDay(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { return readDateValueNode.execute(inliningTarget, selfObj).toLocalDate().getDayOfWeek().getValue() - 1; } } @@ -309,7 +309,7 @@ abstract static class IsoWeekDayNode extends PythonUnaryBuiltinNode { @TruffleBoundary static int isoWeekDay(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { return readDateValueNode.execute(inliningTarget, selfObj).toLocalDate().getDayOfWeek().getValue(); } } @@ -320,7 +320,7 @@ abstract static class IsoCalendarNode extends PythonUnaryBuiltinNode { @Specialization static Object isoCalendar(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDate(readDateValueNode.execute(inliningTarget, selfObj)), T_ISOCALENDAR); } } @@ -331,7 +331,7 @@ abstract static class IsoFormatNode extends PythonUnaryBuiltinNode { @Specialization static TruffleString isoFormat(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { return toIsoFormat(readDateValueNode.execute(inliningTarget, selfObj)); } } @@ -343,7 +343,7 @@ abstract static class CTimeNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString ctime(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { String ctime = readDateValueNode.execute(inliningTarget, selfObj).toLocalDate().format(DateTimeFormatter.ofPattern("EEE LLL ppd 00:00:00 yyyy")); return TruffleString.FromJavaStringNode.getUncached().execute(ctime, TS_ENCODING); } @@ -357,7 +357,7 @@ abstract static class TimeTupleNode extends PythonUnaryBuiltinNode { static PTuple timeTuple(Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode) { + @Cached TemporalNodes.GetDateValue readDateValueNode) { TemporalNodes.DateValue self = readDateValueNode.execute(inliningTarget, selfObj); LocalDate localDate = self.toLocalDate(); Object[] fields = new Object[]{self.year, self.month, self.day, 0, 0, 0, localDate.getDayOfWeek().getValue() - 1, localDate.getDayOfYear(), -1}; @@ -371,7 +371,7 @@ abstract static class StrFTimeNode extends PythonBinaryBuiltinNode { @Specialization static Object strftime(Object selfObj, Object formatObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateValueNode readDateValueNode, + @Cached TemporalNodes.GetDateValue readDateValueNode, @Cached CastToTruffleStringNode castToTruffleStringNode) { TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); return PyObjectCallMethodObjArgs.executeUncached(toPythonDate(readDateValueNode.execute(inliningTarget, selfObj)), T_STRFTIME, format); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java index dc1fea8308..d18e79c8ae 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java @@ -123,7 +123,7 @@ abstract static class ReprNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString repr(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { TemporalNodes.DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); String string = String.format("%s(%d, %d, %d, %d, %d, %d, %d)", typeName, self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond); @@ -137,7 +137,7 @@ abstract static class StrNode extends PythonUnaryBuiltinNode { @Specialization static Object str(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode, + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode, @Cached PyObjectStrAsObjectNode strAsObjectNode) { return strAsObjectNode.execute(inliningTarget, toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); } @@ -151,7 +151,7 @@ static Object richCmp(VirtualFrame frame, Object selfObj, Object otherObj, RichC @Bind Node inliningTarget, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, @Cached PyDateCheckNode dateLikeCheckNode, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode, + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode) { if (!dateTimeLikeCheckNode.execute(inliningTarget, otherObj)) { @@ -199,7 +199,7 @@ abstract static class HashNode extends HashBuiltinNode { @Specialization static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode, + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode, @Cached PyObjectHashNode hashNode) { return hashNode.execute(frame, inliningTarget, toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); } @@ -213,7 +213,7 @@ abstract static class AddNode extends BinaryOpBuiltinNode { static Object add(Object left, Object right, @Bind Node inliningTarget, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { Object dateTimeObj; Object deltaObj; if (dateTimeLikeCheckNode.execute(inliningTarget, left) && PyDeltaCheckNode.executeUncached(right)) { @@ -226,7 +226,7 @@ static Object add(Object left, Object right, return PNotImplemented.NOT_IMPLEMENTED; } PDateTime date = toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, dateTimeObj), inliningTarget); - TemporalNodes.TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, deltaObj); + TemporalNodes.TimeDeltaValue delta = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, deltaObj); LocalDateTime adjusted = toLocalDateTime(date).plusDays(delta.days).plusSeconds(delta.seconds).plusNanos(delta.microseconds * 1_000L); return toPythonDateTime(adjusted, date.tzInfo, date.fold, inliningTarget); } @@ -239,7 +239,7 @@ abstract static class SubNode extends BinaryOpBuiltinNode { static Object sub(VirtualFrame frame, Object left, Object right, @Bind Node inliningTarget, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode, + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode) { if (!dateTimeLikeCheckNode.execute(inliningTarget, left)) { @@ -261,7 +261,7 @@ static Object sub(VirtualFrame frame, Object left, Object right, 0, 0, 0); } if (PyDeltaCheckNode.executeUncached(right)) { - TemporalNodes.TimeDeltaValue delta = TemporalNodes.ReadTimeDeltaValueNode.executeUncached(inliningTarget, right); + TemporalNodes.TimeDeltaValue delta = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); LocalDateTime adjusted = toLocalDateTime(self).minusDays(delta.days).minusSeconds(delta.seconds).minusNanos(delta.microseconds * 1_000L); return toPythonDateTime(adjusted, self.tzInfo, self.fold, inliningTarget); } @@ -275,7 +275,7 @@ abstract static class YearNode extends PythonUnaryBuiltinNode { @Specialization static int year(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return readDateTimeValueNode.execute(inliningTarget, selfObj).year; } } @@ -286,7 +286,7 @@ abstract static class MonthNode extends PythonUnaryBuiltinNode { @Specialization static int month(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return readDateTimeValueNode.execute(inliningTarget, selfObj).month; } } @@ -297,7 +297,7 @@ abstract static class DayNode extends PythonUnaryBuiltinNode { @Specialization static int day(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return readDateTimeValueNode.execute(inliningTarget, selfObj).day; } } @@ -308,7 +308,7 @@ abstract static class HourNode extends PythonUnaryBuiltinNode { @Specialization static int hour(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return readDateTimeValueNode.execute(inliningTarget, selfObj).hour; } } @@ -319,7 +319,7 @@ abstract static class MinuteNode extends PythonUnaryBuiltinNode { @Specialization static int minute(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return readDateTimeValueNode.execute(inliningTarget, selfObj).minute; } } @@ -330,7 +330,7 @@ abstract static class SecondNode extends PythonUnaryBuiltinNode { @Specialization static int second(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return readDateTimeValueNode.execute(inliningTarget, selfObj).second; } } @@ -341,7 +341,7 @@ abstract static class MicrosecondNode extends PythonUnaryBuiltinNode { @Specialization static int microsecond(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return readDateTimeValueNode.execute(inliningTarget, selfObj).microsecond; } } @@ -352,7 +352,7 @@ abstract static class TzInfoNode extends PythonUnaryBuiltinNode { @Specialization static Object tzinfo(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { TemporalNodes.DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); Object tzInfo = TemporalNodes.toPythonTzInfo(self.tzInfo, self.zoneId, inliningTarget); return tzInfo != null ? tzInfo : PNone.NONE; @@ -365,7 +365,7 @@ abstract static class FoldNode extends PythonUnaryBuiltinNode { @Specialization static int fold(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return readDateTimeValueNode.execute(inliningTarget, selfObj).fold; } } @@ -376,7 +376,7 @@ abstract static class DateNode extends PythonUnaryBuiltinNode { @Specialization static Object date(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DATE); } } @@ -387,7 +387,7 @@ abstract static class TimeNode extends PythonUnaryBuiltinNode { @Specialization static Object time(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIME); } } @@ -398,7 +398,7 @@ abstract static class TimeTzNode extends PythonUnaryBuiltinNode { @Specialization static Object timetz(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMETZ); } } @@ -410,7 +410,7 @@ abstract static class ReplaceNode extends PythonBuiltinNode { static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Object monthObject, Object dayObject, Object hourObject, Object minuteObject, Object secondObject, Object microsecondObject, Object tzInfoObject, Object foldObject, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode, + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode, @Cached PyLongAsLongNode asLongNode, @Cached DateTimeNodes.NewNode newDateTimeNode) { TemporalNodes.DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); @@ -440,7 +440,7 @@ abstract static class IsoFormatNode extends PythonBuiltinNode { @Specialization static Object isoformat(Object selfObj, Object sepObj, Object timespecObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { if (sepObj == PNone.NO_VALUE && timespecObj == PNone.NO_VALUE) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT); } @@ -457,7 +457,7 @@ abstract static class UtcOffsetNode extends PythonUnaryBuiltinNode { @Specialization static Object utcoffset(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCOFFSET); } } @@ -468,7 +468,7 @@ abstract static class DstNode extends PythonUnaryBuiltinNode { @Specialization static Object dst(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DST); } } @@ -479,7 +479,7 @@ abstract static class TzNameNode extends PythonUnaryBuiltinNode { @Specialization static Object tzname(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TZNAME); } } @@ -490,7 +490,7 @@ abstract static class TimeTupleNode extends PythonUnaryBuiltinNode { @Specialization static Object timetuple(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMETUPLE); } } @@ -501,7 +501,7 @@ abstract static class UtcTimeTupleNode extends PythonUnaryBuiltinNode { @Specialization static Object utctimetuple(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCTIMETUPLE); } } @@ -512,7 +512,7 @@ abstract static class TimestampNode extends PythonUnaryBuiltinNode { @Specialization static Object timestamp(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMESTAMP); } } @@ -523,7 +523,7 @@ abstract static class AsTimeZoneNode extends PythonBinaryBuiltinNode { @Specialization static Object astimezone(Object selfObj, Object tzObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { if (tzObj == PNone.NO_VALUE) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ASTIMEZONE); } @@ -537,7 +537,7 @@ abstract static class StrFTimeNode extends PythonBinaryBuiltinNode { @Specialization static Object strftime(Object selfObj, Object formatObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode, + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode, @Cached CastToTruffleStringNode castToTruffleStringNode) { TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_STRFTIME, format); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java index ba53d1e859..748f145169 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java @@ -111,7 +111,7 @@ abstract static class ReprNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString repr(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + @Cached TemporalNodes.GetTimeValue readTimeValueNode) { TemporalNodes.TimeValue self = readTimeValueNode.execute(inliningTarget, selfObj); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); String value = self.microsecond == 0 @@ -127,7 +127,7 @@ abstract static class StrNode extends PythonUnaryBuiltinNode { @Specialization static Object str(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode, + @Cached TemporalNodes.GetTimeValue readTimeValueNode, @Cached PyObjectStrAsObjectNode strAsObjectNode) { return strAsObjectNode.execute(inliningTarget, toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); } @@ -140,7 +140,7 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { static Object richCmp(VirtualFrame frame, Object selfObj, Object otherObj, RichCmpOp op, @Bind Node inliningTarget, @Cached PyTimeCheckNode timeLikeCheckNode, - @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode, + @Cached TemporalNodes.GetTimeValue readTimeValueNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode) { if (!timeLikeCheckNode.execute(inliningTarget, otherObj)) { @@ -182,7 +182,7 @@ abstract static class HashNode extends HashBuiltinNode { @Specialization static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode, + @Cached TemporalNodes.GetTimeValue readTimeValueNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode, @Cached PyObjectHashNode hashNode) { @@ -201,7 +201,7 @@ abstract static class HourNode extends PythonUnaryBuiltinNode { @Specialization static int hour(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + @Cached TemporalNodes.GetTimeValue readTimeValueNode) { return readTimeValueNode.execute(inliningTarget, selfObj).hour; } } @@ -212,7 +212,7 @@ abstract static class MinuteNode extends PythonUnaryBuiltinNode { @Specialization static int minute(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + @Cached TemporalNodes.GetTimeValue readTimeValueNode) { return readTimeValueNode.execute(inliningTarget, selfObj).minute; } } @@ -223,7 +223,7 @@ abstract static class SecondNode extends PythonUnaryBuiltinNode { @Specialization static int second(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + @Cached TemporalNodes.GetTimeValue readTimeValueNode) { return readTimeValueNode.execute(inliningTarget, selfObj).second; } } @@ -234,7 +234,7 @@ abstract static class MicrosecondNode extends PythonUnaryBuiltinNode { @Specialization static int microsecond(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + @Cached TemporalNodes.GetTimeValue readTimeValueNode) { return readTimeValueNode.execute(inliningTarget, selfObj).microsecond; } } @@ -245,7 +245,7 @@ abstract static class TzInfoNode extends PythonUnaryBuiltinNode { @Specialization static Object tzinfo(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + @Cached TemporalNodes.GetTimeValue readTimeValueNode) { Object tzInfo = toPythonTzInfo(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget); return tzInfo != null ? tzInfo : PNone.NONE; } @@ -257,7 +257,7 @@ abstract static class FoldNode extends PythonUnaryBuiltinNode { @Specialization static int fold(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + @Cached TemporalNodes.GetTimeValue readTimeValueNode) { return readTimeValueNode.execute(inliningTarget, selfObj).fold; } } @@ -268,7 +268,7 @@ abstract static class ReplaceNode extends PythonBuiltinNode { @Specialization static Object replace(VirtualFrame frame, Object selfObj, Object hourObject, Object minuteObject, Object secondObject, Object microsecondObject, Object tzInfoObject, Object foldObject, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode, + @Cached TemporalNodes.GetTimeValue readTimeValueNode, @Cached PyLongAsLongNode asLongNode, @Cached TimeNodes.NewNode newTimeNode) { TemporalNodes.TimeValue self = readTimeValueNode.execute(inliningTarget, selfObj); @@ -295,7 +295,7 @@ abstract static class IsoFormatNode extends PythonBinaryBuiltinNode { @Specialization static Object isoformat(Object selfObj, Object timespecObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + @Cached TemporalNodes.GetTimeValue readTimeValueNode) { Object timespec = timespecObj == PNone.NO_VALUE ? PNone.NO_VALUE : timespecObj; return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT, timespec); } @@ -307,7 +307,7 @@ abstract static class UtcOffsetNode extends PythonUnaryBuiltinNode { @Specialization static Object utcoffset(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + @Cached TemporalNodes.GetTimeValue readTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCOFFSET); } } @@ -318,7 +318,7 @@ abstract static class DstNode extends PythonUnaryBuiltinNode { @Specialization static Object dst(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + @Cached TemporalNodes.GetTimeValue readTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DST); } } @@ -329,7 +329,7 @@ abstract static class TzNameNode extends PythonUnaryBuiltinNode { @Specialization static Object tzname(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode) { + @Cached TemporalNodes.GetTimeValue readTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TZNAME); } } @@ -340,7 +340,7 @@ abstract static class StrFTimeNode extends PythonBinaryBuiltinNode { @Specialization static Object strftime(Object selfObj, Object formatObj, @Bind Node inliningTarget, - @Cached TemporalNodes.ReadTimeValueNode readTimeValueNode, + @Cached TemporalNodes.GetTimeValue readTimeValueNode, @Cached CastToTruffleStringNode castToTruffleStringNode) { TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_STRFTIME, format); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java index 9081eb4260..f9f97dfa34 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java @@ -126,7 +126,7 @@ static Object utcoffset(Object self, Object dateTime, @Bind Node inliningTarget, @CachedLibrary("self") InteropLibrary interop, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { ZoneId zoneId = asZoneId(self, interop); if (dateTime == PNone.NONE) { Object fixed = TemporalNodes.toFixedOffsetTimeZone(zoneId, inliningTarget); @@ -153,7 +153,7 @@ static Object dst(Object self, Object dateTime, @Bind Node inliningTarget, @CachedLibrary("self") InteropLibrary interop, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { ZoneId zoneId = asZoneId(self, interop); if (dateTime == PNone.NONE) { return PNone.NONE; @@ -177,7 +177,7 @@ static Object tzname(Object self, Object dateTime, @Bind Node inliningTarget, @CachedLibrary("self") InteropLibrary interop, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { ZoneId zoneId = asZoneId(self, interop); if (dateTime == PNone.NONE) { return zoneId.getRules().isFixedOffset() ? TruffleString.FromJavaStringNode.getUncached().execute(zoneId.getId(), TS_ENCODING) : PNone.NONE; @@ -200,7 +200,7 @@ static Object fromutc(Object self, Object dateTime, @Bind Node inliningTarget, @CachedLibrary("self") InteropLibrary interop, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalNodes.ReadDateTimeValueNode readDateTimeValueNode) { + @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { if (!dateTimeLikeCheckNode.execute(inliningTarget, dateTime)) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); } From 427a136ec2cebe75bcd8876189431e411243444c Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Fri, 27 Mar 2026 09:29:14 +0100 Subject: [PATCH 18/30] declare method as static --- .../python/builtins/modules/UnicodeDataModuleBuiltins.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/UnicodeDataModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/UnicodeDataModuleBuiltins.java index 99b88b82fe..7bf5646e6f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/UnicodeDataModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/UnicodeDataModuleBuiltins.java @@ -150,7 +150,7 @@ public void postInitialize(Python3Core core) { } } - private PythonObject createUCDCompatibilityObject(Python3Core core, PythonModule self) { + private static PythonObject createUCDCompatibilityObject(Python3Core core, PythonModule self) { TruffleString t_ucd = toTruffleStringUncached("UCD"); PythonClass clazz = PFactory.createPythonClassAndFixupSlots(null, core.getLanguage(), t_ucd, PythonBuiltinClassType.PythonObject, new PythonAbstractClass[]{core.lookupType(PythonBuiltinClassType.PythonObject)}); From 2726f2dabca4ad62fcdd796cfcfb896e08555187 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Fri, 27 Mar 2026 10:05:10 +0100 Subject: [PATCH 19/30] Add missing truffle boundaries --- .../python/builtins/objects/foreign/ForeignDateBuiltins.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java index 54536948df..a69722c9d4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java @@ -399,10 +399,12 @@ private static PDate toPythonDate(TemporalNodes.DateValue date) { return (PDate) DateNodes.NewUnsafeNode.executeUncached(PythonBuiltinClassType.PDate, date.year, date.month, date.day); } + @TruffleBoundary private static Object toPythonDate(LocalDate date) { return DateNodes.NewUnsafeNode.executeUncached(PythonBuiltinClassType.PDate, date.getYear(), date.getMonthValue(), date.getDayOfMonth()); } + @TruffleBoundary private static TruffleString toIsoFormat(TemporalNodes.DateValue date) { return TruffleString.FromJavaStringNode.getUncached().execute(date.toLocalDate().toString(), TS_ENCODING); } From dafa222927c0da9005bdd49dce08b07cf60ae036 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Fri, 27 Mar 2026 10:44:22 +0100 Subject: [PATCH 20/30] Rename TemporalNodes to TemporalValueNodes --- .../modules/datetime/DateBuiltins.java | 42 +++++----- .../modules/datetime/DateTimeBuiltins.java | 58 +++++++------- .../datetime/DatetimeModuleBuiltins.java | 2 +- ...oralNodes.java => TemporalValueNodes.java} | 20 ++--- .../modules/datetime/TimeBuiltins.java | 20 ++--- .../modules/datetime/TimeDeltaBuiltins.java | 40 +++++----- .../modules/datetime/TimeZoneNodes.java | 4 +- .../objects/foreign/ForeignDateBuiltins.java | 55 +++++++------- .../foreign/ForeignDateTimeBuiltins.java | 76 +++++++++---------- .../objects/foreign/ForeignTimeBuiltins.java | 44 +++++------ .../foreign/ForeignTimeZoneBuiltins.java | 14 ++-- 11 files changed, 188 insertions(+), 187 deletions(-) rename graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/{TemporalNodes.java => TemporalValueNodes.java} (96%) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java index d425c4ad1f..2e8a6464b0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java @@ -81,8 +81,8 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.TimeModuleBuiltins; -import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.DateValue; -import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.TimeDeltaValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.DateValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeDeltaValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.bytes.BytesNodes; @@ -251,7 +251,7 @@ public abstract static class ReprNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString repr(Object self, @Bind Node inliningTarget) { - DateValue date = TemporalNodes.GetDateValue.executeUncached(inliningTarget, self); + DateValue date = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, self); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(self)); var string = String.format("%s(%d, %d, %d)", typeName, date.year, date.month, date.day); return TruffleString.FromJavaStringNode.getUncached().execute(string, TS_ENCODING); @@ -266,7 +266,7 @@ public abstract static class StrNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString str(Object self, @Bind Node inliningTarget) { - DateValue date = TemporalNodes.GetDateValue.executeUncached(inliningTarget, self); + DateValue date = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, self); var string = String.format("%04d-%02d-%02d", date.year, date.month, date.day); return TruffleString.FromJavaStringNode.getUncached().execute(string, TS_ENCODING); } @@ -280,7 +280,7 @@ public abstract static class ReduceNode extends PythonUnaryBuiltinNode { static Object reduce(Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TemporalNodes.GetDateValue readDateValueNode, + @Cached TemporalValueNodes.GetDateValue readDateValueNode, @Cached GetClassNode getClassNode) { DateValue date = readDateValueNode.execute(inliningTarget, self); byte[] bytes = new byte[]{(byte) (date.year / 256), (byte) (date.year % 256), (byte) date.month, (byte) date.day}; @@ -299,7 +299,7 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { static Object richCmp(Object selfObj, Object otherObj, RichCmpOp op, @Bind Node inliningTarget, @Cached PyDateCheckNode dateCheckNode, - @Cached TemporalNodes.GetDateValue readDateValueNode) { + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { if (dateCheckNode.execute(inliningTarget, selfObj) && dateCheckNode.execute(inliningTarget, otherObj)) { DateValue self = readDateValueNode.execute(inliningTarget, selfObj); DateValue other = readDateValueNode.execute(inliningTarget, otherObj); @@ -319,7 +319,7 @@ static long hash(VirtualFrame frame, Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached PyObjectHashNode hashNode, - @Cached TemporalNodes.GetDateValue readDateValueNode) { + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { DateValue d = readDateValueNode.execute(inliningTarget, self); var content = new int[]{d.year, d.month, d.day}; return hashNode.execute(frame, inliningTarget, PFactory.createTuple(language, content)); @@ -360,8 +360,8 @@ private static Object addBoundary(Object left, Object right, Node inliningTarget } else { return PNotImplemented.NOT_IMPLEMENTED; } - DateValue date = TemporalNodes.GetDateValue.executeUncached(inliningTarget, dateObj); - TimeDeltaValue delta = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, deltaObj); + DateValue date = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, dateObj); + TimeDeltaValue delta = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, deltaObj); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(date.year, date.month, date.day); @@ -408,13 +408,13 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget if (!PyDateCheckNode.executeUncached(left)) { return PNotImplemented.NOT_IMPLEMENTED; } - DateValue date = TemporalNodes.GetDateValue.executeUncached(inliningTarget, left); + DateValue date = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, left); if (PyDateCheckNode.executeUncached(right)) { LocalDate from = LocalDate.of(1, 1, 1); LocalDate toSelf = LocalDate.of(date.year, date.month, date.day); long daysSelf = ChronoUnit.DAYS.between(from, toSelf) + 1; - DateValue other = TemporalNodes.GetDateValue.executeUncached(inliningTarget, right); + DateValue other = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, right); LocalDate toOther = LocalDate.of(other.year, other.month, other.day); long daysOther = ChronoUnit.DAYS.between(from, toOther) + 1; @@ -427,7 +427,7 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget 0); } if (PyDeltaCheckNode.executeUncached(right)) { - TimeDeltaValue timeDelta = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); + TimeDeltaValue timeDelta = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(date.year, date.month, date.day); long days = ChronoUnit.DAYS.between(from, to) + 1; @@ -744,7 +744,7 @@ public abstract static class ReplaceNode extends PythonBuiltinNode { @Specialization static Object replace(VirtualFrame frame, Object self, Object yearObject, Object monthObject, Object dayObject, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateValue readDateValueNode, + @Cached TemporalValueNodes.GetDateValue readDateValueNode, @Cached PyLongAsLongNode longAsLongNode, @Cached GetClassNode getClassNode, @Cached DateNodes.NewNode newNode) { @@ -781,7 +781,7 @@ public abstract static class ToOrdinalNode extends PythonUnaryBuiltinNode { @TruffleBoundary static long toOrdinal(Object selfObj, @Bind Node inliningTarget) { - DateValue self = TemporalNodes.GetDateValue.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(self.year, self.month, self.day); return ChronoUnit.DAYS.between(from, to) + 1; @@ -833,7 +833,7 @@ public abstract static class WeekDayNode extends PythonUnaryBuiltinNode { @TruffleBoundary static int weekDay(Object selfObj, @Bind Node inliningTarget) { - DateValue self = TemporalNodes.GetDateValue.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); DayOfWeek dayOfWeek = localDate.getDayOfWeek(); @@ -850,7 +850,7 @@ public abstract static class IsoWeekDayNode extends PythonUnaryBuiltinNode { @TruffleBoundary static int weekDay(Object selfObj, @Bind Node inliningTarget) { - DateValue self = TemporalNodes.GetDateValue.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); DayOfWeek dayOfWeek = localDate.getDayOfWeek(); return dayOfWeek.getValue(); @@ -866,7 +866,7 @@ public abstract static class IsoCalendarNode extends PythonUnaryBuiltinNode { static PTuple isoCalendar(Object selfObj, @Bind PythonLanguage language, @Bind Node inliningTarget) { - DateValue self = TemporalNodes.GetDateValue.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); // use week based year ISO-8601 calendar @@ -886,7 +886,7 @@ public abstract static class IsoFormatNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString isoFormat(Object selfObj, @Bind Node inliningTarget) { - DateValue self = TemporalNodes.GetDateValue.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate locaDate = LocalDate.of(self.year, self.month, self.day); var isoString = locaDate.toString(); return TruffleString.FromJavaStringNode.getUncached().execute(isoString, TS_ENCODING); @@ -901,7 +901,7 @@ public abstract static class CTimeNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString cTime(Object selfObj, @Bind Node inliningTarget) { - DateValue self = TemporalNodes.GetDateValue.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE LLL ppd 00:00:00 yyyy"); String ctime = localDate.format(formatter); @@ -918,7 +918,7 @@ public abstract static class TimeTupleNode extends PythonUnaryBuiltinNode { static PTuple timeTuple(Object selfObj, @Bind PythonLanguage language, @Bind Node inliningTarget) { - DateValue self = TemporalNodes.GetDateValue.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); // Python's day of week is in range 0-6 @@ -944,7 +944,7 @@ protected ArgumentClinicProvider getArgumentClinic() { @TruffleBoundary static TruffleString strftime(Object selfObj, TruffleString format, @Bind Node inliningTarget) { - DateValue self = TemporalNodes.GetDateValue.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); // Reuse time.strftime(format, time_tuple) method. // construct time_tuple diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java index cfa51ed3a1..5a237ccd93 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java @@ -100,10 +100,10 @@ import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.TimeModuleBuiltins; import com.oracle.graal.python.builtins.modules.WarningsModuleBuiltins.WarnNode; -import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.DateValue; -import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.DateTimeValue; -import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.TimeDeltaValue; -import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.TimeValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.DateValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.DateTimeValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeDeltaValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.bytes.BytesNodes; @@ -431,7 +431,7 @@ static TruffleString repr(Object selfObj, @TruffleBoundary private static TruffleString reprBoundary(Node inliningTarget, Object selfObj) { - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); @@ -469,7 +469,7 @@ static Object reduce(Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached GetClassNode getClassNode) { - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); // DateTime is serialized in the following format: // ( // bytes(year 1st byte, year 2nd byte, month, day, hours, minutes, seconds, microseconds @@ -512,7 +512,7 @@ static Object reduceEx(Object selfObj, int protocol, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached GetClassNode getClassNode) { - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); byte[] baseStateBytes = new byte[10]; baseStateBytes[0] = (byte) (self.year / 256); baseStateBytes[1] = (byte) (self.year % 256); @@ -582,8 +582,8 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp } return PNotImplemented.NOT_IMPLEMENTED; } - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); - DateTimeValue other = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, otherObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue other = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, otherObj); // either naive datetimes (without timezone) or timezones are exactly the same objects if (self.tzInfo == other.tzInfo) { int result = compareDateTimeComponents(self, other); @@ -677,7 +677,7 @@ static long hash(VirtualFrame frame, Object selfObj, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode, @Cached TypeNodes.GetInstanceShape getInstanceShape) { - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); final PTimeDelta offset; if (self.tzInfo == null) { offset = null; @@ -749,8 +749,8 @@ private static Object addBoundary(Object left, Object right, Node inliningTarget } else { return PNotImplemented.NOT_IMPLEMENTED; } - DateTimeValue date = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTimeObj); - TimeDeltaValue delta = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, deltaObj); + DateTimeValue date = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTimeObj); + TimeDeltaValue delta = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, deltaObj); LocalDateTime local = date.toLocalDateTime(); LocalDateTime localAdjusted = local.plusDays(delta.days).plusSeconds(delta.seconds).plusNanos(delta.microseconds * 1_000L); @@ -786,9 +786,9 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget if (!PyDateTimeCheckNode.executeUncached(left)) { return PNotImplemented.NOT_IMPLEMENTED; } - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, left); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, left); if (PyDateTimeCheckNode.executeUncached(right)) { - DateTimeValue other = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, right); + DateTimeValue other = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, right); final PTimeDelta selfOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, left, inliningTarget); final PTimeDelta otherOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, right, inliningTarget); @@ -821,7 +821,7 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget 0, 0); } else if (PyDeltaCheckNode.executeUncached(right)) { - TimeDeltaValue timeDelta = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); + TimeDeltaValue timeDelta = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); LocalDateTime local = self.toLocalDateTime(); LocalDateTime localAdjusted = local.minusDays(timeDelta.days).minusSeconds(timeDelta.seconds).minusNanos(timeDelta.microseconds * 1_000L); @@ -1174,8 +1174,8 @@ static Object combine(Object cls, Object dateObject, Object timeObject, Object t timeObject); } - DateValue date = TemporalNodes.GetDateValue.executeUncached(inliningTarget, dateObject); - TimeValue time = TemporalNodes.GetTimeValue.executeUncached(inliningTarget, timeObject); + DateValue date = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, dateObject); + TimeValue time = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, timeObject); final Object tzInfo; if (tzInfoObject instanceof PNone) { @@ -2531,7 +2531,7 @@ abstract static class DateNode extends PythonUnaryBuiltinNode { static Object getDate(Object selfObj, @Bind Node inliningTarget, @Cached DateNodes.NewNode newDateNode) { - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); return newDateNode.execute(inliningTarget, PythonBuiltinClassType.PDate, self.year, @@ -2548,7 +2548,7 @@ abstract static class TimeNode extends PythonUnaryBuiltinNode { static Object getTime(Object selfObj, @Bind Node inliningTarget, @Cached TimeNodes.NewNode newTimeNode) { - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); return newTimeNode.execute(inliningTarget, PythonBuiltinClassType.PTime, self.hour, @@ -2568,7 +2568,7 @@ abstract static class TimeTzNode extends PythonUnaryBuiltinNode { static Object getTime(Object selfObj, @Bind Node inliningTarget, @Cached TimeNodes.NewNode newTimeNode) { - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); return newTimeNode.execute(inliningTarget, PythonBuiltinClassType.PTime, self.hour, @@ -2591,7 +2591,7 @@ static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Obj @Cached PyLongAsLongNode asLongNode, @Cached GetClassNode getClassNode, @Cached DateTimeNodes.NewNode newDateTimeNode) { - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); final long year, month, day; if (yearObject instanceof PNone) { @@ -2678,7 +2678,7 @@ static Object inTimeZone(VirtualFrame frame, Object self, Object tzInfo, @TruffleBoundary private static Object inTimeZoneBoundary(Object selfObj, Object tzInfo, Node inliningTarget) { - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); if (tzInfo == self.tzInfo) { return selfObj; } @@ -2855,7 +2855,7 @@ static PTuple composeTimeTuple(VirtualFrame frame, Object self, @TruffleBoundary private static PTuple composeTimeTupleBoundary(Object selfObj, Node inliningTarget, PythonLanguage language) { - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); int dayOfWeek = localDate.getDayOfWeek().getValue() - 1; // Python's day of week range // is 0-6 @@ -2905,7 +2905,7 @@ static PTuple composeTimeTuple(VirtualFrame frame, Object self, @TruffleBoundary private static PTuple composeTimeTupleBoundary(Object selfObj, Node inliningTarget, PythonLanguage language) { - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); final LocalDateTime localDateTime; PTimeDelta offset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, selfObj, inliningTarget); @@ -2938,7 +2938,7 @@ public abstract static class ToOrdinalNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static long toOrdinal(Object selfObj) { - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(null, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(null, selfObj); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(self.year, self.month, self.day); return ChronoUnit.DAYS.between(from, to) + 1; @@ -2965,7 +2965,7 @@ static double toTimestamp(VirtualFrame frame, Object self, @TruffleBoundary private static double toTimestampBoundary(Object selfObj, Node inliningTarget) { - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); if (self.tzInfo == null) { // CPython: local_to_seconds() TimeZone timeZone = TimeModuleBuiltins.getGlobalTimeZone(getContext(inliningTarget)); @@ -3035,7 +3035,7 @@ static TruffleString isoFormat(VirtualFrame frame, Object self, Object separator @TruffleBoundary private static TruffleString isoFormatBoundary(Object selfObj, Object separatorObject, Object timespecObject, Node inliningTarget) { - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); String dateSection = PythonUtils.formatJString("%04d-%02d-%02d", self.year, self.month, self.day); @@ -3139,7 +3139,7 @@ public abstract static class CTimeNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static TruffleString cTime(Object selfObj) { - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(null, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(null, selfObj); LocalDateTime localDateTime = self.toLocalDateTime(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE LLL ppd HH:mm:ss yyyy"); String ctime = localDateTime.format(formatter); @@ -3174,7 +3174,7 @@ static TruffleString strftime(VirtualFrame frame, Object self, TruffleString for @TruffleBoundary private static TruffleString strftimeBoundary(Object selfObj, TruffleString format, Node inliningTarget) { - DateTimeValue self = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); // Reuse time.strftime(format, time_tuple) method. // construct time_tuple diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DatetimeModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DatetimeModuleBuiltins.java index 4938a0131f..1348d28ff8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DatetimeModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DatetimeModuleBuiltins.java @@ -328,7 +328,7 @@ public static PTimeDelta callDst(Object tzInfo, Object dateTime, VirtualFrame fr @TruffleBoundary public static Object addOffsetToDateTime(Object dateTimeObj, PTimeDelta offset, DateTimeNodes.SubclassNewNode subclassNewNode, Node inliningTarget) { - TemporalNodes.DateTimeValue dateTime = TemporalNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTimeObj); + TemporalValueNodes.DateTimeValue dateTime = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTimeObj); LocalDateTime utc = dateTime.toLocalDateTime().plusDays( offset.days).plusSeconds(offset.seconds).plusNanos(offset.microseconds * 1_000L); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalValueNodes.java similarity index 96% rename from graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java rename to graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalValueNodes.java index aaf0c677e8..a3419ad17b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalValueNodes.java @@ -75,8 +75,8 @@ import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; -public final class TemporalNodes { - private TemporalNodes() { +public final class TemporalValueNodes { + private TemporalValueNodes() { } @ValueType @@ -253,12 +253,12 @@ public static Object toFixedOffsetTimeZone(ZoneId zoneId, Node inliningTarget) { @GenerateUncached @GenerateInline - @GenerateCached(alwaysInlineCached = true) + @GenerateCached(false) public abstract static class GetTimeDeltaValue extends Node { public abstract TimeDeltaValue execute(Node inliningTarget, Object obj); public static TimeDeltaValue executeUncached(Node inliningTarget, Object obj) { - return TemporalNodesFactory.GetTimeDeltaValueNodeGen.getUncached().execute(inliningTarget, obj); + return TemporalValueNodesFactory.GetTimeDeltaValueNodeGen.getUncached().execute(inliningTarget, obj); } @Specialization @@ -283,12 +283,12 @@ static TimeDeltaValue error(Object obj, @GenerateUncached @GenerateInline - @GenerateCached(alwaysInlineCached = true) + @GenerateCached(false) public abstract static class GetDateValue extends Node { public abstract DateValue execute(Node inliningTarget, Object obj); public static DateValue executeUncached(Node inliningTarget, Object obj) { - return TemporalNodesFactory.GetDateValueNodeGen.getUncached().execute(inliningTarget, obj); + return TemporalValueNodesFactory.GetDateValueNodeGen.getUncached().execute(inliningTarget, obj); } @Specialization @@ -324,12 +324,12 @@ static DateValue error(Object obj, @GenerateUncached @GenerateInline - @GenerateCached(alwaysInlineCached = true) + @GenerateCached(false) public abstract static class GetTimeValue extends Node { public abstract TimeValue execute(Node inliningTarget, Object obj); public static TimeValue executeUncached(Node inliningTarget, Object obj) { - return TemporalNodesFactory.GetTimeValueNodeGen.getUncached().execute(inliningTarget, obj); + return TemporalValueNodesFactory.GetTimeValueNodeGen.getUncached().execute(inliningTarget, obj); } @Specialization @@ -369,12 +369,12 @@ static TimeValue error(Object obj, @GenerateUncached @GenerateInline - @GenerateCached(alwaysInlineCached = true) + @GenerateCached(false) public abstract static class GetDateTimeValue extends Node { public abstract DateTimeValue execute(Node inliningTarget, Object obj); public static DateTimeValue executeUncached(Node inliningTarget, Object obj) { - return TemporalNodesFactory.GetDateTimeValueNodeGen.getUncached().execute(inliningTarget, obj); + return TemporalValueNodesFactory.GetDateTimeValueNodeGen.getUncached().execute(inliningTarget, obj); } @Specialization diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java index b072f1c2c9..ab2cfeecfb 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java @@ -68,7 +68,7 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.TimeModuleBuiltins; -import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.TimeValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.bytes.BytesNodes; @@ -272,7 +272,7 @@ static TruffleString repr(VirtualFrame frame, Object self, @TruffleBoundary private static TruffleString reprBoundary(Node inliningTarget, Object selfObj) { - TimeValue self = TemporalNodes.GetTimeValue.executeUncached(null, selfObj); + TimeValue self = TemporalValueNodes.GetTimeValue.executeUncached(null, selfObj); var builder = new StringBuilder(); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); @@ -310,7 +310,7 @@ public abstract static class ReduceNode extends PythonUnaryBuiltinNode { static Object reduce(Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TemporalNodes.GetTimeValue asManagedTimeNode, + @Cached TemporalValueNodes.GetTimeValue asManagedTimeNode, @Cached GetClassNode getClassNode) { TimeValue time = asManagedTimeNode.execute(inliningTarget, self); // Time is serialized in the following format: @@ -350,7 +350,7 @@ public abstract static class ReduceExNode extends PythonBinaryBuiltinNode { static Object reduceEx(Object self, int protocol, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TemporalNodes.GetTimeValue asManagedTimeNode, + @Cached TemporalValueNodes.GetTimeValue asManagedTimeNode, @Cached GetClassNode getClassNode) { TimeValue time = asManagedTimeNode.execute(inliningTarget, self); byte[] baseStateBytes = new byte[6]; @@ -402,8 +402,8 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp if (!PyTimeCheckNode.executeUncached(selfObj) || !PyTimeCheckNode.executeUncached(otherObj)) { return PNotImplemented.NOT_IMPLEMENTED; } - TimeValue self = TemporalNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); - TimeValue other = TemporalNodes.GetTimeValue.executeUncached(inliningTarget, otherObj); + TimeValue self = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); + TimeValue other = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, otherObj); // either naive times (without timezone) or timezones are exactly the same objects if (self.tzInfo == other.tzInfo) { return compareTimeComponents(self, other, op); @@ -455,7 +455,7 @@ abstract static class HashNode extends HashBuiltinNode { static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TemporalNodes.GetTimeValue asManagedTimeNode, + @Cached TemporalValueNodes.GetTimeValue asManagedTimeNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode, @Cached PyObjectHashNode hashNode) { @@ -950,7 +950,7 @@ public abstract static class ReplaceNode extends PythonBuiltinNode { @Specialization static Object replace(VirtualFrame frame, Object self, Object hourObject, Object minuteObject, Object secondObject, Object microsecondObject, Object tzInfoObject, Object foldObject, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeValue asManagedTimeNode, + @Cached TemporalValueNodes.GetTimeValue asManagedTimeNode, @Cached PyLongAsLongNode asLongNode, @Cached GetClassNode getClassNode, @Cached TimeNodes.NewNode newTimeNode) { @@ -1022,7 +1022,7 @@ static TruffleString isoFormat(VirtualFrame frame, Object self, Object timespecO @TruffleBoundary private static TruffleString isoFormatBoundary(Object selfObj, Object timespecObject, Node inliningTarget) { - TimeValue self = TemporalNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); + TimeValue self = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); final String timespec; @@ -1188,7 +1188,7 @@ static TruffleString strftime(VirtualFrame frame, Object self, TruffleString for @TruffleBoundary private static TruffleString strftimeBoundary(Object selfObj, TruffleString format, Node inliningTarget) { - TimeValue self = TemporalNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); + TimeValue self = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); // Reuse time.strftime(format, time_tuple) method. int[] timeTuple = new int[]{1900, 1, 1, self.hour, self.minute, self.second, 0, 1, -1}; String formatPreprocessed = preprocessFormat(format, self, inliningTarget); 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 9bd34e1884..86bde99c63 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 @@ -76,7 +76,7 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotInquiry; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; -import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.TimeDeltaValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeDeltaValue; import com.oracle.graal.python.lib.PyFloatCheckNode; import com.oracle.graal.python.lib.PyDeltaCheckNode; import com.oracle.graal.python.lib.PyLongCheckNode; @@ -165,7 +165,7 @@ abstract static class BoolNode extends TpSlotInquiry.NbBoolBuiltinNode { @Specialization static boolean bool(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); return self.days != 0 || self.seconds != 0 || self.microseconds != 0; } @@ -178,7 +178,7 @@ public abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static TruffleString repr(Object selfObj) { - TimeDeltaValue self = TemporalNodes.GetTimeDeltaValue.executeUncached(null, selfObj); + TimeDeltaValue self = TemporalValueNodes.GetTimeDeltaValue.executeUncached(null, selfObj); var builder = new StringBuilder(); builder.append(TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj))); @@ -227,7 +227,7 @@ public abstract static class StrNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString str(Object selfObj, @Bind Node inliningTarget) { - TimeDeltaValue self = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, selfObj); + TimeDeltaValue self = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); // optional prefix with days, e.g. '1 day' or '5 days' @@ -272,7 +272,7 @@ static Object reduce(Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached GetClassNode getClassNode, - @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); Object type = getClassNode.execute(inliningTarget, selfObj); PTuple arguments = PFactory.createTuple(language, new Object[]{self.days, self.seconds, self.microseconds}); @@ -288,7 +288,7 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { static Object richCmp(Object left, Object right, RichCmpOp op, @Bind Node inliningTarget, @Cached PyDeltaCheckNode checkNode, - @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, right)) { return PNotImplemented.NOT_IMPLEMENTED; } @@ -308,7 +308,7 @@ static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached PyObjectHashNode hashNode, - @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); var content = new int[]{self.days, self.seconds, self.microseconds}; return hashNode.execute(frame, inliningTarget, PFactory.createTuple(language, content)); @@ -325,7 +325,7 @@ static Object add(Object left, Object right, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, @Cached PyDeltaCheckNode checkNode, - @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, right)) { return PNotImplemented.NOT_IMPLEMENTED; } @@ -345,7 +345,7 @@ static Object sub(Object left, Object rigth, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, @Cached PyDeltaCheckNode checkNode, - @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, rigth)) { return PNotImplemented.NOT_IMPLEMENTED; } @@ -417,7 +417,7 @@ static Object mul(VirtualFrame frame, Object left, Object right, @Cached PyNumberMultiplyNode multiplyNode, @Cached TimeDeltaNodes.NewNode newNode, @Cached PyDeltaCheckNode checkNode, - @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { TimeDeltaValue date; Object other; if (checkNode.execute(inliningTarget, left)) { @@ -470,7 +470,7 @@ static Object div(VirtualFrame frame, Object left, Object right, @Cached TimeDeltaNodes.NewNode newNode, @Cached PyDeltaCheckNode checkLeft, @Cached PyDeltaCheckNode checkRight, - @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { if (!checkLeft.execute(inliningTarget, left)) { return PNotImplemented.NOT_IMPLEMENTED; } @@ -518,7 +518,7 @@ static Object div(VirtualFrame frame, Object left, Object right, @Cached PyNumberFloorDivideNode floorDivideNode, @Cached PyDeltaCheckNode checkLeft, @Cached PyDeltaCheckNode checkRight, - @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { if (!checkLeft.execute(inliningTarget, left)) { return PNotImplemented.NOT_IMPLEMENTED; } @@ -550,8 +550,8 @@ static Object divmod(Object left, Object right, if (!PyDeltaCheckNode.executeUncached(left) || !PyDeltaCheckNode.executeUncached(right)) { return PNotImplemented.NOT_IMPLEMENTED; } - TimeDeltaValue self = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, left); - TimeDeltaValue other = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); + TimeDeltaValue self = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, left); + TimeDeltaValue other = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent(); Node encapsulatingNode = encapsulating.set(inliningTarget); @@ -585,8 +585,8 @@ static Object mod(Object left, Object right, EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent(); Node encapsulatingNode = encapsulating.set(inliningTarget); try { - TimeDeltaValue self = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, left); - TimeDeltaValue other = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); + TimeDeltaValue self = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, left); + TimeDeltaValue other = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); Object microsecondsSelf = toMicrosecondsUncached(self); Object microsecondsOther = toMicrosecondsUncached(other); Object remainder = PyNumberRemainderNode.getUncached().execute(null, microsecondsSelf, microsecondsOther); @@ -606,7 +606,7 @@ abstract static class AbsNode extends PythonUnaryBuiltinNode { static PTimeDelta abs(PTimeDelta selfObj, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); if (self.days >= 0) { return newNode.executeBuiltin(inliningTarget, self.days, self.seconds, self.microseconds, 0, 0, 0, 0); @@ -624,7 +624,7 @@ abstract static class PosNode extends PythonUnaryBuiltinNode { static PTimeDelta pos(PTimeDelta selfObj, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); return newNode.executeBuiltin(inliningTarget, self.days, self.seconds, self.microseconds, 0, 0, 0, 0); } @@ -638,7 +638,7 @@ abstract static class NegNode extends PythonUnaryBuiltinNode { static PTimeDelta neg(Object selfObj, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); return newNode.executeBuiltin(inliningTarget, -self.days, -self.seconds, -self.microseconds, 0, 0, 0, 0); } @@ -699,7 +699,7 @@ abstract static class TotalSecondsNode extends PythonUnaryBuiltinNode { @Specialization static Object getTotalSeconds(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeDeltaValue readTimeDeltaValueNode, + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode, @Cached PyNumberAddNode addNode, @Cached PyNumberMultiplyNode multiplyNode, @Cached PyNumberTrueDivideNode trueDivideNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java index 4c16005b56..dd9c59d40f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java @@ -44,7 +44,7 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; -import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.TimeDeltaValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeDeltaValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyDeltaCheckNode; @@ -95,7 +95,7 @@ static PTimeZone newTimezone(Node inliningTarget, PythonContext context, Object if (offsetObj instanceof PTimeDelta value) { offset = value; } else { - TimeDeltaValue offsetValue = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, offsetObj); + TimeDeltaValue offsetValue = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, offsetObj); PythonBuiltinClassType tdcls = PythonBuiltinClassType.PTimeDelta; offset = new PTimeDelta(tdcls, tdcls.getInstanceShape(language), offsetValue.days, offsetValue.seconds, offsetValue.microseconds); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java index a69722c9d4..2b9c389e39 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java @@ -61,8 +61,8 @@ import com.oracle.graal.python.builtins.modules.datetime.DateNodes; import com.oracle.graal.python.builtins.modules.datetime.PDate; import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; -import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes; -import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.TimeDeltaValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeDeltaValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.tuple.PTuple; @@ -116,8 +116,8 @@ abstract static class ReprNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString repr(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateValue readDateValueNode) { - TemporalNodes.DateValue self = readDateValueNode.execute(inliningTarget, selfObj); + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { + TemporalValueNodes.DateValue self = readDateValueNode.execute(inliningTarget, selfObj); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); String string = String.format("%s(%d, %d, %d)", typeName, self.year, self.month, self.day); return TruffleString.FromJavaStringNode.getUncached().execute(string, TS_ENCODING); @@ -130,7 +130,7 @@ abstract static class StrNode extends PythonUnaryBuiltinNode { @Specialization static TruffleString str(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateValue readDateValueNode) { + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { return toIsoFormat(readDateValueNode.execute(inliningTarget, selfObj)); } } @@ -139,10 +139,11 @@ static TruffleString str(Object selfObj, @GenerateNodeFactory abstract static class RichCmpNode extends RichCmpBuiltinNode { @Specialization + @TruffleBoundary static Object richCmp(Object selfObj, Object otherObj, RichCmpOp op, @Bind Node inliningTarget, @Cached PyDateCheckNode dateLikeCheckNode, - @Cached TemporalNodes.GetDateValue readDateValueNode) { + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { if (!dateLikeCheckNode.execute(inliningTarget, otherObj)) { return PNotImplemented.NOT_IMPLEMENTED; } @@ -158,7 +159,7 @@ abstract static class HashNode extends HashBuiltinNode { @Specialization static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateValue readDateValueNode, + @Cached TemporalValueNodes.GetDateValue readDateValueNode, @Cached PyObjectHashNode hashNode) { return hashNode.execute(frame, inliningTarget, toPythonDate(readDateValueNode.execute(inliningTarget, selfObj))); } @@ -172,7 +173,7 @@ abstract static class AddNode extends BinaryOpBuiltinNode { static Object add(Object left, Object right, @Bind Node inliningTarget, @Cached PyDateCheckNode dateLikeCheckNode, - @Cached TemporalNodes.GetDateValue readDateValueNode) { + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { Object dateObj; Object deltaObj; if (dateLikeCheckNode.execute(inliningTarget, left) && PyDeltaCheckNode.executeUncached(right)) { @@ -186,7 +187,7 @@ static Object add(Object left, Object right, } LocalDate date = readDateValueNode.execute(inliningTarget, dateObj).toLocalDate(); - TimeDeltaValue delta = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, deltaObj); + TimeDeltaValue delta = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, deltaObj); long days = ChronoUnit.DAYS.between(LocalDate.of(1, 1, 1), date) + 1 + delta.days; if (days <= 0 || days > MAX_ORDINAL) { throw com.oracle.graal.python.nodes.PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); @@ -203,7 +204,7 @@ abstract static class SubNode extends BinaryOpBuiltinNode { static Object sub(Object left, Object right, @Bind Node inliningTarget, @Cached PyDateCheckNode dateLikeCheckNode, - @Cached TemporalNodes.GetDateValue readDateValueNode) { + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { if (!dateLikeCheckNode.execute(inliningTarget, left)) { return PNotImplemented.NOT_IMPLEMENTED; } @@ -217,7 +218,7 @@ static Object sub(Object left, Object right, return new PTimeDelta(PythonBuiltinClassType.PTimeDelta, PythonBuiltinClassType.PTimeDelta.getInstanceShape(PythonLanguage.get(null)), (int) (leftDays - rightDays), 0, 0); } if (PyDeltaCheckNode.executeUncached(right)) { - TimeDeltaValue delta = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); + TimeDeltaValue delta = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); long days = leftDays - delta.days; if (days <= 0 || days >= MAX_ORDINAL) { throw com.oracle.graal.python.nodes.PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); @@ -234,7 +235,7 @@ abstract static class YearNode extends PythonUnaryBuiltinNode { @Specialization static int year(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateValue readDateValueNode) { + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { return readDateValueNode.execute(inliningTarget, selfObj).year; } } @@ -245,7 +246,7 @@ abstract static class MonthNode extends PythonUnaryBuiltinNode { @Specialization static int month(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateValue readDateValueNode) { + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { return readDateValueNode.execute(inliningTarget, selfObj).month; } } @@ -256,7 +257,7 @@ abstract static class DayNode extends PythonUnaryBuiltinNode { @Specialization static int day(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateValue readDateValueNode) { + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { return readDateValueNode.execute(inliningTarget, selfObj).day; } } @@ -267,10 +268,10 @@ abstract static class ReplaceNode extends PythonBuiltinNode { @Specialization static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Object monthObject, Object dayObject, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateValue readDateValueNode, + @Cached TemporalValueNodes.GetDateValue readDateValueNode, @Cached PyLongAsLongNode longAsLongNode, @Cached DateNodes.NewNode newNode) { - TemporalNodes.DateValue self = readDateValueNode.execute(inliningTarget, selfObj); + TemporalValueNodes.DateValue self = readDateValueNode.execute(inliningTarget, selfObj); int year = yearObject instanceof PNone ? self.year : (int) longAsLongNode.execute(frame, inliningTarget, yearObject); int month = monthObject instanceof PNone ? self.month : (int) longAsLongNode.execute(frame, inliningTarget, monthObject); int day = dayObject instanceof PNone ? self.day : (int) longAsLongNode.execute(frame, inliningTarget, dayObject); @@ -285,7 +286,7 @@ abstract static class ToOrdinalNode extends PythonUnaryBuiltinNode { @TruffleBoundary static long toOrdinal(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateValue readDateValueNode) { + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { return ChronoUnit.DAYS.between(LocalDate.of(1, 1, 1), readDateValueNode.execute(inliningTarget, selfObj).toLocalDate()) + 1; } } @@ -297,7 +298,7 @@ abstract static class WeekDayNode extends PythonUnaryBuiltinNode { @TruffleBoundary static int weekDay(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateValue readDateValueNode) { + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { return readDateValueNode.execute(inliningTarget, selfObj).toLocalDate().getDayOfWeek().getValue() - 1; } } @@ -309,7 +310,7 @@ abstract static class IsoWeekDayNode extends PythonUnaryBuiltinNode { @TruffleBoundary static int isoWeekDay(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateValue readDateValueNode) { + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { return readDateValueNode.execute(inliningTarget, selfObj).toLocalDate().getDayOfWeek().getValue(); } } @@ -320,7 +321,7 @@ abstract static class IsoCalendarNode extends PythonUnaryBuiltinNode { @Specialization static Object isoCalendar(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateValue readDateValueNode) { + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDate(readDateValueNode.execute(inliningTarget, selfObj)), T_ISOCALENDAR); } } @@ -331,7 +332,7 @@ abstract static class IsoFormatNode extends PythonUnaryBuiltinNode { @Specialization static TruffleString isoFormat(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateValue readDateValueNode) { + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { return toIsoFormat(readDateValueNode.execute(inliningTarget, selfObj)); } } @@ -343,7 +344,7 @@ abstract static class CTimeNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString ctime(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateValue readDateValueNode) { + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { String ctime = readDateValueNode.execute(inliningTarget, selfObj).toLocalDate().format(DateTimeFormatter.ofPattern("EEE LLL ppd 00:00:00 yyyy")); return TruffleString.FromJavaStringNode.getUncached().execute(ctime, TS_ENCODING); } @@ -357,8 +358,8 @@ abstract static class TimeTupleNode extends PythonUnaryBuiltinNode { static PTuple timeTuple(Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TemporalNodes.GetDateValue readDateValueNode) { - TemporalNodes.DateValue self = readDateValueNode.execute(inliningTarget, selfObj); + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { + TemporalValueNodes.DateValue self = readDateValueNode.execute(inliningTarget, selfObj); LocalDate localDate = self.toLocalDate(); Object[] fields = new Object[]{self.year, self.month, self.day, 0, 0, 0, localDate.getDayOfWeek().getValue() - 1, localDate.getDayOfYear(), -1}; return PFactory.createStructSeq(language, TimeModuleBuiltins.STRUCT_TIME_DESC, fields); @@ -371,7 +372,7 @@ abstract static class StrFTimeNode extends PythonBinaryBuiltinNode { @Specialization static Object strftime(Object selfObj, Object formatObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateValue readDateValueNode, + @Cached TemporalValueNodes.GetDateValue readDateValueNode, @Cached CastToTruffleStringNode castToTruffleStringNode) { TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); return PyObjectCallMethodObjArgs.executeUncached(toPythonDate(readDateValueNode.execute(inliningTarget, selfObj)), T_STRFTIME, format); @@ -395,7 +396,7 @@ static Object format(VirtualFrame frame, Object selfObj, Object formatObj, } } - private static PDate toPythonDate(TemporalNodes.DateValue date) { + private static PDate toPythonDate(TemporalValueNodes.DateValue date) { return (PDate) DateNodes.NewUnsafeNode.executeUncached(PythonBuiltinClassType.PDate, date.year, date.month, date.day); } @@ -405,7 +406,7 @@ private static Object toPythonDate(LocalDate date) { } @TruffleBoundary - private static TruffleString toIsoFormat(TemporalNodes.DateValue date) { + private static TruffleString toIsoFormat(TemporalValueNodes.DateValue date) { return TruffleString.FromJavaStringNode.getUncached().execute(date.toLocalDate().toString(), TS_ENCODING); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java index d18e79c8ae..d6647d92c9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java @@ -59,7 +59,7 @@ import com.oracle.graal.python.builtins.modules.datetime.DatetimeModuleBuiltins; import com.oracle.graal.python.builtins.modules.datetime.PDateTime; import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; -import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes; import com.oracle.graal.python.builtins.modules.datetime.TimeDeltaNodes; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; @@ -123,8 +123,8 @@ abstract static class ReprNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString repr(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { - TemporalNodes.DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + TemporalValueNodes.DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); String string = String.format("%s(%d, %d, %d, %d, %d, %d, %d)", typeName, self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond); return TruffleString.FromJavaStringNode.getUncached().execute(string, TS_ENCODING); @@ -137,7 +137,7 @@ abstract static class StrNode extends PythonUnaryBuiltinNode { @Specialization static Object str(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, @Cached PyObjectStrAsObjectNode strAsObjectNode) { return strAsObjectNode.execute(inliningTarget, toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); } @@ -151,7 +151,7 @@ static Object richCmp(VirtualFrame frame, Object selfObj, Object otherObj, RichC @Bind Node inliningTarget, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, @Cached PyDateCheckNode dateLikeCheckNode, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode) { if (!dateTimeLikeCheckNode.execute(inliningTarget, otherObj)) { @@ -199,7 +199,7 @@ abstract static class HashNode extends HashBuiltinNode { @Specialization static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, @Cached PyObjectHashNode hashNode) { return hashNode.execute(frame, inliningTarget, toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); } @@ -213,7 +213,7 @@ abstract static class AddNode extends BinaryOpBuiltinNode { static Object add(Object left, Object right, @Bind Node inliningTarget, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { Object dateTimeObj; Object deltaObj; if (dateTimeLikeCheckNode.execute(inliningTarget, left) && PyDeltaCheckNode.executeUncached(right)) { @@ -226,7 +226,7 @@ static Object add(Object left, Object right, return PNotImplemented.NOT_IMPLEMENTED; } PDateTime date = toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, dateTimeObj), inliningTarget); - TemporalNodes.TimeDeltaValue delta = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, deltaObj); + TemporalValueNodes.TimeDeltaValue delta = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, deltaObj); LocalDateTime adjusted = toLocalDateTime(date).plusDays(delta.days).plusSeconds(delta.seconds).plusNanos(delta.microseconds * 1_000L); return toPythonDateTime(adjusted, date.tzInfo, date.fold, inliningTarget); } @@ -239,7 +239,7 @@ abstract static class SubNode extends BinaryOpBuiltinNode { static Object sub(VirtualFrame frame, Object left, Object right, @Bind Node inliningTarget, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode) { if (!dateTimeLikeCheckNode.execute(inliningTarget, left)) { @@ -261,7 +261,7 @@ static Object sub(VirtualFrame frame, Object left, Object right, 0, 0, 0); } if (PyDeltaCheckNode.executeUncached(right)) { - TemporalNodes.TimeDeltaValue delta = TemporalNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); + TemporalValueNodes.TimeDeltaValue delta = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); LocalDateTime adjusted = toLocalDateTime(self).minusDays(delta.days).minusSeconds(delta.seconds).minusNanos(delta.microseconds * 1_000L); return toPythonDateTime(adjusted, self.tzInfo, self.fold, inliningTarget); } @@ -275,7 +275,7 @@ abstract static class YearNode extends PythonUnaryBuiltinNode { @Specialization static int year(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return readDateTimeValueNode.execute(inliningTarget, selfObj).year; } } @@ -286,7 +286,7 @@ abstract static class MonthNode extends PythonUnaryBuiltinNode { @Specialization static int month(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return readDateTimeValueNode.execute(inliningTarget, selfObj).month; } } @@ -297,7 +297,7 @@ abstract static class DayNode extends PythonUnaryBuiltinNode { @Specialization static int day(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return readDateTimeValueNode.execute(inliningTarget, selfObj).day; } } @@ -308,7 +308,7 @@ abstract static class HourNode extends PythonUnaryBuiltinNode { @Specialization static int hour(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return readDateTimeValueNode.execute(inliningTarget, selfObj).hour; } } @@ -319,7 +319,7 @@ abstract static class MinuteNode extends PythonUnaryBuiltinNode { @Specialization static int minute(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return readDateTimeValueNode.execute(inliningTarget, selfObj).minute; } } @@ -330,7 +330,7 @@ abstract static class SecondNode extends PythonUnaryBuiltinNode { @Specialization static int second(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return readDateTimeValueNode.execute(inliningTarget, selfObj).second; } } @@ -341,7 +341,7 @@ abstract static class MicrosecondNode extends PythonUnaryBuiltinNode { @Specialization static int microsecond(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return readDateTimeValueNode.execute(inliningTarget, selfObj).microsecond; } } @@ -352,9 +352,9 @@ abstract static class TzInfoNode extends PythonUnaryBuiltinNode { @Specialization static Object tzinfo(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { - TemporalNodes.DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); - Object tzInfo = TemporalNodes.toPythonTzInfo(self.tzInfo, self.zoneId, inliningTarget); + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + TemporalValueNodes.DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); + Object tzInfo = TemporalValueNodes.toPythonTzInfo(self.tzInfo, self.zoneId, inliningTarget); return tzInfo != null ? tzInfo : PNone.NONE; } } @@ -365,7 +365,7 @@ abstract static class FoldNode extends PythonUnaryBuiltinNode { @Specialization static int fold(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return readDateTimeValueNode.execute(inliningTarget, selfObj).fold; } } @@ -376,7 +376,7 @@ abstract static class DateNode extends PythonUnaryBuiltinNode { @Specialization static Object date(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DATE); } } @@ -387,7 +387,7 @@ abstract static class TimeNode extends PythonUnaryBuiltinNode { @Specialization static Object time(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIME); } } @@ -398,7 +398,7 @@ abstract static class TimeTzNode extends PythonUnaryBuiltinNode { @Specialization static Object timetz(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMETZ); } } @@ -410,10 +410,10 @@ abstract static class ReplaceNode extends PythonBuiltinNode { static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Object monthObject, Object dayObject, Object hourObject, Object minuteObject, Object secondObject, Object microsecondObject, Object tzInfoObject, Object foldObject, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, @Cached PyLongAsLongNode asLongNode, @Cached DateTimeNodes.NewNode newDateTimeNode) { - TemporalNodes.DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); + TemporalValueNodes.DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); long year = yearObject == PNone.NO_VALUE ? self.year : asLongNode.execute(frame, inliningTarget, yearObject); long month = monthObject == PNone.NO_VALUE ? self.month : asLongNode.execute(frame, inliningTarget, monthObject); long day = dayObject == PNone.NO_VALUE ? self.day : asLongNode.execute(frame, inliningTarget, dayObject); @@ -423,7 +423,7 @@ static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Obj long microsecond = microsecondObject == PNone.NO_VALUE ? self.microsecond : asLongNode.execute(frame, inliningTarget, microsecondObject); Object tzInfo; if (tzInfoObject == PNone.NO_VALUE) { - tzInfo = TemporalNodes.toPythonTzInfo(self.tzInfo, self.zoneId, inliningTarget); + tzInfo = TemporalValueNodes.toPythonTzInfo(self.tzInfo, self.zoneId, inliningTarget); } else if (tzInfoObject == PNone.NONE) { tzInfo = null; } else { @@ -440,7 +440,7 @@ abstract static class IsoFormatNode extends PythonBuiltinNode { @Specialization static Object isoformat(Object selfObj, Object sepObj, Object timespecObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { if (sepObj == PNone.NO_VALUE && timespecObj == PNone.NO_VALUE) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT); } @@ -457,7 +457,7 @@ abstract static class UtcOffsetNode extends PythonUnaryBuiltinNode { @Specialization static Object utcoffset(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCOFFSET); } } @@ -468,7 +468,7 @@ abstract static class DstNode extends PythonUnaryBuiltinNode { @Specialization static Object dst(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DST); } } @@ -479,7 +479,7 @@ abstract static class TzNameNode extends PythonUnaryBuiltinNode { @Specialization static Object tzname(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TZNAME); } } @@ -490,7 +490,7 @@ abstract static class TimeTupleNode extends PythonUnaryBuiltinNode { @Specialization static Object timetuple(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMETUPLE); } } @@ -501,7 +501,7 @@ abstract static class UtcTimeTupleNode extends PythonUnaryBuiltinNode { @Specialization static Object utctimetuple(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCTIMETUPLE); } } @@ -512,7 +512,7 @@ abstract static class TimestampNode extends PythonUnaryBuiltinNode { @Specialization static Object timestamp(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMESTAMP); } } @@ -523,7 +523,7 @@ abstract static class AsTimeZoneNode extends PythonBinaryBuiltinNode { @Specialization static Object astimezone(Object selfObj, Object tzObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { if (tzObj == PNone.NO_VALUE) { return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ASTIMEZONE); } @@ -537,7 +537,7 @@ abstract static class StrFTimeNode extends PythonBinaryBuiltinNode { @Specialization static Object strftime(Object selfObj, Object formatObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, @Cached CastToTruffleStringNode castToTruffleStringNode) { TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_STRFTIME, format); @@ -577,8 +577,8 @@ private static LocalDateTime toLocalDateTime(PDateTime self) { return LocalDateTime.of(self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond * 1_000); } - private static PDateTime toPythonDateTime(TemporalNodes.DateTimeValue value, Node inliningTarget) { - Object tzInfo = TemporalNodes.toPythonTzInfo(value.tzInfo, value.zoneId, inliningTarget); + private static PDateTime toPythonDateTime(TemporalValueNodes.DateTimeValue value, Node inliningTarget) { + Object tzInfo = TemporalValueNodes.toPythonTzInfo(value.tzInfo, value.zoneId, inliningTarget); return (PDateTime) DateTimeNodes.NewUnsafeNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PDateTime, value.year, value.month, value.day, value.hour, value.minute, value.second, value.microsecond, tzInfo != null ? tzInfo : PNone.NONE, value.fold); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java index 748f145169..0cd73579ae 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java @@ -57,7 +57,7 @@ import com.oracle.graal.python.builtins.modules.datetime.DatetimeModuleBuiltins; import com.oracle.graal.python.builtins.modules.datetime.PTime; import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; -import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes; import com.oracle.graal.python.builtins.modules.datetime.TimeNodes; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; @@ -111,8 +111,8 @@ abstract static class ReprNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString repr(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeValue readTimeValueNode) { - TemporalNodes.TimeValue self = readTimeValueNode.execute(inliningTarget, selfObj); + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { + TemporalValueNodes.TimeValue self = readTimeValueNode.execute(inliningTarget, selfObj); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); String value = self.microsecond == 0 ? String.format("%s(%d, %d, %d)", typeName, self.hour, self.minute, self.second) @@ -127,7 +127,7 @@ abstract static class StrNode extends PythonUnaryBuiltinNode { @Specialization static Object str(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeValue readTimeValueNode, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, @Cached PyObjectStrAsObjectNode strAsObjectNode) { return strAsObjectNode.execute(inliningTarget, toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); } @@ -140,7 +140,7 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { static Object richCmp(VirtualFrame frame, Object selfObj, Object otherObj, RichCmpOp op, @Bind Node inliningTarget, @Cached PyTimeCheckNode timeLikeCheckNode, - @Cached TemporalNodes.GetTimeValue readTimeValueNode, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode) { if (!timeLikeCheckNode.execute(inliningTarget, otherObj)) { @@ -182,7 +182,7 @@ abstract static class HashNode extends HashBuiltinNode { @Specialization static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeValue readTimeValueNode, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode, @Cached PyObjectHashNode hashNode) { @@ -201,7 +201,7 @@ abstract static class HourNode extends PythonUnaryBuiltinNode { @Specialization static int hour(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeValue readTimeValueNode) { + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { return readTimeValueNode.execute(inliningTarget, selfObj).hour; } } @@ -212,7 +212,7 @@ abstract static class MinuteNode extends PythonUnaryBuiltinNode { @Specialization static int minute(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeValue readTimeValueNode) { + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { return readTimeValueNode.execute(inliningTarget, selfObj).minute; } } @@ -223,7 +223,7 @@ abstract static class SecondNode extends PythonUnaryBuiltinNode { @Specialization static int second(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeValue readTimeValueNode) { + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { return readTimeValueNode.execute(inliningTarget, selfObj).second; } } @@ -234,7 +234,7 @@ abstract static class MicrosecondNode extends PythonUnaryBuiltinNode { @Specialization static int microsecond(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeValue readTimeValueNode) { + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { return readTimeValueNode.execute(inliningTarget, selfObj).microsecond; } } @@ -245,7 +245,7 @@ abstract static class TzInfoNode extends PythonUnaryBuiltinNode { @Specialization static Object tzinfo(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeValue readTimeValueNode) { + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { Object tzInfo = toPythonTzInfo(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget); return tzInfo != null ? tzInfo : PNone.NONE; } @@ -257,7 +257,7 @@ abstract static class FoldNode extends PythonUnaryBuiltinNode { @Specialization static int fold(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeValue readTimeValueNode) { + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { return readTimeValueNode.execute(inliningTarget, selfObj).fold; } } @@ -268,10 +268,10 @@ abstract static class ReplaceNode extends PythonBuiltinNode { @Specialization static Object replace(VirtualFrame frame, Object selfObj, Object hourObject, Object minuteObject, Object secondObject, Object microsecondObject, Object tzInfoObject, Object foldObject, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeValue readTimeValueNode, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, @Cached PyLongAsLongNode asLongNode, @Cached TimeNodes.NewNode newTimeNode) { - TemporalNodes.TimeValue self = readTimeValueNode.execute(inliningTarget, selfObj); + TemporalValueNodes.TimeValue self = readTimeValueNode.execute(inliningTarget, selfObj); long hour = hourObject == PNone.NO_VALUE ? self.hour : asLongNode.execute(frame, inliningTarget, hourObject); long minute = minuteObject == PNone.NO_VALUE ? self.minute : asLongNode.execute(frame, inliningTarget, minuteObject); long second = secondObject == PNone.NO_VALUE ? self.second : asLongNode.execute(frame, inliningTarget, secondObject); @@ -295,7 +295,7 @@ abstract static class IsoFormatNode extends PythonBinaryBuiltinNode { @Specialization static Object isoformat(Object selfObj, Object timespecObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeValue readTimeValueNode) { + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { Object timespec = timespecObj == PNone.NO_VALUE ? PNone.NO_VALUE : timespecObj; return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT, timespec); } @@ -307,7 +307,7 @@ abstract static class UtcOffsetNode extends PythonUnaryBuiltinNode { @Specialization static Object utcoffset(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeValue readTimeValueNode) { + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCOFFSET); } } @@ -318,7 +318,7 @@ abstract static class DstNode extends PythonUnaryBuiltinNode { @Specialization static Object dst(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeValue readTimeValueNode) { + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DST); } } @@ -329,7 +329,7 @@ abstract static class TzNameNode extends PythonUnaryBuiltinNode { @Specialization static Object tzname(Object selfObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeValue readTimeValueNode) { + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TZNAME); } } @@ -340,7 +340,7 @@ abstract static class StrFTimeNode extends PythonBinaryBuiltinNode { @Specialization static Object strftime(Object selfObj, Object formatObj, @Bind Node inliningTarget, - @Cached TemporalNodes.GetTimeValue readTimeValueNode, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, @Cached CastToTruffleStringNode castToTruffleStringNode) { TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_STRFTIME, format); @@ -374,12 +374,12 @@ private static long toMicroseconds(PTime self, PTimeDelta utcOffset) { (long) utcOffset.microseconds; } - private static PTime toPythonTime(TemporalNodes.TimeValue time, Node inliningTarget) { + private static PTime toPythonTime(TemporalValueNodes.TimeValue time, Node inliningTarget) { Object tzInfo = toPythonTzInfo(time, inliningTarget); return (PTime) TimeNodes.NewNode.newTimeUnchecked(PythonBuiltinClassType.PTime, time.hour, time.minute, time.second, time.microsecond, tzInfo != null ? tzInfo : PNone.NONE, time.fold); } - private static Object toPythonTzInfo(TemporalNodes.TimeValue time, Node inliningTarget) { - return TemporalNodes.toPythonTzInfo(time.tzInfo, time.zoneId, inliningTarget); + private static Object toPythonTzInfo(TemporalValueNodes.TimeValue time, Node inliningTarget) { + return TemporalValueNodes.toPythonTzInfo(time.tzInfo, time.zoneId, inliningTarget); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java index f9f97dfa34..a7b13df7ae 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java @@ -58,9 +58,9 @@ import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.datetime.DateTimeNodes; import com.oracle.graal.python.builtins.modules.datetime.DatetimeModuleBuiltins; -import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes; import com.oracle.graal.python.builtins.modules.datetime.TimeDeltaNodes; -import com.oracle.graal.python.builtins.modules.datetime.TemporalNodes.DateTimeValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.DateTimeValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.lib.PyDateTimeCheckNode; @@ -126,10 +126,10 @@ static Object utcoffset(Object self, Object dateTime, @Bind Node inliningTarget, @CachedLibrary("self") InteropLibrary interop, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { ZoneId zoneId = asZoneId(self, interop); if (dateTime == PNone.NONE) { - Object fixed = TemporalNodes.toFixedOffsetTimeZone(zoneId, inliningTarget); + Object fixed = TemporalValueNodes.toFixedOffsetTimeZone(zoneId, inliningTarget); if (fixed == null) { return PNone.NONE; } @@ -153,7 +153,7 @@ static Object dst(Object self, Object dateTime, @Bind Node inliningTarget, @CachedLibrary("self") InteropLibrary interop, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { ZoneId zoneId = asZoneId(self, interop); if (dateTime == PNone.NONE) { return PNone.NONE; @@ -177,7 +177,7 @@ static Object tzname(Object self, Object dateTime, @Bind Node inliningTarget, @CachedLibrary("self") InteropLibrary interop, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { ZoneId zoneId = asZoneId(self, interop); if (dateTime == PNone.NONE) { return zoneId.getRules().isFixedOffset() ? TruffleString.FromJavaStringNode.getUncached().execute(zoneId.getId(), TS_ENCODING) : PNone.NONE; @@ -200,7 +200,7 @@ static Object fromutc(Object self, Object dateTime, @Bind Node inliningTarget, @CachedLibrary("self") InteropLibrary interop, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { if (!dateTimeLikeCheckNode.execute(inliningTarget, dateTime)) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); } From a23029974f34b72e0be96ba74de0d60f629aefa4 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Fri, 27 Mar 2026 12:12:38 +0100 Subject: [PATCH 21/30] Final cleanup pass over new foreign datetime builtins --- .../objects/foreign/ForeignDateBuiltins.java | 125 +++++++---- .../foreign/ForeignDateTimeBuiltins.java | 208 +++++++++++------- .../objects/foreign/ForeignTimeBuiltins.java | 70 +++--- .../foreign/ForeignTimeZoneBuiltins.java | 92 ++++---- 4 files changed, 294 insertions(+), 201 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java index 2b9c389e39..1431d08fdb 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java @@ -42,7 +42,7 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.OverflowError; import static com.oracle.graal.python.nodes.SpecialMethodNames.J___FORMAT__; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; +import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; import java.time.LocalDate; @@ -62,6 +62,7 @@ import com.oracle.graal.python.builtins.modules.datetime.PDate; import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.DateValue; import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeDeltaValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; @@ -79,6 +80,7 @@ import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; import com.oracle.graal.python.lib.RichCmpOp; import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PRaiseNode; 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.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.strings.TruffleString; @CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignDate) @@ -115,12 +118,11 @@ abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static TruffleString repr(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateValue readDateValueNode) { - TemporalValueNodes.DateValue self = readDateValueNode.execute(inliningTarget, selfObj); + @Bind Node inliningTarget) { + TemporalValueNodes.DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); String string = String.format("%s(%d, %d, %d)", typeName, self.year, self.month, self.day); - return TruffleString.FromJavaStringNode.getUncached().execute(string, TS_ENCODING); + return toTruffleStringUncached(string); } } @@ -139,7 +141,6 @@ static TruffleString str(Object selfObj, @GenerateNodeFactory abstract static class RichCmpNode extends RichCmpBuiltinNode { @Specialization - @TruffleBoundary static Object richCmp(Object selfObj, Object otherObj, RichCmpOp op, @Bind Node inliningTarget, @Cached PyDateCheckNode dateLikeCheckNode, @@ -147,8 +148,8 @@ static Object richCmp(Object selfObj, Object otherObj, RichCmpOp op, if (!dateLikeCheckNode.execute(inliningTarget, otherObj)) { return PNotImplemented.NOT_IMPLEMENTED; } - LocalDate self = readDateValueNode.execute(inliningTarget, selfObj).toLocalDate(); - LocalDate other = readDateValueNode.execute(inliningTarget, otherObj).toLocalDate(); + DateValue self = readDateValueNode.execute(inliningTarget, selfObj); + DateValue other = readDateValueNode.execute(inliningTarget, otherObj); return op.compareResultToBool(self.compareTo(other)); } } @@ -159,9 +160,10 @@ abstract static class HashNode extends HashBuiltinNode { @Specialization static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, + @Bind PythonLanguage lang, @Cached TemporalValueNodes.GetDateValue readDateValueNode, @Cached PyObjectHashNode hashNode) { - return hashNode.execute(frame, inliningTarget, toPythonDate(readDateValueNode.execute(inliningTarget, selfObj))); + return hashNode.execute(frame, inliningTarget, toPythonDate(lang, readDateValueNode.execute(inliningTarget, selfObj))); } } @@ -169,30 +171,37 @@ static long hash(VirtualFrame frame, Object selfObj, @GenerateNodeFactory abstract static class AddNode extends BinaryOpBuiltinNode { @Specialization - @TruffleBoundary static Object add(Object left, Object right, @Bind Node inliningTarget, - @Cached PyDateCheckNode dateLikeCheckNode, - @Cached TemporalValueNodes.GetDateValue readDateValueNode) { + @Bind PythonLanguage lang, + @Cached PyDateCheckNode dateLikeCheck, + @Cached PyDeltaCheckNode deltaCheck, + @Cached TemporalValueNodes.GetDateValue readDateValue, + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValue) { Object dateObj; Object deltaObj; - if (dateLikeCheckNode.execute(inliningTarget, left) && PyDeltaCheckNode.executeUncached(right)) { + if (dateLikeCheck.execute(inliningTarget, left) && deltaCheck.execute(inliningTarget, right)) { dateObj = left; deltaObj = right; - } else if (PyDeltaCheckNode.executeUncached(left) && dateLikeCheckNode.execute(inliningTarget, right)) { + } else if (deltaCheck.execute(inliningTarget, left) && dateLikeCheck.execute(inliningTarget, right)) { dateObj = right; deltaObj = left; } else { return PNotImplemented.NOT_IMPLEMENTED; } + DateValue date = readDateValue.execute(inliningTarget, dateObj); + TimeDeltaValue delta = readTimeDeltaValue.execute(inliningTarget, deltaObj); + return op(lang, inliningTarget, date, delta); + } - LocalDate date = readDateValueNode.execute(inliningTarget, dateObj).toLocalDate(); - TimeDeltaValue delta = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, deltaObj); - long days = ChronoUnit.DAYS.between(LocalDate.of(1, 1, 1), date) + 1 + delta.days; + @TruffleBoundary + private static PDate op(PythonLanguage lang, Node inliningTarget, DateValue date, TimeDeltaValue delta) { + long days = ChronoUnit.DAYS.between(LocalDate.of(1, 1, 1), date.toLocalDate()) + 1 + delta.days; if (days <= 0 || days > MAX_ORDINAL) { - throw com.oracle.graal.python.nodes.PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); + throw PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); } - return toPythonDate(ChronoUnit.DAYS.addTo(LocalDate.of(1, 1, 1), days - 1)); + LocalDate ld = ChronoUnit.DAYS.addTo(LocalDate.of(1, 1, 1), days - 1); + return toPythonDate(lang, ld.getYear(), ld.getMonthValue(), ld.getDayOfMonth()); } } @@ -200,33 +209,51 @@ static Object add(Object left, Object right, @GenerateNodeFactory abstract static class SubNode extends BinaryOpBuiltinNode { @Specialization - @TruffleBoundary static Object sub(Object left, Object right, @Bind Node inliningTarget, + @Bind PythonLanguage lang, @Cached PyDateCheckNode dateLikeCheckNode, - @Cached TemporalValueNodes.GetDateValue readDateValueNode) { + @Cached PyDeltaCheckNode deltaCheck, + @Cached TemporalValueNodes.GetDateValue readDateValueNode, + @Cached TemporalValueNodes.GetTimeDeltaValue getTimeDeltaValue) { if (!dateLikeCheckNode.execute(inliningTarget, left)) { return PNotImplemented.NOT_IMPLEMENTED; } + DateValue leftValue = readDateValueNode.execute(inliningTarget, left); - LocalDate leftDate = readDateValueNode.execute(inliningTarget, left).toLocalDate(); - LocalDate from = LocalDate.of(1, 1, 1); - long leftDays = ChronoUnit.DAYS.between(from, leftDate) + 1; if (dateLikeCheckNode.execute(inliningTarget, right)) { - LocalDate rightDate = readDateValueNode.execute(inliningTarget, right).toLocalDate(); - long rightDays = ChronoUnit.DAYS.between(from, rightDate) + 1; - return new PTimeDelta(PythonBuiltinClassType.PTimeDelta, PythonBuiltinClassType.PTimeDelta.getInstanceShape(PythonLanguage.get(null)), (int) (leftDays - rightDays), 0, 0); + return op1(right, inliningTarget, lang, readDateValueNode, leftValue); } - if (PyDeltaCheckNode.executeUncached(right)) { - TimeDeltaValue delta = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); - long days = leftDays - delta.days; - if (days <= 0 || days >= MAX_ORDINAL) { - throw com.oracle.graal.python.nodes.PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); - } - return toPythonDate(ChronoUnit.DAYS.addTo(from, days - 1)); + + if (deltaCheck.execute(inliningTarget, right)) { + TimeDeltaValue delta = getTimeDeltaValue.execute(inliningTarget, right); + return op2(inliningTarget, lang, leftValue, delta); } return PNotImplemented.NOT_IMPLEMENTED; } + + @TruffleBoundary + private static Object op1(Object right, Node inliningTarget, PythonLanguage lang, TemporalValueNodes.GetDateValue readDateValueNode, DateValue leftValue) { + LocalDate leftDate = leftValue.toLocalDate(); + LocalDate from = LocalDate.of(1, 1, 1); + long leftDays = ChronoUnit.DAYS.between(from, leftDate) + 1; + LocalDate rightDate = readDateValueNode.execute(inliningTarget, right).toLocalDate(); + long rightDays = ChronoUnit.DAYS.between(from, rightDate) + 1; + return new PTimeDelta(PythonBuiltinClassType.PTimeDelta, PythonBuiltinClassType.PTimeDelta.getInstanceShape(lang), (int) (leftDays - rightDays), 0, 0); + } + + @TruffleBoundary + private static Object op2(Node inliningTarget, PythonLanguage lang, DateValue leftValue, TimeDeltaValue delta) { + LocalDate leftDate = leftValue.toLocalDate(); + LocalDate from = LocalDate.of(1, 1, 1); + long leftDays = ChronoUnit.DAYS.between(from, leftDate) + 1; + long days = leftDays - delta.days; + if (days <= 0 || days >= MAX_ORDINAL) { + throw PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); + } + from = ChronoUnit.DAYS.addTo(from, days - 1); + return toPythonDate(lang, from.getYear(), from.getMonthValue(), from.getDayOfMonth()); + } } @Builtin(name = "year", minNumOfPositionalArgs = 1, isGetter = true) @@ -319,10 +346,12 @@ static int isoWeekDay(Object selfObj, @GenerateNodeFactory abstract static class IsoCalendarNode extends PythonUnaryBuiltinNode { @Specialization - static Object isoCalendar(Object selfObj, + static Object isoCalendar(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateValue readDateValueNode) { - return PyObjectCallMethodObjArgs.executeUncached(toPythonDate(readDateValueNode.execute(inliningTarget, selfObj)), T_ISOCALENDAR); + @Bind PythonLanguage lang, + @Cached TemporalValueNodes.GetDateValue readDateValueNode, + @Cached PyObjectCallMethodObjArgs callNode) { + return callNode.execute(frame, inliningTarget, toPythonDate(lang, readDateValueNode.execute(inliningTarget, selfObj)), T_ISOCALENDAR); } } @@ -346,7 +375,7 @@ static TruffleString ctime(Object selfObj, @Bind Node inliningTarget, @Cached TemporalValueNodes.GetDateValue readDateValueNode) { String ctime = readDateValueNode.execute(inliningTarget, selfObj).toLocalDate().format(DateTimeFormatter.ofPattern("EEE LLL ppd 00:00:00 yyyy")); - return TruffleString.FromJavaStringNode.getUncached().execute(ctime, TS_ENCODING); + return toTruffleStringUncached(ctime); } } @@ -370,12 +399,14 @@ static PTuple timeTuple(Object selfObj, @GenerateNodeFactory abstract static class StrFTimeNode extends PythonBinaryBuiltinNode { @Specialization - static Object strftime(Object selfObj, Object formatObj, + static Object strftime(VirtualFrame frame, Object selfObj, Object formatObj, @Bind Node inliningTarget, + @Bind PythonLanguage lang, @Cached TemporalValueNodes.GetDateValue readDateValueNode, - @Cached CastToTruffleStringNode castToTruffleStringNode) { + @Cached CastToTruffleStringNode castToTruffleStringNode, + @Cached PyObjectCallMethodObjArgs callNode) { TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); - return PyObjectCallMethodObjArgs.executeUncached(toPythonDate(readDateValueNode.execute(inliningTarget, selfObj)), T_STRFTIME, format); + return callNode.execute(frame, inliningTarget, toPythonDate(lang, readDateValueNode.execute(inliningTarget, selfObj)), T_STRFTIME, format); } } @@ -396,17 +427,17 @@ static Object format(VirtualFrame frame, Object selfObj, Object formatObj, } } - private static PDate toPythonDate(TemporalValueNodes.DateValue date) { - return (PDate) DateNodes.NewUnsafeNode.executeUncached(PythonBuiltinClassType.PDate, date.year, date.month, date.day); + private static PDate toPythonDate(PythonLanguage lang, TemporalValueNodes.DateValue date) { + return toPythonDate(lang, date.year, date.month, date.day); } - @TruffleBoundary - private static Object toPythonDate(LocalDate date) { - return DateNodes.NewUnsafeNode.executeUncached(PythonBuiltinClassType.PDate, date.getYear(), date.getMonthValue(), date.getDayOfMonth()); + private static PDate toPythonDate(PythonLanguage lang, int year, int month, int day) { + Shape shape = PythonBuiltinClassType.PDate.getInstanceShape(lang); + return new PDate(PythonBuiltinClassType.PDate, shape, year, month, day); } @TruffleBoundary private static TruffleString toIsoFormat(TemporalValueNodes.DateValue date) { - return TruffleString.FromJavaStringNode.getUncached().execute(date.toLocalDate().toString(), TS_ENCODING); + return toTruffleStringUncached(date.toLocalDate().toString()); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java index d6647d92c9..ae4ac64f8f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java @@ -41,14 +41,15 @@ package com.oracle.graal.python.builtins.objects.foreign; import static com.oracle.graal.python.nodes.SpecialMethodNames.J___FORMAT__; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; +import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.List; import java.util.Objects; +import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.Builtin; import com.oracle.graal.python.annotations.Slot; import com.oracle.graal.python.annotations.Slot.SlotKind; @@ -60,7 +61,9 @@ import com.oracle.graal.python.builtins.modules.datetime.PDateTime; import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.DateTimeValue; import com.oracle.graal.python.builtins.modules.datetime.TimeDeltaNodes; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeDeltaValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.type.TpSlots; @@ -92,6 +95,7 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.strings.TruffleString; @CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignDateTime) @@ -122,12 +126,11 @@ abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static TruffleString repr(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - TemporalValueNodes.DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); + @Bind Node inliningTarget) { + TemporalValueNodes.DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); String string = String.format("%s(%d, %d, %d, %d, %d, %d, %d)", typeName, self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond); - return TruffleString.FromJavaStringNode.getUncached().execute(string, TS_ENCODING); + return toTruffleStringUncached(string); } } @@ -137,9 +140,10 @@ abstract static class StrNode extends PythonUnaryBuiltinNode { @Specialization static Object str(Object selfObj, @Bind Node inliningTarget, + @Bind PythonLanguage lang, @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, @Cached PyObjectStrAsObjectNode strAsObjectNode) { - return strAsObjectNode.execute(inliningTarget, toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); + return strAsObjectNode.execute(inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); } } @@ -149,6 +153,7 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { @Specialization static Object richCmp(VirtualFrame frame, Object selfObj, Object otherObj, RichCmpOp op, @Bind Node inliningTarget, + @Bind PythonLanguage lang, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, @Cached PyDateCheckNode dateLikeCheckNode, @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, @@ -167,8 +172,8 @@ static Object richCmp(VirtualFrame frame, Object selfObj, Object otherObj, RichC return PNotImplemented.NOT_IMPLEMENTED; } - PDateTime self = toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget); - PDateTime other = toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, otherObj), inliningTarget); + PDateTime self = toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget); + PDateTime other = toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, otherObj), inliningTarget); if (self.tzInfo == other.tzInfo) { return op.compareResultToBool(compareDateTimeComponents(self, other)); } @@ -187,6 +192,11 @@ static Object richCmp(VirtualFrame frame, Object selfObj, Object otherObj, RichC throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.CANT_COMPARE_OFFSET_NAIVE_AND_OFFSET_AWARE_DATETIMES); } } + return boundaryOp(op, self, other, selfUtcOffset, otherUtcOffset); + } + + @TruffleBoundary + private static boolean boundaryOp(RichCmpOp op, PDateTime self, PDateTime other, PTimeDelta selfUtcOffset, PTimeDelta otherUtcOffset) { LocalDateTime selfUtc = subtractOffsetFromDateTime(self, selfUtcOffset); LocalDateTime otherUtc = subtractOffsetFromDateTime(other, otherUtcOffset); return op.compareResultToBool(selfUtc.compareTo(otherUtc)); @@ -199,9 +209,10 @@ abstract static class HashNode extends HashBuiltinNode { @Specialization static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, + @Bind PythonLanguage lang, @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, @Cached PyObjectHashNode hashNode) { - return hashNode.execute(frame, inliningTarget, toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); + return hashNode.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); } } @@ -209,26 +220,33 @@ static long hash(VirtualFrame frame, Object selfObj, @GenerateNodeFactory abstract static class AddNode extends BinaryOpBuiltinNode { @Specialization - @TruffleBoundary static Object add(Object left, Object right, @Bind Node inliningTarget, + @Bind PythonLanguage lang, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + @Cached PyDeltaCheckNode deltaCheckNode, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { Object dateTimeObj; Object deltaObj; - if (dateTimeLikeCheckNode.execute(inliningTarget, left) && PyDeltaCheckNode.executeUncached(right)) { + if (dateTimeLikeCheckNode.execute(inliningTarget, left) && deltaCheckNode.execute(inliningTarget, right)) { dateTimeObj = left; deltaObj = right; - } else if (PyDeltaCheckNode.executeUncached(left) && dateTimeLikeCheckNode.execute(inliningTarget, right)) { + } else if (deltaCheckNode.execute(inliningTarget, left) && dateTimeLikeCheckNode.execute(inliningTarget, right)) { dateTimeObj = right; deltaObj = left; } else { return PNotImplemented.NOT_IMPLEMENTED; } - PDateTime date = toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, dateTimeObj), inliningTarget); - TemporalValueNodes.TimeDeltaValue delta = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, deltaObj); + PDateTime date = toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, dateTimeObj), inliningTarget); + TimeDeltaValue delta = readTimeDeltaValueNode.execute(inliningTarget, deltaObj); + return getAdjusted(lang, inliningTarget, date, delta); + } + + @TruffleBoundary + private static PDateTime getAdjusted(PythonLanguage lang, Node inliningTarget, PDateTime date, TimeDeltaValue delta) { LocalDateTime adjusted = toLocalDateTime(date).plusDays(delta.days).plusSeconds(delta.seconds).plusNanos(delta.microseconds * 1_000L); - return toPythonDateTime(adjusted, date.tzInfo, date.fold, inliningTarget); + return toPythonDateTime(lang, adjusted, date.tzInfo, date.fold); } } @@ -238,35 +256,48 @@ abstract static class SubNode extends BinaryOpBuiltinNode { @Specialization static Object sub(VirtualFrame frame, Object left, Object right, @Bind Node inliningTarget, + @Bind PythonLanguage lang, @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, + @Cached PyDeltaCheckNode deltaCheckNode, @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode) { if (!dateTimeLikeCheckNode.execute(inliningTarget, left)) { return PNotImplemented.NOT_IMPLEMENTED; } - PDateTime self = toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, left), inliningTarget); + PDateTime self = toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, left), inliningTarget); if (dateTimeLikeCheckNode.execute(inliningTarget, right)) { - PDateTime other = toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, right), inliningTarget); + PDateTime other = toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, right), inliningTarget); PTimeDelta selfOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, self, frame, inliningTarget, callMethodObjArgs, raiseNode); PTimeDelta otherOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, other, frame, inliningTarget, callMethodObjArgs, raiseNode); if ((selfOffset == null) != (otherOffset == null)) { throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.CANNOT_SUBTRACT_OFFSET_NAIVE_AND_OFFSET_AWARE_DATETIMES); } - LocalDateTime selfToCompare = selfOffset != null && self.tzInfo != other.tzInfo ? subtractOffsetFromDateTime(self, selfOffset) : toLocalDateTime(self); - LocalDateTime otherToCompare = otherOffset != null && self.tzInfo != other.tzInfo ? subtractOffsetFromDateTime(other, otherOffset) : toLocalDateTime(other); - long selfSeconds = selfToCompare.toEpochSecond(ZoneOffset.UTC); - long otherSeconds = otherToCompare.toEpochSecond(ZoneOffset.UTC); - return TimeDeltaNodes.NewNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PTimeDelta, 0, selfSeconds - otherSeconds, self.microsecond - other.microsecond, 0, - 0, 0, 0); + return op(inliningTarget, self, other, selfOffset, otherOffset); } - if (PyDeltaCheckNode.executeUncached(right)) { - TemporalValueNodes.TimeDeltaValue delta = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); - LocalDateTime adjusted = toLocalDateTime(self).minusDays(delta.days).minusSeconds(delta.seconds).minusNanos(delta.microseconds * 1_000L); - return toPythonDateTime(adjusted, self.tzInfo, self.fold, inliningTarget); + if (deltaCheckNode.execute(inliningTarget, right)) { + TemporalValueNodes.TimeDeltaValue delta = readTimeDeltaValueNode.execute(inliningTarget, right); + return getAdjusted(lang, self, delta); } return PNotImplemented.NOT_IMPLEMENTED; } + + @TruffleBoundary + private static Object getAdjusted(PythonLanguage lang, PDateTime self, TemporalValueNodes.TimeDeltaValue delta) { + LocalDateTime adjusted = toLocalDateTime(self).minusDays(delta.days).minusSeconds(delta.seconds).minusNanos(delta.microseconds * 1_000L); + return toPythonDateTime(lang, adjusted, self.tzInfo, self.fold); + } + + @TruffleBoundary + private static Object op(Node inliningTarget, PDateTime self, PDateTime other, PTimeDelta selfOffset, PTimeDelta otherOffset) { + LocalDateTime selfToCompare = selfOffset != null && self.tzInfo != other.tzInfo ? subtractOffsetFromDateTime(self, selfOffset) : toLocalDateTime(self); + LocalDateTime otherToCompare = otherOffset != null && self.tzInfo != other.tzInfo ? subtractOffsetFromDateTime(other, otherOffset) : toLocalDateTime(other); + long selfSeconds = selfToCompare.toEpochSecond(ZoneOffset.UTC); + long otherSeconds = otherToCompare.toEpochSecond(ZoneOffset.UTC); + return TimeDeltaNodes.NewNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PTimeDelta, 0, selfSeconds - otherSeconds, self.microsecond - other.microsecond, 0, + 0, 0, 0); + } } @Builtin(name = "year", minNumOfPositionalArgs = 1, isGetter = true) @@ -374,10 +405,12 @@ static int fold(Object selfObj, @GenerateNodeFactory abstract static class DateNode extends PythonUnaryBuiltinNode { @Specialization - static Object date(Object selfObj, + static Object date(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DATE); + @Bind PythonLanguage lang, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { + return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DATE); } } @@ -385,10 +418,12 @@ static Object date(Object selfObj, @GenerateNodeFactory abstract static class TimeNode extends PythonUnaryBuiltinNode { @Specialization - static Object time(Object selfObj, + static Object time(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIME); + @Bind PythonLanguage lang, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { + return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIME); } } @@ -396,10 +431,12 @@ static Object time(Object selfObj, @GenerateNodeFactory abstract static class TimeTzNode extends PythonUnaryBuiltinNode { @Specialization - static Object timetz(Object selfObj, + static Object timetz(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMETZ); + @Bind PythonLanguage lang, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { + return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMETZ); } } @@ -438,16 +475,18 @@ static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Obj @GenerateNodeFactory abstract static class IsoFormatNode extends PythonBuiltinNode { @Specialization - static Object isoformat(Object selfObj, Object sepObj, Object timespecObj, + static Object isoformat(VirtualFrame frame, Object selfObj, Object sepObj, Object timespecObj, @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + @Bind PythonLanguage lang, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { if (sepObj == PNone.NO_VALUE && timespecObj == PNone.NO_VALUE) { - return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT); + return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT); } if (timespecObj == PNone.NO_VALUE) { - return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT, sepObj); + return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT, sepObj); } - return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT, sepObj, timespecObj); + return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT, sepObj, timespecObj); } } @@ -455,10 +494,12 @@ static Object isoformat(Object selfObj, Object sepObj, Object timespecObj, @GenerateNodeFactory abstract static class UtcOffsetNode extends PythonUnaryBuiltinNode { @Specialization - static Object utcoffset(Object selfObj, + static Object utcoffset(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCOFFSET); + @Bind PythonLanguage lang, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { + return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCOFFSET); } } @@ -466,10 +507,12 @@ static Object utcoffset(Object selfObj, @GenerateNodeFactory abstract static class DstNode extends PythonUnaryBuiltinNode { @Specialization - static Object dst(Object selfObj, + static Object dst(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DST); + @Bind PythonLanguage lang, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { + return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DST); } } @@ -477,10 +520,12 @@ static Object dst(Object selfObj, @GenerateNodeFactory abstract static class TzNameNode extends PythonUnaryBuiltinNode { @Specialization - static Object tzname(Object selfObj, + static Object tzname(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TZNAME); + @Bind PythonLanguage lang, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { + return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TZNAME); } } @@ -488,10 +533,12 @@ static Object tzname(Object selfObj, @GenerateNodeFactory abstract static class TimeTupleNode extends PythonUnaryBuiltinNode { @Specialization - static Object timetuple(Object selfObj, + static Object timetuple(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMETUPLE); + @Bind PythonLanguage lang, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { + return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMETUPLE); } } @@ -499,10 +546,12 @@ static Object timetuple(Object selfObj, @GenerateNodeFactory abstract static class UtcTimeTupleNode extends PythonUnaryBuiltinNode { @Specialization - static Object utctimetuple(Object selfObj, + static Object utctimetuple(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCTIMETUPLE); + @Bind PythonLanguage lang, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { + return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCTIMETUPLE); } } @@ -510,10 +559,12 @@ static Object utctimetuple(Object selfObj, @GenerateNodeFactory abstract static class TimestampNode extends PythonUnaryBuiltinNode { @Specialization - static Object timestamp(Object selfObj, + static Object timestamp(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMESTAMP); + @Bind PythonLanguage lang, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { + return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMESTAMP); } } @@ -521,13 +572,15 @@ static Object timestamp(Object selfObj, @GenerateNodeFactory abstract static class AsTimeZoneNode extends PythonBinaryBuiltinNode { @Specialization - static Object astimezone(Object selfObj, Object tzObj, + static Object astimezone(VirtualFrame frame, Object selfObj, Object tzObj, @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + @Bind PythonLanguage lang, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { if (tzObj == PNone.NO_VALUE) { - return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ASTIMEZONE); + return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ASTIMEZONE); } - return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ASTIMEZONE, tzObj); + return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ASTIMEZONE, tzObj); } } @@ -535,12 +588,14 @@ static Object astimezone(Object selfObj, Object tzObj, @GenerateNodeFactory abstract static class StrFTimeNode extends PythonBinaryBuiltinNode { @Specialization - static Object strftime(Object selfObj, Object formatObj, + static Object strftime(VirtualFrame frame, Object selfObj, Object formatObj, @Bind Node inliningTarget, + @Bind PythonLanguage lang, @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached CastToTruffleStringNode castToTruffleStringNode) { + @Cached CastToTruffleStringNode castToTruffleStringNode, + @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); - return PyObjectCallMethodObjArgs.executeUncached(toPythonDateTime(readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_STRFTIME, format); + return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_STRFTIME, format); } } @@ -577,14 +632,19 @@ private static LocalDateTime toLocalDateTime(PDateTime self) { return LocalDateTime.of(self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond * 1_000); } - private static PDateTime toPythonDateTime(TemporalValueNodes.DateTimeValue value, Node inliningTarget) { - Object tzInfo = TemporalValueNodes.toPythonTzInfo(value.tzInfo, value.zoneId, inliningTarget); - return (PDateTime) DateTimeNodes.NewUnsafeNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PDateTime, value.year, value.month, value.day, value.hour, value.minute, - value.second, value.microsecond, tzInfo != null ? tzInfo : PNone.NONE, value.fold); + private static PDateTime toPythonDateTime(PythonLanguage lang, DateTimeValue value, Node inliningTarget) { + return toPythonDateTime(lang, value.year, value.month, value.day, value.hour, value.minute, value.second, value.microsecond, + TemporalValueNodes.toPythonTzInfo(value.tzInfo, value.zoneId, inliningTarget), value.fold); + } + + @TruffleBoundary + private static PDateTime toPythonDateTime(PythonLanguage lang, LocalDateTime local, Object tzInfo, int fold) { + return toPythonDateTime(lang, local.getYear(), local.getMonthValue(), local.getDayOfMonth(), local.getHour(), local.getMinute(), local.getSecond(), local.getNano() / 1_000, tzInfo, + fold); } - private static Object toPythonDateTime(LocalDateTime local, Object tzInfo, int fold, Node inliningTarget) { - return DateTimeNodes.NewUnsafeNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PDateTime, local.getYear(), local.getMonthValue(), local.getDayOfMonth(), - local.getHour(), local.getMinute(), local.getSecond(), local.getNano() / 1_000, tzInfo != null ? tzInfo : PNone.NONE, fold); + private static PDateTime toPythonDateTime(PythonLanguage lang, int year, int month, int day, int hour, int minute, int second, int microsecond, Object tzInfo, int fold) { + Shape shape = PythonBuiltinClassType.PDateTime.getInstanceShape(lang); + return new PDateTime(PythonBuiltinClassType.PDateTime, shape, year, month, day, hour, minute, second, microsecond, tzInfo != null ? tzInfo : PNone.NONE, fold); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java index 0cd73579ae..d0abc98a1b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java @@ -48,6 +48,7 @@ import java.util.List; import java.util.Objects; +import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.Builtin; import com.oracle.graal.python.annotations.Slot; import com.oracle.graal.python.annotations.Slot.SlotKind; @@ -59,6 +60,7 @@ import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes; import com.oracle.graal.python.builtins.modules.datetime.TimeNodes; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.type.TpSlots; @@ -87,6 +89,7 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.strings.TruffleString; @CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignTime) @@ -110,9 +113,8 @@ abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static TruffleString repr(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { - TemporalValueNodes.TimeValue self = readTimeValueNode.execute(inliningTarget, selfObj); + @Bind Node inliningTarget) { + TemporalValueNodes.TimeValue self = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); String value = self.microsecond == 0 ? String.format("%s(%d, %d, %d)", typeName, self.hour, self.minute, self.second) @@ -127,9 +129,10 @@ abstract static class StrNode extends PythonUnaryBuiltinNode { @Specialization static Object str(Object selfObj, @Bind Node inliningTarget, + @Bind PythonLanguage lang, @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, @Cached PyObjectStrAsObjectNode strAsObjectNode) { - return strAsObjectNode.execute(inliningTarget, toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); + return strAsObjectNode.execute(inliningTarget, toPythonTime(lang, readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); } } @@ -139,6 +142,7 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { @Specialization static Object richCmp(VirtualFrame frame, Object selfObj, Object otherObj, RichCmpOp op, @Bind Node inliningTarget, + @Bind PythonLanguage lang, @Cached PyTimeCheckNode timeLikeCheckNode, @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @@ -146,8 +150,8 @@ static Object richCmp(VirtualFrame frame, Object selfObj, Object otherObj, RichC if (!timeLikeCheckNode.execute(inliningTarget, otherObj)) { return PNotImplemented.NOT_IMPLEMENTED; } - PTime self = toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget); - PTime other = toPythonTime(readTimeValueNode.execute(inliningTarget, otherObj), inliningTarget); + PTime self = toPythonTime(lang, readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget); + PTime other = toPythonTime(lang, readTimeValueNode.execute(inliningTarget, otherObj), inliningTarget); if (self.tzInfo == other.tzInfo) { return compareTimeComponents(self, other, op); } @@ -182,11 +186,12 @@ abstract static class HashNode extends HashBuiltinNode { @Specialization static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, + @Bind PythonLanguage lang, @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode, @Cached PyObjectHashNode hashNode) { - PTime self = toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget); + PTime self = toPythonTime(lang, readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget); PTimeDelta utcOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, PNone.NONE, frame, inliningTarget, callMethodObjArgs, raiseNode); if (utcOffset == null) { return hashNode.execute(frame, inliningTarget, self); @@ -271,7 +276,7 @@ static Object replace(VirtualFrame frame, Object selfObj, Object hourObject, Obj @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, @Cached PyLongAsLongNode asLongNode, @Cached TimeNodes.NewNode newTimeNode) { - TemporalValueNodes.TimeValue self = readTimeValueNode.execute(inliningTarget, selfObj); + TimeValue self = readTimeValueNode.execute(inliningTarget, selfObj); long hour = hourObject == PNone.NO_VALUE ? self.hour : asLongNode.execute(frame, inliningTarget, hourObject); long minute = minuteObject == PNone.NO_VALUE ? self.minute : asLongNode.execute(frame, inliningTarget, minuteObject); long second = secondObject == PNone.NO_VALUE ? self.second : asLongNode.execute(frame, inliningTarget, secondObject); @@ -293,11 +298,13 @@ static Object replace(VirtualFrame frame, Object selfObj, Object hourObject, Obj @GenerateNodeFactory abstract static class IsoFormatNode extends PythonBinaryBuiltinNode { @Specialization - static Object isoformat(Object selfObj, Object timespecObj, + static Object isoformat(VirtualFrame frame, Object selfObj, Object timespecObj, @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { + @Bind PythonLanguage lang, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, + @Cached PyObjectCallMethodObjArgs callNode) { Object timespec = timespecObj == PNone.NO_VALUE ? PNone.NO_VALUE : timespecObj; - return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT, timespec); + return callNode.execute(frame, inliningTarget, toPythonTime(lang, readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT, timespec); } } @@ -305,10 +312,12 @@ static Object isoformat(Object selfObj, Object timespecObj, @GenerateNodeFactory abstract static class UtcOffsetNode extends PythonUnaryBuiltinNode { @Specialization - static Object utcoffset(Object selfObj, + static Object utcoffset(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { - return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCOFFSET); + @Bind PythonLanguage lang, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, + @Cached PyObjectCallMethodObjArgs callNode) { + return callNode.execute(frame, inliningTarget, toPythonTime(lang, readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCOFFSET); } } @@ -316,10 +325,12 @@ static Object utcoffset(Object selfObj, @GenerateNodeFactory abstract static class DstNode extends PythonUnaryBuiltinNode { @Specialization - static Object dst(Object selfObj, + static Object dst(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { - return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DST); + @Bind PythonLanguage lang, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, + @Cached PyObjectCallMethodObjArgs callNode) { + return callNode.execute(frame, inliningTarget, toPythonTime(lang, readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DST); } } @@ -327,10 +338,12 @@ static Object dst(Object selfObj, @GenerateNodeFactory abstract static class TzNameNode extends PythonUnaryBuiltinNode { @Specialization - static Object tzname(Object selfObj, + static Object tzname(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { - return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TZNAME); + @Bind PythonLanguage lang, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, + @Cached PyObjectCallMethodObjArgs callNode) { + return callNode.execute(frame, inliningTarget, toPythonTime(lang, readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TZNAME); } } @@ -338,12 +351,14 @@ static Object tzname(Object selfObj, @GenerateNodeFactory abstract static class StrFTimeNode extends PythonBinaryBuiltinNode { @Specialization - static Object strftime(Object selfObj, Object formatObj, + static Object strftime(VirtualFrame frame, Object selfObj, Object formatObj, @Bind Node inliningTarget, + @Bind PythonLanguage lang, @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, - @Cached CastToTruffleStringNode castToTruffleStringNode) { + @Cached CastToTruffleStringNode castToTruffleStringNode, + @Cached PyObjectCallMethodObjArgs callNode) { TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); - return PyObjectCallMethodObjArgs.executeUncached(toPythonTime(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_STRFTIME, format); + return callNode.execute(frame, inliningTarget, toPythonTime(lang, readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_STRFTIME, format); } } @@ -368,15 +383,16 @@ private static long toMicroseconds(PTime self, PTimeDelta utcOffset) { return (long) self.hour * 3600 * 1_000_000 + (long) self.minute * 60 * 1_000_000 + (long) self.second * 1_000_000 + - (long) self.microsecond - + self.microsecond - (long) utcOffset.days * 24 * 3600 * 1_000_000 - (long) utcOffset.seconds * 1_000_000 - - (long) utcOffset.microseconds; + utcOffset.microseconds; } - private static PTime toPythonTime(TemporalValueNodes.TimeValue time, Node inliningTarget) { + private static PTime toPythonTime(PythonLanguage lang, TemporalValueNodes.TimeValue time, Node inliningTarget) { Object tzInfo = toPythonTzInfo(time, inliningTarget); - return (PTime) TimeNodes.NewNode.newTimeUnchecked(PythonBuiltinClassType.PTime, time.hour, time.minute, time.second, time.microsecond, tzInfo != null ? tzInfo : PNone.NONE, time.fold); + Shape shape = PythonBuiltinClassType.PTime.getInstanceShape(lang); + return new PTime(PythonBuiltinClassType.PTime, shape, time.hour, time.minute, time.second, time.microsecond, tzInfo, time.fold); } private static Object toPythonTzInfo(TemporalValueNodes.TimeValue time, Node inliningTarget) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java index a7b13df7ae..3c16548698 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java @@ -41,7 +41,7 @@ package com.oracle.graal.python.builtins.objects.foreign; import static com.oracle.graal.python.nodes.SpecialMethodNames.J___REDUCE__; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; +import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; import java.time.LocalDateTime; import java.time.ZoneId; @@ -63,6 +63,7 @@ import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.DateTimeValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.type.TpSlots; +import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyDateTimeCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; @@ -73,13 +74,11 @@ import com.oracle.truffle.api.CompilerDirectives; 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; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.strings.TruffleString; @@ -95,39 +94,35 @@ protected List> getNodeFa @Slot(value = SlotKind.tp_repr, isComplex = true) @GenerateNodeFactory abstract static class ReprNode extends PythonUnaryBuiltinNode { - @Specialization(limit = "1") + @Specialization @TruffleBoundary static TruffleString repr(Object self, - @Bind Node inliningTarget, - @CachedLibrary("self") InteropLibrary interop) { - ZoneId zoneId = asZoneId(self, interop); - TruffleString typeName = com.oracle.graal.python.builtins.objects.type.TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(self)); + @Bind Node inliningTarget) { + ZoneId zoneId = asZoneId(self); + TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(self)); String value = String.format("%s('%s')", typeName, zoneId.getId()); - return TruffleString.FromJavaStringNode.getUncached().execute(value, TS_ENCODING); + return toTruffleStringUncached(value); } } @Slot(value = SlotKind.tp_str, isComplex = true) @GenerateNodeFactory abstract static class StrNode extends PythonUnaryBuiltinNode { - @Specialization(limit = "1") - static TruffleString str(Object self, - @CachedLibrary("self") InteropLibrary interop) { - return TruffleString.FromJavaStringNode.getUncached().execute(asZoneId(self, interop).getId(), TS_ENCODING); + @Specialization + @TruffleBoundary + static TruffleString str(Object self) { + return toTruffleStringUncached(asZoneId(self).getId()); } } @Builtin(name = "utcoffset", minNumOfPositionalArgs = 1, parameterNames = {"$self", "dt"}) @GenerateNodeFactory abstract static class UtcOffsetNode extends PythonBinaryBuiltinNode { - @Specialization(limit = "1") + @Specialization @TruffleBoundary static Object utcoffset(Object self, Object dateTime, - @Bind Node inliningTarget, - @CachedLibrary("self") InteropLibrary interop, - @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - ZoneId zoneId = asZoneId(self, interop); + @Bind Node inliningTarget) { + ZoneId zoneId = asZoneId(self); if (dateTime == PNone.NONE) { Object fixed = TemporalValueNodes.toFixedOffsetTimeZone(zoneId, inliningTarget); if (fixed == null) { @@ -135,10 +130,10 @@ static Object utcoffset(Object self, Object dateTime, } return DatetimeModuleBuiltins.callUtcOffset(fixed, PNone.NONE, inliningTarget); } - if (!dateTimeLikeCheckNode.execute(inliningTarget, dateTime)) { + if (!PyDateTimeCheckNode.executeUncached(dateTime)) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); } - LocalDateTime localDateTime = readDateTimeValueNode.execute(inliningTarget, dateTime).toLocalDateTime(); + LocalDateTime localDateTime = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTime).toLocalDateTime(); ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId); return TimeDeltaNodes.NewNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PTimeDelta, 0, zonedDateTime.getOffset().getTotalSeconds(), 0, 0, 0, 0, 0); } @@ -147,21 +142,18 @@ static Object utcoffset(Object self, Object dateTime, @Builtin(name = "dst", minNumOfPositionalArgs = 1, parameterNames = {"$self", "dt"}) @GenerateNodeFactory abstract static class DstNode extends PythonBinaryBuiltinNode { - @Specialization(limit = "1") + @Specialization @TruffleBoundary static Object dst(Object self, Object dateTime, - @Bind Node inliningTarget, - @CachedLibrary("self") InteropLibrary interop, - @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - ZoneId zoneId = asZoneId(self, interop); + @Bind Node inliningTarget) { + ZoneId zoneId = asZoneId(self); if (dateTime == PNone.NONE) { return PNone.NONE; } - if (!dateTimeLikeCheckNode.execute(inliningTarget, dateTime)) { + if (!PyDateTimeCheckNode.executeUncached(dateTime)) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); } - LocalDateTime localDateTime = readDateTimeValueNode.execute(inliningTarget, dateTime).toLocalDateTime(); + LocalDateTime localDateTime = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTime).toLocalDateTime(); ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId); int dstSeconds = (int) zoneId.getRules().getDaylightSavings(zonedDateTime.toInstant()).getSeconds(); return TimeDeltaNodes.NewNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PTimeDelta, 0, dstSeconds, 0, 0, 0, 0, 0); @@ -171,44 +163,38 @@ static Object dst(Object self, Object dateTime, @Builtin(name = "tzname", minNumOfPositionalArgs = 1, parameterNames = {"$self", "dt"}) @GenerateNodeFactory abstract static class TzNameNode extends PythonBinaryBuiltinNode { - @Specialization(limit = "1") + @Specialization @TruffleBoundary static Object tzname(Object self, Object dateTime, - @Bind Node inliningTarget, - @CachedLibrary("self") InteropLibrary interop, - @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - ZoneId zoneId = asZoneId(self, interop); + @Bind Node inliningTarget) { + ZoneId zoneId = asZoneId(self); if (dateTime == PNone.NONE) { - return zoneId.getRules().isFixedOffset() ? TruffleString.FromJavaStringNode.getUncached().execute(zoneId.getId(), TS_ENCODING) : PNone.NONE; + return zoneId.getRules().isFixedOffset() ? toTruffleStringUncached(zoneId.getId()) : PNone.NONE; } - if (!dateTimeLikeCheckNode.execute(inliningTarget, dateTime)) { + if (!PyDateTimeCheckNode.executeUncached(dateTime)) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); } - LocalDateTime localDateTime = readDateTimeValueNode.execute(inliningTarget, dateTime).toLocalDateTime(); + LocalDateTime localDateTime = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTime).toLocalDateTime(); String name = DateTimeFormatter.ofPattern("z", Locale.ENGLISH).format(localDateTime.atZone(zoneId)); - return TruffleString.FromJavaStringNode.getUncached().execute(name, TS_ENCODING); + return toTruffleStringUncached(name); } } @Builtin(name = "fromutc", minNumOfPositionalArgs = 1, parameterNames = {"$self", "dt"}) @GenerateNodeFactory abstract static class FromUtcNode extends PythonBinaryBuiltinNode { - @Specialization(limit = "1") + @Specialization @TruffleBoundary static Object fromutc(Object self, Object dateTime, - @Bind Node inliningTarget, - @CachedLibrary("self") InteropLibrary interop, - @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - if (!dateTimeLikeCheckNode.execute(inliningTarget, dateTime)) { + @Bind Node inliningTarget) { + if (!PyDateTimeCheckNode.executeUncached(dateTime)) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); } - DateTimeValue asDateTime = readDateTimeValueNode.execute(inliningTarget, dateTime); + DateTimeValue asDateTime = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTime); if (asDateTime.tzInfo != self) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.ValueError, ErrorMessages.FROMUTC_DT_TZINFO_IS_NOT_SELF); } - ZoneId zoneId = asZoneId(self, interop); + ZoneId zoneId = asZoneId(self); LocalDateTime utcDateTime = asDateTime.toLocalDateTime(); ZonedDateTime zonedDateTime = utcDateTime.atOffset(java.time.ZoneOffset.UTC).atZoneSameInstant(zoneId); return DateTimeNodes.NewUnsafeNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PDateTime, zonedDateTime.getYear(), zonedDateTime.getMonthValue(), @@ -219,16 +205,16 @@ static Object fromutc(Object self, Object dateTime, @Builtin(name = J___REDUCE__, minNumOfPositionalArgs = 1) @GenerateNodeFactory abstract static class ReduceNode extends PythonUnaryBuiltinNode { - @Specialization(limit = "1") - static Object reduce(Object self, - @CachedLibrary("self") InteropLibrary interop) { - return TruffleString.FromJavaStringNode.getUncached().execute(asZoneId(self, interop).getId(), TS_ENCODING); + @Specialization + @TruffleBoundary + static Object reduce(Object self) { + return toTruffleStringUncached(asZoneId(self).getId()); } } - private static ZoneId asZoneId(Object self, InteropLibrary interop) { + private static ZoneId asZoneId(Object self) { try { - return interop.asTimeZone(self); + return InteropLibrary.getUncached(self).asTimeZone(self); } catch (UnsupportedMessageException e) { throw CompilerDirectives.shouldNotReachHere(e); } From ba0946d9258aeb3282b662081314764c6f92232e Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Fri, 27 Mar 2026 14:40:39 +0100 Subject: [PATCH 22/30] One more fix around tzinfo and foreign objects --- .../src/tests/test_interop.py | 20 +++++++ .../modules/datetime/TemporalValueNodes.java | 6 +- .../foreign/ForeignTimeZoneBuiltins.java | 55 ++++++++++++++++--- 3 files changed, 72 insertions(+), 9 deletions(-) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py index c581ddf113..ea4c2583bc 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py @@ -272,6 +272,8 @@ def test_foreign_datetime_behavior(self): import java LocalDateTime = java.type("java.time.LocalDateTime") + ZonedDateTime = java.type("java.time.ZonedDateTime") + ZoneId = java.type("java.time.ZoneId") dt = LocalDateTime.of(2025, 3, 23, 7, 8, 9) self.assertEqual(dt.year, 2025) @@ -300,11 +302,20 @@ def test_foreign_datetime_behavior(self): self.assertIsNone(dt.dst()) self.assertIsNone(dt.tzname()) + berlin = ZoneId.of("Europe/Berlin") + zoned_dt = ZonedDateTime.of(2025, 3, 23, 7, 8, 9, 0, berlin) + self.assertIsInstance(zoned_dt.tzinfo, datetime.tzinfo) + self.assertEqual(zoned_dt.utcoffset(), datetime.timedelta(hours=1)) + self.assertEqual(zoned_dt.dst(), datetime.timedelta()) + self.assertEqual(zoned_dt.tzname(), "CET") + self.assertEqual(zoned_dt.isoformat(), "2025-03-23T07:08:09+01:00") + def test_foreign_timezone_behavior(self): import datetime import java ZoneId = java.type("java.time.ZoneId") + ZonedDateTime = java.type("java.time.ZonedDateTime") utc = ZoneId.of("UTC") self.assertIsInstance(utc, datetime.tzinfo) @@ -333,6 +344,15 @@ def test_foreign_timezone_behavior(self): self.assertEqual(berlin.fromutc(datetime.datetime(2025, 3, 23, 6, 8, 9, tzinfo=berlin)), datetime.datetime(2025, 3, 23, 7, 8, 9, tzinfo=berlin)) + foreign_aware = ZonedDateTime.of(2025, 3, 23, 6, 8, 9, 0, berlin) + self.assertEqual(berlin.fromutc(foreign_aware), + datetime.datetime(2025, 3, 23, 7, 8, 9, tzinfo=berlin)) + + overlap = berlin.fromutc(datetime.datetime(2025, 10, 26, 1, 30, tzinfo=berlin)) + self.assertEqual(overlap, datetime.datetime(2025, 10, 26, 2, 30, tzinfo=berlin, fold=1)) + self.assertEqual(overlap.fold, 1) + self.assertEqual(overlap.utcoffset(), datetime.timedelta(hours=1)) + def test_read(self): o = CustomObject() assert polyglot.__read__(o, "field") == o.field diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalValueNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalValueNodes.java index a3419ad17b..88243e4a45 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalValueNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalValueNodes.java @@ -232,7 +232,11 @@ public static Object toPythonTzInfo(Object tzInfo, ZoneId zoneId, Node inliningT if (zoneId == null) { return null; } - return zoneId; + Object fixedOffsetTimeZone = toFixedOffsetTimeZone(zoneId, inliningTarget); + if (fixedOffsetTimeZone != null) { + return fixedOffsetTimeZone; + } + return PythonContext.get(inliningTarget).getEnv().asGuestValue(zoneId); } public static Object toFixedOffsetTimeZone(ZoneId zoneId, Node inliningTarget) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java index 3c16548698..7ab72d1417 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java @@ -45,6 +45,7 @@ import java.time.LocalDateTime; import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.List; @@ -133,8 +134,7 @@ static Object utcoffset(Object self, Object dateTime, if (!PyDateTimeCheckNode.executeUncached(dateTime)) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); } - LocalDateTime localDateTime = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTime).toLocalDateTime(); - ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId); + ZonedDateTime zonedDateTime = resolveDateTimeAtZone(TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTime), zoneId); return TimeDeltaNodes.NewNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PTimeDelta, 0, zonedDateTime.getOffset().getTotalSeconds(), 0, 0, 0, 0, 0); } } @@ -153,8 +153,7 @@ static Object dst(Object self, Object dateTime, if (!PyDateTimeCheckNode.executeUncached(dateTime)) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); } - LocalDateTime localDateTime = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTime).toLocalDateTime(); - ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId); + ZonedDateTime zonedDateTime = resolveDateTimeAtZone(TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTime), zoneId); int dstSeconds = (int) zoneId.getRules().getDaylightSavings(zonedDateTime.toInstant()).getSeconds(); return TimeDeltaNodes.NewNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PTimeDelta, 0, dstSeconds, 0, 0, 0, 0, 0); } @@ -174,8 +173,7 @@ static Object tzname(Object self, Object dateTime, if (!PyDateTimeCheckNode.executeUncached(dateTime)) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); } - LocalDateTime localDateTime = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTime).toLocalDateTime(); - String name = DateTimeFormatter.ofPattern("z", Locale.ENGLISH).format(localDateTime.atZone(zoneId)); + String name = DateTimeFormatter.ofPattern("z", Locale.ENGLISH).format(resolveDateTimeAtZone(TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTime), zoneId)); return toTruffleStringUncached(name); } } @@ -191,14 +189,15 @@ static Object fromutc(Object self, Object dateTime, throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); } DateTimeValue asDateTime = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTime); - if (asDateTime.tzInfo != self) { + if (!hasMatchingTimeZone(asDateTime, self)) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.ValueError, ErrorMessages.FROMUTC_DT_TZINFO_IS_NOT_SELF); } ZoneId zoneId = asZoneId(self); LocalDateTime utcDateTime = asDateTime.toLocalDateTime(); ZonedDateTime zonedDateTime = utcDateTime.atOffset(java.time.ZoneOffset.UTC).atZoneSameInstant(zoneId); + int fold = getFold(zonedDateTime); return DateTimeNodes.NewUnsafeNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PDateTime, zonedDateTime.getYear(), zonedDateTime.getMonthValue(), - zonedDateTime.getDayOfMonth(), zonedDateTime.getHour(), zonedDateTime.getMinute(), zonedDateTime.getSecond(), zonedDateTime.getNano() / 1_000, self, 0); + zonedDateTime.getDayOfMonth(), zonedDateTime.getHour(), zonedDateTime.getMinute(), zonedDateTime.getSecond(), zonedDateTime.getNano() / 1_000, self, fold); } } @@ -219,4 +218,44 @@ private static ZoneId asZoneId(Object self) { throw CompilerDirectives.shouldNotReachHere(e); } } + + private static boolean hasMatchingTimeZone(DateTimeValue dateTime, Object self) { + if (dateTime.tzInfo == self) { + return true; + } + if (dateTime.zoneId != null) { + return asZoneId(self).equals(dateTime.zoneId); + } + if (dateTime.tzInfo == null) { + return false; + } + InteropLibrary interop = InteropLibrary.getUncached(dateTime.tzInfo); + if (!interop.isTimeZone(dateTime.tzInfo)) { + return false; + } + try { + return asZoneId(self).equals(interop.asTimeZone(dateTime.tzInfo)); + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + private static int getFold(ZonedDateTime zonedDateTime) { + List validOffsets = zonedDateTime.getZone().getRules().getValidOffsets(zonedDateTime.toLocalDateTime()); + if (validOffsets.size() < 2) { + return 0; + } + return zonedDateTime.getOffset().equals(validOffsets.get(validOffsets.size() - 1)) ? 1 : 0; + } + + @TruffleBoundary + private static ZonedDateTime resolveDateTimeAtZone(DateTimeValue dateTime, ZoneId zoneId) { + LocalDateTime localDateTime = dateTime.toLocalDateTime(); + ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId); + List validOffsets = zoneId.getRules().getValidOffsets(localDateTime); + if (validOffsets.size() < 2) { + return zonedDateTime; + } + return dateTime.fold == 1 ? zonedDateTime.withLaterOffsetAtOverlap() : zonedDateTime.withEarlierOffsetAtOverlap(); + } } From c63f2f0b71060768521870639f51de29dac9261d Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Fri, 27 Mar 2026 15:11:03 +0100 Subject: [PATCH 23/30] Fix regression around native datetime objects now flowing into more places --- .../modules/datetime/DateTimeBuiltins.java | 4 +- .../modules/datetime/TimeZoneBuiltins.java | 64 ++++++++----------- 2 files changed, 28 insertions(+), 40 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java index 5a237ccd93..b4fba33243 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java @@ -590,8 +590,8 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp return op.compareResultToBool(result); } - PTimeDelta selfUtcOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, self, inliningTarget); - PTimeDelta otherUtcOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, other, inliningTarget); + PTimeDelta selfUtcOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, selfObj, inliningTarget); + PTimeDelta otherUtcOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, otherObj, inliningTarget); if (Objects.equals(selfUtcOffset, otherUtcOffset)) { int result = compareDateTimeComponents(self, other); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneBuiltins.java index 8a4e05cef2..835c58cccb 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneBuiltins.java @@ -248,25 +248,18 @@ static long hash(VirtualFrame frame, PTimeZone self, @GenerateNodeFactory public abstract static class UtcOffsetNode extends PythonBinaryBuiltinNode { - @Specialization - static PTimeDelta utcOffset(PTimeZone self, PDateTime dt) { - return self.offset; - } - @Specialization(guards = {"isNone(dt)"}) static PTimeDelta utcOffsetForNone(PTimeZone self, PNone dt) { return self.offset; } - @Fallback - static void doGeneric(Object self, Object dt, + @Specialization(guards = {"!isNone(dt)"}) + static PTimeDelta utcOffset(PTimeZone self, Object dt, @Bind Node inliningTarget, + @Cached PyDateTimeCheckNode dateTimeCheckNode, @Cached PRaiseNode raiseNode) { - throw raiseNode.raise(inliningTarget, TypeError, - ErrorMessages.S_ARGUMENT_MUST_BE_A_S_INSTANCE_OR_NONE_NOT_P, - "utcoffset(dt)", - "datetime", - dt); + validateDateTimeOrNone(dt, "utcoffset(dt)", inliningTarget, dateTimeCheckNode, raiseNode); + return self.offset; } } @@ -274,26 +267,18 @@ static void doGeneric(Object self, Object dt, @GenerateNodeFactory public abstract static class DstNode extends PythonBinaryBuiltinNode { - @Specialization - static Object dst(PTimeZone self, PDateTime dt) { - return PNone.NONE; - } - @Specialization(guards = {"isNone(dt)"}) static Object dst(PTimeZone self, PNone dt) { return PNone.NONE; } - @Fallback - static void doGeneric(Object self, Object dt, + @Specialization(guards = {"!isNone(dt)"}) + static Object dst(PTimeZone self, Object dt, @Bind Node inliningTarget, + @Cached PyDateTimeCheckNode dateTimeCheckNode, @Cached PRaiseNode raiseNode) { - throw raiseNode.raise(inliningTarget, - TypeError, - ErrorMessages.S_ARGUMENT_MUST_BE_A_S_INSTANCE_OR_NONE_NOT_P, - "dst(dt)", - "datetime", - dt); + validateDateTimeOrNone(dt, "dst(dt)", inliningTarget, dateTimeCheckNode, raiseNode); + return PNone.NONE; } } @@ -301,26 +286,18 @@ static void doGeneric(Object self, Object dt, @GenerateNodeFactory public abstract static class TzNameNode extends PythonBinaryBuiltinNode { - @Specialization - static TruffleString tzName(PTimeZone self, PDateTime dt) { - return getTzName(self); - } - @Specialization(guards = {"isNone(dt)"}) static TruffleString tzName(PTimeZone self, PNone dt) { return getTzName(self); } - @Fallback - static void doGeneric(Object self, Object dt, + @Specialization(guards = {"!isNone(dt)"}) + static TruffleString tzName(PTimeZone self, Object dt, @Bind Node inliningTarget, + @Cached PyDateTimeCheckNode dateTimeCheckNode, @Cached PRaiseNode raiseNode) { - throw raiseNode.raise(inliningTarget, - TypeError, - ErrorMessages.S_ARGUMENT_MUST_BE_A_S_INSTANCE_OR_NONE_NOT_P, - "tzname(dt)", - "datetime", - dt); + validateDateTimeOrNone(dt, "tzname(dt)", inliningTarget, dateTimeCheckNode, raiseNode); + return getTzName(self); } } @@ -401,4 +378,15 @@ static TruffleString getTzName(PTimeZone timezone) { return TruffleString.FromJavaStringNode.getUncached().execute(builder.toString(), TS_ENCODING); } + + private static void validateDateTimeOrNone(Object dt, String methodName, Node inliningTarget, PyDateTimeCheckNode dateTimeCheckNode, PRaiseNode raiseNode) { + if (!dateTimeCheckNode.execute(inliningTarget, dt)) { + throw raiseNode.raise(inliningTarget, + TypeError, + ErrorMessages.S_ARGUMENT_MUST_BE_A_S_INSTANCE_OR_NONE_NOT_P, + methodName, + "datetime", + dt); + } + } } From cf454866bde9a0daa28319aafac8ee9da069cf43 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Fri, 27 Mar 2026 17:01:51 +0100 Subject: [PATCH 24/30] Add missing truffle boundary --- .../modules/datetime/TemporalValueNodes.java | 2 ++ .../objects/foreign/ForeignDateBuiltins.java | 10 +++++----- .../objects/foreign/ForeignDateTimeBuiltins.java | 12 ++++++------ .../objects/foreign/ForeignTimeBuiltins.java | 6 +++--- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalValueNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalValueNodes.java index 88243e4a45..0ec7b88917 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalValueNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalValueNodes.java @@ -63,6 +63,7 @@ import com.oracle.graal.python.runtime.PythonContext; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.ValueType; +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.Fallback; @@ -239,6 +240,7 @@ public static Object toPythonTzInfo(Object tzInfo, ZoneId zoneId, Node inliningT return PythonContext.get(inliningTarget).getEnv().asGuestValue(zoneId); } + @TruffleBoundary public static Object toFixedOffsetTimeZone(ZoneId zoneId, Node inliningTarget) { if (zoneId == null) { return null; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java index 1431d08fdb..c30c6b001e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java @@ -119,7 +119,7 @@ abstract static class ReprNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString repr(Object selfObj, @Bind Node inliningTarget) { - TemporalValueNodes.DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); String string = String.format("%s(%d, %d, %d)", typeName, self.year, self.month, self.day); return toTruffleStringUncached(string); @@ -298,7 +298,7 @@ static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Obj @Cached TemporalValueNodes.GetDateValue readDateValueNode, @Cached PyLongAsLongNode longAsLongNode, @Cached DateNodes.NewNode newNode) { - TemporalValueNodes.DateValue self = readDateValueNode.execute(inliningTarget, selfObj); + DateValue self = readDateValueNode.execute(inliningTarget, selfObj); int year = yearObject instanceof PNone ? self.year : (int) longAsLongNode.execute(frame, inliningTarget, yearObject); int month = monthObject instanceof PNone ? self.month : (int) longAsLongNode.execute(frame, inliningTarget, monthObject); int day = dayObject instanceof PNone ? self.day : (int) longAsLongNode.execute(frame, inliningTarget, dayObject); @@ -388,7 +388,7 @@ static PTuple timeTuple(Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached TemporalValueNodes.GetDateValue readDateValueNode) { - TemporalValueNodes.DateValue self = readDateValueNode.execute(inliningTarget, selfObj); + DateValue self = readDateValueNode.execute(inliningTarget, selfObj); LocalDate localDate = self.toLocalDate(); Object[] fields = new Object[]{self.year, self.month, self.day, 0, 0, 0, localDate.getDayOfWeek().getValue() - 1, localDate.getDayOfYear(), -1}; return PFactory.createStructSeq(language, TimeModuleBuiltins.STRUCT_TIME_DESC, fields); @@ -427,7 +427,7 @@ static Object format(VirtualFrame frame, Object selfObj, Object formatObj, } } - private static PDate toPythonDate(PythonLanguage lang, TemporalValueNodes.DateValue date) { + private static PDate toPythonDate(PythonLanguage lang, DateValue date) { return toPythonDate(lang, date.year, date.month, date.day); } @@ -437,7 +437,7 @@ private static PDate toPythonDate(PythonLanguage lang, int year, int month, int } @TruffleBoundary - private static TruffleString toIsoFormat(TemporalValueNodes.DateValue date) { + private static TruffleString toIsoFormat(DateValue date) { return toTruffleStringUncached(date.toLocalDate().toString()); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java index ae4ac64f8f..d3b64e68fc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java @@ -62,8 +62,8 @@ import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes; import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.DateTimeValue; -import com.oracle.graal.python.builtins.modules.datetime.TimeDeltaNodes; import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeDeltaValue; +import com.oracle.graal.python.builtins.modules.datetime.TimeDeltaNodes; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.type.TpSlots; @@ -127,7 +127,7 @@ abstract static class ReprNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString repr(Object selfObj, @Bind Node inliningTarget) { - TemporalValueNodes.DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); String string = String.format("%s(%d, %d, %d, %d, %d, %d, %d)", typeName, self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond); return toTruffleStringUncached(string); @@ -277,14 +277,14 @@ static Object sub(VirtualFrame frame, Object left, Object right, return op(inliningTarget, self, other, selfOffset, otherOffset); } if (deltaCheckNode.execute(inliningTarget, right)) { - TemporalValueNodes.TimeDeltaValue delta = readTimeDeltaValueNode.execute(inliningTarget, right); + TimeDeltaValue delta = readTimeDeltaValueNode.execute(inliningTarget, right); return getAdjusted(lang, self, delta); } return PNotImplemented.NOT_IMPLEMENTED; } @TruffleBoundary - private static Object getAdjusted(PythonLanguage lang, PDateTime self, TemporalValueNodes.TimeDeltaValue delta) { + private static Object getAdjusted(PythonLanguage lang, PDateTime self, TimeDeltaValue delta) { LocalDateTime adjusted = toLocalDateTime(self).minusDays(delta.days).minusSeconds(delta.seconds).minusNanos(delta.microseconds * 1_000L); return toPythonDateTime(lang, adjusted, self.tzInfo, self.fold); } @@ -384,7 +384,7 @@ abstract static class TzInfoNode extends PythonUnaryBuiltinNode { static Object tzinfo(Object selfObj, @Bind Node inliningTarget, @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - TemporalValueNodes.DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); + DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); Object tzInfo = TemporalValueNodes.toPythonTzInfo(self.tzInfo, self.zoneId, inliningTarget); return tzInfo != null ? tzInfo : PNone.NONE; } @@ -450,7 +450,7 @@ static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Obj @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, @Cached PyLongAsLongNode asLongNode, @Cached DateTimeNodes.NewNode newDateTimeNode) { - TemporalValueNodes.DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); + DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); long year = yearObject == PNone.NO_VALUE ? self.year : asLongNode.execute(frame, inliningTarget, yearObject); long month = monthObject == PNone.NO_VALUE ? self.month : asLongNode.execute(frame, inliningTarget, monthObject); long day = dayObject == PNone.NO_VALUE ? self.day : asLongNode.execute(frame, inliningTarget, dayObject); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java index d0abc98a1b..f67a3a7b60 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java @@ -114,7 +114,7 @@ abstract static class ReprNode extends PythonUnaryBuiltinNode { @TruffleBoundary static TruffleString repr(Object selfObj, @Bind Node inliningTarget) { - TemporalValueNodes.TimeValue self = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); + TimeValue self = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); String value = self.microsecond == 0 ? String.format("%s(%d, %d, %d)", typeName, self.hour, self.minute, self.second) @@ -389,13 +389,13 @@ private static long toMicroseconds(PTime self, PTimeDelta utcOffset) { utcOffset.microseconds; } - private static PTime toPythonTime(PythonLanguage lang, TemporalValueNodes.TimeValue time, Node inliningTarget) { + private static PTime toPythonTime(PythonLanguage lang, TimeValue time, Node inliningTarget) { Object tzInfo = toPythonTzInfo(time, inliningTarget); Shape shape = PythonBuiltinClassType.PTime.getInstanceShape(lang); return new PTime(PythonBuiltinClassType.PTime, shape, time.hour, time.minute, time.second, time.microsecond, tzInfo, time.fold); } - private static Object toPythonTzInfo(TemporalValueNodes.TimeValue time, Node inliningTarget) { + private static Object toPythonTzInfo(TimeValue time, Node inliningTarget) { return TemporalValueNodes.toPythonTzInfo(time.tzInfo, time.zoneId, inliningTarget); } } From e9a8669e06eff8597a905b6c6408389a641d57d8 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 30 Mar 2026 12:33:09 +0200 Subject: [PATCH 25/30] Remove trivially redundant foreign date and datetime builtins --- .../modules/datetime/DateBuiltins.java | 21 ++ .../objects/foreign/ForeignDateBuiltins.java | 259 ------------------ .../foreign/ForeignDateTimeBuiltins.java | 58 ---- .../objects/foreign/ForeignTimeBuiltins.java | 33 --- 4 files changed, 21 insertions(+), 350 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java index 2e8a6464b0..9adac08dec 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java @@ -466,6 +466,13 @@ static int getYear(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { return DateNodes.FromNative.getYear(self, readNode); } + + @Specialization + static int getYear(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { + return readDateValueNode.execute(inliningTarget, self).year; + } } @Builtin(name = "month", minNumOfPositionalArgs = 1, isGetter = true) @@ -482,6 +489,13 @@ static int getMonth(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { return DateNodes.FromNative.getMonth(self, readNode); } + + @Specialization + static int getMonth(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { + return readDateValueNode.execute(inliningTarget, self).month; + } } @Builtin(name = "day", minNumOfPositionalArgs = 1, isGetter = true) @@ -498,6 +512,13 @@ static int getDay(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { return DateNodes.FromNative.getDay(self, readNode); } + + @Specialization + static int getDay(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { + return readDateValueNode.execute(inliningTarget, self).day; + } } @Builtin(name = "today", minNumOfPositionalArgs = 1, isClassmethod = true, parameterNames = {"self"}) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java index c30c6b001e..55ec1d4f89 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java @@ -41,132 +41,49 @@ package com.oracle.graal.python.builtins.objects.foreign; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.OverflowError; -import static com.oracle.graal.python.nodes.SpecialMethodNames.J___FORMAT__; -import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; -import static com.oracle.graal.python.util.PythonUtils.tsLiteral; import java.time.LocalDate; -import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.List; import com.oracle.graal.python.PythonLanguage; -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.builtins.CoreFunctions; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; -import com.oracle.graal.python.builtins.modules.TimeModuleBuiltins; -import com.oracle.graal.python.builtins.modules.datetime.DateNodes; import com.oracle.graal.python.builtins.modules.datetime.PDate; import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes; import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.DateValue; import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeDeltaValue; -import com.oracle.graal.python.builtins.objects.PNone; -import com.oracle.graal.python.builtins.objects.PNotImplemented; -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.TpSlotBinaryOp.BinaryOpBuiltinNode; -import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun.HashBuiltinNode; -import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; import com.oracle.graal.python.lib.PyDateCheckNode; import com.oracle.graal.python.lib.PyDeltaCheckNode; -import com.oracle.graal.python.lib.PyLongAsLongNode; -import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; -import com.oracle.graal.python.lib.PyObjectHashNode; -import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; -import com.oracle.graal.python.lib.RichCmpOp; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; 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; -import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; -import com.oracle.graal.python.nodes.object.GetClassNode; -import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; -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; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.object.Shape; -import com.oracle.truffle.api.strings.TruffleString; @CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignDate) public final class ForeignDateBuiltins extends PythonBuiltins { public static final TpSlots SLOTS = ForeignDateBuiltinsSlotsGen.SLOTS; private static final int MAX_ORDINAL = 3_652_059; - private static final TruffleString T_ISOCALENDAR = tsLiteral("isocalendar"); - private static final TruffleString T_STRFTIME = tsLiteral("strftime"); @Override protected List> getNodeFactories() { return ForeignDateBuiltinsFactory.getFactories(); } - @Slot(value = SlotKind.tp_repr, isComplex = true) - @GenerateNodeFactory - abstract static class ReprNode extends PythonUnaryBuiltinNode { - @Specialization - @TruffleBoundary - static TruffleString repr(Object selfObj, - @Bind Node inliningTarget) { - DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); - TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); - String string = String.format("%s(%d, %d, %d)", typeName, self.year, self.month, self.day); - return toTruffleStringUncached(string); - } - } - - @Slot(value = SlotKind.tp_str, isComplex = true) - @GenerateNodeFactory - abstract static class StrNode extends PythonUnaryBuiltinNode { - @Specialization - static TruffleString str(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateValue readDateValueNode) { - return toIsoFormat(readDateValueNode.execute(inliningTarget, selfObj)); - } - } - - @Slot(value = SlotKind.tp_richcompare, isComplex = true) - @GenerateNodeFactory - abstract static class RichCmpNode extends RichCmpBuiltinNode { - @Specialization - static Object richCmp(Object selfObj, Object otherObj, RichCmpOp op, - @Bind Node inliningTarget, - @Cached PyDateCheckNode dateLikeCheckNode, - @Cached TemporalValueNodes.GetDateValue readDateValueNode) { - if (!dateLikeCheckNode.execute(inliningTarget, otherObj)) { - return PNotImplemented.NOT_IMPLEMENTED; - } - DateValue self = readDateValueNode.execute(inliningTarget, selfObj); - DateValue other = readDateValueNode.execute(inliningTarget, otherObj); - return op.compareResultToBool(self.compareTo(other)); - } - } - - @Slot(value = SlotKind.tp_hash, isComplex = true) - @GenerateNodeFactory - abstract static class HashNode extends HashBuiltinNode { - @Specialization - static long hash(VirtualFrame frame, Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateValue readDateValueNode, - @Cached PyObjectHashNode hashNode) { - return hashNode.execute(frame, inliningTarget, toPythonDate(lang, readDateValueNode.execute(inliningTarget, selfObj))); - } - } - @Slot(value = SlotKind.nb_add, isComplex = true) @GenerateNodeFactory abstract static class AddNode extends BinaryOpBuiltinNode { @@ -256,177 +173,6 @@ private static Object op2(Node inliningTarget, PythonLanguage lang, DateValue le } } - @Builtin(name = "year", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class YearNode extends PythonUnaryBuiltinNode { - @Specialization - static int year(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateValue readDateValueNode) { - return readDateValueNode.execute(inliningTarget, selfObj).year; - } - } - - @Builtin(name = "month", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class MonthNode extends PythonUnaryBuiltinNode { - @Specialization - static int month(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateValue readDateValueNode) { - return readDateValueNode.execute(inliningTarget, selfObj).month; - } - } - - @Builtin(name = "day", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class DayNode extends PythonUnaryBuiltinNode { - @Specialization - static int day(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateValue readDateValueNode) { - return readDateValueNode.execute(inliningTarget, selfObj).day; - } - } - - @Builtin(name = "replace", minNumOfPositionalArgs = 1, parameterNames = {"self", "year", "month", "day"}) - @GenerateNodeFactory - abstract static class ReplaceNode extends PythonBuiltinNode { - @Specialization - static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Object monthObject, Object dayObject, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateValue readDateValueNode, - @Cached PyLongAsLongNode longAsLongNode, - @Cached DateNodes.NewNode newNode) { - DateValue self = readDateValueNode.execute(inliningTarget, selfObj); - int year = yearObject instanceof PNone ? self.year : (int) longAsLongNode.execute(frame, inliningTarget, yearObject); - int month = monthObject instanceof PNone ? self.month : (int) longAsLongNode.execute(frame, inliningTarget, monthObject); - int day = dayObject instanceof PNone ? self.day : (int) longAsLongNode.execute(frame, inliningTarget, dayObject); - return newNode.execute(inliningTarget, PythonBuiltinClassType.PDate, year, month, day); - } - } - - @Builtin(name = "toordinal", minNumOfPositionalArgs = 1, parameterNames = {"self"}) - @GenerateNodeFactory - abstract static class ToOrdinalNode extends PythonUnaryBuiltinNode { - @Specialization - @TruffleBoundary - static long toOrdinal(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateValue readDateValueNode) { - return ChronoUnit.DAYS.between(LocalDate.of(1, 1, 1), readDateValueNode.execute(inliningTarget, selfObj).toLocalDate()) + 1; - } - } - - @Builtin(name = "weekday", minNumOfPositionalArgs = 1, parameterNames = {"self"}) - @GenerateNodeFactory - abstract static class WeekDayNode extends PythonUnaryBuiltinNode { - @Specialization - @TruffleBoundary - static int weekDay(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateValue readDateValueNode) { - return readDateValueNode.execute(inliningTarget, selfObj).toLocalDate().getDayOfWeek().getValue() - 1; - } - } - - @Builtin(name = "isoweekday", minNumOfPositionalArgs = 1, parameterNames = {"self"}) - @GenerateNodeFactory - abstract static class IsoWeekDayNode extends PythonUnaryBuiltinNode { - @Specialization - @TruffleBoundary - static int isoWeekDay(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateValue readDateValueNode) { - return readDateValueNode.execute(inliningTarget, selfObj).toLocalDate().getDayOfWeek().getValue(); - } - } - - @Builtin(name = "isocalendar", minNumOfPositionalArgs = 1, parameterNames = {"self"}) - @GenerateNodeFactory - abstract static class IsoCalendarNode extends PythonUnaryBuiltinNode { - @Specialization - static Object isoCalendar(VirtualFrame frame, Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateValue readDateValueNode, - @Cached PyObjectCallMethodObjArgs callNode) { - return callNode.execute(frame, inliningTarget, toPythonDate(lang, readDateValueNode.execute(inliningTarget, selfObj)), T_ISOCALENDAR); - } - } - - @Builtin(name = "isoformat", minNumOfPositionalArgs = 1, parameterNames = {"self"}) - @GenerateNodeFactory - abstract static class IsoFormatNode extends PythonUnaryBuiltinNode { - @Specialization - static TruffleString isoFormat(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateValue readDateValueNode) { - return toIsoFormat(readDateValueNode.execute(inliningTarget, selfObj)); - } - } - - @Builtin(name = "ctime", minNumOfPositionalArgs = 1, parameterNames = {"self"}) - @GenerateNodeFactory - abstract static class CTimeNode extends PythonUnaryBuiltinNode { - @Specialization - @TruffleBoundary - static TruffleString ctime(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateValue readDateValueNode) { - String ctime = readDateValueNode.execute(inliningTarget, selfObj).toLocalDate().format(DateTimeFormatter.ofPattern("EEE LLL ppd 00:00:00 yyyy")); - return toTruffleStringUncached(ctime); - } - } - - @Builtin(name = "timetuple", minNumOfPositionalArgs = 1, parameterNames = {"self"}) - @GenerateNodeFactory - abstract static class TimeTupleNode extends PythonUnaryBuiltinNode { - @Specialization - @TruffleBoundary - static PTuple timeTuple(Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage language, - @Cached TemporalValueNodes.GetDateValue readDateValueNode) { - DateValue self = readDateValueNode.execute(inliningTarget, selfObj); - LocalDate localDate = self.toLocalDate(); - Object[] fields = new Object[]{self.year, self.month, self.day, 0, 0, 0, localDate.getDayOfWeek().getValue() - 1, localDate.getDayOfYear(), -1}; - return PFactory.createStructSeq(language, TimeModuleBuiltins.STRUCT_TIME_DESC, fields); - } - } - - @Builtin(name = "strftime", minNumOfPositionalArgs = 2, parameterNames = {"self", "format"}) - @GenerateNodeFactory - abstract static class StrFTimeNode extends PythonBinaryBuiltinNode { - @Specialization - static Object strftime(VirtualFrame frame, Object selfObj, Object formatObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateValue readDateValueNode, - @Cached CastToTruffleStringNode castToTruffleStringNode, - @Cached PyObjectCallMethodObjArgs callNode) { - TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); - return callNode.execute(frame, inliningTarget, toPythonDate(lang, readDateValueNode.execute(inliningTarget, selfObj)), T_STRFTIME, format); - } - } - - @Builtin(name = J___FORMAT__, minNumOfPositionalArgs = 2, parameterNames = {"$self", "format"}) - @GenerateNodeFactory - abstract static class FormatNode extends PythonBinaryBuiltinNode { - @Specialization - static Object format(VirtualFrame frame, Object selfObj, Object formatObj, - @Bind Node inliningTarget, - @Cached PyObjectStrAsObjectNode strAsObjectNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs, - @Cached CastToTruffleStringNode castToTruffleStringNode) { - TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); - if (format.isEmpty()) { - return strAsObjectNode.execute(inliningTarget, selfObj); - } - return callMethodObjArgs.execute(frame, inliningTarget, selfObj, T_STRFTIME, format); - } - } - private static PDate toPythonDate(PythonLanguage lang, DateValue date) { return toPythonDate(lang, date.year, date.month, date.day); } @@ -435,9 +181,4 @@ private static PDate toPythonDate(PythonLanguage lang, int year, int month, int Shape shape = PythonBuiltinClassType.PDate.getInstanceShape(lang); return new PDate(PythonBuiltinClassType.PDate, shape, year, month, day); } - - @TruffleBoundary - private static TruffleString toIsoFormat(DateValue date) { - return toTruffleStringUncached(date.toLocalDate().toString()); - } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java index d3b64e68fc..d0f84ffad4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java @@ -42,7 +42,6 @@ import static com.oracle.graal.python.nodes.SpecialMethodNames.J___FORMAT__; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; -import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; import java.time.LocalDateTime; import java.time.ZoneOffset; @@ -67,7 +66,6 @@ import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; 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.TpSlotBinaryOp.BinaryOpBuiltinNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun.HashBuiltinNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; @@ -85,7 +83,6 @@ import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; -import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Bind; @@ -103,11 +100,9 @@ public final class ForeignDateTimeBuiltins extends PythonBuiltins { public static final TpSlots SLOTS = ForeignDateTimeBuiltinsSlotsGen.SLOTS; private static final TruffleString T_ASTIMEZONE = tsLiteral("astimezone"); - private static final TruffleString T_DATE = tsLiteral("date"); private static final TruffleString T_DST = tsLiteral("dst"); private static final TruffleString T_ISOFORMAT = tsLiteral("isoformat"); private static final TruffleString T_STRFTIME = tsLiteral("strftime"); - private static final TruffleString T_TIME = tsLiteral("time"); private static final TruffleString T_TIMETZ = tsLiteral("timetz"); private static final TruffleString T_TIMESTAMP = tsLiteral("timestamp"); private static final TruffleString T_TIMETUPLE = tsLiteral("timetuple"); @@ -120,33 +115,6 @@ protected List> getNodeFa return ForeignDateTimeBuiltinsFactory.getFactories(); } - @Slot(value = SlotKind.tp_repr, isComplex = true) - @GenerateNodeFactory - abstract static class ReprNode extends PythonUnaryBuiltinNode { - @Specialization - @TruffleBoundary - static TruffleString repr(Object selfObj, - @Bind Node inliningTarget) { - DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); - TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); - String string = String.format("%s(%d, %d, %d, %d, %d, %d, %d)", typeName, self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond); - return toTruffleStringUncached(string); - } - } - - @Slot(value = SlotKind.tp_str, isComplex = true) - @GenerateNodeFactory - abstract static class StrNode extends PythonUnaryBuiltinNode { - @Specialization - static Object str(Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached PyObjectStrAsObjectNode strAsObjectNode) { - return strAsObjectNode.execute(inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); - } - } - @Slot(value = SlotKind.tp_richcompare, isComplex = true) @GenerateNodeFactory abstract static class RichCmpNode extends RichCmpBuiltinNode { @@ -401,32 +369,6 @@ static int fold(Object selfObj, } } - @Builtin(name = "date", minNumOfPositionalArgs = 1, parameterNames = {"self"}) - @GenerateNodeFactory - abstract static class DateNode extends PythonUnaryBuiltinNode { - @Specialization - static Object date(VirtualFrame frame, Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { - return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DATE); - } - } - - @Builtin(name = "time", minNumOfPositionalArgs = 1, parameterNames = {"self"}) - @GenerateNodeFactory - abstract static class TimeNode extends PythonUnaryBuiltinNode { - @Specialization - static Object time(VirtualFrame frame, Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { - return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIME); - } - } - @Builtin(name = "timetz", minNumOfPositionalArgs = 1, parameterNames = {"self"}) @GenerateNodeFactory abstract static class TimeTzNode extends PythonUnaryBuiltinNode { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java index f67a3a7b60..84010a6427 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java @@ -41,7 +41,6 @@ package com.oracle.graal.python.builtins.objects.foreign; import static com.oracle.graal.python.nodes.SpecialMethodNames.J___FORMAT__; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; import java.util.Arrays; @@ -64,7 +63,6 @@ import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; 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.TpSlotHashFun.HashBuiltinNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; import com.oracle.graal.python.lib.PyLongAsLongNode; @@ -79,9 +77,7 @@ import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; -import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; -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; @@ -107,35 +103,6 @@ protected List> getNodeFa return ForeignTimeBuiltinsFactory.getFactories(); } - @Slot(value = SlotKind.tp_repr, isComplex = true) - @GenerateNodeFactory - abstract static class ReprNode extends PythonUnaryBuiltinNode { - @Specialization - @TruffleBoundary - static TruffleString repr(Object selfObj, - @Bind Node inliningTarget) { - TimeValue self = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); - TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); - String value = self.microsecond == 0 - ? String.format("%s(%d, %d, %d)", typeName, self.hour, self.minute, self.second) - : String.format("%s(%d, %d, %d, %d)", typeName, self.hour, self.minute, self.second, self.microsecond); - return TruffleString.FromJavaStringNode.getUncached().execute(value, TS_ENCODING); - } - } - - @Slot(value = SlotKind.tp_str, isComplex = true) - @GenerateNodeFactory - abstract static class StrNode extends PythonUnaryBuiltinNode { - @Specialization - static Object str(Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, - @Cached PyObjectStrAsObjectNode strAsObjectNode) { - return strAsObjectNode.execute(inliningTarget, toPythonTime(lang, readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); - } - } - @Slot(value = SlotKind.tp_richcompare, isComplex = true) @GenerateNodeFactory abstract static class RichCmpNode extends RichCmpBuiltinNode { From ff608c13ee9c30085e63181075dc68fe17270a41 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 30 Mar 2026 13:09:01 +0200 Subject: [PATCH 26/30] Remove ForeignDateBuiltins --- .../graal/python/builtins/Python3Core.java | 2 - .../builtins/PythonBuiltinClassType.java | 3 +- .../modules/datetime/DateBuiltins.java | 9 +- .../objects/foreign/ForeignDateBuiltins.java | 184 ------------------ 4 files changed, 8 insertions(+), 190 deletions(-) delete mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java index 28bdba2f63..2cdc0aa408 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java @@ -263,7 +263,6 @@ import com.oracle.graal.python.builtins.objects.floats.PFloat; import com.oracle.graal.python.builtins.objects.foreign.ForeignAbstractClassBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignBooleanBuiltins; -import com.oracle.graal.python.builtins.objects.foreign.ForeignDateBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignDateTimeBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignExecutableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignInstantiableBuiltins; @@ -504,7 +503,6 @@ private static PythonBuiltins[] initializeBuiltins(TruffleLanguage.Env env) { new ForeignObjectBuiltins(), new ForeignNumberBuiltins(), new ForeignBooleanBuiltins(), - new ForeignDateBuiltins(), new ForeignDateTimeBuiltins(), new ForeignTimeBuiltins(), new ForeignTimeZoneBuiltins(), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java index d3bc0e6935..ada51136d7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java @@ -181,7 +181,6 @@ import com.oracle.graal.python.builtins.objects.floats.FloatBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignAbstractClassBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignBooleanBuiltins; -import com.oracle.graal.python.builtins.objects.foreign.ForeignDateBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignDateTimeBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignExecutableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignInstantiableBuiltins; @@ -1235,7 +1234,7 @@ def takewhile(predicate, iterable): newBuilder().moduleName("datetime").publishInModule("_datetime").slots(TimeZoneBuiltins.SLOTS).doc("Fixed offset from UTC implementation of tzinfo.")), // foreign datetime - ForeignDate("ForeignDate", PDate, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignDateBuiltins.SLOTS)), + ForeignDate("ForeignDate", PDate, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), ForeignTime("ForeignTime", PTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignTimeBuiltins.SLOTS)), ForeignDateTime("ForeignDateTime", PDateTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignDateTimeBuiltins.SLOTS)), ForeignTimeZone("ForeignTimeZone", PTzInfo, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignTimeZoneBuiltins.SLOTS)), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java index 9adac08dec..e38f391955 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java @@ -116,6 +116,7 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToJavaStringNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; @@ -378,7 +379,7 @@ private static Object addBoundary(Object left, Object right, Node inliningTarget LocalDate localDate = ChronoUnit.DAYS.addTo(from, days - 1); return DateNodes.SubclassNewNode.getUncached().execute(inliningTarget, - GetClassNode.executeUncached(dateObj), + getResultDateType(dateObj), localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth()); @@ -443,7 +444,7 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget LocalDate localDate = ChronoUnit.DAYS.addTo(from, days - 1); return DateNodes.SubclassNewNode.getUncached().execute(inliningTarget, - GetClassNode.executeUncached(left), + getResultDateType(left), localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth()); @@ -521,6 +522,10 @@ static int getDay(Object self, } } + private static Object getResultDateType(Object dateObj) { + return IsForeignObjectNode.executeUncached(dateObj) ? PythonBuiltinClassType.PDate : GetClassNode.executeUncached(dateObj); + } + @Builtin(name = "today", minNumOfPositionalArgs = 1, isClassmethod = true, parameterNames = {"self"}) @GenerateNodeFactory public abstract static class TodayNode extends PythonBuiltinNode { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java deleted file mode 100644 index 55ec1d4f89..0000000000 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateBuiltins.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 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 - * - * Subject to the condition set forth below, permission is hereby granted to any - * person obtaining a copy of this software, associated documentation and/or - * data (collectively the "Software"), free of charge and under any and all - * copyright rights in the Software, and any and all patent rights owned or - * freely licensable by each licensor hereunder covering either (i) the - * unmodified Software as contributed to or provided by such licensor, or (ii) - * the Larger Works (as defined below), to deal in both - * - * (a) the Software, and - * - * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if - * one is included with the Software each a "Larger Work" to which the Software - * is contributed by such licensors), - * - * without restriction, including without limitation the rights to copy, create - * derivative works of, display, perform, and distribute the Software and make, - * use, sell, offer for sale, import, export, have made, and have sold the - * Software and the Larger Work(s), and to sublicense the foregoing rights on - * either these or other terms. - * - * This license is subject to the following condition: - * - * The above copyright notice and either this complete permission notice or at a - * minimum a reference to the UPL must be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * 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. - */ -package com.oracle.graal.python.builtins.objects.foreign; - -import static com.oracle.graal.python.builtins.PythonBuiltinClassType.OverflowError; - -import java.time.LocalDate; -import java.time.temporal.ChronoUnit; -import java.util.List; - -import com.oracle.graal.python.PythonLanguage; -import com.oracle.graal.python.annotations.Slot; -import com.oracle.graal.python.annotations.Slot.SlotKind; -import com.oracle.graal.python.builtins.CoreFunctions; -import com.oracle.graal.python.builtins.PythonBuiltinClassType; -import com.oracle.graal.python.builtins.PythonBuiltins; -import com.oracle.graal.python.builtins.modules.datetime.PDate; -import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; -import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes; -import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.DateValue; -import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeDeltaValue; -import com.oracle.graal.python.builtins.objects.type.TpSlots; -import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.BinaryOpBuiltinNode; -import com.oracle.graal.python.lib.PyDateCheckNode; -import com.oracle.graal.python.lib.PyDeltaCheckNode; -import com.oracle.graal.python.nodes.ErrorMessages; -import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; -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; -import com.oracle.truffle.api.dsl.NodeFactory; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.object.Shape; - -@CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignDate) -public final class ForeignDateBuiltins extends PythonBuiltins { - public static final TpSlots SLOTS = ForeignDateBuiltinsSlotsGen.SLOTS; - - private static final int MAX_ORDINAL = 3_652_059; - - @Override - protected List> getNodeFactories() { - return ForeignDateBuiltinsFactory.getFactories(); - } - - @Slot(value = SlotKind.nb_add, isComplex = true) - @GenerateNodeFactory - abstract static class AddNode extends BinaryOpBuiltinNode { - @Specialization - static Object add(Object left, Object right, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached PyDateCheckNode dateLikeCheck, - @Cached PyDeltaCheckNode deltaCheck, - @Cached TemporalValueNodes.GetDateValue readDateValue, - @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValue) { - Object dateObj; - Object deltaObj; - if (dateLikeCheck.execute(inliningTarget, left) && deltaCheck.execute(inliningTarget, right)) { - dateObj = left; - deltaObj = right; - } else if (deltaCheck.execute(inliningTarget, left) && dateLikeCheck.execute(inliningTarget, right)) { - dateObj = right; - deltaObj = left; - } else { - return PNotImplemented.NOT_IMPLEMENTED; - } - DateValue date = readDateValue.execute(inliningTarget, dateObj); - TimeDeltaValue delta = readTimeDeltaValue.execute(inliningTarget, deltaObj); - return op(lang, inliningTarget, date, delta); - } - - @TruffleBoundary - private static PDate op(PythonLanguage lang, Node inliningTarget, DateValue date, TimeDeltaValue delta) { - long days = ChronoUnit.DAYS.between(LocalDate.of(1, 1, 1), date.toLocalDate()) + 1 + delta.days; - if (days <= 0 || days > MAX_ORDINAL) { - throw PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); - } - LocalDate ld = ChronoUnit.DAYS.addTo(LocalDate.of(1, 1, 1), days - 1); - return toPythonDate(lang, ld.getYear(), ld.getMonthValue(), ld.getDayOfMonth()); - } - } - - @Slot(value = SlotKind.nb_subtract, isComplex = true) - @GenerateNodeFactory - abstract static class SubNode extends BinaryOpBuiltinNode { - @Specialization - static Object sub(Object left, Object right, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached PyDateCheckNode dateLikeCheckNode, - @Cached PyDeltaCheckNode deltaCheck, - @Cached TemporalValueNodes.GetDateValue readDateValueNode, - @Cached TemporalValueNodes.GetTimeDeltaValue getTimeDeltaValue) { - if (!dateLikeCheckNode.execute(inliningTarget, left)) { - return PNotImplemented.NOT_IMPLEMENTED; - } - DateValue leftValue = readDateValueNode.execute(inliningTarget, left); - - if (dateLikeCheckNode.execute(inliningTarget, right)) { - return op1(right, inliningTarget, lang, readDateValueNode, leftValue); - } - - if (deltaCheck.execute(inliningTarget, right)) { - TimeDeltaValue delta = getTimeDeltaValue.execute(inliningTarget, right); - return op2(inliningTarget, lang, leftValue, delta); - } - return PNotImplemented.NOT_IMPLEMENTED; - } - - @TruffleBoundary - private static Object op1(Object right, Node inliningTarget, PythonLanguage lang, TemporalValueNodes.GetDateValue readDateValueNode, DateValue leftValue) { - LocalDate leftDate = leftValue.toLocalDate(); - LocalDate from = LocalDate.of(1, 1, 1); - long leftDays = ChronoUnit.DAYS.between(from, leftDate) + 1; - LocalDate rightDate = readDateValueNode.execute(inliningTarget, right).toLocalDate(); - long rightDays = ChronoUnit.DAYS.between(from, rightDate) + 1; - return new PTimeDelta(PythonBuiltinClassType.PTimeDelta, PythonBuiltinClassType.PTimeDelta.getInstanceShape(lang), (int) (leftDays - rightDays), 0, 0); - } - - @TruffleBoundary - private static Object op2(Node inliningTarget, PythonLanguage lang, DateValue leftValue, TimeDeltaValue delta) { - LocalDate leftDate = leftValue.toLocalDate(); - LocalDate from = LocalDate.of(1, 1, 1); - long leftDays = ChronoUnit.DAYS.between(from, leftDate) + 1; - long days = leftDays - delta.days; - if (days <= 0 || days >= MAX_ORDINAL) { - throw PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); - } - from = ChronoUnit.DAYS.addTo(from, days - 1); - return toPythonDate(lang, from.getYear(), from.getMonthValue(), from.getDayOfMonth()); - } - } - - private static PDate toPythonDate(PythonLanguage lang, DateValue date) { - return toPythonDate(lang, date.year, date.month, date.day); - } - - private static PDate toPythonDate(PythonLanguage lang, int year, int month, int day) { - Shape shape = PythonBuiltinClassType.PDate.getInstanceShape(lang); - return new PDate(PythonBuiltinClassType.PDate, shape, year, month, day); - } -} From 51c198255adb8a2d8e668f4e01e786510fce9f07 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 30 Mar 2026 15:34:11 +0200 Subject: [PATCH 27/30] Remove ForeignTimeBuiltins and make foreign objects work with TimeBuiltins --- .../graal/python/builtins/Python3Core.java | 2 - .../builtins/PythonBuiltinClassType.java | 3 +- .../modules/datetime/TimeBuiltins.java | 108 +++-- .../builtins/modules/datetime/TimeNodes.java | 11 + .../objects/foreign/ForeignTimeBuiltins.java | 368 ------------------ 5 files changed, 93 insertions(+), 399 deletions(-) delete mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java index 2cdc0aa408..036c593d68 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java @@ -269,7 +269,6 @@ import com.oracle.graal.python.builtins.objects.foreign.ForeignIterableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignNumberBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignObjectBuiltins; -import com.oracle.graal.python.builtins.objects.foreign.ForeignTimeBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignTimeZoneBuiltins; import com.oracle.graal.python.builtins.objects.frame.FrameBuiltins; import com.oracle.graal.python.builtins.objects.function.AbstractFunctionBuiltins; @@ -504,7 +503,6 @@ private static PythonBuiltins[] initializeBuiltins(TruffleLanguage.Env env) { new ForeignNumberBuiltins(), new ForeignBooleanBuiltins(), new ForeignDateTimeBuiltins(), - new ForeignTimeBuiltins(), new ForeignTimeZoneBuiltins(), new ForeignAbstractClassBuiltins(), new ForeignExecutableBuiltins(), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java index ada51136d7..852a6c4b1a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java @@ -187,7 +187,6 @@ import com.oracle.graal.python.builtins.objects.foreign.ForeignIterableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignNumberBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignObjectBuiltins; -import com.oracle.graal.python.builtins.objects.foreign.ForeignTimeBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignTimeZoneBuiltins; import com.oracle.graal.python.builtins.objects.frame.FrameBuiltins; import com.oracle.graal.python.builtins.objects.function.AbstractFunctionBuiltins; @@ -1235,7 +1234,7 @@ def takewhile(predicate, iterable): // foreign datetime ForeignDate("ForeignDate", PDate, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), - ForeignTime("ForeignTime", PTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignTimeBuiltins.SLOTS)), + ForeignTime("ForeignTime", PTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), ForeignDateTime("ForeignDateTime", PDateTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignDateTimeBuiltins.SLOTS)), ForeignTimeZone("ForeignTimeZone", PTzInfo, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignTimeZoneBuiltins.SLOTS)), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java index ab2cfeecfb..78789de622 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java @@ -101,6 +101,7 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToJavaStringNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; @@ -259,10 +260,11 @@ public abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization static TruffleString repr(VirtualFrame frame, Object self, @Bind Node inliningTarget, + @Cached TimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return reprBoundary(inliningTarget, self); + return reprBoundary(inliningTarget, self, tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using PyObjectReprAsObjectNode) should be // connected to a current node. @@ -271,7 +273,7 @@ static TruffleString repr(VirtualFrame frame, Object self, } @TruffleBoundary - private static TruffleString reprBoundary(Node inliningTarget, Object selfObj) { + private static TruffleString reprBoundary(Node inliningTarget, Object selfObj, Object tzInfo) { TimeValue self = TemporalValueNodes.GetTimeValue.executeUncached(null, selfObj); var builder = new StringBuilder(); @@ -284,10 +286,10 @@ private static TruffleString reprBoundary(Node inliningTarget, Object selfObj) { builder.append(PythonUtils.formatJString(", %d", self.second)); } - if (self.tzInfo != null) { + if (tzInfo != null) { builder.append(", tzinfo="); - Object tzinfoReprObject = PyObjectReprAsObjectNode.executeUncached(self.tzInfo); + Object tzinfoReprObject = PyObjectReprAsObjectNode.executeUncached(tzInfo); String tzinfoRepr = CastToJavaStringNode.getUncached().execute(tzinfoReprObject); builder.append(tzinfoRepr); } @@ -311,8 +313,10 @@ static Object reduce(Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached TemporalValueNodes.GetTimeValue asManagedTimeNode, + @Cached TimeNodes.TzInfoNode tzInfoNode, @Cached GetClassNode getClassNode) { TimeValue time = asManagedTimeNode.execute(inliningTarget, self); + Object tzInfo = tzInfoNode.execute(inliningTarget, self); // Time is serialized in the following format: // ( // bytes(hours, minutes, seconds, microseconds 1st byte, microseconds 2nd byte, @@ -331,8 +335,8 @@ static Object reduce(Object self, PBytes baseState = PFactory.createBytes(language, baseStateBytes); final PTuple arguments; - if (time.tzInfo != null) { - arguments = PFactory.createTuple(language, new Object[]{baseState, time.tzInfo}); + if (tzInfo != null) { + arguments = PFactory.createTuple(language, new Object[]{baseState, tzInfo}); } else { arguments = PFactory.createTuple(language, new Object[]{baseState}); } @@ -351,8 +355,10 @@ static Object reduceEx(Object self, int protocol, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached TemporalValueNodes.GetTimeValue asManagedTimeNode, + @Cached TimeNodes.TzInfoNode tzInfoNode, @Cached GetClassNode getClassNode) { TimeValue time = asManagedTimeNode.execute(inliningTarget, self); + Object tzInfo = tzInfoNode.execute(inliningTarget, self); byte[] baseStateBytes = new byte[6]; baseStateBytes[0] = (byte) time.hour; baseStateBytes[1] = (byte) time.minute; @@ -368,8 +374,8 @@ static Object reduceEx(Object self, int protocol, PBytes baseState = PFactory.createBytes(language, baseStateBytes); final PTuple arguments; - if (time.tzInfo != null) { - arguments = PFactory.createTuple(language, new Object[]{baseState, time.tzInfo}); + if (tzInfo != null) { + arguments = PFactory.createTuple(language, new Object[]{baseState, tzInfo}); } else { arguments = PFactory.createTuple(language, new Object[]{baseState}); } @@ -386,10 +392,11 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { @Specialization static Object richCmp(VirtualFrame frame, Object self, Object other, RichCmpOp op, @Bind Node inliningTarget, + @Cached TimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return richCmpBoundary(self, other, op, inliningTarget); + return richCmpBoundary(self, other, op, inliningTarget, tzInfoNode); } finally { // A Python method call (using DatetimeModuleBuiltins.callUtcOffset) // should be connected to a current node. @@ -398,19 +405,21 @@ static Object richCmp(VirtualFrame frame, Object self, Object other, RichCmpOp o } @TruffleBoundary - private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp op, Node inliningTarget) { + private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp op, Node inliningTarget, TimeNodes.TzInfoNode tzInfoNode) { if (!PyTimeCheckNode.executeUncached(selfObj) || !PyTimeCheckNode.executeUncached(otherObj)) { return PNotImplemented.NOT_IMPLEMENTED; } TimeValue self = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); TimeValue other = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, otherObj); + Object selfTzInfo = tzInfoNode.execute(inliningTarget, selfObj); + Object otherTzInfo = tzInfoNode.execute(inliningTarget, otherObj); // either naive times (without timezone) or timezones are exactly the same objects - if (self.tzInfo == other.tzInfo) { + if (selfTzInfo == otherTzInfo) { return compareTimeComponents(self, other, op); } - PTimeDelta selfUtcOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, PNone.NONE, inliningTarget); - PTimeDelta otherUtcOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, PNone.NONE, inliningTarget); + PTimeDelta selfUtcOffset = DatetimeModuleBuiltins.callUtcOffset(selfTzInfo, PNone.NONE, inliningTarget); + PTimeDelta otherUtcOffset = DatetimeModuleBuiltins.callUtcOffset(otherTzInfo, PNone.NONE, inliningTarget); if (Objects.equals(selfUtcOffset, otherUtcOffset)) { return compareTimeComponents(self, other, op); @@ -456,11 +465,13 @@ static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached TemporalValueNodes.GetTimeValue asManagedTimeNode, + @Cached TimeNodes.TzInfoNode tzInfoNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode, @Cached PyObjectHashNode hashNode) { TimeValue self = asManagedTimeNode.execute(inliningTarget, selfObj); - PTimeDelta utcOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, PNone.NONE, frame, inliningTarget, callMethodObjArgs, raiseNode); + Object tzInfo = tzInfoNode.execute(inliningTarget, selfObj); + PTimeDelta utcOffset = DatetimeModuleBuiltins.callUtcOffset(tzInfo, PNone.NONE, frame, inliningTarget, callMethodObjArgs, raiseNode); if (utcOffset == null) { var content = new int[]{self.hour, self.minute, self.second, self.microsecond}; @@ -488,6 +499,13 @@ static int getHour(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { return TimeNodes.FromNative.getHour(self, readNode); } + + @Specialization + static int getHour(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { + return readTimeValueNode.execute(inliningTarget, self).hour; + } } @Builtin(name = "minute", minNumOfPositionalArgs = 1, isGetter = true) @@ -504,6 +522,13 @@ static int getMinute(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { return TimeNodes.FromNative.getMinute(self, readNode); } + + @Specialization + static int getMinute(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { + return readTimeValueNode.execute(inliningTarget, self).minute; + } } @Builtin(name = "second", minNumOfPositionalArgs = 1, isGetter = true) @@ -520,6 +545,13 @@ static int getSecond(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { return TimeNodes.FromNative.getSecond(self, readNode); } + + @Specialization + static int getSecond(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { + return readTimeValueNode.execute(inliningTarget, self).second; + } } @Builtin(name = "microsecond", minNumOfPositionalArgs = 1, isGetter = true) @@ -536,6 +568,13 @@ static int getMicrosecond(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { return TimeNodes.FromNative.getMicrosecond(self, readNode); } + + @Specialization + static int getMicrosecond(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { + return readTimeValueNode.execute(inliningTarget, self).microsecond; + } } @Builtin(name = "tzinfo", minNumOfPositionalArgs = 1, isGetter = true) @@ -565,6 +604,13 @@ static int getFold(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { return TimeNodes.FromNative.getFold(self, readNode); } + + @Specialization + static int getFold(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { + return readTimeValueNode.execute(inliningTarget, self).fold; + } } @Builtin(name = "fromisoformat", minNumOfPositionalArgs = 2, isClassmethod = true, parameterNames = {"self", "time_string"}) @@ -952,6 +998,8 @@ static Object replace(VirtualFrame frame, Object self, Object hourObject, Object @Bind Node inliningTarget, @Cached TemporalValueNodes.GetTimeValue asManagedTimeNode, @Cached PyLongAsLongNode asLongNode, + @Cached IsForeignObjectNode isForeignObjectNode, + @Cached TimeNodes.TzInfoNode tzInfoNode, @Cached GetClassNode getClassNode, @Cached TimeNodes.NewNode newTimeNode) { TimeValue time = asManagedTimeNode.execute(inliningTarget, self); @@ -983,7 +1031,7 @@ static Object replace(VirtualFrame frame, Object self, Object hourObject, Object } if (tzInfoObject == PNone.NO_VALUE) { - tzInfo = time.tzInfo; + tzInfo = tzInfoNode.execute(inliningTarget, self); } else if (tzInfoObject == PNone.NONE) { tzInfo = null; } else { @@ -996,7 +1044,7 @@ static Object replace(VirtualFrame frame, Object self, Object hourObject, Object fold = asLongNode.execute(frame, inliningTarget, foldObject); } - Object type = getClassNode.execute(inliningTarget, self); + Object type = getResultTimeType(self, inliningTarget, isForeignObjectNode, getClassNode); return newTimeNode.execute(inliningTarget, type, hour, minute, second, microsecond, tzInfo, fold); } } @@ -1008,10 +1056,11 @@ public abstract static class IsoFormatNode extends PythonBinaryBuiltinNode { @Specialization static TruffleString isoFormat(VirtualFrame frame, Object self, Object timespecObject, @Bind Node inliningTarget, + @Cached TimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return isoFormatBoundary(self, timespecObject, inliningTarget); + return isoFormatBoundary(self, timespecObject, inliningTarget, tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using PyObjectCallMethodObjArgs and // DatetimeModuleBuiltins.callUtcOffset) should be connected to a @@ -1021,7 +1070,7 @@ static TruffleString isoFormat(VirtualFrame frame, Object self, Object timespecO } @TruffleBoundary - private static TruffleString isoFormatBoundary(Object selfObj, Object timespecObject, Node inliningTarget) { + private static TruffleString isoFormatBoundary(Object selfObj, Object timespecObject, Node inliningTarget, Object tzInfo) { TimeValue self = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); @@ -1079,7 +1128,7 @@ private static TruffleString isoFormatBoundary(Object selfObj, Object timespecOb ErrorMessages.UNKNOWN_TIMESPEC_VALUE); } - Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, PNone.NONE, true, inliningTarget); + Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(tzInfo, PNone.NONE, true, inliningTarget); builder.append(utcOffsetString); return FromJavaStringNode.getUncached().execute(builder.toString(), TS_ENCODING); @@ -1174,10 +1223,11 @@ protected ArgumentClinicProvider getArgumentClinic() { @Specialization static TruffleString strftime(VirtualFrame frame, Object self, TruffleString format, @Bind Node inliningTarget, + @Cached TimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return strftimeBoundary(self, format, inliningTarget); + return strftimeBoundary(self, format, inliningTarget, tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using PyObjectCallMethodObjArgs and // DatetimeModuleBuiltins.formatUtcOffset) should be connected to a @@ -1187,11 +1237,11 @@ static TruffleString strftime(VirtualFrame frame, Object self, TruffleString for } @TruffleBoundary - private static TruffleString strftimeBoundary(Object selfObj, TruffleString format, Node inliningTarget) { + private static TruffleString strftimeBoundary(Object selfObj, TruffleString format, Node inliningTarget, Object tzInfo) { TimeValue self = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); // Reuse time.strftime(format, time_tuple) method. int[] timeTuple = new int[]{1900, 1, 1, self.hour, self.minute, self.second, 0, 1, -1}; - String formatPreprocessed = preprocessFormat(format, self, inliningTarget); + String formatPreprocessed = preprocessFormat(format, self, inliningTarget, tzInfo); return TimeModuleBuiltins.StrfTimeNode.format(formatPreprocessed, timeTuple, TruffleString.FromJavaStringNode.getUncached()); } @@ -1199,7 +1249,7 @@ private static TruffleString strftimeBoundary(Object selfObj, TruffleString form // %Z so handle them here. // CPython: wrap_strftime() @TruffleBoundary - private static String preprocessFormat(TruffleString tsformat, TimeValue self, Node inliningTarget) { + private static String preprocessFormat(TruffleString tsformat, TimeValue self, Node inliningTarget, Object tzInfo) { String format = tsformat.toString(); StringBuilder builder = new StringBuilder(); int i = 0; @@ -1222,13 +1272,13 @@ private static String preprocessFormat(TruffleString tsformat, TimeValue self, N char c = format.charAt(p + 1); if (c == 'z') { - Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, PNone.NONE, false, inliningTarget); + Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(tzInfo, PNone.NONE, false, inliningTarget); builder.append(utcOffsetString); i = p + 2; } else if (c == 'Z') { - if (self.tzInfo != null) { + if (tzInfo != null) { // call tzname() - Object tzNameObject = PyObjectCallMethodObjArgs.executeUncached(self.tzInfo, T_TZNAME, PNone.NONE); + Object tzNameObject = PyObjectCallMethodObjArgs.executeUncached(tzInfo, T_TZNAME, PNone.NONE); // ignore None value if (tzNameObject != PNone.NONE) { @@ -1259,7 +1309,7 @@ private static String preprocessFormat(TruffleString tsformat, TimeValue self, N char d = format.charAt(p + 2); if (d == 'z') { - Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, PNone.NONE, true, inliningTarget); + Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(tzInfo, PNone.NONE, true, inliningTarget); builder.append(utcOffsetString); i = p + 3; @@ -1308,4 +1358,8 @@ private static long toMicroseconds(TimeValue self, PTimeDelta utcOffset) { (long) utcOffset.seconds * 1_000_000 - (long) utcOffset.microseconds; } + + private static Object getResultTimeType(Object self, Node inliningTarget, IsForeignObjectNode isForeignObjectNode, GetClassNode getClassNode) { + return isForeignObjectNode.execute(inliningTarget, self) ? PythonBuiltinClassType.PTime : getClassNode.execute(inliningTarget, self); + } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java index fa00394f4d..c79fb86e0c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java @@ -270,6 +270,10 @@ static int getFold(PythonAbstractNativeObject self, CStructAccess.ReadByteNode r abstract static class TzInfoNode extends Node { public abstract Object execute(Node inliningTarget, Object obj); + public static Object executeUncached(Node inliningTarget, Object obj) { + return TimeNodesFactory.TzInfoNodeGen.getUncached().execute(inliningTarget, obj); + } + @Specialization static Object getTzInfo(PTime self) { return self.tzInfo; @@ -282,5 +286,12 @@ static Object getTzInfo(PythonAbstractNativeObject self, @Cached CStructAccess.ReadObjectNode readObjectNode) { return FromNative.getTzInfo(self, readByteNode, readObjectNode); } + + @Specialization + static Object getTzInfo(Node inliningTarget, Object self, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { + TemporalValueNodes.TimeValue timeValue = readTimeValueNode.execute(inliningTarget, self); + return TemporalValueNodes.toPythonTzInfo(timeValue.tzInfo, timeValue.zoneId, inliningTarget); + } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java deleted file mode 100644 index 84010a6427..0000000000 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeBuiltins.java +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Copyright (c) 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 - * - * Subject to the condition set forth below, permission is hereby granted to any - * person obtaining a copy of this software, associated documentation and/or - * data (collectively the "Software"), free of charge and under any and all - * copyright rights in the Software, and any and all patent rights owned or - * freely licensable by each licensor hereunder covering either (i) the - * unmodified Software as contributed to or provided by such licensor, or (ii) - * the Larger Works (as defined below), to deal in both - * - * (a) the Software, and - * - * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if - * one is included with the Software each a "Larger Work" to which the Software - * is contributed by such licensors), - * - * without restriction, including without limitation the rights to copy, create - * derivative works of, display, perform, and distribute the Software and make, - * use, sell, offer for sale, import, export, have made, and have sold the - * Software and the Larger Work(s), and to sublicense the foregoing rights on - * either these or other terms. - * - * This license is subject to the following condition: - * - * The above copyright notice and either this complete permission notice or at a - * minimum a reference to the UPL must be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * 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. - */ -package com.oracle.graal.python.builtins.objects.foreign; - -import static com.oracle.graal.python.nodes.SpecialMethodNames.J___FORMAT__; -import static com.oracle.graal.python.util.PythonUtils.tsLiteral; - -import java.util.Arrays; -import java.util.List; -import java.util.Objects; - -import com.oracle.graal.python.PythonLanguage; -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.builtins.CoreFunctions; -import com.oracle.graal.python.builtins.PythonBuiltinClassType; -import com.oracle.graal.python.builtins.PythonBuiltins; -import com.oracle.graal.python.builtins.modules.datetime.DatetimeModuleBuiltins; -import com.oracle.graal.python.builtins.modules.datetime.PTime; -import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; -import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes; -import com.oracle.graal.python.builtins.modules.datetime.TimeNodes; -import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeValue; -import com.oracle.graal.python.builtins.objects.PNone; -import com.oracle.graal.python.builtins.objects.PNotImplemented; -import com.oracle.graal.python.builtins.objects.type.TpSlots; -import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun.HashBuiltinNode; -import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; -import com.oracle.graal.python.lib.PyLongAsLongNode; -import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; -import com.oracle.graal.python.lib.PyObjectHashNode; -import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; -import com.oracle.graal.python.lib.PyTimeCheckNode; -import com.oracle.graal.python.lib.RichCmpOp; -import com.oracle.graal.python.nodes.ErrorMessages; -import com.oracle.graal.python.nodes.PRaiseNode; -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; -import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; -import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; -import com.oracle.truffle.api.dsl.Bind; -import com.oracle.truffle.api.dsl.Cached; -import com.oracle.truffle.api.dsl.GenerateNodeFactory; -import com.oracle.truffle.api.dsl.NodeFactory; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.object.Shape; -import com.oracle.truffle.api.strings.TruffleString; - -@CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignTime) -public final class ForeignTimeBuiltins extends PythonBuiltins { - public static final TpSlots SLOTS = ForeignTimeBuiltinsSlotsGen.SLOTS; - - private static final TruffleString T_DST = tsLiteral("dst"); - private static final TruffleString T_ISOFORMAT = tsLiteral("isoformat"); - private static final TruffleString T_STRFTIME = tsLiteral("strftime"); - private static final TruffleString T_TZNAME = tsLiteral("tzname"); - private static final TruffleString T_UTCOFFSET = tsLiteral("utcoffset"); - - @Override - protected List> getNodeFactories() { - return ForeignTimeBuiltinsFactory.getFactories(); - } - - @Slot(value = SlotKind.tp_richcompare, isComplex = true) - @GenerateNodeFactory - abstract static class RichCmpNode extends RichCmpBuiltinNode { - @Specialization - static Object richCmp(VirtualFrame frame, Object selfObj, Object otherObj, RichCmpOp op, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached PyTimeCheckNode timeLikeCheckNode, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs, - @Cached PRaiseNode raiseNode) { - if (!timeLikeCheckNode.execute(inliningTarget, otherObj)) { - return PNotImplemented.NOT_IMPLEMENTED; - } - PTime self = toPythonTime(lang, readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget); - PTime other = toPythonTime(lang, readTimeValueNode.execute(inliningTarget, otherObj), inliningTarget); - if (self.tzInfo == other.tzInfo) { - return compareTimeComponents(self, other, op); - } - - PTimeDelta selfUtcOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, PNone.NONE, frame, inliningTarget, callMethodObjArgs, raiseNode); - PTimeDelta otherUtcOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, PNone.NONE, frame, inliningTarget, callMethodObjArgs, raiseNode); - if (Objects.equals(selfUtcOffset, otherUtcOffset)) { - return compareTimeComponents(self, other, op); - } - if ((selfUtcOffset == null) != (otherUtcOffset == null)) { - if (op == RichCmpOp.Py_EQ) { - return false; - } else if (op == RichCmpOp.Py_NE) { - return true; - } else { - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.CANT_COMPARE_OFFSET_NAIVE_AND_OFFSET_AWARE_TIMES); - } - } - return op.compareResultToBool(Long.compare(toMicroseconds(self, selfUtcOffset), toMicroseconds(other, otherUtcOffset))); - } - - private static boolean compareTimeComponents(PTime self, PTime other, RichCmpOp op) { - int[] selfComponents = new int[]{self.hour, self.minute, self.second, self.microsecond}; - int[] otherComponents = new int[]{other.hour, other.minute, other.second, other.microsecond}; - return op.compareResultToBool(Arrays.compare(selfComponents, otherComponents)); - } - } - - @Slot(value = SlotKind.tp_hash, isComplex = true) - @GenerateNodeFactory - abstract static class HashNode extends HashBuiltinNode { - @Specialization - static long hash(VirtualFrame frame, Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs, - @Cached PRaiseNode raiseNode, - @Cached PyObjectHashNode hashNode) { - PTime self = toPythonTime(lang, readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget); - PTimeDelta utcOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, PNone.NONE, frame, inliningTarget, callMethodObjArgs, raiseNode); - if (utcOffset == null) { - return hashNode.execute(frame, inliningTarget, self); - } - return hashNode.execute(frame, inliningTarget, toMicroseconds(self, utcOffset)); - } - } - - @Builtin(name = "hour", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class HourNode extends PythonUnaryBuiltinNode { - @Specialization - static int hour(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { - return readTimeValueNode.execute(inliningTarget, selfObj).hour; - } - } - - @Builtin(name = "minute", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class MinuteNode extends PythonUnaryBuiltinNode { - @Specialization - static int minute(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { - return readTimeValueNode.execute(inliningTarget, selfObj).minute; - } - } - - @Builtin(name = "second", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class SecondNode extends PythonUnaryBuiltinNode { - @Specialization - static int second(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { - return readTimeValueNode.execute(inliningTarget, selfObj).second; - } - } - - @Builtin(name = "microsecond", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class MicrosecondNode extends PythonUnaryBuiltinNode { - @Specialization - static int microsecond(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { - return readTimeValueNode.execute(inliningTarget, selfObj).microsecond; - } - } - - @Builtin(name = "tzinfo", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class TzInfoNode extends PythonUnaryBuiltinNode { - @Specialization - static Object tzinfo(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { - Object tzInfo = toPythonTzInfo(readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget); - return tzInfo != null ? tzInfo : PNone.NONE; - } - } - - @Builtin(name = "fold", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class FoldNode extends PythonUnaryBuiltinNode { - @Specialization - static int fold(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { - return readTimeValueNode.execute(inliningTarget, selfObj).fold; - } - } - - @Builtin(name = "replace", minNumOfPositionalArgs = 1, parameterNames = {"self", "hour", "minute", "second", "microsecond", "tzinfo"}, keywordOnlyNames = {"fold"}) - @GenerateNodeFactory - abstract static class ReplaceNode extends PythonBuiltinNode { - @Specialization - static Object replace(VirtualFrame frame, Object selfObj, Object hourObject, Object minuteObject, Object secondObject, Object microsecondObject, Object tzInfoObject, Object foldObject, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, - @Cached PyLongAsLongNode asLongNode, - @Cached TimeNodes.NewNode newTimeNode) { - TimeValue self = readTimeValueNode.execute(inliningTarget, selfObj); - long hour = hourObject == PNone.NO_VALUE ? self.hour : asLongNode.execute(frame, inliningTarget, hourObject); - long minute = minuteObject == PNone.NO_VALUE ? self.minute : asLongNode.execute(frame, inliningTarget, minuteObject); - long second = secondObject == PNone.NO_VALUE ? self.second : asLongNode.execute(frame, inliningTarget, secondObject); - long microsecond = microsecondObject == PNone.NO_VALUE ? self.microsecond : asLongNode.execute(frame, inliningTarget, microsecondObject); - Object tzInfo; - if (tzInfoObject == PNone.NO_VALUE) { - tzInfo = toPythonTzInfo(self, inliningTarget); - } else if (tzInfoObject == PNone.NONE) { - tzInfo = null; - } else { - tzInfo = tzInfoObject; - } - long fold = foldObject == PNone.NO_VALUE ? self.fold : asLongNode.execute(frame, inliningTarget, foldObject); - return newTimeNode.execute(inliningTarget, PythonBuiltinClassType.PTime, hour, minute, second, microsecond, tzInfo != null ? tzInfo : PNone.NONE, fold); - } - } - - @Builtin(name = "isoformat", minNumOfPositionalArgs = 1, parameterNames = {"self", "timespec"}) - @GenerateNodeFactory - abstract static class IsoFormatNode extends PythonBinaryBuiltinNode { - @Specialization - static Object isoformat(VirtualFrame frame, Object selfObj, Object timespecObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, - @Cached PyObjectCallMethodObjArgs callNode) { - Object timespec = timespecObj == PNone.NO_VALUE ? PNone.NO_VALUE : timespecObj; - return callNode.execute(frame, inliningTarget, toPythonTime(lang, readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT, timespec); - } - } - - @Builtin(name = "utcoffset", minNumOfPositionalArgs = 1, parameterNames = {"$self"}) - @GenerateNodeFactory - abstract static class UtcOffsetNode extends PythonUnaryBuiltinNode { - @Specialization - static Object utcoffset(VirtualFrame frame, Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, - @Cached PyObjectCallMethodObjArgs callNode) { - return callNode.execute(frame, inliningTarget, toPythonTime(lang, readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCOFFSET); - } - } - - @Builtin(name = "dst", minNumOfPositionalArgs = 1, parameterNames = {"$self"}) - @GenerateNodeFactory - abstract static class DstNode extends PythonUnaryBuiltinNode { - @Specialization - static Object dst(VirtualFrame frame, Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, - @Cached PyObjectCallMethodObjArgs callNode) { - return callNode.execute(frame, inliningTarget, toPythonTime(lang, readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DST); - } - } - - @Builtin(name = "tzname", minNumOfPositionalArgs = 1, parameterNames = {"$self"}) - @GenerateNodeFactory - abstract static class TzNameNode extends PythonUnaryBuiltinNode { - @Specialization - static Object tzname(VirtualFrame frame, Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, - @Cached PyObjectCallMethodObjArgs callNode) { - return callNode.execute(frame, inliningTarget, toPythonTime(lang, readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TZNAME); - } - } - - @Builtin(name = "strftime", minNumOfPositionalArgs = 2, parameterNames = {"self", "format"}) - @GenerateNodeFactory - abstract static class StrFTimeNode extends PythonBinaryBuiltinNode { - @Specialization - static Object strftime(VirtualFrame frame, Object selfObj, Object formatObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetTimeValue readTimeValueNode, - @Cached CastToTruffleStringNode castToTruffleStringNode, - @Cached PyObjectCallMethodObjArgs callNode) { - TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); - return callNode.execute(frame, inliningTarget, toPythonTime(lang, readTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_STRFTIME, format); - } - } - - @Builtin(name = J___FORMAT__, minNumOfPositionalArgs = 2, parameterNames = {"$self", "format"}) - @GenerateNodeFactory - abstract static class FormatNode extends PythonBinaryBuiltinNode { - @Specialization - static Object format(VirtualFrame frame, Object selfObj, Object formatObj, - @Bind Node inliningTarget, - @Cached PyObjectStrAsObjectNode strAsObjectNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs, - @Cached CastToTruffleStringNode castToTruffleStringNode) { - TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); - if (format.isEmpty()) { - return strAsObjectNode.execute(inliningTarget, selfObj); - } - return callMethodObjArgs.execute(frame, inliningTarget, selfObj, T_STRFTIME, format); - } - } - - private static long toMicroseconds(PTime self, PTimeDelta utcOffset) { - return (long) self.hour * 3600 * 1_000_000 + - (long) self.minute * 60 * 1_000_000 + - (long) self.second * 1_000_000 + - self.microsecond - - (long) utcOffset.days * 24 * 3600 * 1_000_000 - - (long) utcOffset.seconds * 1_000_000 - - utcOffset.microseconds; - } - - private static PTime toPythonTime(PythonLanguage lang, TimeValue time, Node inliningTarget) { - Object tzInfo = toPythonTzInfo(time, inliningTarget); - Shape shape = PythonBuiltinClassType.PTime.getInstanceShape(lang); - return new PTime(PythonBuiltinClassType.PTime, shape, time.hour, time.minute, time.second, time.microsecond, tzInfo, time.fold); - } - - private static Object toPythonTzInfo(TimeValue time, Node inliningTarget) { - return TemporalValueNodes.toPythonTzInfo(time.tzInfo, time.zoneId, inliningTarget); - } -} From a7c39a7d1b7762a7c876fa6677db2178f2f12ce8 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 30 Mar 2026 16:47:57 +0200 Subject: [PATCH 28/30] Remove ForeignDateTimeBuiltins --- .../graal/python/builtins/Python3Core.java | 2 - .../builtins/PythonBuiltinClassType.java | 3 +- .../modules/datetime/DateTimeBuiltins.java | 216 ++++--- .../modules/datetime/DateTimeNodes.java | 11 + .../foreign/ForeignDateTimeBuiltins.java | 592 ------------------ 5 files changed, 155 insertions(+), 669 deletions(-) delete mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java index 036c593d68..cabb086c89 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java @@ -263,7 +263,6 @@ import com.oracle.graal.python.builtins.objects.floats.PFloat; import com.oracle.graal.python.builtins.objects.foreign.ForeignAbstractClassBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignBooleanBuiltins; -import com.oracle.graal.python.builtins.objects.foreign.ForeignDateTimeBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignExecutableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignInstantiableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignIterableBuiltins; @@ -502,7 +501,6 @@ private static PythonBuiltins[] initializeBuiltins(TruffleLanguage.Env env) { new ForeignObjectBuiltins(), new ForeignNumberBuiltins(), new ForeignBooleanBuiltins(), - new ForeignDateTimeBuiltins(), new ForeignTimeZoneBuiltins(), new ForeignAbstractClassBuiltins(), new ForeignExecutableBuiltins(), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java index 852a6c4b1a..de0c818540 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java @@ -181,7 +181,6 @@ import com.oracle.graal.python.builtins.objects.floats.FloatBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignAbstractClassBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignBooleanBuiltins; -import com.oracle.graal.python.builtins.objects.foreign.ForeignDateTimeBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignExecutableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignInstantiableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignIterableBuiltins; @@ -1235,7 +1234,7 @@ def takewhile(predicate, iterable): // foreign datetime ForeignDate("ForeignDate", PDate, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), ForeignTime("ForeignTime", PTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), - ForeignDateTime("ForeignDateTime", PDateTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignDateTimeBuiltins.SLOTS)), + ForeignDateTime("ForeignDateTime", PDateTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), ForeignTimeZone("ForeignTimeZone", PTzInfo, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignTimeZoneBuiltins.SLOTS)), // re diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java index b4fba33243..946b5e0ce7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java @@ -142,6 +142,7 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToJavaDoubleNode; import com.oracle.graal.python.nodes.util.CastToJavaLongExactNode; @@ -416,11 +417,12 @@ public abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization static TruffleString repr(Object selfObj, - @Bind Node inliningTarget) { + @Bind Node inliningTarget, + @Cached DateTimeNodes.TzInfoNode tzInfoNode) { EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent(); Node encapsulatingNode = encapsulating.set(inliningTarget); try { - return reprBoundary(inliningTarget, selfObj); + return reprBoundary(inliningTarget, selfObj, tzInfoNode.execute(inliningTarget, selfObj)); } finally { // Some uncached nodes (e.g. PyFloatAsDoubleNode, PyLongAsLongNode, // PyObjectReprAsObjectNode) may raise exceptions that are not @@ -430,7 +432,7 @@ static TruffleString repr(Object selfObj, } @TruffleBoundary - private static TruffleString reprBoundary(Node inliningTarget, Object selfObj) { + private static TruffleString reprBoundary(Node inliningTarget, Object selfObj, Object tzInfo) { DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); @@ -447,10 +449,10 @@ private static TruffleString reprBoundary(Node inliningTarget, Object selfObj) { builder.append(", fold=1"); } - if (self.tzInfo != null) { + if (tzInfo != null) { builder.append(", tzinfo="); - Object tzinfoReprObject = PyObjectReprAsObjectNode.executeUncached(self.tzInfo); + Object tzinfoReprObject = PyObjectReprAsObjectNode.executeUncached(tzInfo); String tzinfoRepr = CastToJavaStringNode.getUncached().execute(tzinfoReprObject); builder.append(tzinfoRepr); } @@ -468,8 +470,10 @@ public abstract static class ReduceNode extends PythonUnaryBuiltinNode { static Object reduce(Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached GetClassNode getClassNode) { DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + Object tzInfo = tzInfoNode.execute(inliningTarget, selfObj); // DateTime is serialized in the following format: // ( // bytes(year 1st byte, year 2nd byte, month, day, hours, minutes, seconds, microseconds @@ -493,8 +497,8 @@ static Object reduce(Object selfObj, PBytes baseState = PFactory.createBytes(language, baseStateBytes); final PTuple arguments; - if (self.tzInfo != null) { - arguments = PFactory.createTuple(language, new Object[]{baseState, self.tzInfo}); + if (tzInfo != null) { + arguments = PFactory.createTuple(language, new Object[]{baseState, tzInfo}); } else { arguments = PFactory.createTuple(language, new Object[]{baseState}); } @@ -511,8 +515,10 @@ public abstract static class ReduceExNode extends PythonBinaryBuiltinNode { static Object reduceEx(Object selfObj, int protocol, @Bind Node inliningTarget, @Bind PythonLanguage language, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached GetClassNode getClassNode) { DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + Object tzInfo = tzInfoNode.execute(inliningTarget, selfObj); byte[] baseStateBytes = new byte[10]; baseStateBytes[0] = (byte) (self.year / 256); baseStateBytes[1] = (byte) (self.year % 256); @@ -532,8 +538,8 @@ static Object reduceEx(Object selfObj, int protocol, PBytes baseState = PFactory.createBytes(language, baseStateBytes); final PTuple arguments; - if (self.tzInfo != null) { - arguments = PFactory.createTuple(language, new Object[]{baseState, self.tzInfo}); + if (tzInfo != null) { + arguments = PFactory.createTuple(language, new Object[]{baseState, tzInfo}); } else { arguments = PFactory.createTuple(language, new Object[]{baseState}); } @@ -550,10 +556,11 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { @Specialization static Object richCmp(VirtualFrame frame, Object self, Object other, RichCmpOp op, @Bind Node inliningTarget, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return richCmpBoundary(self, other, op, inliningTarget); + return richCmpBoundary(self, other, op, inliningTarget, tzInfoNode); } finally { // A Python method call (using DatetimeModuleBuiltins.callUtcOffset) // should be connected to a current node. @@ -562,7 +569,7 @@ static Object richCmp(VirtualFrame frame, Object self, Object other, RichCmpOp o } @TruffleBoundary - private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp op, Node inliningTarget) { + private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp op, Node inliningTarget, DateTimeNodes.TzInfoNode tzInfoNode) { if (!PyDateTimeCheckNode.executeUncached(otherObj)) { /* * Prevent invocation of date_richcompare. We want to return NotImplemented here to @@ -584,21 +591,23 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp } DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); DateTimeValue other = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, otherObj); + Object selfTzInfo = tzInfoNode.execute(inliningTarget, selfObj); + Object otherTzInfo = tzInfoNode.execute(inliningTarget, otherObj); // either naive datetimes (without timezone) or timezones are exactly the same objects - if (self.tzInfo == other.tzInfo) { + if (selfTzInfo == otherTzInfo) { int result = compareDateTimeComponents(self, other); return op.compareResultToBool(result); } - PTimeDelta selfUtcOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, selfObj, inliningTarget); - PTimeDelta otherUtcOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, otherObj, inliningTarget); + PTimeDelta selfUtcOffset = DatetimeModuleBuiltins.callUtcOffset(selfTzInfo, selfObj, inliningTarget); + PTimeDelta otherUtcOffset = DatetimeModuleBuiltins.callUtcOffset(otherTzInfo, otherObj, inliningTarget); if (Objects.equals(selfUtcOffset, otherUtcOffset)) { int result = compareDateTimeComponents(self, other); if (result == 0 && (op == RichCmpOp.Py_EQ || op == RichCmpOp.Py_NE) && selfUtcOffset != null) { // if any utc offset is affected by a fold value - return false - if (isExceptionInPep495(selfObj, self, selfUtcOffset, other, otherUtcOffset, inliningTarget)) { + if (isExceptionInPep495(selfObj, self, selfTzInfo, selfUtcOffset, otherObj, other, otherTzInfo, otherUtcOffset, inliningTarget)) { result = 1; } } @@ -627,7 +636,7 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp if (result == 0 && (op == RichCmpOp.Py_EQ || op == RichCmpOp.Py_NE)) { // if any utc offset is affected by a fold value - return false - if (isExceptionInPep495(selfObj, self, selfUtcOffset, other, otherUtcOffset, inliningTarget)) { + if (isExceptionInPep495(selfObj, self, selfTzInfo, selfUtcOffset, otherObj, other, otherTzInfo, otherUtcOffset, inliningTarget)) { result = 1; } } @@ -649,19 +658,17 @@ private static int compareDateTimeComponents(DateTimeValue self, DateTimeValue o * 495 – Local Time Disambiguation". See PEP 495 * – Local Time Disambiguation */ - private static boolean isExceptionInPep495(Object selfObj, DateTimeValue self, PTimeDelta selfUtcOffset, DateTimeValue other, PTimeDelta otherUtcOffset, Node inliningTarget) { - return isExceptionInPep495(selfObj, self, selfUtcOffset, inliningTarget) || isExceptionInPep495(selfObj, other, otherUtcOffset, inliningTarget); + private static boolean isExceptionInPep495(Object selfObj, DateTimeValue self, Object selfTzInfo, PTimeDelta selfUtcOffset, Object otherObj, DateTimeValue other, Object otherTzInfo, + PTimeDelta otherUtcOffset, Node inliningTarget) { + return isExceptionInPep495(selfObj, self, selfTzInfo, selfUtcOffset, inliningTarget) || isExceptionInPep495(otherObj, other, otherTzInfo, otherUtcOffset, inliningTarget); } - @TruffleBoundary - private static boolean isExceptionInPep495(Object dateTimeObj, DateTimeValue dateTime, PTimeDelta utcOffset, Node inliningTarget) { - Object cls = GetClassNode.executeUncached(dateTimeObj); - Shape shape = TypeNodes.GetInstanceShape.getUncached().execute(cls); + private static boolean isExceptionInPep495(Object dateTimeObj, DateTimeValue dateTime, Object tzInfo, PTimeDelta utcOffset, Node inliningTarget) { int fold = dateTime.fold == 1 ? 0 : 1; - - PDateTime newDateTime = new PDateTime(cls, shape, dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second, - dateTime.microsecond, dateTime.tzInfo, fold); - PTimeDelta newUtcOffset = DatetimeModuleBuiltins.callUtcOffset(newDateTime.tzInfo, newDateTime, inliningTarget); + Shape shape = PythonBuiltinClassType.PDateTime.getInstanceShape(PythonLanguage.get(inliningTarget)); + Object newDateTime = new PDateTime(PythonBuiltinClassType.PDateTime, shape, dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second, + dateTime.microsecond, tzInfo, fold); + PTimeDelta newUtcOffset = DatetimeModuleBuiltins.callUtcOffset(tzInfo, newDateTime, inliningTarget); return !utcOffset.equals(newUtcOffset); } @@ -674,26 +681,27 @@ abstract static class HashNode extends HashBuiltinNode { @Specialization static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, - @Cached PRaiseNode raiseNode, - @Cached TypeNodes.GetInstanceShape getInstanceShape) { + @Cached PRaiseNode raiseNode) { DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + Object tzInfo = tzInfoNode.execute(inliningTarget, selfObj); final PTimeDelta offset; - if (self.tzInfo == null) { + if (tzInfo == null) { offset = null; } else { // ignore fold in calculating utc offset final Object getUtcOffsetFrom; if (self.fold == 1) { // reset fold - Object cls = GetClassNode.executeUncached(selfObj); - Shape shape = getInstanceShape.execute(cls); - getUtcOffsetFrom = new PDateTime(cls, shape, self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond, self.tzInfo, 0); + Shape shape = PythonBuiltinClassType.PDateTime.getInstanceShape(PythonLanguage.get(inliningTarget)); + getUtcOffsetFrom = new PDateTime(PythonBuiltinClassType.PDateTime, shape, self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond, + tzInfo, 0); } else { getUtcOffsetFrom = selfObj; } - offset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, getUtcOffsetFrom, frame, inliningTarget, callMethodObjArgs, raiseNode); + offset = DatetimeModuleBuiltins.callUtcOffset(tzInfo, getUtcOffsetFrom, frame, inliningTarget, callMethodObjArgs, raiseNode); } if (offset == null) { @@ -722,10 +730,13 @@ abstract static class AddNode extends BinaryOpBuiltinNode { @Specialization static Object add(VirtualFrame frame, Object left, Object right, @Bind Node inliningTarget, + @Cached IsForeignObjectNode isForeignObjectNode, + @Cached GetClassNode getClassNode, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return addBoundary(left, right, inliningTarget); + return addBoundary(left, right, inliningTarget, isForeignObjectNode, getClassNode, tzInfoNode); } finally { // A Python method call (using DateTimeNodes.SubclassNewNode) should be // connected to a current node. @@ -734,7 +745,8 @@ static Object add(VirtualFrame frame, Object left, Object right, } @TruffleBoundary - private static Object addBoundary(Object left, Object right, Node inliningTarget) { + private static Object addBoundary(Object left, Object right, Node inliningTarget, IsForeignObjectNode isForeignObjectNode, GetClassNode getClassNode, + DateTimeNodes.TzInfoNode tzInfoNode) { Object dateTimeObj, deltaObj; if (PyDateTimeCheckNode.executeUncached(left)) { if (PyDeltaCheckNode.executeUncached(right)) { @@ -759,7 +771,8 @@ private static Object addBoundary(Object left, Object right, Node inliningTarget throw PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); } - return toPDateTime(localAdjusted, date.tzInfo, date.fold, inliningTarget, GetClassNode.executeUncached(dateTimeObj)); + Object tzInfo = tzInfoNode.execute(inliningTarget, dateTimeObj); + return toPDateTime(localAdjusted, tzInfo, date.fold, inliningTarget, getResultDateTimeType(dateTimeObj, inliningTarget, isForeignObjectNode, getClassNode)); } } @@ -770,10 +783,13 @@ abstract static class SubNode extends BinaryOpBuiltinNode { @Specialization static Object sub(VirtualFrame frame, Object left, Object right, @Bind Node inliningTarget, + @Cached IsForeignObjectNode isForeignObjectNode, + @Cached GetClassNode getClassNode, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return subBoundary(left, right, inliningTarget); + return subBoundary(left, right, inliningTarget, isForeignObjectNode, getClassNode, tzInfoNode); } finally { // A Python method call (using DatetimeModuleBuiltins.callUtcOffset) // should be connected to a current node. @@ -782,16 +798,19 @@ static Object sub(VirtualFrame frame, Object left, Object right, } @TruffleBoundary - private static Object subBoundary(Object left, Object right, Node inliningTarget) { + private static Object subBoundary(Object left, Object right, Node inliningTarget, IsForeignObjectNode isForeignObjectNode, GetClassNode getClassNode, + DateTimeNodes.TzInfoNode tzInfoNode) { if (!PyDateTimeCheckNode.executeUncached(left)) { return PNotImplemented.NOT_IMPLEMENTED; } DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, left); + Object selfTzInfo = tzInfoNode.execute(inliningTarget, left); if (PyDateTimeCheckNode.executeUncached(right)) { DateTimeValue other = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, right); + Object otherTzInfo = tzInfoNode.execute(inliningTarget, right); - final PTimeDelta selfOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, left, inliningTarget); - final PTimeDelta otherOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, right, inliningTarget); + final PTimeDelta selfOffset = DatetimeModuleBuiltins.callUtcOffset(selfTzInfo, left, inliningTarget); + final PTimeDelta otherOffset = DatetimeModuleBuiltins.callUtcOffset(otherTzInfo, right, inliningTarget); if ((selfOffset == null) != (otherOffset == null)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.CANNOT_SUBTRACT_OFFSET_NAIVE_AND_OFFSET_AWARE_DATETIMES); @@ -800,7 +819,7 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget final LocalDateTime selfToCompare; final LocalDateTime otherToCompare; - if (selfOffset != null && self.tzInfo != other.tzInfo) { + if (selfOffset != null && selfTzInfo != otherTzInfo) { selfToCompare = subtractOffsetFromDateTime(self, selfOffset); otherToCompare = subtractOffsetFromDateTime(other, otherOffset); } else { @@ -829,7 +848,7 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget throw PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); } - return toPDateTime(localAdjusted, self.tzInfo, self.fold, inliningTarget, GetClassNode.executeUncached(left)); + return toPDateTime(localAdjusted, selfTzInfo, self.fold, inliningTarget, getResultDateTimeType(left, inliningTarget, isForeignObjectNode, getClassNode)); } else { return PNotImplemented.NOT_IMPLEMENTED; } @@ -849,6 +868,13 @@ static int getHour(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readByteNode) { return DateTimeNodes.FromNative.getHour(self, readByteNode); } + + @Specialization + static int getHour(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, self).hour; + } } @Builtin(name = "minute", minNumOfPositionalArgs = 1, isGetter = true) @@ -865,6 +891,13 @@ static int getMinute(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { return DateTimeNodes.FromNative.getMinute(self, readNode); } + + @Specialization + static int getMinute(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, self).minute; + } } @Builtin(name = "second", minNumOfPositionalArgs = 1, isGetter = true) @@ -881,6 +914,13 @@ static int getSecond(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { return DateTimeNodes.FromNative.getSecond(self, readNode); } + + @Specialization + static int getSecond(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, self).second; + } } @Builtin(name = "microsecond", minNumOfPositionalArgs = 1, isGetter = true) @@ -897,6 +937,13 @@ static int getMicrosecond(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { return DateTimeNodes.FromNative.getMicrosecond(self, readNode); } + + @Specialization + static int getMicrosecond(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, self).microsecond; + } } @Builtin(name = "tzinfo", minNumOfPositionalArgs = 1, isGetter = true) @@ -926,6 +973,13 @@ static int getFold(PythonAbstractNativeObject self, @Cached CStructAccess.ReadByteNode readNode) { return DateTimeNodes.FromNative.getFold(self, readNode); } + + @Specialization + static int getFold(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, self).fold; + } } @Builtin(name = "utcnow", minNumOfPositionalArgs = 1, isClassmethod = true, parameterNames = {"$cls"}) @@ -1153,6 +1207,7 @@ public abstract static class CombineNode extends PythonBuiltinNode { static Object combine(Object cls, Object dateObject, Object timeObject, Object tzInfoObject, @Bind Node inliningTarget, @Cached PRaiseNode raiseNode, + @Cached TimeNodes.TzInfoNode timeTzInfoNode, @Cached DateTimeNodes.SubclassNewNode newNode) { if (!PyDateCheckNode.executeUncached(dateObject)) { throw raiseNode.raise(inliningTarget, @@ -1179,7 +1234,7 @@ static Object combine(Object cls, Object dateObject, Object timeObject, Object t final Object tzInfo; if (tzInfoObject instanceof PNone) { - tzInfo = time.tzInfo; + tzInfo = timeTzInfoNode.execute(inliningTarget, timeObject); } else { tzInfo = tzInfoObject; } @@ -2567,6 +2622,7 @@ abstract static class TimeTzNode extends PythonUnaryBuiltinNode { @Specialization static Object getTime(Object selfObj, @Bind Node inliningTarget, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached TimeNodes.NewNode newTimeNode) { DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); return newTimeNode.execute(inliningTarget, @@ -2575,7 +2631,7 @@ static Object getTime(Object selfObj, self.minute, self.second, self.microsecond, - self.tzInfo, + tzInfoNode.execute(inliningTarget, selfObj), self.fold); } } @@ -2588,6 +2644,8 @@ public abstract static class ReplaceNode extends PythonBuiltinNode { static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Object monthObject, Object dayObject, Object hourObject, Object minuteObject, Object secondObject, Object microsecondObject, Object tzInfoObject, Object foldObject, @Bind Node inliningTarget, + @Cached IsForeignObjectNode isForeignObjectNode, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached PyLongAsLongNode asLongNode, @Cached GetClassNode getClassNode, @Cached DateTimeNodes.NewNode newDateTimeNode) { @@ -2640,7 +2698,7 @@ static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Obj } if (tzInfoObject == PNone.NO_VALUE) { - tzInfo = self.tzInfo; + tzInfo = tzInfoNode.execute(inliningTarget, selfObj); } else if (tzInfoObject == PNone.NONE) { tzInfo = null; } else { @@ -2653,7 +2711,7 @@ static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Obj fold = asLongNode.execute(frame, inliningTarget, foldObject); } - Object type = getClassNode.execute(inliningTarget, selfObj); + Object type = getResultDateTimeType(selfObj, inliningTarget, isForeignObjectNode, getClassNode); return newDateTimeNode.execute(inliningTarget, type, year, month, day, hour, minute, second, microsecond, tzInfo, fold); } } @@ -2665,10 +2723,13 @@ abstract static class AsTimeZoneNode extends PythonBinaryBuiltinNode { @Specialization static Object inTimeZone(VirtualFrame frame, Object self, Object tzInfo, @Bind Node inliningTarget, + @Cached IsForeignObjectNode isForeignObjectNode, + @Cached GetClassNode getClassNode, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return inTimeZoneBoundary(self, tzInfo, inliningTarget); + return inTimeZoneBoundary(self, tzInfo, inliningTarget, getResultDateTimeType(self, inliningTarget, isForeignObjectNode, getClassNode), tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using DatetimeModuleBuiltins.callUtcOffset // and PyObjectCallMethodObjArgs) should be connected to a current node. @@ -2677,15 +2738,15 @@ static Object inTimeZone(VirtualFrame frame, Object self, Object tzInfo, } @TruffleBoundary - private static Object inTimeZoneBoundary(Object selfObj, Object tzInfo, Node inliningTarget) { + private static Object inTimeZoneBoundary(Object selfObj, Object tzInfo, Node inliningTarget, Object resultType, Object selfTzInfo) { DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); - if (tzInfo == self.tzInfo) { + if (tzInfo == selfTzInfo) { return selfObj; } Object sourceTimeZone; - if (self.tzInfo != null) { - sourceTimeZone = self.tzInfo; + if (selfTzInfo != null) { + sourceTimeZone = selfTzInfo; } else { sourceTimeZone = getSystemTimeZoneAt(self.toLocalDateTime(), self.fold, inliningTarget); } @@ -2711,7 +2772,7 @@ private static Object inTimeZoneBoundary(Object selfObj, Object tzInfo, Node inl targetTimeZone = tzInfo; } - Object selfInUtc = toPDateTime(selfAsLocalDateTimeInUtc, targetTimeZone, 0, inliningTarget, GetClassNode.executeUncached(selfObj)); + Object selfInUtc = toPDateTime(selfAsLocalDateTimeInUtc, targetTimeZone, 0, inliningTarget, resultType); return PyObjectCallMethodObjArgs.executeUncached(targetTimeZone, T_FROMUTC, selfInUtc); } @@ -2842,10 +2903,11 @@ public abstract static class TimeTupleNode extends PythonUnaryBuiltinNode { static PTuple composeTimeTuple(VirtualFrame frame, Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return composeTimeTupleBoundary(self, inliningTarget, language); + return composeTimeTupleBoundary(self, inliningTarget, language, tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using DatetimeModuleBuiltins.callDst) should // be connected to a current node. @@ -2854,13 +2916,13 @@ static PTuple composeTimeTuple(VirtualFrame frame, Object self, } @TruffleBoundary - private static PTuple composeTimeTupleBoundary(Object selfObj, Node inliningTarget, PythonLanguage language) { + private static PTuple composeTimeTupleBoundary(Object selfObj, Node inliningTarget, PythonLanguage language, Object tzInfo) { DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); int dayOfWeek = localDate.getDayOfWeek().getValue() - 1; // Python's day of week range // is 0-6 int dayOfYear = localDate.getDayOfYear(); - int isDst = getIsDst(self.tzInfo, selfObj, inliningTarget); + int isDst = getIsDst(tzInfo, selfObj, inliningTarget); Object[] fields = new Object[]{self.year, self.month, self.day, self.hour, self.minute, self.second, dayOfWeek, dayOfYear, isDst}; return PFactory.createStructSeq(language, TimeModuleBuiltins.STRUCT_TIME_DESC, fields); @@ -2892,10 +2954,11 @@ public abstract static class UtcTimeTupleNode extends PythonUnaryBuiltinNode { static PTuple composeTimeTuple(VirtualFrame frame, Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return composeTimeTupleBoundary(self, inliningTarget, language); + return composeTimeTupleBoundary(self, inliningTarget, language, tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using DatetimeModuleBuiltins.callUtcOffset // and PyObjectCallMethodObjArgs) should be connected to a current node. @@ -2904,10 +2967,10 @@ static PTuple composeTimeTuple(VirtualFrame frame, Object self, } @TruffleBoundary - private static PTuple composeTimeTupleBoundary(Object selfObj, Node inliningTarget, PythonLanguage language) { + private static PTuple composeTimeTupleBoundary(Object selfObj, Node inliningTarget, PythonLanguage language, Object tzInfo) { DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); final LocalDateTime localDateTime; - PTimeDelta offset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, selfObj, inliningTarget); + PTimeDelta offset = DatetimeModuleBuiltins.callUtcOffset(tzInfo, selfObj, inliningTarget); if (offset == null) { localDateTime = self.toLocalDateTime(); @@ -2952,10 +3015,11 @@ public abstract static class TimestampNode extends PythonUnaryBuiltinNode { @Specialization static double toTimestamp(VirtualFrame frame, Object self, @Bind Node inliningTarget, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return toTimestampBoundary(self, inliningTarget); + return toTimestampBoundary(self, inliningTarget, tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using DatetimeModuleBuiltins.callUtcOffset) // should be connected to a current node. @@ -2964,9 +3028,9 @@ static double toTimestamp(VirtualFrame frame, Object self, } @TruffleBoundary - private static double toTimestampBoundary(Object selfObj, Node inliningTarget) { + private static double toTimestampBoundary(Object selfObj, Node inliningTarget, Object tzInfo) { DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); - if (self.tzInfo == null) { + if (tzInfo == null) { // CPython: local_to_seconds() TimeZone timeZone = TimeModuleBuiltins.getGlobalTimeZone(getContext(inliningTarget)); ZoneId zoneId = timeZone.toZoneId(); @@ -2998,7 +3062,7 @@ private static double toTimestampBoundary(Object selfObj, Node inliningTarget) { } } else { final LocalDateTime localDateTime; - PTimeDelta offset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, selfObj, inliningTarget); + PTimeDelta offset = DatetimeModuleBuiltins.callUtcOffset(tzInfo, selfObj, inliningTarget); if (offset == null) { localDateTime = self.toLocalDateTime(); @@ -3022,10 +3086,11 @@ public abstract static class IsoFormatNode extends PythonTernaryBuiltinNode { @Specialization static TruffleString isoFormat(VirtualFrame frame, Object self, Object separatorObject, Object timespecObject, @Bind Node inliningTarget, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return isoFormatBoundary(self, separatorObject, timespecObject, inliningTarget); + return isoFormatBoundary(self, separatorObject, timespecObject, inliningTarget, tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using DatetimeModuleBuiltins.callUtcOffset) // should be connected to a current node. @@ -3034,7 +3099,7 @@ static TruffleString isoFormat(VirtualFrame frame, Object self, Object separator } @TruffleBoundary - private static TruffleString isoFormatBoundary(Object selfObj, Object separatorObject, Object timespecObject, Node inliningTarget) { + private static TruffleString isoFormatBoundary(Object selfObj, Object separatorObject, Object timespecObject, Node inliningTarget, Object tzInfo) { DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); @@ -3125,7 +3190,7 @@ private static TruffleString isoFormatBoundary(Object selfObj, Object separatorO ErrorMessages.UNKNOWN_TIMESPEC_VALUE); } - Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, selfObj, true, inliningTarget); + Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(tzInfo, selfObj, true, inliningTarget); builder.append(utcOffsetString); return TruffleString.FromJavaStringNode.getUncached().execute(builder.toString(), TS_ENCODING); @@ -3160,10 +3225,11 @@ protected ArgumentClinicProvider getArgumentClinic() { @Specialization static TruffleString strftime(VirtualFrame frame, Object self, TruffleString format, @Bind Node inliningTarget, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return strftimeBoundary(self, format, inliningTarget); + return strftimeBoundary(self, format, inliningTarget, tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using PyObjectCallMethodObjArgs and // DatetimeModuleBuiltins.callUtcOffset) should be connected to a @@ -3173,7 +3239,7 @@ static TruffleString strftime(VirtualFrame frame, Object self, TruffleString for } @TruffleBoundary - private static TruffleString strftimeBoundary(Object selfObj, TruffleString format, Node inliningTarget) { + private static TruffleString strftimeBoundary(Object selfObj, TruffleString format, Node inliningTarget, Object tzInfo) { DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); // Reuse time.strftime(format, time_tuple) method. @@ -3183,7 +3249,7 @@ private static TruffleString strftimeBoundary(Object selfObj, TruffleString form int dayOfYear = localDate.getDayOfYear(); int[] timeTuple = new int[]{self.year, self.month, self.day, self.hour, self.minute, self.second, dayOfWeek, dayOfYear, -1}; - String formatPreprocessed = preprocessFormat(format, self, selfObj, inliningTarget); + String formatPreprocessed = preprocessFormat(format, self, selfObj, inliningTarget, tzInfo); return TimeModuleBuiltins.StrfTimeNode.format(formatPreprocessed, timeTuple, TruffleString.FromJavaStringNode.getUncached()); } @@ -3191,7 +3257,7 @@ private static TruffleString strftimeBoundary(Object selfObj, TruffleString form // The datetime.datetime.strftime() method supports some extra formatters - %f, %z, %:z, // and %Z so handle them here. // CPython: wrap_strftime() - private static String preprocessFormat(TruffleString tsformat, DateTimeValue self, Object selfObj, Node inliningTarget) { + private static String preprocessFormat(TruffleString tsformat, DateTimeValue self, Object selfObj, Node inliningTarget, Object tzInfo) { String format = tsformat.toString(); StringBuilder builder = new StringBuilder(); int i = 0; @@ -3214,13 +3280,13 @@ private static String preprocessFormat(TruffleString tsformat, DateTimeValue sel char c = format.charAt(p + 1); if (c == 'z') { - Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, selfObj, false, inliningTarget); + Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(tzInfo, selfObj, false, inliningTarget); builder.append(utcOffsetString); i = p + 2; } else if (c == 'Z') { - if (self.tzInfo != null) { + if (tzInfo != null) { // call tzname() - Object tzNameObject = PyObjectCallMethodObjArgs.executeUncached(self.tzInfo, T_TZNAME, selfObj); + Object tzNameObject = PyObjectCallMethodObjArgs.executeUncached(tzInfo, T_TZNAME, selfObj); // ignore None value if (tzNameObject != PNone.NONE) { @@ -3251,7 +3317,7 @@ private static String preprocessFormat(TruffleString tsformat, DateTimeValue sel char d = format.charAt(p + 2); if (d == 'z') { - Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, selfObj, true, inliningTarget); + Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(tzInfo, selfObj, true, inliningTarget); builder.append(utcOffsetString); i = p + 3; @@ -3309,6 +3375,10 @@ private static Object toPDateTime(ZonedDateTime local, Object tzInfo, int fold, fold); } + private static Object getResultDateTimeType(Object selfObj, Node inliningTarget, IsForeignObjectNode isForeignObjectNode, GetClassNode getClassNode) { + return isForeignObjectNode.execute(inliningTarget, selfObj) ? PythonBuiltinClassType.PDateTime : getClassNode.execute(inliningTarget, selfObj); + } + /** * Check whether there was setting clocks back due to daylight saving time transition. CPython: * datetime_from_timet_and_us() diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java index 059a38a5fd..b2ad472e00 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java @@ -346,6 +346,10 @@ static int getFold(PythonAbstractNativeObject self, CStructAccess.ReadByteNode r abstract static class TzInfoNode extends Node { public abstract Object execute(Node inliningTarget, Object obj); + public static Object executeUncached(Node inliningTarget, Object obj) { + return DateTimeNodesFactory.TzInfoNodeGen.getUncached().execute(inliningTarget, obj); + } + @Specialization static Object getTzInfo(PDateTime self) { return self.tzInfo; @@ -358,6 +362,13 @@ static Object getTzInfo(PythonAbstractNativeObject self, @Cached CStructAccess.ReadObjectNode readObjectNode) { return FromNative.getTzInfo(self, readByteNode, readObjectNode); } + + @Specialization + static Object getTzInfo(Node inliningTarget, Object self, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + TemporalValueNodes.DateTimeValue value = readDateTimeValueNode.execute(inliningTarget, self); + return TemporalValueNodes.toPythonTzInfo(value.tzInfo, value.zoneId, inliningTarget); + } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java deleted file mode 100644 index d0f84ffad4..0000000000 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignDateTimeBuiltins.java +++ /dev/null @@ -1,592 +0,0 @@ -/* - * Copyright (c) 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 - * - * Subject to the condition set forth below, permission is hereby granted to any - * person obtaining a copy of this software, associated documentation and/or - * data (collectively the "Software"), free of charge and under any and all - * copyright rights in the Software, and any and all patent rights owned or - * freely licensable by each licensor hereunder covering either (i) the - * unmodified Software as contributed to or provided by such licensor, or (ii) - * the Larger Works (as defined below), to deal in both - * - * (a) the Software, and - * - * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if - * one is included with the Software each a "Larger Work" to which the Software - * is contributed by such licensors), - * - * without restriction, including without limitation the rights to copy, create - * derivative works of, display, perform, and distribute the Software and make, - * use, sell, offer for sale, import, export, have made, and have sold the - * Software and the Larger Work(s), and to sublicense the foregoing rights on - * either these or other terms. - * - * This license is subject to the following condition: - * - * The above copyright notice and either this complete permission notice or at a - * minimum a reference to the UPL must be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * 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. - */ -package com.oracle.graal.python.builtins.objects.foreign; - -import static com.oracle.graal.python.nodes.SpecialMethodNames.J___FORMAT__; -import static com.oracle.graal.python.util.PythonUtils.tsLiteral; - -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.util.List; -import java.util.Objects; - -import com.oracle.graal.python.PythonLanguage; -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.builtins.CoreFunctions; -import com.oracle.graal.python.builtins.PythonBuiltinClassType; -import com.oracle.graal.python.builtins.PythonBuiltins; -import com.oracle.graal.python.builtins.modules.datetime.DateTimeNodes; -import com.oracle.graal.python.builtins.modules.datetime.DatetimeModuleBuiltins; -import com.oracle.graal.python.builtins.modules.datetime.PDateTime; -import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; -import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes; -import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.DateTimeValue; -import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeDeltaValue; -import com.oracle.graal.python.builtins.modules.datetime.TimeDeltaNodes; -import com.oracle.graal.python.builtins.objects.PNone; -import com.oracle.graal.python.builtins.objects.PNotImplemented; -import com.oracle.graal.python.builtins.objects.type.TpSlots; -import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.BinaryOpBuiltinNode; -import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun.HashBuiltinNode; -import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; -import com.oracle.graal.python.lib.PyDateCheckNode; -import com.oracle.graal.python.lib.PyDateTimeCheckNode; -import com.oracle.graal.python.lib.PyDeltaCheckNode; -import com.oracle.graal.python.lib.PyLongAsLongNode; -import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; -import com.oracle.graal.python.lib.PyObjectHashNode; -import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; -import com.oracle.graal.python.lib.RichCmpOp; -import com.oracle.graal.python.nodes.ErrorMessages; -import com.oracle.graal.python.nodes.PRaiseNode; -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; -import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; -import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; -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; -import com.oracle.truffle.api.dsl.NodeFactory; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.object.Shape; -import com.oracle.truffle.api.strings.TruffleString; - -@CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignDateTime) -public final class ForeignDateTimeBuiltins extends PythonBuiltins { - public static final TpSlots SLOTS = ForeignDateTimeBuiltinsSlotsGen.SLOTS; - - private static final TruffleString T_ASTIMEZONE = tsLiteral("astimezone"); - private static final TruffleString T_DST = tsLiteral("dst"); - private static final TruffleString T_ISOFORMAT = tsLiteral("isoformat"); - private static final TruffleString T_STRFTIME = tsLiteral("strftime"); - private static final TruffleString T_TIMETZ = tsLiteral("timetz"); - private static final TruffleString T_TIMESTAMP = tsLiteral("timestamp"); - private static final TruffleString T_TIMETUPLE = tsLiteral("timetuple"); - private static final TruffleString T_TZNAME = tsLiteral("tzname"); - private static final TruffleString T_UTCOFFSET = tsLiteral("utcoffset"); - private static final TruffleString T_UTCTIMETUPLE = tsLiteral("utctimetuple"); - - @Override - protected List> getNodeFactories() { - return ForeignDateTimeBuiltinsFactory.getFactories(); - } - - @Slot(value = SlotKind.tp_richcompare, isComplex = true) - @GenerateNodeFactory - abstract static class RichCmpNode extends RichCmpBuiltinNode { - @Specialization - static Object richCmp(VirtualFrame frame, Object selfObj, Object otherObj, RichCmpOp op, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached PyDateCheckNode dateLikeCheckNode, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs, - @Cached PRaiseNode raiseNode) { - if (!dateTimeLikeCheckNode.execute(inliningTarget, otherObj)) { - if (dateLikeCheckNode.execute(inliningTarget, otherObj)) { - if (op == RichCmpOp.Py_EQ) { - return false; - } else if (op == RichCmpOp.Py_NE) { - return true; - } else { - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.CANT_COMPARE, selfObj, otherObj); - } - } - return PNotImplemented.NOT_IMPLEMENTED; - } - - PDateTime self = toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget); - PDateTime other = toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, otherObj), inliningTarget); - if (self.tzInfo == other.tzInfo) { - return op.compareResultToBool(compareDateTimeComponents(self, other)); - } - - PTimeDelta selfUtcOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, self, frame, inliningTarget, callMethodObjArgs, raiseNode); - PTimeDelta otherUtcOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, other, frame, inliningTarget, callMethodObjArgs, raiseNode); - if (Objects.equals(selfUtcOffset, otherUtcOffset)) { - return op.compareResultToBool(compareDateTimeComponents(self, other)); - } - if ((selfUtcOffset == null) != (otherUtcOffset == null)) { - if (op == RichCmpOp.Py_EQ) { - return false; - } else if (op == RichCmpOp.Py_NE) { - return true; - } else { - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.CANT_COMPARE_OFFSET_NAIVE_AND_OFFSET_AWARE_DATETIMES); - } - } - return boundaryOp(op, self, other, selfUtcOffset, otherUtcOffset); - } - - @TruffleBoundary - private static boolean boundaryOp(RichCmpOp op, PDateTime self, PDateTime other, PTimeDelta selfUtcOffset, PTimeDelta otherUtcOffset) { - LocalDateTime selfUtc = subtractOffsetFromDateTime(self, selfUtcOffset); - LocalDateTime otherUtc = subtractOffsetFromDateTime(other, otherUtcOffset); - return op.compareResultToBool(selfUtc.compareTo(otherUtc)); - } - } - - @Slot(value = SlotKind.tp_hash, isComplex = true) - @GenerateNodeFactory - abstract static class HashNode extends HashBuiltinNode { - @Specialization - static long hash(VirtualFrame frame, Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached PyObjectHashNode hashNode) { - return hashNode.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget)); - } - } - - @Slot(value = SlotKind.nb_add, isComplex = true) - @GenerateNodeFactory - abstract static class AddNode extends BinaryOpBuiltinNode { - @Specialization - static Object add(Object left, Object right, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached PyDeltaCheckNode deltaCheckNode, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { - Object dateTimeObj; - Object deltaObj; - if (dateTimeLikeCheckNode.execute(inliningTarget, left) && deltaCheckNode.execute(inliningTarget, right)) { - dateTimeObj = left; - deltaObj = right; - } else if (deltaCheckNode.execute(inliningTarget, left) && dateTimeLikeCheckNode.execute(inliningTarget, right)) { - dateTimeObj = right; - deltaObj = left; - } else { - return PNotImplemented.NOT_IMPLEMENTED; - } - PDateTime date = toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, dateTimeObj), inliningTarget); - TimeDeltaValue delta = readTimeDeltaValueNode.execute(inliningTarget, deltaObj); - return getAdjusted(lang, inliningTarget, date, delta); - } - - @TruffleBoundary - private static PDateTime getAdjusted(PythonLanguage lang, Node inliningTarget, PDateTime date, TimeDeltaValue delta) { - LocalDateTime adjusted = toLocalDateTime(date).plusDays(delta.days).plusSeconds(delta.seconds).plusNanos(delta.microseconds * 1_000L); - return toPythonDateTime(lang, adjusted, date.tzInfo, date.fold); - } - } - - @Slot(value = SlotKind.nb_subtract, isComplex = true) - @GenerateNodeFactory - abstract static class SubNode extends BinaryOpBuiltinNode { - @Specialization - static Object sub(VirtualFrame frame, Object left, Object right, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached PyDateTimeCheckNode dateTimeLikeCheckNode, - @Cached PyDeltaCheckNode deltaCheckNode, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs, - @Cached PRaiseNode raiseNode) { - if (!dateTimeLikeCheckNode.execute(inliningTarget, left)) { - return PNotImplemented.NOT_IMPLEMENTED; - } - PDateTime self = toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, left), inliningTarget); - if (dateTimeLikeCheckNode.execute(inliningTarget, right)) { - PDateTime other = toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, right), inliningTarget); - PTimeDelta selfOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, self, frame, inliningTarget, callMethodObjArgs, raiseNode); - PTimeDelta otherOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, other, frame, inliningTarget, callMethodObjArgs, raiseNode); - if ((selfOffset == null) != (otherOffset == null)) { - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.CANNOT_SUBTRACT_OFFSET_NAIVE_AND_OFFSET_AWARE_DATETIMES); - } - return op(inliningTarget, self, other, selfOffset, otherOffset); - } - if (deltaCheckNode.execute(inliningTarget, right)) { - TimeDeltaValue delta = readTimeDeltaValueNode.execute(inliningTarget, right); - return getAdjusted(lang, self, delta); - } - return PNotImplemented.NOT_IMPLEMENTED; - } - - @TruffleBoundary - private static Object getAdjusted(PythonLanguage lang, PDateTime self, TimeDeltaValue delta) { - LocalDateTime adjusted = toLocalDateTime(self).minusDays(delta.days).minusSeconds(delta.seconds).minusNanos(delta.microseconds * 1_000L); - return toPythonDateTime(lang, adjusted, self.tzInfo, self.fold); - } - - @TruffleBoundary - private static Object op(Node inliningTarget, PDateTime self, PDateTime other, PTimeDelta selfOffset, PTimeDelta otherOffset) { - LocalDateTime selfToCompare = selfOffset != null && self.tzInfo != other.tzInfo ? subtractOffsetFromDateTime(self, selfOffset) : toLocalDateTime(self); - LocalDateTime otherToCompare = otherOffset != null && self.tzInfo != other.tzInfo ? subtractOffsetFromDateTime(other, otherOffset) : toLocalDateTime(other); - long selfSeconds = selfToCompare.toEpochSecond(ZoneOffset.UTC); - long otherSeconds = otherToCompare.toEpochSecond(ZoneOffset.UTC); - return TimeDeltaNodes.NewNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PTimeDelta, 0, selfSeconds - otherSeconds, self.microsecond - other.microsecond, 0, - 0, 0, 0); - } - } - - @Builtin(name = "year", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class YearNode extends PythonUnaryBuiltinNode { - @Specialization - static int year(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return readDateTimeValueNode.execute(inliningTarget, selfObj).year; - } - } - - @Builtin(name = "month", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class MonthNode extends PythonUnaryBuiltinNode { - @Specialization - static int month(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return readDateTimeValueNode.execute(inliningTarget, selfObj).month; - } - } - - @Builtin(name = "day", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class DayNode extends PythonUnaryBuiltinNode { - @Specialization - static int day(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return readDateTimeValueNode.execute(inliningTarget, selfObj).day; - } - } - - @Builtin(name = "hour", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class HourNode extends PythonUnaryBuiltinNode { - @Specialization - static int hour(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return readDateTimeValueNode.execute(inliningTarget, selfObj).hour; - } - } - - @Builtin(name = "minute", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class MinuteNode extends PythonUnaryBuiltinNode { - @Specialization - static int minute(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return readDateTimeValueNode.execute(inliningTarget, selfObj).minute; - } - } - - @Builtin(name = "second", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class SecondNode extends PythonUnaryBuiltinNode { - @Specialization - static int second(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return readDateTimeValueNode.execute(inliningTarget, selfObj).second; - } - } - - @Builtin(name = "microsecond", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class MicrosecondNode extends PythonUnaryBuiltinNode { - @Specialization - static int microsecond(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return readDateTimeValueNode.execute(inliningTarget, selfObj).microsecond; - } - } - - @Builtin(name = "tzinfo", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class TzInfoNode extends PythonUnaryBuiltinNode { - @Specialization - static Object tzinfo(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); - Object tzInfo = TemporalValueNodes.toPythonTzInfo(self.tzInfo, self.zoneId, inliningTarget); - return tzInfo != null ? tzInfo : PNone.NONE; - } - } - - @Builtin(name = "fold", minNumOfPositionalArgs = 1, isGetter = true) - @GenerateNodeFactory - abstract static class FoldNode extends PythonUnaryBuiltinNode { - @Specialization - static int fold(Object selfObj, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { - return readDateTimeValueNode.execute(inliningTarget, selfObj).fold; - } - } - - @Builtin(name = "timetz", minNumOfPositionalArgs = 1, parameterNames = {"self"}) - @GenerateNodeFactory - abstract static class TimeTzNode extends PythonUnaryBuiltinNode { - @Specialization - static Object timetz(VirtualFrame frame, Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { - return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMETZ); - } - } - - @Builtin(name = "replace", minNumOfPositionalArgs = 1, parameterNames = {"self", "year", "month", "day", "hour", "minute", "second", "microsecond", "tzinfo"}, keywordOnlyNames = {"fold"}) - @GenerateNodeFactory - abstract static class ReplaceNode extends PythonBuiltinNode { - @Specialization - static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Object monthObject, Object dayObject, Object hourObject, Object minuteObject, Object secondObject, - Object microsecondObject, Object tzInfoObject, Object foldObject, - @Bind Node inliningTarget, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached PyLongAsLongNode asLongNode, - @Cached DateTimeNodes.NewNode newDateTimeNode) { - DateTimeValue self = readDateTimeValueNode.execute(inliningTarget, selfObj); - long year = yearObject == PNone.NO_VALUE ? self.year : asLongNode.execute(frame, inliningTarget, yearObject); - long month = monthObject == PNone.NO_VALUE ? self.month : asLongNode.execute(frame, inliningTarget, monthObject); - long day = dayObject == PNone.NO_VALUE ? self.day : asLongNode.execute(frame, inliningTarget, dayObject); - long hour = hourObject == PNone.NO_VALUE ? self.hour : asLongNode.execute(frame, inliningTarget, hourObject); - long minute = minuteObject == PNone.NO_VALUE ? self.minute : asLongNode.execute(frame, inliningTarget, minuteObject); - long second = secondObject == PNone.NO_VALUE ? self.second : asLongNode.execute(frame, inliningTarget, secondObject); - long microsecond = microsecondObject == PNone.NO_VALUE ? self.microsecond : asLongNode.execute(frame, inliningTarget, microsecondObject); - Object tzInfo; - if (tzInfoObject == PNone.NO_VALUE) { - tzInfo = TemporalValueNodes.toPythonTzInfo(self.tzInfo, self.zoneId, inliningTarget); - } else if (tzInfoObject == PNone.NONE) { - tzInfo = null; - } else { - tzInfo = tzInfoObject; - } - long fold = foldObject == PNone.NO_VALUE ? self.fold : asLongNode.execute(frame, inliningTarget, foldObject); - return newDateTimeNode.execute(inliningTarget, PythonBuiltinClassType.PDateTime, year, month, day, hour, minute, second, microsecond, tzInfo != null ? tzInfo : PNone.NONE, fold); - } - } - - @Builtin(name = "isoformat", minNumOfPositionalArgs = 1, parameterNames = {"self", "sep", "timespec"}) - @GenerateNodeFactory - abstract static class IsoFormatNode extends PythonBuiltinNode { - @Specialization - static Object isoformat(VirtualFrame frame, Object selfObj, Object sepObj, Object timespecObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { - if (sepObj == PNone.NO_VALUE && timespecObj == PNone.NO_VALUE) { - return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT); - } - if (timespecObj == PNone.NO_VALUE) { - return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT, sepObj); - } - return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ISOFORMAT, sepObj, timespecObj); - } - } - - @Builtin(name = "utcoffset", minNumOfPositionalArgs = 1, parameterNames = {"$self"}) - @GenerateNodeFactory - abstract static class UtcOffsetNode extends PythonUnaryBuiltinNode { - @Specialization - static Object utcoffset(VirtualFrame frame, Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { - return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCOFFSET); - } - } - - @Builtin(name = "dst", minNumOfPositionalArgs = 1, parameterNames = {"$self"}) - @GenerateNodeFactory - abstract static class DstNode extends PythonUnaryBuiltinNode { - @Specialization - static Object dst(VirtualFrame frame, Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { - return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_DST); - } - } - - @Builtin(name = "tzname", minNumOfPositionalArgs = 1, parameterNames = {"$self"}) - @GenerateNodeFactory - abstract static class TzNameNode extends PythonUnaryBuiltinNode { - @Specialization - static Object tzname(VirtualFrame frame, Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { - return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TZNAME); - } - } - - @Builtin(name = "timetuple", minNumOfPositionalArgs = 1, parameterNames = {"self"}) - @GenerateNodeFactory - abstract static class TimeTupleNode extends PythonUnaryBuiltinNode { - @Specialization - static Object timetuple(VirtualFrame frame, Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { - return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMETUPLE); - } - } - - @Builtin(name = "utctimetuple", minNumOfPositionalArgs = 1, parameterNames = {"self"}) - @GenerateNodeFactory - abstract static class UtcTimeTupleNode extends PythonUnaryBuiltinNode { - @Specialization - static Object utctimetuple(VirtualFrame frame, Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { - return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_UTCTIMETUPLE); - } - } - - @Builtin(name = "timestamp", minNumOfPositionalArgs = 1, parameterNames = {"self"}) - @GenerateNodeFactory - abstract static class TimestampNode extends PythonUnaryBuiltinNode { - @Specialization - static Object timestamp(VirtualFrame frame, Object selfObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { - return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_TIMESTAMP); - } - } - - @Builtin(name = "astimezone", minNumOfPositionalArgs = 1, parameterNames = {"self", "tz"}) - @GenerateNodeFactory - abstract static class AsTimeZoneNode extends PythonBinaryBuiltinNode { - @Specialization - static Object astimezone(VirtualFrame frame, Object selfObj, Object tzObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { - if (tzObj == PNone.NO_VALUE) { - return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ASTIMEZONE); - } - return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_ASTIMEZONE, tzObj); - } - } - - @Builtin(name = "strftime", minNumOfPositionalArgs = 2, parameterNames = {"self", "format"}) - @GenerateNodeFactory - abstract static class StrFTimeNode extends PythonBinaryBuiltinNode { - @Specialization - static Object strftime(VirtualFrame frame, Object selfObj, Object formatObj, - @Bind Node inliningTarget, - @Bind PythonLanguage lang, - @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode, - @Cached CastToTruffleStringNode castToTruffleStringNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs) { - TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); - return callMethodObjArgs.execute(frame, inliningTarget, toPythonDateTime(lang, readDateTimeValueNode.execute(inliningTarget, selfObj), inliningTarget), T_STRFTIME, format); - } - } - - @Builtin(name = J___FORMAT__, minNumOfPositionalArgs = 2, parameterNames = {"$self", "format"}) - @GenerateNodeFactory - abstract static class FormatNode extends PythonBinaryBuiltinNode { - @Specialization - static Object format(VirtualFrame frame, Object selfObj, Object formatObj, - @Bind Node inliningTarget, - @Cached PyObjectStrAsObjectNode strAsObjectNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs, - @Cached CastToTruffleStringNode castToTruffleStringNode) { - TruffleString format = castToTruffleStringNode.execute(inliningTarget, formatObj); - if (format.isEmpty()) { - return strAsObjectNode.execute(inliningTarget, selfObj); - } - return callMethodObjArgs.execute(frame, inliningTarget, selfObj, T_STRFTIME, format); - } - } - - @TruffleBoundary - private static int compareDateTimeComponents(PDateTime self, PDateTime other) { - return java.util.Arrays.compare(new int[]{self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond}, - new int[]{other.year, other.month, other.day, other.hour, other.minute, other.second, other.microsecond}); - } - - @TruffleBoundary - private static LocalDateTime subtractOffsetFromDateTime(PDateTime self, PTimeDelta offset) { - return toLocalDateTime(self).minusDays(offset.days).minusSeconds(offset.seconds).minusNanos(offset.microseconds * 1_000L); - } - - @TruffleBoundary - private static LocalDateTime toLocalDateTime(PDateTime self) { - return LocalDateTime.of(self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond * 1_000); - } - - private static PDateTime toPythonDateTime(PythonLanguage lang, DateTimeValue value, Node inliningTarget) { - return toPythonDateTime(lang, value.year, value.month, value.day, value.hour, value.minute, value.second, value.microsecond, - TemporalValueNodes.toPythonTzInfo(value.tzInfo, value.zoneId, inliningTarget), value.fold); - } - - @TruffleBoundary - private static PDateTime toPythonDateTime(PythonLanguage lang, LocalDateTime local, Object tzInfo, int fold) { - return toPythonDateTime(lang, local.getYear(), local.getMonthValue(), local.getDayOfMonth(), local.getHour(), local.getMinute(), local.getSecond(), local.getNano() / 1_000, tzInfo, - fold); - } - - private static PDateTime toPythonDateTime(PythonLanguage lang, int year, int month, int day, int hour, int minute, int second, int microsecond, Object tzInfo, int fold) { - Shape shape = PythonBuiltinClassType.PDateTime.getInstanceShape(lang); - return new PDateTime(PythonBuiltinClassType.PDateTime, shape, year, month, day, hour, minute, second, microsecond, tzInfo != null ? tzInfo : PNone.NONE, fold); - } -} From b7517a7551a2a0e110b3df0dca7788226611c840 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 30 Mar 2026 16:55:55 +0200 Subject: [PATCH 29/30] Patch our old asv to work on CPython 3.12, too (for now) --- .../mx_graalpython_python_benchmarks.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/mx.graalpython/mx_graalpython_python_benchmarks.py b/mx.graalpython/mx_graalpython_python_benchmarks.py index 953dca2c42..ad178e5d2b 100644 --- a/mx.graalpython/mx_graalpython_python_benchmarks.py +++ b/mx.graalpython/mx_graalpython_python_benchmarks.py @@ -207,6 +207,41 @@ def create_asv_benchmark_selection(benchmarks, skipped=()): return '^(?!' + '|'.join(negative_lookaheads) + ')(' + regex + ')' +def patch_asv_for_cpython_312(workdir, vm_venv): + pattern = join(workdir, vm_venv, "lib", "python*", "site-packages", "asv", "plugins", "virtualenv.py") + candidates = glob.glob(pattern) + if not candidates: + mx.abort(f"Could not find ASV virtualenv plugin to patch: {pattern}") + + if len(candidates) != 1: + mx.abort(f"Found multiple ASV virtualenv plugins to patch: {candidates}") + + virtualenv_py = candidates[0] + with open(virtualenv_py) as f: + content = f.read() + + patched_import = "from packaging.version import parse as LooseVersion" + if patched_import in content: + mx.log(f"ASV virtualenv plugin already patched: {virtualenv_py}") + return + + distutils_import = "from distutils.version import LooseVersion" + if distutils_import not in content: + mx.abort(f"Unexpected ASV virtualenv plugin contents, cannot patch: {virtualenv_py}") + + content = content.replace( + distutils_import, + "try:\n" + " from packaging.version import parse as LooseVersion\n" + "except Exception:\n" + " from distutils.version import LooseVersion", + 1, + ) + with open(virtualenv_py, "w") as f: + f.write(content) + mx.log(f"Patched ASV virtualenv plugin for CPython 3.12+: {virtualenv_py}") + + class PyPerfJsonRule(mx_benchmark.Rule): """Parses a JSON file produced by PyPerf and creates a measurement result.""" @@ -638,6 +673,8 @@ def _vmRun(self, vm, workdir, command, benchmarks, bmSuiteArgs): vm.run(workdir, ["-m", "venv", join(workdir, vm_venv)]) pip = join(workdir, vm_venv, "bin", "pip") mx.run([pip, "install", *self.BENCHMARK_REQ], cwd=workdir) + if vm.name() == "cpython": + patch_asv_for_cpython_312(workdir, vm_venv) mx.run( [join(workdir, vm_venv, "bin", "asv"), "machine", "--yes"], cwd=benchdir ) @@ -773,6 +810,8 @@ def _vmRun(self, vm, workdir, command, benchmarks, bmSuiteArgs): env = os.environ.copy() env['PIP_CONSTRAINT'] = constraints.name mx.run([pip, "install", *self.BENCHMARK_REQ], cwd=workdir, env=env) + if vm.name() == "cpython": + patch_asv_for_cpython_312(workdir, vm_venv) mx.run( [join(workdir, vm_venv, "bin", "asv"), "machine", "--yes"], cwd=benchdir ) From 857957cd1e8d5304e4fb85b7932dc162a0d95dc1 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 30 Mar 2026 19:45:42 +0200 Subject: [PATCH 30/30] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb93d64ee4..276a85af44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ language runtime. The main focus is on user-observable behavior of the engine. * Added support for specifying generics on foreign classes, and inheriting from such classes. Especially when using Java classes that support generics, this allows expressing the generic types in Python type annotations as well. * Added a new `java` backend for the `pyexpat` module that uses a Java XML parser instead of the native `expat` library. It can be useful when running without native access or multiple-context scenarios. This backend is the default when embedding and can be switched back to native `expat` by setting `python.PyExpatModuleBackend` option to `native`. Standalone distribution still defaults to native expat backend. * Add a new context option `python.UnicodeCharacterDatabaseNativeFallback` to control whether the ICU database may fall back to the native unicode character database from CPython for features and characters not supported by ICU. This requires native access to be enabled and is disabled by default for embeddings. +* Foreign temporal objects (dates, times, and timezones) are now given a Python class corresponding to their interop traits, i.e., `date`, `time`, `datetime`, or `tzinfo`. This allows any foreign objects with these traits to be used in place of the native Python types and Python methods available on these types work on the foreign types. ## Version 25.0.1 * Allow users to keep going on unsupported JDK/OS/ARCH combinations at their own risk by opting out of early failure using `-Dtruffle.UseFallbackRuntime=true`, `-Dpolyglot.engine.userResourceCache=/set/to/a/writeable/dir`, `-Dpolyglot.engine.allowUnsupportedPlatform=true`, and `-Dpolyglot.python.UnsupportedPlatformEmulates=[linux|macos|windows]` and `-Dorg.graalvm.python.resources.exclude=native.files`.