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

Fix overloaded constructor selection under interop #1543

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
45 changes: 0 additions & 45 deletions Jint.Tests/Runtime/ConstructorSignature.cs

This file was deleted.

69 changes: 69 additions & 0 deletions Jint.Tests/Runtime/ConstructorSignatureTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.Globalization;
using Jint.Runtime.Interop;

namespace Jint.Tests.Runtime;

public class ConstructorSignature
{
[Fact]
public void OptionalConstructorParametersShouldBeSupported()
{
var engine = new Engine();

engine.SetValue("A", TypeReference.CreateTypeReference(engine, typeof(A)));

// ParamArray tests
Assert.Equal("3", engine.Evaluate("new A(1, 2).Result").AsString());
Assert.Equal("3", engine.Evaluate("new A(1, 2, null).Result").AsString());
Assert.Equal("3", engine.Evaluate("new A(1, 2, undefined).Result").AsString());
Assert.Equal("5", engine.Evaluate("new A(1, 2, null, undefined).Result").AsString());
Assert.Equal("9", engine.Evaluate("new A(1, 2, ...'packed').Result").AsString());
Assert.Equal("3", engine.Evaluate("new A(1, 2, []).Result").AsString());
Assert.Equal("7", engine.Evaluate("new A(1, 2, [...'abcd']).Result").AsString());

// Optional parameter tests
Assert.Equal("3", engine.Evaluate("new A(1, 2).Result").AsString());
Assert.Equal("6", engine.Evaluate("new A(1).Result").AsString());
Assert.Equal("7", engine.Evaluate("new A(2, undefined).Result").AsString());
Assert.Equal("8", engine.Evaluate("new A(3, undefined).Result").AsString());
Assert.Equal("ab", engine.Evaluate("new A('a').Result").AsString());
Assert.Equal("ab", engine.Evaluate("new A('a', undefined).Result").AsString());
Assert.Equal("ac", engine.Evaluate("new A('a', 'c').Result").AsString());
Assert.Equal("adc", engine.Evaluate("new A('a', 'd', undefined).Result").AsString());
Assert.Equal("ade", engine.Evaluate("new A('a', 'd', 'e').Result").AsString());
}

[Fact]
public void CorrectOverloadShouldBeSelected()
{
var engine = new Engine();
engine.SetValue("B", typeof(B));

Assert.Equal("A-30", engine.Evaluate("new B('A', 30).Result"));
}

private class A
{
public A(int param1, int param2 = 5) => Result = (param1 + param2).ToString();
public A(string param1, string param2 = "b") => Result = string.Concat(param1, param2);
public A(string param1, string param2 = "b", string param3 = "c") => Result = string.Concat(param1, param2, param3);
public A(int param1, int param2, params object[] param3) => Result = (param1 + param2 + param3?.Length).ToString();

public string Result { get; }
}

private class B
{
public B(string param1, float param2, string param3)
{
Result = string.Join("-", param1, param2.ToString(CultureInfo.InvariantCulture), param3);
}

public B(string param1, float param2)
{
Result = string.Join("-", param1, param2.ToString(CultureInfo.InvariantCulture));
}

public string Result { get;}
}
}
18 changes: 15 additions & 3 deletions Jint/Runtime/Interop/TypeReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,26 @@ static ObjectInstance ObjectCreator(Engine engine, Realm realm, ObjectCreateStat
return newArguments;
}
// TODO: edge case, last parameter is ParamArray with optional parameter before?
else if (isParamArray && arguments.Length < parameters.Length - 1)
if (isParamArray && arguments.Length < parameters.Length - 1)
{
return arguments;
}
// optional parameters
else if (parameters.Length >= arguments.Length)
if (parameters.Length >= arguments.Length)
{
// all missing ones must be optional
foreach (var parameter in parameters.AsSpan(parameters.Length - arguments.Length + 1))
{
if (!parameter.IsOptional)
{
// use original arguments
return arguments;
}
}
Array.Copy(arguments, 0, newArguments, 0, arguments.Length);
for (var i = parameters.Length - 1; i >= 0; i--)
Expand All @@ -146,7 +158,7 @@ static ObjectInstance ObjectCreator(Engine engine, Realm realm, ObjectCreateStat
if (arguments.Length - 1 < i || arguments[i].IsUndefined())
{
newArguments[i] = JsValue.FromObject(engine, currentParameter.DefaultValue);
newArguments[i] = FromObject(engine, currentParameter.DefaultValue);
}
}
}
Expand Down