Skip to content

Commit 29f63ee

Browse files
committed
add support for interface extension methods
1 parent 1a703b9 commit 29f63ee

File tree

3 files changed

+47
-13
lines changed

3 files changed

+47
-13
lines changed

src/Jokenizer.Net/ExtensionMethods.cs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,24 +31,40 @@ public static IList<MethodInfo> ScanType(Type type) {
3131
return methods;
3232
}
3333

34-
internal static MethodInfo? Find(Type forType, string name, Expression?[] args) {
34+
internal static (MethodInfo, ParameterInfo[])? Find(Type forType, string name, Expression?[] args) {
3535
var genericArgs = forType.IsConstructedGenericType ? forType.GetGenericArguments() : [];
3636

3737
foreach (var extension in _extensions.Where(e => e.Name == name)) {
3838
var m = extension;
39+
40+
var prmType = m.GetParameters()[0].ParameterType;
41+
if (prmType.IsConstructedGenericType) {
42+
prmType = prmType.GetGenericTypeDefinition();
43+
}
44+
if (!prmType.IsAssignableFrom(forType)) {
45+
if (!prmType.IsGenericType) continue;
46+
47+
if (!forType.IsConstructedGenericType || forType.GetGenericTypeDefinition() != prmType) {
48+
var interfaces = forType.GetInterfaces();
49+
var forGeneric = interfaces
50+
.FirstOrDefault(i => i == prmType || (i.IsGenericType && i.GetGenericTypeDefinition() == prmType));
51+
52+
if (forGeneric == null) continue;
53+
54+
genericArgs = forGeneric.GetGenericArguments();
55+
}
56+
}
57+
3958
if (m.IsGenericMethodDefinition) {
4059
if (m.GetGenericArguments().Length != genericArgs.Length) continue;
4160

4261
m = m.MakeGenericMethod(genericArgs);
4362
}
4463

45-
var allPrms = m.GetParameters();
46-
if (!allPrms[0].ParameterType.IsAssignableFrom(forType)) continue;
47-
48-
var prms = allPrms.Skip(1).ToArray();
64+
var prms = m.GetParameters().Skip(1).ToArray();
4965
if (!Helper.IsSuitable(prms, args)) continue;
5066

51-
return m;
67+
return (m, prms);
5268
}
5369

5470
return null;

src/Jokenizer.Net/TokenVisitor.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,15 +218,19 @@ protected MethodCallExpression GetMethodCall(Expression owner, string methodName
218218
.FirstOrDefault(m => m.Name == methodName && Helper.IsSuitable(m.GetParameters(), methodArgs));
219219
}
220220

221+
ParameterInfo[] methodPrms;
221222
if (method == null) {
222223
isExtension = true;
223-
method = ExtensionMethods.Find(owner.Type, methodName, methodArgs);
224-
}
224+
var extResult = ExtensionMethods.Find(owner.Type, methodName, methodArgs);
225+
if (extResult == null)
226+
throw new InvalidTokenException($"Could not find instance or extension method for {methodName} for {owner.Type}");
225227

226-
if (method == null)
227-
throw new InvalidTokenException($"Could not find instance or extension method for {methodName} for {owner.Type}");
228+
(method, methodPrms) = extResult.Value;
229+
}
230+
else {
231+
methodPrms = method.GetParameters();
232+
}
228233

229-
var methodPrms = method.GetParameters();
230234
// we might need to cast to target types
231235
for (var i = 0; i < methodArgs.Length; i++) {
232236
var prm = methodPrms[i];

test/Jokenizer.Net.Tests/EvaluatorTests.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,7 @@ public void ShouldThrowForUnknownToken() {
264264
[Fact]
265265
public void ShouldThrowForInvalidToken() {
266266
Assert.Throws<InvalidTokenException>(() => Evaluator.ToLambda<string>(new AssignToken("Name", new LiteralToken("Netflix"))));
267-
Assert.Throws<InvalidTokenException>(() => Evaluator.ToLambda<string>(new GroupToken([new LiteralToken("Netflix"), new LiteralToken("Google")
268-
])));
267+
Assert.Throws<InvalidTokenException>(() => Evaluator.ToLambda<string>(new GroupToken([new LiteralToken("Netflix"), new LiteralToken("Google")])));
269268
Assert.Throws<InvalidTokenException>(() => Evaluator.ToLambda<string>("a < b => b*2"));
270269
}
271270

@@ -362,4 +361,19 @@ public void SettingTests() {
362361
Assert.True(settings.ContainsUnary('!'));
363362
Assert.True(settings.ContainsBinary("%"));
364363
}
364+
365+
[Fact]
366+
public void ShouldHandleEnumerableParameter() {
367+
var source = new[] { 1, 2, 3, 4, 5 };
368+
var sample = new[] { 2, 4 };
369+
370+
var f = Evaluator.ToFunc<IEnumerable<int>>(
371+
"source.Where(x => @0.Contains(x))",
372+
new Dictionary<string, object?> { { "source", source } },
373+
sample
374+
);
375+
376+
var result = f();
377+
Assert.Equal([2, 4], result);
378+
}
365379
}

0 commit comments

Comments
 (0)