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 ko.observable handling in Linq translations #1870

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ public GenericMethodCompiler(Func<JsExpression[], Expression[], JsExpression> bu
: builder(new[] { t?.JsExpression()! }.Concat(arg.Select(a => a.JsExpression())).ToArray(), arg.Select(a => a.OriginalExpression).ToArray());
}

public GenericMethodCompiler(Func<JsExpression[], Expression[], MethodInfo, JsExpression> builder, Func<MethodInfo, Expression?, Expression[], bool>? check = null)
{
TryTranslateDelegate =
(t, arg, m) => check?.Invoke(m, t?.OriginalExpression, arg.Select(a => a.OriginalExpression).ToArray()) == false
? null
: builder(new[] { t?.JsExpression()! }.Concat(arg.Select(a => a.JsExpression())).ToArray(), arg.Select(a => a.OriginalExpression).ToArray(), m);
}

public GenericMethodCompiler(Func<JsExpression[], MethodInfo, JsExpression> builder, Func<MethodInfo, Expression?, Expression[], bool>? check = null)
{
TryTranslateDelegate =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ public void AddPropertyGetterTranslator(Type declaringType, string propName, IJa
}

public static JsExpression BuildIndexer(JsExpression target, JsExpression index, [AllowNull] MemberInfo member) =>
target.Indexer(index).WithAnnotation(new VMPropertyInfoAnnotation(member.NotNull()));
target.Indexer(index).WithAnnotation(new VMPropertyInfoAnnotation(member.NotNull(), objectPath: ImmutableArray.Create("Item")!));

public void AddDefaultMethodTranslators()
{
Expand All @@ -192,7 +192,7 @@ JsExpression listSetIndexer(JsExpression[] args, MethodInfo method) =>
JsExpression arrayElementSetter(JsExpression[] args, MethodInfo method) =>
new JsIdentifierExpression("dotvvm").Member("translations").Member("array").Member("setItem").Invoke(args[0].WithAnnotation(ShouldBeObservableAnnotation.Instance), args[2], args[1]);
JsExpression dictionaryGetIndexer(JsExpression[] args, MethodInfo method) =>
new JsIdentifierExpression("dotvvm").Member("translations").Member("dictionary").Member("getItem").Invoke(args[0], args[1]);
new JsIdentifierExpression("dotvvm").Member("translations").Member("dictionary").Member("getItem").Invoke(args[0], args[1]).WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.FirstArgumentMethodTargetPath, isObservable: false));
JsExpression dictionarySetIndexer(JsExpression[] args, MethodInfo method) =>
new JsIdentifierExpression("dotvvm").Member("translations").Member("dictionary").Member("setItem").Invoke(args[0].WithAnnotation(ShouldBeObservableAnnotation.Instance), args[1], args[2]);

Expand Down Expand Up @@ -550,9 +550,13 @@ string GetDelegateReturnTypeHash(Type type)
var anyPred = new GenericMethodCompiler(args => args[1].Member("some").Invoke(args[2]));
AddMethodTranslator(() => Enumerable.Any(Enumerable.Empty<Generic.T>(), _ => false), anyPred);
AddMethodTranslator(() => ImmutableArrayExtensions.Any(default(ImmutableArray<Generic.T>), _ => false), anyPred);
AddMethodTranslator(() => Enumerable.Concat(Enumerable.Empty<Generic.T>(), Enumerable.Empty<Generic.T>()), new GenericMethodCompiler(args => args[1].Member("concat").Invoke(args[2])));
AddMethodTranslator(() => Enumerable.Concat(Enumerable.Empty<Generic.T>(), Enumerable.Empty<Generic.T>()), new GenericMethodCompiler((args, method) =>
args[1].Member("concat").Invoke(args[2])
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.InstanceMethodTargetPath, isObservable: false, objectPath: ImmutableArray<string?>.Empty))));
AddMethodTranslator(() => Enumerable.Count(Enumerable.Empty<Generic.T>()), new GenericMethodCompiler(args => args[1].Member("length")));
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().Distinct(), new GenericMethodCompiler(args => new JsIdentifierExpression("dotvvm").Member("translations").Member("array").Member("distinct").Invoke(args[1]),
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().Distinct(), new GenericMethodCompiler((args, method) =>
new JsIdentifierExpression("dotvvm").Member("translations").Member("array").Member("distinct").Invoke(args[1])
.WithAnnotation(new ViewModelInfoAnnotation(method.ReturnType, containsObservables: false)), // distinct unwraps all observables, and only supports primitives anyway
check: (method, target, arguments) => EnsureIsComparableInJavascript(method, ReflectionUtils.GetEnumerableType(arguments.First().Type).NotNull())));

