From e9260227c4f30437b41d7036f4879800f3fa6fff Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 14 Oct 2022 20:08:08 +0200 Subject: [PATCH] Duration type is a builtin type (#3759) - Reimplement the `Duration` type to a built-in type. - `Duration` is an interop type. - Allow Enso method dispatch on `Duration` interop coming from different languages. # Important Notes - The older `Duration` type should now be split into new `Duration` builtin type and a `Period` type. - This PR does not implement `Period` type, so all the `Period`-related functionality is currently not working, e.g., `Date - Period`. - This PR removes `Integer.milliseconds`, `Integer.seconds`, ..., `Integer.years` extension methods. --- CHANGELOG.md | 2 + build.sbt | 3 +- .../Base/0.0.0-dev/src/Data/Time/Date.enso | 12 +- .../0.0.0-dev/src/Data/Time/Date_Time.enso | 11 +- .../0.0.0-dev/src/Data/Time/Duration.enso | 496 +++++------------- .../0.0.0-dev/src/Data/Time/Time_Of_Day.enso | 10 +- .../Base/0.0.0-dev/src/Network/Http.enso | 8 +- .../Standard/Examples/0.0.0-dev/src/Main.enso | 2 +- .../lib/Standard/Test/0.0.0-dev/src/Main.enso | 6 +- docs/infrastructure/native-image.md | 7 + .../org/enso/runner/reflect-config.json | 84 +++ .../interpreter/epb/node/ReadOnlyArray.java | 2 +- .../epb/runtime/PolyglotProxy.java | 2 +- .../FunctionCallInstrumentationNode.java | 2 +- .../node/callable/InvokeMethodNode.java | 50 +- .../callable/resolver/HostMethodCallNode.java | 20 +- .../org/enso/interpreter/runtime/Module.java | 2 +- .../interpreter/runtime/builtin/Builtins.java | 11 + .../callable/UnresolvedConversion.java | 2 +- .../runtime/callable/UnresolvedSymbol.java | 2 +- .../interpreter/runtime/data/ArraySlice.java | 2 +- .../runtime/data/EnsoDateTime.java | 38 +- .../runtime/data/EnsoDuration.java | 195 +++++++ .../interpreter/runtime/data/EnsoFile.java | 2 +- .../runtime/data/EnsoTimeOfDay.java | 37 +- .../runtime/data/ManagedResource.java | 2 +- .../enso/interpreter/runtime/data/Ref.java | 2 +- .../enso/interpreter/runtime/data/Type.java | 2 +- .../interpreter/runtime/data/text/Text.java | 2 +- .../interpreter/runtime/error/Warning.java | 4 +- .../runtime/error/WithWarnings.java | 2 +- .../runtime/number/EnsoBigInteger.java | 2 +- .../runtime/scope/ModuleScope.java | 2 +- .../runtime/scope/TopLevelScope.java | 2 +- .../enso/interpreter/runtime/type/Types.java | 4 + .../dsl/builtins/TypeWithKind.java | 1 + project/StdBits.scala | 45 +- .../main/java/org/enso/base/Time_Utils.java | 38 +- test/Tests/src/Data/Time/Date_Spec.enso | 28 +- test/Tests/src/Data/Time/Date_Time_Spec.enso | 34 +- test/Tests/src/Data/Time/Duration_Spec.enso | 130 +++-- .../Tests/src/Data/Time/Time_Of_Day_Spec.enso | 12 +- test/Tests/src/Network/Http_Spec.enso | 4 +- .../Tests/src/Semantic/Java_Interop_Spec.enso | 2 +- 44 files changed, 730 insertions(+), 596 deletions(-) create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoDuration.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 58a64389e55..7ae8aee0aa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -209,6 +209,7 @@ - [Added `Filter_Condition` to `Vector`, `Range` and `List`.][3770] - [Extended `Filter_Condition` with `Is_Empty`, `Not_Empty`, `Like` and `Not_Like`.][3775] +- [Reimplemented `Duration` as a built-in type.][3759] - [Implemented `Table.replace_text` for in-memory table.][3793] [debug-shortcuts]: @@ -336,6 +337,7 @@ [3750]: https://github.com/enso-org/enso/pull/3750 [3770]: https://github.com/enso-org/enso/pull/3770 [3775]: https://github.com/enso-org/enso/pull/3775 +[3759]: https://github.com/enso-org/enso/pull/3759 [3793]: https://github.com/enso-org/enso/pull/3793 #### Enso Compiler diff --git a/build.sbt b/build.sbt index 3f6f55386f4..0884317e221 100644 --- a/build.sbt +++ b/build.sbt @@ -1891,8 +1891,7 @@ lazy val `std-table` = project .copyDependencies( `table-polyglot-root`, Some("std-table.jar"), - ignoreScalaLibrary = true, - unpackedDeps = Set("xmlbeans") + ignoreScalaLibrary = true ) .value result diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso index 7f110b8ea00..d45f0ac4c56 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso @@ -4,7 +4,7 @@ import Standard.Base.Data.Time.Duration import Standard.Base.Data.Time.Date_Period import Standard.Base.Polyglot -from Standard.Base.Error.Common import Time_Error_Data +from Standard.Base.Error.Common import Time_Error_Data, unimplemented polyglot java import org.enso.base.Time_Utils polyglot java import java.time.temporal.ChronoField @@ -308,9 +308,8 @@ type Date import Standard.Base.Data.Time.Duration example_add = Date.new 2020 + 6.months - + : Duration -> Date - + self amount = if amount.is_time then Error.throw (Time_Error_Data "Date does not support time intervals") else - (Time_Utils.date_adjust self Time_Utils.AdjustOp.PLUS amount.internal_period) . internal_local_date + + : Date_Period -> Date + + self _ = unimplemented "Date.+" ## Shift the date by the specified amount of business days. @@ -433,9 +432,8 @@ type Date import Standard.Base.Data.Time.Duration example_subtract = Date.new 2020 - 7.days - - : Duration -> Date - - self amount = if amount.is_time then Error.throw (Time_Error_Data "Date does not support time intervals") else - (Time_Utils.date_adjust self Time_Utils.AdjustOp.MINUS amount.internal_period) . internal_local_date + - : Date_Period -> Date + - self _ = unimplemented "Date.-" ## A Date to Json conversion. diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso index fe3e5dcc394..e41988133c0 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso @@ -8,6 +8,7 @@ from Standard.Base.Error.Common import Time_Error polyglot java import java.time.format.DateTimeFormatter polyglot java import java.time.temporal.ChronoField polyglot java import java.time.temporal.IsoFields +polyglot java import java.lang.ArithmeticException polyglot java import org.enso.base.Time_Utils @@ -387,10 +388,10 @@ type Date_Time from Standard.Base import Date_Time import Standard.Base.Data.Time.Duration - example_plus = Date_Time.new 2020 + 15.years + 3.hours - + : Duration -> Date_Time + example_plus = Date_Time.new 2020 + 15.years + (Duration.new hours=3) + + : Duration -> Date_Time ! Time_Error + self amount = - Time_Utils.datetime_adjust self Time_Utils.AdjustOp.PLUS amount.internal_period amount.internal_duration + Panic.catch ArithmeticException (self.plus_builtin amount) (err -> Error.throw (Time_Error_Data err.getMessage)) ## Shift the date by the specified amount of business days. @@ -442,9 +443,9 @@ type Date_Time import Standard.Base.Data.Time.Duration example_minus = Date_Time.new 2020 - 1.year - 9.months - - : Duration -> Date_Time + - : Duration -> Date_Time ! Time_Error - self amount = - Time_Utils.datetime_adjust self Time_Utils.AdjustOp.MINUS amount.internal_period amount.internal_duration + Panic.catch ArithmeticException (self.minus_builtin amount) (err -> Error.throw (Time_Error_Data err.getMessage)) ## Convert this time to text using the default formatter. diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Duration.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Duration.enso index 3127c9b9de1..d6fc64af3aa 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Duration.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Duration.enso @@ -1,17 +1,18 @@ from Standard.Base import all import Standard.Base.System -polyglot java import java.lang.ArithmeticException polyglot java import java.time.Duration as Java_Duration polyglot java import java.time.Period as Java_Period polyglot java import org.enso.base.Time_Utils +polyglot java import java.lang.ArithmeticException ## Create an interval representing the duration between two points in time. Arguments: - - start_inclusive: The start time of the duration. - - end_inclusive: The end time of the duration. - - timezone_aware: Should the creation of the interval be timezone-aware. + - start_inclusive: The start datetime of the duration. + - end_inclusive: The end datetime of the duration. + - timezone_aware: Whether the duration between two given times should be + aware of the timezone, that can be set for start or end times. > Example An hour interval between two points in time. @@ -20,12 +21,40 @@ polyglot java import org.enso.base.Time_Utils import Standard.Base.Data.Time.Duration example_between = Duration.between Date_Time.now (Date_Time.new 2010 10 20) -between : Date_Time -> Date_Time -> Duration +between : Date_Time -> Date_Time -> Boolean -> Duration between start_inclusive end_exclusive timezone_aware=True = - period = Java_Period.ofDays 0 . normalized - duration = Time_Utils.duration_between start_inclusive end_exclusive timezone_aware - Duration_Data period duration + Duration.between_builtin start_inclusive end_exclusive timezone_aware +## Create a duration from time units. + + Arguments: + - hours: hours + - minutes: minutes + - seconds: seconds + - milliseconds: milliseconds + - nanoseconds: nanoseconds + + > Example + Duration of 2 hours. + + import Standard.Base.Data.Time.Duration + + example_duration = Duration.new hours=2 +new : Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Duration +new hours=0 minutes=0 seconds=0 milliseconds=0 nanoseconds=0 = + Duration.new_builtin hours minutes seconds milliseconds nanoseconds + + +## Create a zero (empty) duration. + > Example + Folding a vector of durations. + + import Standard.Base.Data.Time.Duration + + durations = [(Duration.new seconds=1), (Duration.new seconds=2), (Duration.new seconds=3)] + example_sum = durations.fold Duration.zero (+) +zero : Duration +zero = new ## ADVANCED @@ -38,25 +67,13 @@ time_execution ~function = start = System.nano_time result = Runtime.no_inline function end = System.nano_time - duration = Duration_Data (Java_Period.ofDays 0) (Java_Duration.ofNanos (end - start)) + duration = new nanoseconds=(end - start) Pair_Data duration result -# TODO Dubious constructor export -from project.Data.Time.Duration.Duration import all -from project.Data.Time.Duration.Duration export all - +@Builtin_Type type Duration - ## An amount of time in terms of years, months, days, hours, minutes, - seconds and nanoseconds. - - Arguments: - - internal_period: The internal representation of the time as a period. - - internal_duration: The internal representation of the time as a - duration. - Duration_Data internal_period internal_duration - ## Add the specified amount of time to this duration. Arguments: @@ -67,19 +84,17 @@ type Duration import Standard.Base.Data.Time.Duration - example_add = 3.minutes + 6.seconds + example_add = (Duration.new minutes=3) + (Duration.new seconds=6) > Example Add 12 hours to a duration of a month. import Standard.Base.Data.Time.Duration - example_add = 1.month + 12.hours - + : Duration -> Duration + example_add = 1.month + (Duration.new hours=12) + + : Duration -> Duration ! Time_Error + self that = - period = self.internal_period . plus that.internal_period . normalized - duration = self.internal_duration . plus that.internal_duration - Duration_Data period duration + Panic.catch ArithmeticException (self.plus_builtin that) (err-> Error.throw (Time_Error_Data err.getMessage)) ## Subtract the specified amount of time from this duration. @@ -94,186 +109,14 @@ type Duration example_subtract = 3.years - 11.months > Example - Substract 30 minutes from a duration of 7 months. + Substract 30 minutes from a duration of 6 hours. import Standard.Base.Data.Time.Duration - example_subtract = 7.months - 30.minutes - - : Duration -> Duration + example_subtract = (Duration.new hours=6) - (Duration.new minutes=30) + - : Duration -> Duration ! Time_Error - self that = - period = self.internal_period . minus that.internal_period . normalized - duration = self.internal_duration . minus that.internal_duration - Duration_Data period duration - - ## Get the portion of the duration expressed in nanoseconds. - - > Example - Get the portion of the duration expressed in nanoseconds. - - import Standard.Examples - - example_nanos = Examples.duration.nanoseconds - nanoseconds : Integer - nanoseconds self = self.internal_duration . toNanosPart - - ## Get the portion of the duration expressed in milliseconds. - - > Example - Get the portion of the duration expressed in milliseconds. - - import Standard.Examples - - example_millis = Examples.duration.milliseconds - milliseconds : Integer - milliseconds self = self.internal_duration . toMillisPart - - ## UNSTABLE - Convert the duration to total milliseconds. - total_milliseconds : Integer ! Illegal_Argument_Error - total_milliseconds self = - Panic.catch ArithmeticException (self.internal_duration . toMillis) _-> - Error.throw (Illegal_Argument_Error_Data "The duration is too large to convert it to milliseconds.") - - ## Get the portion of the duration expressed in seconds. - - > Example - Get the portion of the duration expressed in seconds. - - import Standard.Examples - - example_seconds = Examples.duration.milliseconds - seconds : Integer - seconds self = self.internal_duration . toSecondsPart - - ## Get the portion of the duration expressed in minutes. - - > Example - Get the portion of the duration expressed in minutes. - - import Standard.Examples - - example_minutes = Examples.duration.milliseconds - minutes : Integer - minutes self = self.internal_duration . toMinutesPart - - ## Get the portion of the duration expressed in hours. - - > Example - Get the portion of the duration expressed in hours. - - import Standard.Examples - - example_hours = Examples.duration.milliseconds - hours : Integer - hours self = self.internal_duration . toHours - - ## Get the portion of the duration expressed in days. - - > Example - Get the portion of the duration expressed in days. - - import Standard.Examples - - example_days = Examples.duration.milliseconds - days : Integer - days self = self.internal_period . getDays - - ## Get the portion of the duration expressed in months. - - > Example - Get the portion of the duration expressed in months. - - import Standard.Examples - - example_months = Examples.duration.months - months : Integer - months self = self.internal_period . getMonths - - ## Get the portion of the duration expressed in years. - - > Example - Get the portion of the duration expressed in years. - - import Standard.Examples - - example_years = Examples.duration.years - years : Integer - years self = self.internal_period . getYears - - ## Convert this duration to a Vector of years, months, days, hours, minutes, - seconds and nanosecnods. - - > Example - Convert duration of a year and a hour to a vector returning - `[1, 0, 0, 1, 0, 0, 0]`. - - import Standard.Base.Data.Time.Duration - - example_to_vec = (1.year + 1.hour).to_vector - - > Example - Convert duration of 800 nanoseconds to a vector returning - `[0, 0, 0, 0, 0, 0, 800]` - - import Standard.Base.Data.Time.Duration - - example_to_vec = 800.nanoseconds . to_vector - to_vector : Vector.Vector Integer - to_vector self = [self.years, self.months, self.days, self.hours, self.minutes, self.seconds, self.nanoseconds] - - ## A Duration to Json conversion. - - > Example - Convert a duration of 10 seconds to Json. - - import Standard.Base.Data.Time.Duration - - example_to_json = 10.seconds.to_json - to_json : Json.Object - to_json self = - b = Vector.new_builder - b.append ["type", "Duration"] - if self.years==0 . not then b.append ["years", self.years] - if self.months==0 . not then b.append ["months", self.months] - if self.days==0 . not then b.append ["days", self.days] - if self.hours==0 . not then b.append ["hours", self.hours] - if self.minutes==0 . not then b.append ["minutes", self.minutes] - if self.seconds==0 . not then b.append ["seconds", self.seconds] - if self.nanoseconds==0 . not then b.append ["nanoseconds", self.nanoseconds] - Json.from_pairs b.to_vector - - ## Check if this duration is date-based. - - > Example - Check if the duration of 10 seconds is date-based. - - import Standard.Base.Data.Time.Duration - - example_is_date = 10.seconds.is_date - is_date : Boolean - is_date self = (self.years==0 . not) || (self.months==0 . not) || (self.days==0 . not) - - ## Check if this duration is time-based. - - > Example - Check if the duration of 10 seconds is time-based. - - import Standard.Base.Data.Time.Duration - - example_is_time = 10.seconds.is_time - is_time : Boolean - is_time self = (self.hours==0 . not) || (self.minutes==0 . not) || (self.seconds==0 . not) || (self.nanoseconds==0 . not) - - ## Check if this duration represents an empty time-span. - - > Example - Check if the duration of 10 seconds is empty. - - import Standard.Base.Data.Time.Duration - - example_is_empty = 10.seconds.is_empty - is_empty : Boolean - is_empty self = self.is_date.not && self.is_time.not + Panic.catch ArithmeticException (self.minus_builtin that) (err -> Error.throw (Time_Error_Data err.getMessage)) ## Check two durations for equality. @@ -285,9 +128,9 @@ type Duration import Standard.Base.Data.Time.Duration - example_eq = 60.seconds == 1.minute + example_eq = (Duration.new seconds=60).total_minutes == (Duration.new minutes=1).total_minutes == : Duration -> Boolean - == self that = self.to_vector == that.to_vector + == self that = self.equals_builtin that ## Compares `self` to `that` to produce an ordering. @@ -300,193 +143,136 @@ type Duration import Standard.Base.Data.Time.Duration example_compare_to = - duration_1 = 1.year - duration_2 = 12.months + 1.day + duration_1 = (Duration.new hour=1) + duration_2 = (Duration.new minutes=60) + (Duration.new minutes=5) duration_1.compare_to duration_2 compare_to : Duration -> Ordering - compare_to self that = - if self.years > that.years then Ordering.Greater else - if self.years < that.years then Ordering.Less else - if self.months > that.months then Ordering.Greater else - if self.months < that.months then Ordering.Less else - if self.days > that.days then Ordering.Greater else - if self.days < that.days then Ordering.Less else - duration_sign = self.internal_duration.compareTo that.internal_duration - Ordering.from_sign duration_sign + compare_to self that = Ordering.from_sign (self.compare_to_builtin that) -## Create a duration of `self` nanoseconds. + ## Get the portion of the duration expressed in nanoseconds. - > Examples - Create a duration of 1 nanosecond. + > Example + Get the portion of the duration expressed in nanoseconds. - import Standard.Base.Data.Time.Duration + import Standard.Examples - example_nano = 1.nanosecond -Integer.nanosecond : Duration -Integer.nanosecond self = Duration_Data (Java_Period.ofDays 0) (Java_Duration.ofNanos self) + example_nanos = Examples.duration.nanoseconds + nanoseconds : Integer + nanoseconds self = @Builtin_Method "Duration.nanoseconds" -## Create a duration of `self` nanoseconds. + ## Get the portion of the duration expressed in milliseconds. - > Examples - Create a duration of 20 nanoseconds. + > Example + Get the portion of the duration expressed in milliseconds. - import Standard.Base.Data.Time.Duration + import Standard.Examples - example_nano = 20.nanoseconds -Integer.nanoseconds : Duration -Integer.nanoseconds self = self.nanosecond + example_millis = Examples.duration.milliseconds + milliseconds : Integer + milliseconds self = @Builtin_Method "Duration.milliseconds" -## Create a duration of `self` milliseconds. + ## Get the portion of the duration expressed in seconds. - > Example - Create a duration of 1 millisecond. + > Example + Get the portion of the duration expressed in seconds. - import Standard.Base.Data.Time.Duration + import Standard.Examples - example_milli = 1.millisecond -Integer.millisecond : Duration -Integer.millisecond self = Duration_Data (Java_Period.ofDays 0) (Java_Duration.ofMillis self) + example_seconds = Examples.duration.seconds + seconds : Integer + seconds self = @Builtin_Method "Duration.seconds" -## Create a duration of `self` milliseconds. + ## Get the portion of the duration expressed in minutes. - > Example - Create a duration of 20 milliseconds. + > Example + Get the portion of the duration expressed in minutes. - import Standard.Base.Data.Time.Duration + import Standard.Examples - example_milli = 20.milliseconds -Integer.milliseconds : Duration -Integer.milliseconds self = self.millisecond + example_minutes = Examples.duration.minutes + minutes : Integer + minutes self = @Builtin_Method "Duration.minutes" -## Create a duration of `self` seconds. + ## Get the portion of the duration expressed in hours. - > Example - Create a duration of 1 second. + > Example + Get the portion of the duration expressed in hours. - import Standard.Base.Data.Time.Duration + import Standard.Examples - example_second = 1.second -Integer.second : Duration -Integer.second self = Duration_Data (Java_Period.ofDays 0) (Java_Duration.ofSeconds self) + example_hours = Examples.duration.milliseconds + hours : Integer + hours self = @Builtin_Method "Duration.hours" -## Create a duration of `self` seconds. + ## UNSTABLE + Convert the duration to total milliseconds. + total_milliseconds : Integer ! Illegal_State_Error + total_milliseconds self = + Panic.catch ArithmeticException (self.total_milliseconds_builtin) _-> + Error.throw (Illegal_State_Error_Data "The duration is too large to convert it to milliseconds") - > Example - Create a duration of 20 seconds. + ## UNSTABLE + Convert the duration to total seconds. + total_seconds : Decimal ! Illegal_State_Error + total_seconds self = self.total_milliseconds / 1000.0 - import Standard.Base.Data.Time.Duration + ## UNSTABLE + Convert the duration to total minutes. + total_minutes : Decimal ! Illegal_State_Error + total_minutes self = self.total_seconds / 60.0 - example_second = 20.seconds -Integer.seconds : Duration -Integer.seconds self = self.second + ## UNSTABLE + Convert the duration to total minutes. + total_hours : Decimal ! Illegal_State_Error + total_hours self = self.total_minutes / 60.0 -## Create a duration of `self` minutes. + ## Convert this duration to a Vector of hours, minutes, seconds, milliseconds + and nanoseconds. - > Example - Create a duration of 1 minute. + > Example + Convert a duration of one hour to a vector resulting in + `[1, 0, 30, 0, 0]`. - import Standard.Base.Data.Time.Duration + import Standard.Base.Data.Time.Duration - example_min = 1.minute -Integer.minute : Duration -Integer.minute self = Duration_Data (Java_Period.ofDays 0) (Java_Duration.ofMinutes self) + example_to_vec = (Duration.new hours=1 seconds=30).to_vector -## Create a duration of `self` minutes. + > Example + Convert duration of 800 nanoseconds to a vector returning + `[0, 0, 0, 0, 0, 800]` - > Example - Create a duration of 20 minutes. + import Standard.Base.Data.Time.Duration - import Standard.Base.Data.Time.Duration + example_to_vec = (Duration.new nanoseconds=800)).to_vector + to_vector : Vector.Vector Integer + to_vector self = [self.hours, self.minutes, self.seconds, self.milliseconds, self.nanoseconds] - example_min = 20.minutes -Integer.minutes : Duration -Integer.minutes self = self.minute + ## A Duration to Json conversion. -## Create a duration of `self` hours. + > Example + Convert a duration of 10 seconds to Json. - > Example - Create a duration of 1 hour. + import Standard.Base.Data.Time.Duration - import Standard.Base.Data.Time.Duration + example_to_json = (Duration.new seconds=10).to_json + to_json : Json.Object + to_json self = + b = Vector.new_builder + b.append ["type", "Duration"] + if self.hours==0 . not then b.append ["hours", self.hours] + if self.minutes==0 . not then b.append ["minutes", self.minutes] + if self.seconds==0 . not then b.append ["seconds", self.seconds] + if self.milliseconds==0 . not then b.append ["milliseconds", self.milliseconds] + if self.nanoseconds==0 . not then b.append ["nanoseconds", self.nanoseconds] + Json.from_pairs b.to_vector - example_hour = 1.hour -Integer.hour : Duration -Integer.hour self = Duration_Data (Java_Period.ofDays 0) (Java_Duration.ofHours self) + ## Check if this duration represents an empty time-span. -## Create a duration of `self` hours. + > Example + Check if the duration of 10 seconds is empty. - > Example - Create a duration of 20 hours. - - import Standard.Base.Data.Time.Duration - - example_hour = 20.hours -Integer.hours : Duration -Integer.hours self = self.hour - -## Create a duration of `self` days. - - > Example - Create a duration of 1 day. - - import Standard.Base.Data.Time.Duration - - example_day = 1.day -Integer.day : Duration -Integer.day self = Duration_Data (Java_Period.ofDays self . normalized) (Java_Duration.ofSeconds 0) - -## Create a duration of `self` days. - - > Example - Create a duration of 20 days. - - import Standard.Base.Data.Time.Duration - - example_day = 20.days -Integer.days : Duration -Integer.days self = self.day - -## Create a duration of `self` months. - - > Example - Create a duration of 1 month. - - import Standard.Base.Data.Time.Duration - - example_month = 1.month -Integer.month : Duration -Integer.month self = Duration_Data (Java_Period.ofMonths self . normalized) (Java_Duration.ofSeconds 0) - -## Create a duration of `self` months. - - > Example - Create a duration of 6 months. - - import Standard.Base.Data.Time.Duration - - example_month = 6.months -Integer.months : Duration -Integer.months self = self.month - -## Create a duration of `self` years. - - > Example - Create a duration of 1 year. - - import Standard.Base.Data.Time.Duration - - example_year = 1.year -Integer.year : Duration -Integer.year self = Duration_Data (Java_Period.ofYears self . normalized) (Java_Duration.ofSeconds 0) - -## Create a duration of `self` years. - - > Example - Create a duration of 20 years. - - import Standard.Base.Data.Time.Duration - - example_year = 20.years -Integer.years : Duration -Integer.years self = self.year + import Standard.Base.Data.Time.Duration + example_is_empty = 10.seconds.is_empty + is_empty : Boolean + is_empty self = self.to_vector . all (==0) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso index 417fc7fb01c..a4621c0e3f8 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso @@ -215,10 +215,9 @@ type Time_Of_Day from Standard.Base import Time_Of_Day import Standard.Base.Data.Time.Duration - example_plus = Time_Of_Day.new + 3.seconds + example_plus = Time_Of_Day.new + (Duration.new seconds=3) + : Duration -> Time_Of_Day - + self amount = if amount.is_date then Error.throw (Time_Error_Data "Time_Of_Day does not support date intervals") else - Time_Utils.time_adjust self Time_Utils.AdjustOp.PLUS amount.internal_duration + + self amount = self.plus_builtin amount ## Subtract the specified amount of time from this instant to get a new instant. @@ -232,10 +231,9 @@ type Time_Of_Day from Standard.Base import Time_Of_Day import Standard.Base.Data.Time.Duration - example_minus = Time_Of_Day.now - 12.hours + example_minus = Time_Of_Day.now - (Duration.new hours=12) - : Duration -> Time_Of_Day - - self amount = if amount.is_date then Error.throw (Time_Error_Data "Time_Of_Day does not support date intervals") else - Time_Utils.time_adjust self Time_Utils.AdjustOp.MINUS amount.internal_duration + - self amount = self.minus_builtin amount ## Format this time of day as text using the default formatter. diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Http.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Http.enso index aca73408c7e..d9271619082 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Http.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Http.enso @@ -36,7 +36,7 @@ polyglot java import org.enso.base.Http_Utils > Example Create an HTTP client with extended timeout. - Http.new timeout=30.seconds + Http.new timeout=(Duration.new seconds=30) > Example Create an HTTP client with extended timeout and proxy settings. @@ -48,7 +48,7 @@ polyglot java import org.enso.base.Http_Utils example_new = Http.new (timeout = 30.seconds) (proxy = Proxy.new "example.com" 8080) new : Duration -> Boolean -> Proxy -> Http -new (timeout = 10.seconds) (follow_redirects = True) (proxy = Proxy.System) (version = Version.Http_1_1) = +new (timeout = (Duration.new seconds=10)) (follow_redirects = True) (proxy = Proxy.System) (version = Version.Http_1_1) = Http_Data timeout follow_redirects proxy version ## Send an Options request. @@ -662,9 +662,7 @@ type Http internal_http_client : HttpClient internal_http_client self = builder = HttpClient.newBuilder - # timeout - if self.timeout.is_date then Panic.throw (Time_Error_Data "Connection timeout does not support date intervals") else - builder.connectTimeout self.timeout.internal_duration + builder.connectTimeout self.timeout # redirect redirect = HttpClient.Redirect redirect_policy = case self.follow_redirects of diff --git a/distribution/lib/Standard/Examples/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Examples/0.0.0-dev/src/Main.enso index e70e65a6d3c..086b7832084 100644 --- a/distribution/lib/Standard/Examples/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Examples/0.0.0-dev/src/Main.enso @@ -165,7 +165,7 @@ get_geo_data = Http.fetch geo_data_url ## A simple HTTP client for examples. http_client : Http -http_client = Http.new (timeout = 30.seconds) +http_client = Http.new (timeout = Duration.new seconds=30) ## A basic URI for examples. uri : URI diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso index 5371b99b79b..8b9d38adfb5 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso @@ -166,7 +166,7 @@ specify : Text -> Any -> (Text | Nothing) -> Nothing specify label ~behavior pending=Nothing = pair = case pending of Nothing -> Duration.time_execution (run_spec behavior) - reason -> Pair_Data 0.milliseconds (Pending reason) + reason -> Pair_Data Duration.zero (Pending reason) result = pair.second time_taken = pair.first spec = State.get Spec @@ -838,14 +838,14 @@ report_pending_group name reason config builder = Prints a report on the tests to standard output. Spec.print_report : Suite_Config -> (StringBuilder|Nothing) -> Nothing Spec.print_report self config builder = - total_time = self.behaviors.fold 0.milliseconds acc-> behavior-> + total_time = self.behaviors.fold Duration.zero acc-> behavior-> acc + behavior.time_taken if config.should_output_junit then builder.append (' \n') self.behaviors.reverse.each behavior-> diff --git a/docs/infrastructure/native-image.md b/docs/infrastructure/native-image.md index 6847382d917..b01097c501b 100644 --- a/docs/infrastructure/native-image.md +++ b/docs/infrastructure/native-image.md @@ -111,6 +111,13 @@ For example, to update settings for the Launcher: java -agentlib:native-image-agent=config-merge-dir=engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher -jar launcher.jar ``` +Note that for convenience, you can run the launcher/engine runner via +`bin/enso`, e.g. + +```bash +env JAVA_OPTS="-agentlib:native-image-agent=config-merge-dir=./engine/runner-native/src/main/resources/META-INF/native-image/org/enso/runner" ./built-distribution/enso-engine-0.0.0-dev-linux-amd64/enso-0.0.0-dev/bin/enso --run tmp.enso +``` + The command may need to be re-run with different arguments to ensure that all execution paths that use reflection are covered. The configuration files between consecutive runs will be merged (a warning may be issued for the first run if diff --git a/engine/runner-native/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json b/engine/runner-native/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json index 5f290fef593..90c19e00f38 100644 --- a/engine/runner-native/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json +++ b/engine/runner-native/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json @@ -261,6 +261,90 @@ { "name":"org.enso.interpreter.node.expression.builtin.date.ToTextTimeOfDayMethodGen", "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.MinusBuiltinTimeOfDayMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.MillisecondsDurationMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.MinusBuiltinDateTimeMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.MinusBuiltinDurationMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.BetweenBuiltinDurationMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.CompareToBuiltinDurationMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.TotalMillisecondsBuiltinDurationMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.Duration", + "methods":[{"name":"","parameterTypes":[] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.EqualsBuiltinDurationMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.HoursDurationMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.MillisecondsDurationMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.MinutesDurationMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.MinusBuiltinDateTimeMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.MinusBuiltinDurationMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.MinusBuiltinTimeOfDayMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.NanosecondsDurationMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.NewBuiltinDurationMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.PlusBuiltinDateTimeMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.PlusBuiltinDurationMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.PlusBuiltinTimeOfDayMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} +, + { + "name":"org.enso.interpreter.node.expression.builtin.date.SecondsDurationMethodGen", + "methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]} , { "name":"org.enso.interpreter.node.expression.builtin.date.ToTextTimeZoneMethodGen", diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ReadOnlyArray.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ReadOnlyArray.java index 2697b3c132d..502849b7099 100644 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ReadOnlyArray.java +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ReadOnlyArray.java @@ -18,7 +18,7 @@ import java.util.Arrays; * in EPB because EPB is a dependency of runtime. */ @ExportLibrary(InteropLibrary.class) -public class ReadOnlyArray implements TruffleObject { +public final class ReadOnlyArray implements TruffleObject { private final Object[] items; diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/PolyglotProxy.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/PolyglotProxy.java index ffe55beceb2..72524df5630 100644 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/PolyglotProxy.java +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/PolyglotProxy.java @@ -27,7 +27,7 @@ import org.enso.interpreter.epb.node.ContextRewrapNode; * org.enso.interpreter.epb.EpbLanguage} for the explanation of this system. */ @ExportLibrary(InteropLibrary.class) -public class PolyglotProxy implements TruffleObject { +public final class PolyglotProxy implements TruffleObject { final Object delegate; private final GuardedTruffleContext origin; private final GuardedTruffleContext target; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/FunctionCallInstrumentationNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/FunctionCallInstrumentationNode.java index 35a1abff298..863bf493779 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/FunctionCallInstrumentationNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/FunctionCallInstrumentationNode.java @@ -54,7 +54,7 @@ public class FunctionCallInstrumentationNode extends Node implements Instrumenta /** A simple value class for function call information. */ @ExportLibrary(InteropLibrary.class) - public static class FunctionCall implements TruffleObject { + public static final class FunctionCall implements TruffleObject { private final Function function; private final Object state; private final @CompilerDirectives.CompilationFinal(dimensions = 1) Object[] arguments; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodNode.java index 84cec501ed7..9e7d80f84ac 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodNode.java @@ -14,11 +14,6 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.source.SourceSection; - -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.ZoneId; - import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode; import org.enso.interpreter.node.callable.resolver.HostMethodCallNode; @@ -28,12 +23,24 @@ import org.enso.interpreter.runtime.Context; import org.enso.interpreter.runtime.callable.UnresolvedSymbol; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; import org.enso.interpreter.runtime.callable.function.Function; -import org.enso.interpreter.runtime.data.*; +import org.enso.interpreter.runtime.data.ArrayRope; +import org.enso.interpreter.runtime.data.EnsoDate; +import org.enso.interpreter.runtime.data.EnsoDateTime; +import org.enso.interpreter.runtime.data.EnsoDuration; +import org.enso.interpreter.runtime.data.EnsoTimeOfDay; +import org.enso.interpreter.runtime.data.EnsoTimeZone; import org.enso.interpreter.runtime.data.text.Text; -import org.enso.interpreter.runtime.error.*; +import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.error.PanicSentinel; +import org.enso.interpreter.runtime.error.Warning; +import org.enso.interpreter.runtime.error.WithWarnings; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; import org.enso.interpreter.runtime.state.Stateful; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.UUID; import java.util.concurrent.locks.Lock; @@ -318,6 +325,35 @@ public abstract class InvokeMethodNode extends BaseNode { } } + @Specialization( + guards = { + "!types.hasType(self)", + "!types.hasSpecialDispatch(self)", + "getPolyglotCallType(self, symbol, interop) == CONVERT_TO_DURATION" + }) + Stateful doConvertDuration( + VirtualFrame frame, + Object state, + UnresolvedSymbol symbol, + Object self, + Object[] arguments, + @CachedLibrary(limit = "10") TypesLibrary types, + @CachedLibrary(limit = "10") InteropLibrary interop, + @Cached MethodResolverNode methodResolverNode) { + var ctx = Context.get(this); + try { + var duration = interop.asDuration(self); + var ensoDuration = new EnsoDuration(duration); + Function function = + methodResolverNode.expectNonNull(ensoDuration, ctx.getBuiltins().duration(), symbol); + + arguments[0] = ensoDuration; + return invokeFunctionNode.execute(function, frame, state, arguments); + } catch (UnsupportedMessageException e) { + throw new PanicException(ctx.getBuiltins().error().makeNoSuchMethodError(self, symbol), this); + } + } + @CompilerDirectives.TruffleBoundary private ZonedDateTime dateTime(LocalDate date, LocalTime time) { return date.atTime(time).atZone(ZoneId.systemDefault()); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/resolver/HostMethodCallNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/resolver/HostMethodCallNode.java index 06cd79209d9..222ddac1c90 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/resolver/HostMethodCallNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/resolver/HostMethodCallNode.java @@ -1,7 +1,15 @@ package org.enso.interpreter.node.callable.resolver; -import com.oracle.truffle.api.dsl.*; -import com.oracle.truffle.api.interop.*; +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.ImportStatic; +import com.oracle.truffle.api.dsl.ReportPolymorphism; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.interop.ArityException; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnknownIdentifierException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.interop.UnsupportedTypeException; import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.BranchProfile; @@ -57,6 +65,11 @@ public abstract class HostMethodCallNode extends Node { * Standard.Base.Data.Time.Date_Time} with a system Time_Zone and dispatching natively. */ CONVERT_TO_DATE_TIME, + /** + * The method call should be handled by converting {@code self} to a {@code + * Standard.Base.Data.Time.Duration} and dispatching natively. + */ + CONVERT_TO_DURATION, /** * The method call should be handled by converting {@code self} to a {@code * Standard.Base.Data.Time.Time_Of_Day} and dispatching natively. @@ -83,6 +96,7 @@ public abstract class HostMethodCallNode extends Node { && this != CONVERT_TO_TEXT && this != CONVERT_TO_DATE && this != CONVERT_TO_DATE_TIME + && this != CONVERT_TO_DURATION && this != CONVERT_TO_ZONED_DATE_TIME && this != CONVERT_TO_TIME_OF_DAY && this != CONVERT_TO_TIME_ZONE; @@ -134,6 +148,8 @@ public abstract class HostMethodCallNode extends Node { } } else if (library.isTime(self)) { return PolyglotCallType.CONVERT_TO_TIME_OF_DAY; + } else if (library.isDuration(self)) { + return PolyglotCallType.CONVERT_TO_DURATION; } else if (library.isTimeZone(self)) { return PolyglotCallType.CONVERT_TO_TIME_ZONE; } else if (library.isString(self)) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/Module.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/Module.java index b475bef4545..00c61ce94fe 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/Module.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/Module.java @@ -46,7 +46,7 @@ import scala.Function1; /** Represents a source module with a known location. */ @ExportLibrary(InteropLibrary.class) -public class Module implements TruffleObject { +public final class Module implements TruffleObject { /** Defines a stage of compilation of the module. */ public enum CompilationStage { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java index e2eba4a4013..c551dca62e3 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java @@ -88,6 +88,7 @@ public class Builtins { private final Builtin file; private final Builtin date; private final Builtin dateTime; + private final Builtin duration; private final Builtin timeOfDay; private final Builtin timeZone; private final Builtin warning; @@ -129,6 +130,7 @@ public class Builtins { file = builtins.get(File.class); date = builtins.get(org.enso.interpreter.node.expression.builtin.date.Date.class); dateTime = builtins.get(org.enso.interpreter.node.expression.builtin.date.DateTime.class); + duration = builtins.get(org.enso.interpreter.node.expression.builtin.date.Duration.class); timeOfDay = builtins.get(org.enso.interpreter.node.expression.builtin.date.TimeOfDay.class); timeZone = builtins.get(org.enso.interpreter.node.expression.builtin.date.TimeZone.class); special = new Special(language); @@ -457,6 +459,15 @@ public class Builtins { return timeOfDay.getType(); } + /** + * Returns the {@code Duration} atom constructor. + * + * @return the {@code Duration} atom constructor. + */ + public Type duration() { + return duration.getType(); + } + /** * Returns the {@code TimeZone} atom constructor. * diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedConversion.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedConversion.java index f65833c6bbf..09b2299c077 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedConversion.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedConversion.java @@ -15,7 +15,7 @@ import org.enso.interpreter.runtime.state.data.EmptyMap; /** Simple runtime value representing a yet-unresolved by-name symbol. */ @ExportLibrary(InteropLibrary.class) -public class UnresolvedConversion implements TruffleObject { +public final class UnresolvedConversion implements TruffleObject { private final ModuleScope scope; /** diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedSymbol.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedSymbol.java index cbffe835f40..7ef9454697c 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedSymbol.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedSymbol.java @@ -15,7 +15,7 @@ import org.enso.interpreter.runtime.state.data.EmptyMap; /** Simple runtime value representing a yet-unresolved by-name symbol. */ @ExportLibrary(InteropLibrary.class) -public class UnresolvedSymbol implements TruffleObject { +public final class UnresolvedSymbol implements TruffleObject { private final String name; private final ModuleScope scope; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/ArraySlice.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/ArraySlice.java index 66769c0a5c4..8e50934e7e1 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/ArraySlice.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/ArraySlice.java @@ -12,7 +12,7 @@ import com.oracle.truffle.api.library.ExportMessage; import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode; @ExportLibrary(InteropLibrary.class) -class ArraySlice implements TruffleObject { +public final class ArraySlice implements TruffleObject { private final Object storage; private final long start; private final long end; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoDateTime.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoDateTime.java index 312650cf4ac..be963a5763f 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoDateTime.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoDateTime.java @@ -3,20 +3,26 @@ package org.enso.interpreter.runtime.data; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; -import org.enso.interpreter.dsl.Builtin; -import org.enso.interpreter.node.expression.builtin.error.PolyglotError; -import org.enso.interpreter.runtime.Context; -import org.enso.interpreter.runtime.data.text.Text; -import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; - -import java.time.*; +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.time.temporal.TemporalAccessor; +import org.enso.interpreter.dsl.Builtin; +import org.enso.interpreter.node.expression.builtin.error.PolyglotError; +import org.enso.interpreter.runtime.Context; +import org.enso.interpreter.runtime.data.text.Text; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; @ExportLibrary(InteropLibrary.class) @ExportLibrary(TypesLibrary.class) @@ -138,6 +144,24 @@ public final class EnsoDateTime implements TruffleObject { return new EnsoTimeZone(dateTime.getZone()); } + @Builtin.Method(name = "plus_builtin", description = "Adds a duration to this date time") + @Builtin.Specialize + @Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class) + @CompilerDirectives.TruffleBoundary + public EnsoDateTime plus(Object durationObject, InteropLibrary interop) + throws UnsupportedMessageException { + return new EnsoDateTime(dateTime.plus(interop.asDuration(durationObject))); + } + + @Builtin.Method(name = "minus_builtin", description = "Subtracts a duration from this date time") + @Builtin.Specialize + @Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class) + @CompilerDirectives.TruffleBoundary + public EnsoDateTime minus(Object durationObject, InteropLibrary interop) + throws UnsupportedMessageException { + return new EnsoDateTime(dateTime.minus(interop.asDuration(durationObject))); + } + @Builtin.Method(description = "Return the number of seconds from the Unix epoch.") @CompilerDirectives.TruffleBoundary public long toEpochSeconds() { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoDuration.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoDuration.java new file mode 100644 index 00000000000..e07192a617b --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoDuration.java @@ -0,0 +1,195 @@ +package org.enso.interpreter.runtime.data; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.Temporal; +import org.enso.interpreter.dsl.Builtin; +import org.enso.interpreter.runtime.Context; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; + +@ExportLibrary(InteropLibrary.class) +@ExportLibrary(TypesLibrary.class) +@Builtin(pkg = "date", name = "Duration", stdlibName = "Standard.Base.Data.Time.Duration") +public final class EnsoDuration implements TruffleObject { + private final Duration duration; + + public EnsoDuration(Duration duration) { + this.duration = duration; + } + + @ExportMessage + boolean hasType() { + return true; + } + + @ExportMessage + Type getType(@CachedLibrary("this") TypesLibrary thisLib) { + return Context.get(thisLib).getBuiltins().duration(); + } + + @Builtin.Method( + name = "new_builtin", + description = + "Constructs a new Duration from hours, minutes, seconds, milliseconds and nanoseconds") + @TruffleBoundary + public static EnsoDuration create( + long hours, long minutes, long seconds, long milliseconds, long nanoseconds) { + var duration = + Duration.ofHours(hours) + .plusMinutes(minutes) + .plusSeconds(seconds) + .plusMillis(milliseconds) + .plusNanos(nanoseconds); + return new EnsoDuration(duration); + } + + @Builtin.Method( + name = "between_builtin", + description = + "Construct a new Duration that is between the given start date inclusive, and end date exclusive") + @Builtin.Specialize + @TruffleBoundary + public static EnsoDuration between( + Object startInclusive, Object endExclusive, boolean timeZoneAware, InteropLibrary interop) { + if (!isDateTime(startInclusive, interop) + || (timeZoneAware && !hasTimeZone(startInclusive, interop))) { + throw createNotDateTimePanic("start_inclusive", startInclusive, interop); + } + if (!isDateTime(endExclusive, interop) + || (timeZoneAware && !hasTimeZone(endExclusive, interop))) { + throw createNotDateTimePanic("end_exclusive", endExclusive, interop); + } + Temporal startTime = convertToDateTime(startInclusive, timeZoneAware, interop); + Temporal endTime = convertToDateTime(endExclusive, timeZoneAware, interop); + return new EnsoDuration(Duration.between(startTime, endTime)); + } + + private static boolean isDateTime(Object dateTime, InteropLibrary interop) { + return interop.isDate(dateTime) && interop.isTime(dateTime); + } + + private static boolean hasTimeZone(Object dateTime, InteropLibrary interop) { + return interop.isTimeZone(dateTime); + } + + private static PanicException createNotDateTimePanic( + String varName, Object object, InteropLibrary interop) { + return new PanicException( + Context.get(interop).getBuiltins().error().makeTypeError("Date_Time", object, varName), + interop); + } + + private static Temporal convertToDateTime( + Object dateTimeObject, boolean timeZoneAware, InteropLibrary interop) { + assert interop.isDate(dateTimeObject); + assert interop.isTime(dateTimeObject); + try { + LocalDate date = interop.asDate(dateTimeObject); + LocalTime time = interop.asTime(dateTimeObject); + if (timeZoneAware) { + assert interop.isTime(dateTimeObject); + ZoneId zone = interop.asTimeZone(dateTimeObject); + return ZonedDateTime.of(date, time, zone); + } else { + return LocalDateTime.of(date, time); + } + } catch (UnsupportedMessageException e) { + throw new IllegalStateException(e); + } + } + + @Builtin.Method(description = "Gets the hours part") + public long hours() { + return duration.toHoursPart(); + } + + @Builtin.Method(description = "Gets the minutes part") + public long minutes() { + return duration.toMinutesPart(); + } + + @Builtin.Method(description = "Gets the seconds part") + public long seconds() { + return duration.toSecondsPart(); + } + + @Builtin.Method(description = "Gets the milliseconds part") + public long milliseconds() { + return duration.toMillisPart(); + } + + @Builtin.Method(description = "Gets the nanoseconds part") + public long nanoseconds() { + return duration.toNanosPart(); + } + + @Builtin.Method( + name = "total_milliseconds_builtin", + description = "Gets the total amount of milliseconds") + public long totalMilliseconds() { + return duration.toMillis(); + } + + @Builtin.Method(name = "plus_builtin", description = "Adds another Duration") + @Builtin.Specialize + @Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class) + @TruffleBoundary + public EnsoDuration plus(Object durationObject, InteropLibrary interop) + throws UnsupportedMessageException { + return new EnsoDuration(duration.plus(interop.asDuration(durationObject))); + } + + @Builtin.Method(name = "minus_builtin", description = "Subtracts another Duration") + @Builtin.Specialize + @Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class) + @TruffleBoundary + public EnsoDuration minus(Object durationObject, InteropLibrary interop) + throws UnsupportedMessageException { + return new EnsoDuration(duration.minus(interop.asDuration(durationObject))); + } + + @Builtin.Method(name = "compare_to_builtin", description = "Compares to other duration") + @Builtin.Specialize + @Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class) + public long compareTo(Object durationObject, InteropLibrary interop) + throws UnsupportedMessageException { + return duration.compareTo(interop.asDuration(durationObject)); + } + + @Builtin.Method(name = "equals_builtin") + @Builtin.Specialize + @Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class) + public boolean equalsDuration(Object durationObject, InteropLibrary interop) + throws UnsupportedMessageException { + return duration.equals(interop.asDuration(durationObject)); + } + + @ExportMessage + public boolean isDuration() { + return true; + } + + @ExportMessage + public Duration asDuration() { + return duration; + } + + @ExportMessage + @TruffleBoundary + public String toDisplayString(boolean allowSideEffects) { + return duration.toString(); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoFile.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoFile.java index 0c28d4df136..8b17924f094 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoFile.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoFile.java @@ -30,7 +30,7 @@ import java.util.Set; */ @ExportLibrary(TypesLibrary.class) @Builtin(pkg = "io", name = "File", stdlibName = "Standard.Base.System.File.File") -public class EnsoFile implements TruffleObject { +public final class EnsoFile implements TruffleObject { private final TruffleFile truffleFile; public EnsoFile(TruffleFile truffleFile) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoTimeOfDay.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoTimeOfDay.java index be1f385f27e..35c08ffafa5 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoTimeOfDay.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoTimeOfDay.java @@ -1,28 +1,29 @@ package org.enso.interpreter.runtime.data; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; -import org.enso.interpreter.dsl.Builtin; -import org.enso.interpreter.node.expression.builtin.error.PolyglotError; -import org.enso.interpreter.runtime.Context; -import org.enso.interpreter.runtime.data.text.Text; -import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; - import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; +import org.enso.interpreter.dsl.Builtin; +import org.enso.interpreter.node.expression.builtin.error.PolyglotError; +import org.enso.interpreter.runtime.Context; +import org.enso.interpreter.runtime.data.text.Text; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; @ExportLibrary(InteropLibrary.class) @ExportLibrary(TypesLibrary.class) @Builtin(pkg = "date", name = "TimeOfDay", stdlibName = "Standard.Base.Data.Time.Time_Of_Day") -public class EnsoTimeOfDay implements TruffleObject { +public final class EnsoTimeOfDay implements TruffleObject { private LocalTime localTime; public EnsoTimeOfDay(LocalTime localTime) { @@ -77,6 +78,28 @@ public class EnsoTimeOfDay implements TruffleObject { return localTime.getNano(); } + @Builtin.Method(name = "plus_builtin", description = "Adds a duration to this Time_Of_Day") + @Builtin.Specialize + @Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class) + @TruffleBoundary + public EnsoTimeOfDay plus(Object durationObject, InteropLibrary interop) + throws UnsupportedMessageException { + assert interop.isDuration(durationObject); + return new EnsoTimeOfDay(localTime.plus(interop.asDuration(durationObject))); + } + + @Builtin.Method( + name = "minus_builtin", + description = "Subtracts a duration from this Time_Of_Day") + @Builtin.Specialize + @Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class) + @TruffleBoundary + public EnsoTimeOfDay minus(Object durationObject, InteropLibrary interop) + throws UnsupportedMessageException { + assert interop.isDuration(durationObject); + return new EnsoTimeOfDay(localTime.minus(interop.asDuration(durationObject))); + } + @Builtin.Method(description = "Gets a value second") @CompilerDirectives.TruffleBoundary public long toSeconds() { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/ManagedResource.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/ManagedResource.java index 98f4b71061c..a926ed478d9 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/ManagedResource.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/ManagedResource.java @@ -14,7 +14,7 @@ import java.lang.ref.PhantomReference; /** A runtime representation of a managed resource. */ @ExportLibrary(TypesLibrary.class) @Builtin(pkg = "resource", stdlibName = "Standard.Base.Runtime.Resource.Managed_Resource") -public class ManagedResource implements TruffleObject { +public final class ManagedResource implements TruffleObject { private final Object resource; private PhantomReference phantomReference; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Ref.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Ref.java index c5d40e8da5b..b13742c2e20 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Ref.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Ref.java @@ -11,7 +11,7 @@ import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; /** A mutable reference type. */ @ExportLibrary(TypesLibrary.class) @Builtin(pkg = "mutable", stdlibName = "Standard.Base.Runtime.Ref.Ref") -public class Ref implements TruffleObject { +public final class Ref implements TruffleObject { private volatile Object value; /** diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Type.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Type.java index df93fc629b0..a9acaacc35b 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Type.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Type.java @@ -29,7 +29,7 @@ import java.util.*; @ExportLibrary(TypesLibrary.class) @ExportLibrary(InteropLibrary.class) -public class Type implements TruffleObject { +public final class Type implements TruffleObject { private final String name; private @CompilerDirectives.CompilationFinal ModuleScope definitionScope; private final boolean builtin; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/text/Text.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/text/Text.java index efd3ff805aa..60d6771eaa4 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/text/Text.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/text/Text.java @@ -18,7 +18,7 @@ import java.util.concurrent.locks.ReentrantLock; /** The main runtime type for Enso's Text. */ @ExportLibrary(InteropLibrary.class) @ExportLibrary(TypesLibrary.class) -public class Text implements TruffleObject { +public final class Text implements TruffleObject { private volatile Object contents; private volatile boolean isFlat; private static final Lock lock = new ReentrantLock(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/Warning.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/Warning.java index fd4488617c5..9b9b137a563 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/Warning.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/Warning.java @@ -23,7 +23,7 @@ import java.util.Comparator; @Builtin(pkg = "error", stdlibName = "Standard.Base.Warning.Warning") @ExportLibrary(TypesLibrary.class) -public class Warning implements TruffleObject { +public final class Warning implements TruffleObject { private final Object value; private final Object origin; private final ArrayRope reassignments; @@ -137,7 +137,7 @@ public class Warning implements TruffleObject { } @ExportLibrary(InteropLibrary.class) - public static class Reassignment implements TruffleObject { + public static final class Reassignment implements TruffleObject { private final String methodName; private final SourceSection location; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/WithWarnings.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/WithWarnings.java index 8a24a107b2b..39d09c81fba 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/WithWarnings.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/WithWarnings.java @@ -8,7 +8,7 @@ import org.enso.interpreter.runtime.data.ArrayRope; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; @ExportLibrary(TypesLibrary.class) -public class WithWarnings implements TruffleObject { +public final class WithWarnings implements TruffleObject { private final ArrayRope warnings; private final Object value; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/number/EnsoBigInteger.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/number/EnsoBigInteger.java index c7dab1b59bc..3ac4081d17a 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/number/EnsoBigInteger.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/number/EnsoBigInteger.java @@ -17,7 +17,7 @@ import java.math.BigInteger; /** Internal wrapper for a {@link BigInteger}. */ @ExportLibrary(InteropLibrary.class) @ExportLibrary(TypesLibrary.class) -public class EnsoBigInteger implements TruffleObject { +public final class EnsoBigInteger implements TruffleObject { private final BigInteger value; /** diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java index c176f178980..0591246828c 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java @@ -14,7 +14,7 @@ import org.enso.interpreter.runtime.error.RedefinedMethodException; import org.enso.interpreter.runtime.error.RedefinedConversionException; /** A representation of Enso's per-file top-level scope. */ -public class ModuleScope implements TruffleObject { +public final class ModuleScope implements TruffleObject { private final Type associatedType; private final Module module; private Map polyglotSymbols = new HashMap<>(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/TopLevelScope.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/TopLevelScope.java index 5aa775989b0..169289f65ca 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/TopLevelScope.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/TopLevelScope.java @@ -28,7 +28,7 @@ import org.enso.polyglot.MethodNames; /** Represents the top scope of Enso execution, containing all the importable modules. */ @ExportLibrary(InteropLibrary.class) -public class TopLevelScope implements TruffleObject { +public final class TopLevelScope implements TruffleObject { private final Builtins builtins; private final PackageRepository packageRepository; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/Types.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/Types.java index aaed02ddbd9..ccee4a8d33f 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/Types.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/Types.java @@ -52,6 +52,7 @@ import org.enso.polyglot.data.TypeGraph; EnsoDateTime.class, EnsoTimeOfDay.class, EnsoTimeZone.class, + EnsoDuration.class }) public class Types { @@ -142,6 +143,8 @@ public class Types { return ConstantsGen.DATE_TIME; } else if (TypesGen.isEnsoTimeOfDay(value)) { return ConstantsGen.TIME_OF_DAY; + } else if (TypesGen.isEnsoDuration(value)) { + return ConstantsGen.DURATION; } else if (TypesGen.isEnsoTimeZone(value)) { return ConstantsGen.TIME_ZONE; } else if (TypesGen.isEnsoFile(value)) { @@ -245,6 +248,7 @@ public class Types { graph.insert(ConstantsGen.DATE, ConstantsGen.ANY); graph.insert(ConstantsGen.DATE_TIME, ConstantsGen.ANY); graph.insert(ConstantsGen.TIME_OF_DAY, ConstantsGen.ANY); + graph.insert(ConstantsGen.DURATION, ConstantsGen.ANY); graph.insert(ConstantsGen.TIME_ZONE, ConstantsGen.ANY); graph.insertWithoutParent(ConstantsGen.PANIC); graph.insertWithoutParent(Constants.THUNK); diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/TypeWithKind.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/TypeWithKind.java index 6adafbfa041..d8c56129e14 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/TypeWithKind.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/TypeWithKind.java @@ -49,6 +49,7 @@ public record TypeWithKind(String baseType, TypeKind kind) { "org.enso.interpreter.runtime.data.EnsoDateTime", "org.enso.interpreter.runtime.data.EnsoTimeOfDay", "org.enso.interpreter.runtime.data.EnsoTimeZone", + "org.enso.interpreter.runtime.data.EnsoDuration", "org.enso.interpreter.runtime.data.ManagedResource", "org.enso.interpreter.runtime.data.Ref", "org.enso.interpreter.runtime.data.text.Text", diff --git a/project/StdBits.scala b/project/StdBits.scala index 292a94c9fc6..7138ffcb4cf 100644 --- a/project/StdBits.scala +++ b/project/StdBits.scala @@ -1,4 +1,3 @@ -import LibraryManifestGenerator.BundledLibrary import sbt.Keys._ import sbt._ import sbt.internal.util.ManagedLogger @@ -6,7 +5,6 @@ import sbt.io.IO import sbt.librarymanagement.{ConfigurationFilter, DependencyFilter} import java.io.File -import scala.sys.process.Process object StdBits { @@ -24,8 +22,7 @@ object StdBits { def copyDependencies( destination: File, baseJarName: Option[String], - ignoreScalaLibrary: Boolean, - unpackedDeps: Set[String] = Set() + ignoreScalaLibrary: Boolean ): Def.Initialize[Task[Unit]] = Def.task { val libraryUpdates = (Compile / update).value @@ -53,7 +50,7 @@ object StdBits { report => val expectedFileNames = report.checked.map( - getDestinationFileName(_, unpackedDeps) + file => file.getName ) ++ baseJarName.toSeq for (existing <- IO.listFiles(destination)) { if (!expectedFileNames.contains(existing.getName)) { @@ -67,53 +64,25 @@ object StdBits { log.info( s"Updating changed std-bits dependency ${changed.getName}." ) - updateDependency(changed, unpackedDeps, destination, log) + updateDependency(changed, destination, log) } for (file <- report.unmodified) { - val dest = destination / getDestinationFileName(file, unpackedDeps) + val dest = destination / file.getName if (!dest.exists()) { log.info(s"Adding missing std-bits dependency ${file.getName}.") - updateDependency(file, unpackedDeps, destination, log) + updateDependency(file, destination, log) } } } } - private def shouldUnpack(jar: File, unpacked: Set[String]): Boolean = { - // Maven stores dependencies like this: - // .../repo1.maven.org/maven2/org/apache/xmlbeans/xmlbeans/5.0.1/xmlbeans-5.0.1.jar - // therefore, the parent of the parent of the jar file is the directory - // named with the un-versioned library name. - unpacked.contains(jar.getParentFile.getParentFile.getName) - } - private def updateDependency( jar: File, - unpacked: Set[String], destinationDir: File, logger: ManagedLogger ): Unit = { - val destination = destinationDir / getDestinationFileName(jar, unpacked) - if (shouldUnpack(jar, unpacked)) { - destination.mkdirs() - val exitCode = Process(s"jar xf ${jar.getAbsolutePath}", destination).! - if (exitCode != 0) { - logger.err(s"Could not unpack a dependency jar: $jar.") - throw new RuntimeException(s"Could not unpack a dependency jar: $jar.") - } - } else { - IO.copyFile(jar, destination) - } - } - - private def getDestinationFileName( - file: File, - unpacked: Set[String] - ): String = { - if (shouldUnpack(file, unpacked)) { - val name = file.getName - name.stripSuffix(".jar") - } else file.getName + val destination = destinationDir / jar.getName + IO.copyFile(jar, destination) } /** diff --git a/std-bits/base/src/main/java/org/enso/base/Time_Utils.java b/std-bits/base/src/main/java/org/enso/base/Time_Utils.java index 1adb018b787..2fef10edfca 100644 --- a/std-bits/base/src/main/java/org/enso/base/Time_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/Time_Utils.java @@ -6,10 +6,21 @@ import org.enso.base.time.TimeUtilsBase; import org.enso.base.time.Time_Of_Day_Utils; import org.graalvm.polyglot.Value; -import java.time.*; +import java.time.DateTimeException; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Period; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; -import java.time.temporal.*; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalField; +import java.time.temporal.WeekFields; import java.util.Locale; /** Utils for standard library operations on Time. */ @@ -79,29 +90,6 @@ public class Time_Utils { } } - public static ZonedDateTime datetime_adjust( - ZonedDateTime datetime, AdjustOp op, Period period, Duration duration) { - switch (op) { - case PLUS: - return datetime.plus(period).plus(duration); - case MINUS: - return datetime.minus(period).minus(duration); - default: - throw new DateTimeException("Unknown adjust operation"); - } - } - - public static LocalTime time_adjust(LocalTime time, AdjustOp op, Duration duration) { - switch (op) { - case PLUS: - return time.plus(duration); - case MINUS: - return time.minus(duration); - default: - throw new DateTimeException("Unknown adjust operation"); - } - } - public static int get_field_as_localdate(LocalDate date, TemporalField field) { return date.get(field); } diff --git a/test/Tests/src/Data/Time/Date_Spec.enso b/test/Tests/src/Data/Time/Date_Spec.enso index ae767d51c48..87be8310923 100644 --- a/test/Tests/src/Data/Time/Date_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Spec.enso @@ -92,33 +92,33 @@ spec_with name create_new_date parse_date = date.to_json.should_equal <| Json.from_pairs [["type", "Date"], ["day", date.day], ["month", date.month], ["year", date.year]] - Test.specify "should add date-based interval" <| + Test.specify "should add date-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| date = create_new_date 1970 + 1.day date . year . should_equal 1970 date . month . should_equal 1 date . day . should_equal 2 - Test.specify "should subtract date-based interval" <| + Test.specify "should subtract date-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| date = create_new_date 1970 - 1.year date . year . should_equal 1969 date . month . should_equal 1 date . day . should_equal 1 - Test.specify "should support mixed interval operators" <| + Test.specify "should support mixed interval operators" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| date = create_new_date 1970 + 1.month - 1.year date . year . should_equal 1969 date . month . should_equal 2 date . day . should_equal 1 - Test.specify "should throw error when adding time-based interval" <| - case (create_new_date 1970 + 1.hour) . catch of + Test.specify "should throw error when adding time-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| + case (create_new_date 1970 + (Duration.new hours=1)) . catch of Time_Error_Data message -> message . should_equal "Date does not support time intervals" result -> Test.fail ("Unexpected result: " + result.to_text) - Test.specify "should throw error when subtracting time-based interval" <| - case (create_new_date 1970 - (1.day - 1.minute)) . catch of + Test.specify "should throw error when subtracting time-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| + case (create_new_date 1970 - (1.day - (Duration.new minutes=1))) . catch of Time_Error_Data message -> message . should_equal "Date does not support time intervals" result -> @@ -199,7 +199,7 @@ spec_with name create_new_date parse_date = (create_new_date 2000 7 1).end_of Date_Period.Quarter . should_equal (Date.new 2000 9 30) (create_new_date 2000 6 30).end_of Date_Period.Quarter . should_equal (Date.new 2000 6 30) - Test.specify "should allow to compute the number of working days until a later date" <| + Test.specify "should allow to compute the number of working days until a later date" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| # 2000-2-1 is a Tuesday create_new_date 2000 2 1 . work_days_until (create_new_date 2000 2 1) . should_equal 0 create_new_date 2000 2 1 . work_days_until (create_new_date 2000 2 2) . should_equal 1 @@ -250,7 +250,7 @@ spec_with name create_new_date parse_date = # We duplicate the holiday entries to check that the functions are resilient to such input data. duplicated_holiday_november year = holiday_november year + holiday_november year + holiday_november year - Test.specify "should allow to compute the number of working days until a date, skipping custom set holidays" <| + Test.specify "should allow to compute the number of working days until a date, skipping custom set holidays" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| holiday_february = Vector.new 29 (i -> create_new_date 2000 2 i+1) create_new_date 2000 2 1 . work_days_until (create_new_date 2000 3 1) holiday_february . should_equal 0 create_new_date 2000 2 10 . work_days_until (create_new_date 2000 2 12) holiday_february . should_equal 0 @@ -265,7 +265,7 @@ spec_with name create_new_date parse_date = create_new_date 2000 11 1 . work_days_until (create_new_date 2000 12 1) (duplicated_holiday_november 2020) . should_equal 22 create_new_date 1999 11 1 . work_days_until (create_new_date 1999 12 1) (duplicated_holiday_november 1999) . should_equal 19 - Test.specify "should allow to compute the number of working days including the end, in a manner consistent with NETWORKDAYS" <| + Test.specify "should allow to compute the number of working days including the end, in a manner consistent with NETWORKDAYS" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| create_new_date 2000 2 1 . work_days_until (create_new_date 2000 2 1) include_end_date=True . should_equal 1 create_new_date 2000 2 1 . work_days_until (create_new_date 2000 2 2) include_end_date=True . should_equal 2 create_new_date 2000 2 3 . work_days_until (create_new_date 2000 2 16) include_end_date=True . should_equal 10 @@ -282,7 +282,7 @@ spec_with name create_new_date parse_date = create_new_date 2000 2 6 . work_days_until (create_new_date 2000 2 8) include_end_date=True . should_equal 2 create_new_date 2000 2 6 . work_days_until (create_new_date 2000 2 5) include_end_date=True . should_equal 0 - Test.specify "should allow to shift the date by N working days" <| + Test.specify "should allow to shift the date by N working days" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| # 2000-2-1 is a Tuesday create_new_date 2000 2 1 . add_work_days 0 . should_equal (Date.new 2000 2 1) create_new_date 2000 2 1 . add_work_days . should_equal (Date.new 2000 2 2) @@ -314,7 +314,7 @@ spec_with name create_new_date parse_date = create_new_date 2022 3 27 . add_work_days 0 . should_equal (Date.new 2022 3 28) create_new_date 2022 3 27 . add_work_days 1 . should_equal (Date.new 2022 3 29) - Test.specify "should allow to shift the date by negative amount of working days" <| + Test.specify "should allow to shift the date by negative amount of working days" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| # 2000-2-1 is a Tuesday create_new_date 2000 2 1 . add_work_days -1 . should_equal (Date.new 2000 1 31) create_new_date 2000 2 1 . add_work_days -2 . should_equal (Date.new 2000 1 28) @@ -343,7 +343,7 @@ spec_with name create_new_date parse_date = create_new_date 2000 2 6 . add_work_days -5 . should_equal (Date.new 2000 1 31) create_new_date 2000 2 6 . add_work_days -6 . should_equal (Date.new 2000 1 28) - Test.specify "should allow to shift the date by N working days, skipping custom holidays" <| + Test.specify "should allow to shift the date by N working days, skipping custom holidays" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| all_year_holiday year = first_day = create_new_date year 1 1 Vector.new first_day.length_of_year (n -> first_day + n.days) @@ -364,7 +364,7 @@ spec_with name create_new_date parse_date = create_new_date 1999 10 30 . add_work_days 0 (duplicated_holiday_november 1999) . should_equal (Date.new 1999 11 3) create_new_date 1999 10 30 . add_work_days 1 (duplicated_holiday_november 1999) . should_equal (Date.new 1999 11 4) - Test.specify "add_work_days and work_days_until should be consistent with each other" <| + Test.specify "add_work_days and work_days_until should be consistent with each other" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| first_day = create_new_date 2020 1 1 dates = Vector.new 100 (n -> first_day + n.days) holidays = [1, 2, 10, 11, 12, 13, 14, 15, 30, 40, 41, 42, 50, 60].map (n -> first_day + n.days) diff --git a/test/Tests/src/Data/Time/Date_Time_Spec.enso b/test/Tests/src/Data/Time/Date_Time_Spec.enso index 1d14dabf60c..666cff7cb7c 100644 --- a/test/Tests/src/Data/Time/Date_Time_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Time_Spec.enso @@ -230,7 +230,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= time . day . should_equal 1 Test.specify "should add time interval" <| - time = create_new_datetime 1970 (zone = Time_Zone.utc) + 1.nanosecond + time = create_new_datetime 1970 (zone = Time_Zone.utc) + (Duration.new nanoseconds=1) time . year . should_equal 1970 time . month . should_equal 1 time . day . should_equal 1 @@ -240,7 +240,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= time . nanosecond . should_equal 1 time . zone . should_equal Time_Zone.utc - Test.specify "should add date interval" <| + Test.specify "should add date interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| time = create_new_datetime 1970 (zone = Time_Zone.utc) + 1.month time . year . should_equal 1970 time . month . should_equal 2 @@ -251,8 +251,8 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= time . nanosecond . should_equal 0 time . zone . zone_id . should_equal Time_Zone.utc.zone_id - Test.specify "should add mixed date time interval" <| - time = create_new_datetime 1970 (zone = Time_Zone.utc) + (1.month + 3.hours) + Test.specify "should add mixed date time interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| + time = create_new_datetime 1970 (zone = Time_Zone.utc) + (1.month + (Duration.new hours=3)) time . year . should_equal 1970 time . month . should_equal 2 time . day . should_equal 1 @@ -263,7 +263,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= time . zone . zone_id . should_equal Time_Zone.utc.zone_id Test.specify "should subtract time interval" <| - time = create_new_datetime 1970 (zone = Time_Zone.utc) - 1.hour + time = create_new_datetime 1970 (zone = Time_Zone.utc) - (Duration.new hours=1) time . year . should_equal 1969 time . month . should_equal 12 time . day . should_equal 31 @@ -273,7 +273,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= time . nanosecond . should_equal 0 time . zone . zone_id . should_equal Time_Zone.utc.zone_id - Test.specify "should subtract date interval" <| + Test.specify "should subtract date interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| time = create_new_datetime 1970 (zone = Time_Zone.utc) - 1.month time . year . should_equal 1969 time . month . should_equal 12 @@ -284,8 +284,8 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= time . nanosecond . should_equal 0 time . zone . zone_id . should_equal Time_Zone.utc.zone_id - Test.specify "should subtract mixed date time interval" <| - time = create_new_datetime 1970 (zone = Time_Zone.utc) - (1.month - 3.hours) + Test.specify "should subtract mixed date time interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| + time = create_new_datetime 1970 (zone = Time_Zone.utc) - (1.month - (Duration.new hours=3)) time . year . should_equal 1969 time . month . should_equal 12 time . day . should_equal 1 @@ -295,8 +295,8 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= time . nanosecond . should_equal 0 time . zone . zone_id . should_equal Time_Zone.utc.zone_id - Test.specify "should support mixed interval operators" <| - time = create_new_datetime 1970 (zone = Time_Zone.utc) - 1.month + 12.hours + Test.specify "should support mixed interval operators" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| + time = create_new_datetime 1970 (zone = Time_Zone.utc) - 1.month + (Duration.new hours=12) time . year . should_equal 1969 time . month . should_equal 12 time . day . should_equal 1 @@ -428,7 +428,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= Test.specify "should find start/end of a Date_Period or Time_Period containing the current datetime correctly near the spring DST switch" pending=js_dst_pending <| d1 = create_new_datetime 2022 3 27 1 34 15 0 tz d2 = create_new_datetime 2022 3 27 3 34 15 0 tz - d1_plus = d1 + 1.hour + d1_plus = d1 + (Duration.new hours=1) d1_plus . should_equal d2 check_dates_spring date = @@ -460,7 +460,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= dst_overlap_message = "We cannot correctly migrate the datetime inside of the timeline overlap through the polyglot boundar - as due to polyglot conversion limitation, always the earlier one is chosen. See the bug report: https://github.com/oracle/graal/issues/4918" Test.specify "should find start/end of a Date_Period or Time_Period containing the current datetime correctly near the autumn DST switch" pending=dst_overlap_message <| d3 = create_new_datetime 2022 10 30 2 30 15 0 tz - d4 = d3 + (1.hour) + d4 = d3 + (Duration.new hours=1) d3.hour . should_equal 2 d4.hour . should_equal 2 @@ -502,7 +502,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= d4_end.nanosecond . should_equal max_nanos Time_Utils.get_datetime_offset d4_end . should_equal offset_1_h - Test.specify "should allow to shift the date by N working days" <| + Test.specify "should allow to shift the date by N working days" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| # 2000-2-1 is a Tuesday create_new_datetime 2000 2 1 12 30 . add_work_days 0 . should_equal (Date_Time.new 2000 2 1 12 30) create_new_datetime 2000 2 1 12 15 55 . add_work_days . should_equal (Date_Time.new 2000 2 2 12 15 55) @@ -523,7 +523,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= create_new_datetime 2000 2 27 12 10 . add_work_days 3 . should_equal (Date_Time.new 2000 3 2 12 10) create_new_datetime 1999 2 27 12 10 . add_work_days 3 . should_equal (Date_Time.new 1999 3 4 12 10) - Test.specify "should handle shifting dates around spring DST edge cases" pending=js_dst_pending <| + Test.specify "should handle shifting dates around spring DST edge cases" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| # 2022-10-30 and 2022-03-27 are DST switch days, Sundays. create_new_datetime 2022 10 30 2 30 55 1234 . add_work_days 0 . should_equal (Date_Time.new 2022 10 31 2 30 55 1234) create_new_datetime 2022 10 30 1 30 . add_work_days 1 . should_equal (Date_Time.new 2022 11 1 1 30) @@ -534,12 +534,12 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= Test.specify "should handle shifting dates around autumn DST edge cases" pending=dst_overlap_message <| d3 = create_new_datetime 2022 10 30 2 30 15 0 tz - d4 = d3 + (1.hour) + d4 = d3 + (Duration.new hours=1) # TODO we need to check and document the actual behaviour once it is expressible, it may be equally acceptable to shift to 3:30 instead of 2:30. d4 . add_work_days 0 . should_equal (Date_Time.new 2022 10 31 2 30 15 0 tz) - Test.specify "should allow to shift the date by negative amount of working days" <| + Test.specify "should allow to shift the date by negative amount of working days" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| # 2000-2-1 is a Tuesday create_new_datetime 2000 2 1 12 30 . add_work_days -1 . should_equal (Date_Time.new 2000 1 31 12 30) create_new_datetime 2000 2 1 13 30 . add_work_days -2 . should_equal (Date_Time.new 2000 1 28 13 30) @@ -549,7 +549,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= create_new_datetime 2000 2 6 0 1 . add_work_days -2 . should_equal (Date_Time.new 2000 2 3 0 1) create_new_datetime 2000 2 6 23 59 . add_work_days -6 . should_equal (Date_Time.new 2000 1 28 23 59) - Test.specify "should allow to shift the date by N working days, skipping custom holidays" <| + Test.specify "should allow to shift the date by N working days, skipping custom holidays" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| all_year_holiday year = first_day = Date.new year 1 1 Vector.new first_day.length_of_year (n -> first_day + n.days) diff --git a/test/Tests/src/Data/Time/Duration_Spec.enso b/test/Tests/src/Data/Time/Duration_Spec.enso index 818bcd55a53..f0ab1cbfbf1 100644 --- a/test/Tests/src/Data/Time/Duration_Spec.enso +++ b/test/Tests/src/Data/Time/Duration_Spec.enso @@ -3,99 +3,93 @@ from Standard.Base import all import Standard.Base.Data.Time.Duration import Standard.Test +polyglot java import java.time.Duration as Java_Duration +polyglot java import java.time.LocalDate +polyglot java import java.time.LocalDateTime as Java_DateTime + + +java_datetime year month=1 day=1 hour=0 minute=0 second=0 nanosecond=0 = + Panic.catch Any (Java_DateTime.of year month day hour minute second nanosecond) (err -> Error.throw (Time_Error_Data <| err.payload.to_display_text.drop (Text_Sub_Range.First 16))) + +js_datetime year month=1 day=1 hour=0 minute=0 second=0 nanosecond=0 = + Panic.catch Any (js_datetime_impl year month day hour minute second nanosecond) (err -> Error.throw (Time_Error_Data err.payload.cause)) + +foreign js js_datetime_impl year month day hour minute second nanosecond = """ + if (month > 12 || month < 1) { + throw `Invalid value for MonthOfYear (valid values 1 - 12): ${month}`; + } + return new Date(year, month - 1, day, hour, minute, second, nanosecond / 1000000); + spec = Test.group "Duration" <| Test.specify "should create interval seconds" <| - interval = 5.seconds - interval.to_vector . should_equal [0, 0, 0, 0, 0, 5, 0] - - Test.specify "should create interval months" <| - interval = 9.months - interval.to_vector . should_equal [0, 9, 0, 0, 0, 0, 0] - - Test.specify "should add days to nanoseconds" <| - interval = 7.nanoseconds + 3.days - interval.to_vector . should_equal [0, 0, 3, 0, 0, 0, 7] - - Test.specify "should add milliseconds to years" <| - interval = 4.years + 8.milliseconds - interval.to_vector . should_equal [4, 0, 0, 0, 0, 0, 8000000] - - Test.specify "should substract seconds from months" <| - interval = 8.months - 8.seconds - interval.to_vector . should_equal [0, 8, 0, 0, 0, -8, 0] - - Test.specify "should subtract years from hours" <| - interval = 2.hours - 11.years - interval.to_vector . should_equal [-11, 0, 0, 2, 0, 0, 0] - - Test.specify "should support mixed operators" <| - interval = 2.hours + 12.seconds - 11.years - interval.to_vector . should_equal [-11, 0, 0, 2, 0, 12, 0] + duration = (Duration.new seconds=5) + duration.seconds . should_equal 5 + duration.milliseconds . should_equal 0 Test.specify "should create interval between two points in time" <| - time1 = Date_Time.new 2001 1 2 - time2 = Date_Time.new 2001 2 1 - interval = Duration.between time1 time2 - interval.to_vector . should_equal [0, 0, 0, 720, 0, 0, 0] - - Test.specify "should check if time based" <| - interval = 10.hours - interval.is_date . should_be_false - interval.is_time . should_be_true - - Test.specify "should check if date based" <| - interval = 10.years - interval.is_date . should_be_true - interval.is_time . should_be_false - - Test.specify "should check if mixed based" <| - interval = 10.years + 3.hours - interval.is_date . should_be_true - interval.is_time . should_be_true + (Duration.between (Date_Time.new 2001 1 1 3) (Date_Time.new 2001 1 1 10)).total_hours . should_equal 7 + (Duration.between (Date_Time.new 2001 1 1) (Date_Time.new 2001 1 7)).total_hours . should_equal (6 * 24) + (Duration.between (Date_Time.new 2001 1 1 13) (Date_Time.new 2001 1 7 16)).total_hours . should_equal (3 + 6 * 24) Test.specify "should check if empty" <| - interval = 0.seconds + interval = Duration.zero interval.is_empty . should_be_true Test.specify "should normalize periods" <| - interval = 12.months - interval.to_vector . should_equal [1, 0, 0, 0, 0, 0, 0] + (Duration.new seconds=60).total_minutes . should_equal 1 + (Duration.new milliseconds=1000).total_seconds . should_equal 1 Test.specify "should normalize addition" <| - interval = 11.months + 1.month - interval.to_vector . should_equal [1, 0, 0, 0, 0, 0, 0] + duration = (Duration.new hours=11) + (Duration.new hours=1) + duration.hours . should_equal 12 Test.specify "should normalize subtraction" <| - interval = 13.months - 1.month - interval.to_vector . should_equal [1, 0, 0, 0, 0, 0, 0] - - Test.specify "should check equality" <| - 3.seconds.should_equal 3.seconds - 60.seconds.should_equal 1.minute - 61.seconds.should_equal (1.minute + 1.second) - 60.minutes.should_equal 1.hour - (24.hours == 1.day) . should_be_false - (30.days == 1.month) . should_be_false - 12.months.should_equal 1.year - 18.months.should_equal (1.year + 6.months) - 1.year.should_equal (11.months + 1.month) - 10.years.should_equal 10.years + duration = (Duration.new hours=13) - (Duration.new hours=1) + duration.hours . should_equal 12 Test.specify "should convert to Json" <| - interval = 120.nanoseconds + 30.seconds + 14.hours + 12.days + 1.month + 9.years + interval = (Duration.new nanoseconds=120) + (Duration.new seconds=30) + (Duration.new hours=14) interval.to_json.should_equal <| - duration_pairs = [["nanoseconds", interval.nanoseconds], ["seconds", interval.seconds], ["hours", interval.hours], ["days", interval.days], ["months", interval.months], ["years", interval.years]] + duration_pairs = [["nanoseconds", interval.nanoseconds], ["seconds", interval.seconds], ["hours", interval.hours]] Json.from_pairs ([["type", "Duration"]] + duration_pairs) Test.specify "should be comparable" <| - duration_1 = 1.year - duration_2 = 24.days + duration_1 = (Duration.new hours=5) + duration_2 = (Duration.new minutes=1) duration_1.compare_to duration_1 . should_equal Ordering.Equal duration_1==duration_1 . should_be_true duration_1!=duration_2 . should_be_true duration_1>duration_2 . should_be_true duration_1 message . should_equal "Time_Of_Day does not support date intervals" result -> Test.fail ("Unexpected result: " + result.to_text) - Test.specify "should throw error when subtracting date-based interval" <| - case (create_new_time 0 - (1.day - 1.minute)) . catch of + Test.specify "should throw error when subtracting date-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <| + case (create_new_time 0 - (1.day - (Duration.new minutes=1))) . catch of Time_Error_Data message -> message . should_equal "Time_Of_Day does not support date intervals" result -> diff --git a/test/Tests/src/Network/Http_Spec.enso b/test/Tests/src/Network/Http_Spec.enso index db77f749ba2..c8d8adb1696 100644 --- a/test/Tests/src/Network/Http_Spec.enso +++ b/test/Tests/src/Network/Http_Spec.enso @@ -30,8 +30,8 @@ spec = url_get = base_url_with_slash + "get" url_post = base_url_with_slash + "post" Test.specify "should create HTTP client with timeout setting" <| - http = Http.new (timeout = 30.seconds) - http.timeout.should_equal 30.seconds + http = Http.new (timeout = (Duration.new seconds=30)) + http.timeout.should_equal (Duration.new seconds=30) Test.specify "should create HTTP client with follow_redirects setting" <| http = Http.new (follow_redirects = False) http.follow_redirects.should_equal False diff --git a/test/Tests/src/Semantic/Java_Interop_Spec.enso b/test/Tests/src/Semantic/Java_Interop_Spec.enso index 3c14bb109ec..25eb597a216 100644 --- a/test/Tests/src/Semantic/Java_Interop_Spec.enso +++ b/test/Tests/src/Semantic/Java_Interop_Spec.enso @@ -1,4 +1,5 @@ from Standard.Base import all +import Standard.Test polyglot java import java.lang.Float polyglot java import java.lang.Integer @@ -9,7 +10,6 @@ polyglot java import java.util.ArrayList polyglot java import java.time.LocalDate polyglot java import java.time.LocalTime -import Standard.Test Any.test_me x = x.is_nothing