瀏覽代碼

Always allow operator overloaded conversion (#1099)

Marko Lahma 3 年之前
父節點
當前提交
d21f36a2e8
共有 3 個文件被更改,包括 62 次插入48 次删除
  1. 5 6
      Jint.Tests/Runtime/InteropTests.cs
  2. 53 35
      Jint/Runtime/Interop/DefaultTypeConverter.cs
  3. 4 7
      Jint/Runtime/TypeConverter.cs

+ 5 - 6
Jint.Tests/Runtime/InteropTests.cs

@@ -2770,13 +2770,12 @@ namespace Jint.Tests.Runtime
         [Fact]
         public void ShouldBeAbleToUseConvertibleStructAsMethodParameter()
         {
-            var engine = new Engine(options => options.AllowOperatorOverloading());
-            engine.SetValue("test", new DiscordTestClass());
-            engine.SetValue("id", new DiscordId("12345"));
+            _engine.SetValue("test", new DiscordTestClass());
+            _engine.SetValue("id", new DiscordId("12345"));
 
-            Assert.Equal("12345", engine.Evaluate("String(id)").AsString());
-            Assert.Equal("12345", engine.Evaluate("test.echo('12345')").AsString());
-            Assert.Equal("12345", engine.Evaluate("test.create(12345)").AsString());
+            Assert.Equal("12345", _engine.Evaluate("String(id)").AsString());
+            Assert.Equal("12345", _engine.Evaluate("test.echo('12345')").AsString());
+            Assert.Equal("12345", _engine.Evaluate("test.create(12345)").AsString());
         }
 
         private class Profile

+ 53 - 35
Jint/Runtime/Interop/DefaultTypeConverter.cs

@@ -207,47 +207,18 @@ namespace Jint.Runtime.Interop
                 return obj;
             }
 
-            if (_engine.Options.Interop.AllowOperatorOverloading)
-            {
-                var key = new TypeConversionKey(valueType, type);
-
-                static MethodInfo CreateValueFactory(TypeConversionKey k)
-                {
-                    foreach (var m in k.Source.GetOperatorOverloadMethods().Concat(k.Target.GetOperatorOverloadMethods()))
-                    {
-                        var parameters = m.GetParameters();
-                        if (parameters.Length != 1)
-                        {
-                            continue;
-                        }
-
-                        if (!parameters[0].ParameterType.IsAssignableFrom(k.Source))
-                        {
-                            continue;
-                        }
-
-                        if (k.Target.IsAssignableFrom(m.ReturnType) && m.Name is "op_Implicit" or "op_Explicit")
-                        {
-                            return m;
-                        }
-                    }
-                    return null;
-                }
-
-                var castOperator = _knownCastOperators.GetOrAdd(key, CreateValueFactory);
-
-                if (castOperator != null)
-                {
-                    return castOperator.Invoke(null, new[] { value });
-                }
-            }
-
             try
             {
                 return System.Convert.ChangeType(value, type, formatProvider);
             }
             catch (Exception e)
             {
+                // check if we can do a cast with operator overloading
+                if (TryCastWithOperators(value, type, valueType, out var invoke))
+                {
+                    return invoke;
+                }
+                
                 if (!_engine.Options.Interop.ExceptionHandler(e))
                 {
                     throw;
@@ -258,6 +229,53 @@ namespace Jint.Runtime.Interop
             }
         }
 
+        private bool TryCastWithOperators(object value, Type type, Type valueType, out object converted)
+        {
+            var key = new TypeConversionKey(valueType, type);
+
+            static MethodInfo CreateValueFactory(TypeConversionKey k)
+            {
+                var (source, target) = k;
+                foreach (var m in source.GetOperatorOverloadMethods().Concat(target.GetOperatorOverloadMethods()))
+                {
+                    if (!target.IsAssignableFrom(m.ReturnType) || m.Name is not ("op_Implicit" or "op_Explicit"))
+                    {
+                        continue;
+                    }
+
+                    var parameters = m.GetParameters();
+                    if (parameters.Length != 1 || !parameters[0].ParameterType.IsAssignableFrom(source))
+                    {
+                        continue;
+                    }
+
+                    // we found a match
+                    return m;
+                }
+
+                return null;
+            }
+
+            var castOperator = _knownCastOperators.GetOrAdd(key, CreateValueFactory);
+
+            if (castOperator != null)
+            {
+                try
+                {
+                    converted = castOperator.Invoke(null, new[] { value });
+                    return true;
+                }
+                catch
+                {
+                    converted = null;
+                    return false;
+                }
+            }
+
+            converted = null;
+            return false;
+        }
+
         public virtual bool TryConvert(object value, Type type, IFormatProvider formatProvider, out object converted)
         {
             var key = new TypeConversionKey(value?.GetType(), type);

+ 4 - 7
Jint/Runtime/TypeConverter.cs

@@ -1197,15 +1197,12 @@ namespace Jint.Runtime
                 return 1;
             }
 
-            if (engine.Options.Interop.AllowOperatorOverloading)
+            foreach (var m in objectValueType.GetOperatorOverloadMethods())
             {
-                foreach (var m in objectValueType.GetOperatorOverloadMethods())
+                if (paramType.IsAssignableFrom(m.ReturnType) && m.Name is "op_Implicit" or "op_Explicit")
                 {
-                    if (paramType.IsAssignableFrom(m.ReturnType) && m.Name is "op_Implicit" or "op_Explicit")
-                    {
-                        // implicit/explicit operator conversion is OK, but not ideal
-                        return 1;
-                    }
+                    // implicit/explicit operator conversion is OK, but not ideal
+                    return 1;
                 }
             }