AddMethodTranslator(() => Enumerable.Empty<Generic.T>().ElementAt(0),
Expand All @@ -564,43 +568,59 @@ string GetDelegateReturnTypeHash(Type type)
AddMethodTranslator(() => ImmutableArrayExtensions.ElementAtOrDefault(default(ImmutableArray<Generic.T>), 0),
new GenericMethodCompiler((args, method) => BuildIndexer(args[1], args[2], method)));

var firstOrDefault = new GenericMethodCompiler((args, m) => BuildIndexer(args[1], new JsLiteral(0), m).WithAnnotation(MayBeNullAnnotation.Instance));
var firstOrDefault = new GenericMethodCompiler((args, m) => BuildIndexer(args[1], new JsLiteral(0), m));
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().FirstOrDefault(), firstOrDefault);
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().First(), firstOrDefault);
AddMethodTranslator(() => ImmutableArrayExtensions.FirstOrDefault(default(ImmutableArray<Generic.T>)), firstOrDefault);
AddMethodTranslator(() => ImmutableArrayExtensions.First(default(ImmutableArray<Generic.T>)), firstOrDefault);

var firstOrDefaultPred = new GenericMethodCompiler(args =>
args[1].Member("find").Invoke(args[2]).WithAnnotation(MayBeNullAnnotation.Instance));
var firstOrDefaultPred = new GenericMethodCompiler((args, method) =>
args[1].Member("find").Invoke(args[2])
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.InstanceMethodTargetPath, objectPath: ImmutableArray.Create("Item")!)));
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().FirstOrDefault(_ => true), firstOrDefaultPred);
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().First(_ => true), firstOrDefaultPred);
AddMethodTranslator(() => ImmutableArrayExtensions.FirstOrDefault(default(ImmutableArray<Generic.T>), _ => true), firstOrDefaultPred);
AddMethodTranslator(() => ImmutableArrayExtensions.First(default(ImmutableArray<Generic.T>), _ => true), firstOrDefaultPred);

var lastOrDefault = new GenericMethodCompiler(args => args[1].Member("at").Invoke(new JsLiteral(-1)).WithAnnotation(MayBeNullAnnotation.Instance));
var lastOrDefault = new GenericMethodCompiler((args, method) =>
args[1].Member("at").Invoke(new JsLiteral(-1))
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.InstanceMethodTargetPath, objectPath: ImmutableArray.Create("Item")!)));
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().LastOrDefault(), lastOrDefault);
AddMethodTranslator(() => ImmutableArrayExtensions.LastOrDefault(default(ImmutableArray<Generic.T>)), lastOrDefault);
var lastOrDefaultPred = new GenericMethodCompiler(args =>
args[1].Member("findLast").Invoke(args[2]).WithAnnotation(MayBeNullAnnotation.Instance));
var lastOrDefaultPred = new GenericMethodCompiler((args, method) =>
args[1].Member("findLast").Invoke(args[2])
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.InstanceMethodTargetPath, objectPath: ImmutableArray.Create("Item")!)));
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().LastOrDefault(_ => false), lastOrDefaultPred);
AddMethodTranslator(() => ImmutableArrayExtensions.LastOrDefault(default(ImmutableArray<Generic.T>), _ => false), lastOrDefaultPred);

