Browse Source

Optimize new Date() (#1281)

Marko Lahma 2 years ago
parent
commit
6f11ba671e

+ 1 - 1
Jint.Tests/Runtime/JsValueConversionTests.cs

@@ -54,7 +54,7 @@ namespace Jint.Tests.Runtime
         [Fact]
         [Fact]
         public void ShouldBeADate()
         public void ShouldBeADate()
         {
         {
-            var value = new DateInstance(_engine);
+            var value = new DateInstance(_engine, double.NaN);
             Assert.Equal(false, value.IsBoolean());
             Assert.Equal(false, value.IsBoolean());
             Assert.Equal(false, value.IsArray());
             Assert.Equal(false, value.IsArray());
             Assert.Equal(true, value.IsDate());
             Assert.Equal(true, value.IsDate());

+ 21 - 11
Jint/Native/Date/DateConstructor.cs

@@ -1,4 +1,5 @@
 using System.Globalization;
 using System.Globalization;
+using System.Runtime.CompilerServices;
 using Jint.Collections;
 using Jint.Collections;
 using Jint.Native.Function;
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Native.Object;
@@ -163,14 +164,26 @@ namespace Jint.Native.Date
         /// <summary>
         /// <summary>
         /// https://tc39.es/ecma262/#sec-date
         /// https://tc39.es/ecma262/#sec-date
         /// </summary>
         /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         private ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
         {
-            double dv;
+            // fast path is building default, new Date()
             if (arguments.Length == 0 || newTarget.IsUndefined())
             if (arguments.Length == 0 || newTarget.IsUndefined())
             {
             {
-                dv = FromDateTime(DateTime.UtcNow);
+                return OrdinaryCreateFromConstructor(
+                    newTarget,
+                    static intrinsics => intrinsics.Date.PrototypeObject,
+                    static (engine, _, dateValue) => new DateInstance(engine, dateValue),
+                    (DateTime.UtcNow - Epoch).TotalMilliseconds);
             }
             }
-            else if (arguments.Length == 1)
+
+            return ConstructUnlikely(arguments, newTarget);
+        }
+
+        private DateInstance ConstructUnlikely(JsValue[] arguments, JsValue newTarget)
+        {
+            double dv;
+            if (arguments.Length == 1)
             {
             {
                 if (arguments[0] is DateInstance date)
                 if (arguments[0] is DateInstance date)
                 {
                 {
@@ -208,12 +221,10 @@ namespace Jint.Native.Date
                 dv = TimeClip(PrototypeObject.Utc(finalDate));
                 dv = TimeClip(PrototypeObject.Utc(finalDate));
             }
             }
 
 
-            var o = OrdinaryCreateFromConstructor(
+            return OrdinaryCreateFromConstructor(
                 newTarget,
                 newTarget,
                 static intrinsics => intrinsics.Date.PrototypeObject,
                 static intrinsics => intrinsics.Date.PrototypeObject,
-                static (Engine engine, Realm _, object? _) => new DateInstance(engine));
-            o.DateValue = dv;
-            return o;
+                static (engine, _, dateValue) => new DateInstance(engine, dateValue), dv);
         }
         }
 
 
         public DateInstance Construct(DateTimeOffset value)
         public DateInstance Construct(DateTimeOffset value)
@@ -228,10 +239,9 @@ namespace Jint.Native.Date
 
 
         public DateInstance Construct(double time)
         public DateInstance Construct(double time)
         {
         {
-            var instance = new DateInstance(_engine)
+            var instance = new DateInstance(_engine, TimeClip(time))
             {
             {
-                _prototype = PrototypeObject,
-                DateValue = TimeClip(time)
+                _prototype = PrototypeObject
             };
             };
 
 
             return instance;
             return instance;
@@ -254,7 +264,7 @@ namespace Jint.Native.Date
 
 
         private double FromDateTime(DateTime dt, bool negative = false)
         private double FromDateTime(DateTime dt, bool negative = false)
         {
         {
-            var convertToUtcAfter = (dt.Kind == DateTimeKind.Unspecified);
+            var convertToUtcAfter = dt.Kind == DateTimeKind.Unspecified;
 
 
             var dateAsUtc = dt.Kind == DateTimeKind.Local
             var dateAsUtc = dt.Kind == DateTimeKind.Local
                 ? dt.ToUniversalTime()
                 ? dt.ToUniversalTime()

+ 32 - 33
Jint/Native/Date/DateInstance.cs

@@ -2,50 +2,49 @@ using System.Globalization;
 using Jint.Native.Object;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime;
 
 
-namespace Jint.Native.Date
+namespace Jint.Native.Date;
+
+public sealed class DateInstance : ObjectInstance
 {
 {
-    public sealed class DateInstance : ObjectInstance
-    {
-        // Maximum allowed value to prevent DateTime overflow
-        private static readonly double Max = (DateTime.MaxValue - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
+    // Maximum allowed value to prevent DateTime overflow
+    private static readonly double Max = (DateTime.MaxValue - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
 
 
-        // Minimum allowed value to prevent DateTime overflow
-        private static readonly double Min = -(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) - DateTime.MinValue).TotalMilliseconds;
+    // Minimum allowed value to prevent DateTime overflow
+    private static readonly double Min = -(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) - DateTime.MinValue).TotalMilliseconds;
 
 
-        public DateInstance(Engine engine)
-            : base(engine, ObjectClass.Date)
-        {
-            DateValue = double.NaN;
-        }
+    public DateInstance(Engine engine, double dateValue)
+        : base(engine, ObjectClass.Date)
+    {
+        DateValue = dateValue;
+    }
 
 
-        public DateTime ToDateTime()
+    public DateTime ToDateTime()
+    {
+        if (DateTimeRangeValid)
         {
         {
-            if (DateTimeRangeValid)
-            {
-                return DateConstructor.Epoch.AddMilliseconds(DateValue);
-            }
-
-            ExceptionHelper.ThrowRangeError(_engine.Realm);
-            return DateTime.MinValue;
+            return DateConstructor.Epoch.AddMilliseconds(DateValue);
         }
         }
 
 
-        public double DateValue { get; internal set; }
+        ExceptionHelper.ThrowRangeError(_engine.Realm);
+        return DateTime.MinValue;
+    }
 
 
-        internal bool DateTimeRangeValid => !double.IsNaN(DateValue) && DateValue <= Max && DateValue >= Min;
+    public double DateValue { get; internal set; }
 
 
-        public override string ToString()
-        {
-            if (double.IsNaN(DateValue))
-            {
-                return "NaN";
-            }
+    internal bool DateTimeRangeValid => !double.IsNaN(DateValue) && DateValue <= Max && DateValue >= Min;
 
 
-            if (double.IsInfinity(DateValue))
-            {
-                return "Infinity";
-            }
+    public override string ToString()
+    {
+        if (double.IsNaN(DateValue))
+        {
+            return "NaN";
+        }
 
 
-            return ToDateTime().ToString("ddd MMM dd yyyy HH:mm:ss 'GMT'zzz", CultureInfo.InvariantCulture);
+        if (double.IsInfinity(DateValue))
+        {
+            return "Infinity";
         }
         }
+
+        return ToDateTime().ToString("ddd MMM dd yyyy HH:mm:ss 'GMT'zzz", CultureInfo.InvariantCulture);
     }
     }
 }
 }