Просмотр исходного кода

Interface type wrapper can call object methods. (#1629)

Fix #1626
viruscamp 1 год назад
Родитель
Сommit
d48ebd50ba
2 измененных файлов с 72 добавлено и 5 удалено
  1. 57 3
      Jint.Tests/Runtime/InteropExplicitTypeTests.cs
  2. 15 2
      Jint/Runtime/Interop/TypeResolver.cs

+ 57 - 3
Jint.Tests/Runtime/InteropExplicitTypeTests.cs

@@ -1,5 +1,6 @@
 namespace Jint.Tests.Runtime;
 
+using Jint.Native;
 using Jint.Runtime.Interop;
 
 public class InteropExplicitTypeTests
@@ -154,9 +155,9 @@ public class InteropExplicitTypeTests
         }
         public string name = "NullabeStruct";
 
-        public string Name { get => name; }
+        public string Name => name;
 
-        string I1.Name { get => "NullabeStruct as I1"; }
+        string I1.Name => "NullabeStruct as I1";
     }
 
     public class NullableHolder
@@ -172,7 +173,7 @@ public class InteropExplicitTypeTests
         _engine.SetValue("nullableHolder", nullableHolder);
         _engine.SetValue("nullabeStruct", new NullabeStruct());
 
-        Assert.Equal(_engine.Evaluate("nullableHolder.NullabeStruct"), Native.JsValue.Null);
+        Assert.Equal(_engine.Evaluate("nullableHolder.NullabeStruct"), JsValue.Null);
         _engine.Evaluate("nullableHolder.NullabeStruct = nullabeStruct");
         Assert.Equal(_engine.Evaluate("nullableHolder.NullabeStruct.Name"), nullableHolder.NullabeStruct?.Name);
     }
@@ -288,4 +289,57 @@ public class InteropExplicitTypeTests
         });
         Assert.Equal("Invalid when Engine.Options.Interop.AllowGetType == false", ex.Message);
     }
+
+    public interface ICallObjectMethodFromInterface
+    {
+        ICallObjectMethodFromInterface Instance { get; }
+        // hide Object.GetHashCode
+        string GetHashCode();
+        // overload Object.Equals
+        string Equals();
+    }
+    public class CallObjectMethodFromInterface : ICallObjectMethodFromInterface
+    {
+        public ICallObjectMethodFromInterface Instance => this;
+        public override string ToString() => nameof(CallObjectMethodFromInterface);
+        public new string GetHashCode() => "new GetHashCode, hide Object.GetHashCode";
+        public string Equals() => "overload Object.Equals";
+    }
+
+    // issue#1626 ToString method is now unavailable in some CLR Interop scenarios
+    [Fact]
+    public void CallObjectMethodFromInterfaceWrapper()
+    {
+        var inst = new CallObjectMethodFromInterface();
+        _engine.SetValue("inst", inst);
+        Assert.Equal(inst.Instance.ToString(), _engine.Evaluate("inst.Instance.ToString()"));
+    }
+
+    [Fact]
+    public void CallInterfaceMethodWhichHideObjectMethod()
+    {
+        var inst = new CallObjectMethodFromInterface();
+        _engine.SetValue("inst", inst);
+        Assert.Equal(inst.Instance.GetHashCode(), _engine.Evaluate("inst.Instance.GetHashCode()"));
+    }
+
+    [Fact(Skip = "TODO, no solution now.")]
+    public void CallObjectMethodHiddenByInterface()
+    {
+        var inst = new CallObjectMethodFromInterface();
+        _engine.SetValue("inst", inst);
+        Assert.Equal(
+            (inst.Instance as object).GetHashCode(),
+            _engine.Evaluate("clrHelper.unwrap(inst.Instance).GetHashCode()")
+        );
+    }
+
+    [Fact]
+    public void CallInterfaceMethodWhichOverloadObjectMethod()
+    {
+        var inst = new CallObjectMethodFromInterface();
+        _engine.SetValue("inst", inst);
+        Assert.Equal(inst.Instance.Equals(), _engine.Evaluate("inst.Instance.Equals()"));
+        Assert.Equal(inst.Instance.Equals(inst), _engine.Evaluate("inst.Instance.Equals(inst)"));
+    }
 }

+ 15 - 2
Jint/Runtime/Interop/TypeResolver.cs

@@ -267,11 +267,11 @@ namespace Jint.Runtime.Interop
 
             // if no properties were found then look for a method
             List<MethodInfo>? methods = null;
-            foreach (var m in type.GetMethods(bindingFlags))
+            void AddMethod(MethodInfo m)
             {
                 if (!Filter(engine, m))
                 {
-                    continue;
+                    return;
                 }
 
                 foreach (var name in typeResolverMemberNameCreator(m))
@@ -283,6 +283,10 @@ namespace Jint.Runtime.Interop
                     }
                 }
             }
+            foreach (var m in type.GetMethods(bindingFlags))
+            {
+                AddMethod(m);
+            }
 
             // TPC: need to grab the extension methods here - for overloads
             if (engine._extensionMethods.TryGetExtensionMethods(type, out var extensionMethods))
@@ -297,6 +301,15 @@ namespace Jint.Runtime.Interop
                 }
             }
 
+            // Add Object methods to interface
+            if (type.IsInterface)
+            {
+                foreach (var m in typeof(object).GetMethods(bindingFlags))
+                {
+                    AddMethod(m);
+                }
+            }
+
             if (methods?.Count > 0)
             {
                 accessor = new MethodAccessor(type, memberName, MethodDescriptor.Build(methods));