Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interface type wrapper can call object methods. #1629

Merged
merged 1 commit into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 57 additions & 3 deletions Jint.Tests/Runtime/InteropExplicitTypeTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace Jint.Tests.Runtime;

using Jint.Native;
using Jint.Runtime.Interop;

public class InteropExplicitTypeTests
Expand Down Expand Up @@ -154,9 +155,9 @@ public NullabeStruct()
}
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
Expand All @@ -172,7 +173,7 @@ public void TypedObjectWrapperForNullableType()
_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);
}
Expand Down Expand Up @@ -288,4 +289,57 @@ public void ClrHelperObjectToType()
});
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)"));
}
}
17 changes: 15 additions & 2 deletions Jint/Runtime/Interop/TypeResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,11 +267,11 @@ internal bool TryFindMemberAccessor(

// 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))
Expand All @@ -283,6 +283,10 @@ internal bool TryFindMemberAccessor(
}
}
}
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))
Expand All @@ -297,6 +301,15 @@ internal bool TryFindMemberAccessor(
}
}

// 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));
Expand Down