AddMethodTranslator(() => Enumerable.Empty<Generic.T>().OrderBy(_ => Generic.Enum.Something), new GenericMethodCompiler((jArgs, dArgs) => new JsIdentifierExpression("dotvvm").Member("translations").Member("array").Member("orderBy")
.Invoke(jArgs[1], jArgs[2], new JsLiteral((IsDelegateReturnTypeEnum(dArgs.Last().Type)) ? GetDelegateReturnTypeHash(dArgs.Last().Type) : null)),
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().OrderBy(_ => Generic.Enum.Something), new GenericMethodCompiler((jArgs, dArgs, method) => new JsIdentifierExpression("dotvvm").Member("translations").Member("array").Member("orderBy")
.Invoke(jArgs[1], jArgs[2], new JsLiteral((IsDelegateReturnTypeEnum(dArgs.Last().Type)) ? GetDelegateReturnTypeHash(dArgs.Last().Type) : null))
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.FirstArgumentMethodTargetPath, isObservable: false, objectPath: ImmutableArray<string?>.Empty)),
check: (method, _, arguments) => EnsureIsComparableInJavascript(method, arguments.Last().Type.GetGenericArguments().Last())));
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().OrderByDescending(_ => Generic.Enum.Something), new GenericMethodCompiler((jArgs, dArgs) => new JsIdentifierExpression("dotvvm").Member("translations").Member("array").Member("orderByDesc")
.Invoke(jArgs[1], jArgs[2], new JsLiteral((IsDelegateReturnTypeEnum(dArgs.Last().Type)) ? GetDelegateReturnTypeHash(dArgs.Last().Type) : null)),
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().OrderByDescending(_ => Generic.Enum.Something), new GenericMethodCompiler((jArgs, dArgs, method) => new JsIdentifierExpression("dotvvm").Member("translations").Member("array").Member("orderByDesc")
.Invoke(jArgs[1], jArgs[2], new JsLiteral((IsDelegateReturnTypeEnum(dArgs.Last().Type)) ? GetDelegateReturnTypeHash(dArgs.Last().Type) : null))
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.FirstArgumentMethodTargetPath, isObservable: false, objectPath: ImmutableArray<string?>.Empty)),
check: (method, _, arguments) => EnsureIsComparableInJavascript(method, arguments.Last().Type.GetGenericArguments().Last())));

var select = new GenericMethodCompiler(args => args[1].Member("map").Invoke(args[2]));
// the lambda function will not return observable, but nested properties will again be observable
var select = new GenericMethodCompiler((args, method) =>
args[1].Member("map").Invoke(args[2])
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.InstanceMethodTargetPath, isObservable: false))
.WithAnnotation(new ViewModelInfoAnnotation(method.ReturnType, false, null, new JsObjectObservableMap { ContainsObservables = false, DefaultChild = JsObjectObservableMap.Default }))
);
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().Select(_ => Generic.Enum.Something), select);
AddMethodTranslator(() => ImmutableArrayExtensions.Select(default(ImmutableArray<Generic.T>), _ => Generic.Enum.Something), select);
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().Skip(0), new GenericMethodCompiler(args => args[1].Member("slice").Invoke(args[2])));
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().Skip(0), new GenericMethodCompiler((args, method) =>
args[1].Member("slice").Invoke(args[2])
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.InstanceMethodTargetPath, isObservable: false, objectPath: ImmutableArray<string?>.Empty))));

AddMethodTranslator(() => Enumerable.Empty<Generic.T>().Take(0), new GenericMethodCompiler(args =>
args[1].Member("slice").Invoke(new JsLiteral(0), args[2])));
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().Take(0), new GenericMethodCompiler((args, method) =>
args[1].Member("slice").Invoke(new JsLiteral(0), args[2])
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.InstanceMethodTargetPath, isObservable: false, objectPath: ImmutableArray<string?>.Empty))));

var where = new GenericMethodCompiler(args => args[1].Member("filter").Invoke(args[2]));
var where = new GenericMethodCompiler((args, method) =>
args[1].Member("filter").Invoke(args[2])
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.InstanceMethodTargetPath, isObservable: false, objectPath: ImmutableArray<string?>.Empty)));
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().Where(_ => true), where);
AddMethodTranslator(() => ImmutableArrayExtensions.Where(default(ImmutableArray<Generic.T>), _ => true), where);

Expand Down Expand Up @@ -698,7 +718,8 @@ private void AddDefaultDictionaryTranslations()
var defaultValue =
args.Length > 3 ? args[3] :
new JsLiteral(ReflectionUtils.GetDefaultValue(method.GetGenericArguments().Last()));
return new JsIdentifierExpression("dotvvm").Member("translations").Member("dictionary").Member("getItem").Invoke(args[1], args[2], defaultValue);
return new JsIdentifierExpression("dotvvm").Member("translations").Member("dictionary").Member("getItem").Invoke(args[1], args[2], defaultValue)
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.FirstArgumentMethodTargetPath, isObservable: false));
});
#if DotNetCore
AddMethodTranslator(() => default(IReadOnlyDictionary<Generic.T, Generic.T>)!.GetValueOrDefault(null!), getValueOrDefault);
Expand Down
Loading
Loading