Browse Source

Improve interop conversion logic between numeric types (#1995)

Marko Lahma 9 months ago
parent
commit
44a0ce0d17
2 changed files with 72 additions and 6 deletions
  1. 20 0
      Jint.Tests/Runtime/InteropTests.cs
  2. 52 6
      Jint/Runtime/Interop/InteropHelper.cs

+ 20 - 0
Jint.Tests/Runtime/InteropTests.cs

@@ -3698,4 +3698,24 @@ try {
     {
         public static JsNumber MAX_SAFE_INTEGER = new JsNumber(NumberConstructor.MaxSafeInteger);
     }
+
+    [Fact]
+    public void ShouldFindShortOverload()
+    {
+        _engine.SetValue("target", new ShortOverloadWithBoolean());
+        _engine.Evaluate("target.method(42)").AsString().Should().Be("short");
+    }
+
+    private class ShortOverloadWithBoolean
+    {
+        public string Method(short s, bool b = true)
+        {
+            return "short";
+        }
+
+        public string Method(bool b)
+        {
+            return "boolean";
+        }
+    }
 }

+ 52 - 6
Jint/Runtime/Interop/InteropHelper.cs

@@ -1,6 +1,7 @@
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Reflection;
+using System.Runtime.CompilerServices;
 using Jint.Extensions;
 using Jint.Native;
 
@@ -120,14 +121,59 @@ internal sealed class InteropHelper
             return 5;
         }
 
-        if (paramType == typeof(int) && parameterValue.IsInteger())
+        const int ScoreForDifferentTypeButFittingNumberRange = 2;
+        if (parameterValue.IsNumber())
         {
-            return 0;
-        }
+            var num = (JsNumber) parameterValue;
+            var numValue = num._value;
 
-        if (paramType == typeof(float) && objectValueType == typeof(double))
-        {
-            return parameterValue.IsInteger() ? 1 : 2;
+            if (paramType == typeof(double))
+            {
+                return 0;
+            }
+
+            if (paramType == typeof(float) && numValue is <= float.MaxValue and >= float.MinValue)
+            {
+                return ScoreForDifferentTypeButFittingNumberRange;
+            }
+
+            var isInteger = num.IsInteger() || TypeConverter.IsIntegralNumber(num._value);
+
+            // if value is integral number and within allowed range for the parameter type, we consider this perfect match
+            if (isInteger)
+            {
+                if (paramType == typeof(int))
+                {
+                    return 0;
+                }
+
+                if (paramType == typeof(long))
+                {
+                    return ScoreForDifferentTypeButFittingNumberRange;
+                }
+
+                // check if we can narrow without exception throwing versions (CanChangeType)
+                var integerValue = (int) num._value;
+                if (paramType == typeof(short) && integerValue is <= short.MaxValue and >= short.MinValue)
+                {
+                    return ScoreForDifferentTypeButFittingNumberRange;
+                }
+
+                if (paramType == typeof(ushort) && integerValue is <= ushort.MaxValue and >= ushort.MinValue)
+                {
+                    return ScoreForDifferentTypeButFittingNumberRange;
+                }
+
+                if (paramType == typeof(byte) && integerValue is <= byte.MaxValue and >= byte.MinValue)
+                {
+                    return ScoreForDifferentTypeButFittingNumberRange;
+                }
+
+                if (paramType == typeof(sbyte) && integerValue is <= sbyte.MaxValue and >= sbyte.MinValue)
+                {
+                    return ScoreForDifferentTypeButFittingNumberRange;
+                }
+            }
         }
 
         if (paramType.IsEnum &&