Skip to content

Commit 06964d7

Browse files
authored
Merge pull request #1870 from riganti/fix-linq-translations-observables
Fix ko.observable handling in Linq translations
2 parents 86a56fc + 79963b5 commit 06964d7

File tree

6 files changed

+397
-59
lines changed

6 files changed

+397
-59
lines changed

src/Framework/Framework/Compilation/Javascript/GenericMethodCompiler.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ public GenericMethodCompiler(Func<JsExpression[], Expression[], JsExpression> bu
2929
: builder(new[] { t?.JsExpression()! }.Concat(arg.Select(a => a.JsExpression())).ToArray(), arg.Select(a => a.OriginalExpression).ToArray());
3030
}
3131

32+
public GenericMethodCompiler(Func<JsExpression[], Expression[], MethodInfo, JsExpression> builder, Func<MethodInfo, Expression?, Expression[], bool>? check = null)
33+
{
34+
TryTranslateDelegate =
35+
(t, arg, m) => check?.Invoke(m, t?.OriginalExpression, arg.Select(a => a.OriginalExpression).ToArray()) == false
36+
? null
37+
: builder(new[] { t?.JsExpression()! }.Concat(arg.Select(a => a.JsExpression())).ToArray(), arg.Select(a => a.OriginalExpression).ToArray(), m);
38+
}
39+
3240
public GenericMethodCompiler(Func<JsExpression[], MethodInfo, JsExpression> builder, Func<MethodInfo, Expression?, Expression[], bool>? check = null)
3341
{
3442
TryTranslateDelegate =

src/Framework/Framework/Compilation/Javascript/JavascriptTranslatableMethodCollection.cs

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ public void AddPropertyGetterTranslator(Type declaringType, string propName, IJa
167167
}
168168

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

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

@@ -552,57 +552,77 @@ string GetDelegateReturnTypeHash(Type type)
552552
var anyPred = new GenericMethodCompiler(args => args[1].Member("some").Invoke(args[2]));
553553
AddMethodTranslator(() => Enumerable.Any(Enumerable.Empty<Generic.T>(), _ => false), anyPred);
554554
AddMethodTranslator(() => ImmutableArrayExtensions.Any(default(ImmutableArray<Generic.T>), _ => false), anyPred);
555-
AddMethodTranslator(() => Enumerable.Concat(Enumerable.Empty<Generic.T>(), Enumerable.Empty<Generic.T>()), new GenericMethodCompiler(args => args[1].Member("concat").Invoke(args[2])));
555+
AddMethodTranslator(() => Enumerable.Concat(Enumerable.Empty<Generic.T>(), Enumerable.Empty<Generic.T>()), new GenericMethodCompiler((args, method) =>
556+
args[1].Member("concat").Invoke(args[2])
557+
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.InstanceMethodTargetPath, isObservable: false, objectPath: ImmutableArray<string?>.Empty))));
556558
AddMethodTranslator(() => Enumerable.Count(Enumerable.Empty<Generic.T>()), new GenericMethodCompiler(args => args[1].Member("length")));
557-
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().Distinct(), new GenericMethodCompiler(args => new JsIdentifierExpression("dotvvm").Member("translations").Member("array").Member("distinct").Invoke(args[1]),
559+
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().Distinct(), new GenericMethodCompiler((args, method) =>
560+
new JsIdentifierExpression("dotvvm").Member("translations").Member("array").Member("distinct").Invoke(args[1])
561+
.WithAnnotation(new ViewModelInfoAnnotation(method.ReturnType, containsObservables: false)), // distinct unwraps all observables, and only supports primitives anyway
558562
check: (method, target, arguments) => EnsureIsComparableInJavascript(method, ReflectionUtils.GetEnumerableType(arguments.First().Type).NotNull())));
559563

560564
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().ElementAt(0),
561-
new GenericMethodCompiler((args, method) => BuildIndexer(args[1], args[2], method).WithAnnotation(ResultMayBeObservableAnnotation.Instance)));
565+
new GenericMethodCompiler((args, method) => BuildIndexer(args[1], args[2], method)));
562566
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().ElementAtOrDefault(0),
563-
new GenericMethodCompiler((args, method) => BuildIndexer(args[1], args[2], method).WithAnnotation(ResultMayBeObservableAnnotation.Instance)));
567+
new GenericMethodCompiler((args, method) => BuildIndexer(args[1], args[2], method)));
564568
AddMethodTranslator(() => ImmutableArrayExtensions.ElementAt(default(ImmutableArray<Generic.T>), 0),
565-
new GenericMethodCompiler((args, method) => BuildIndexer(args[1], args[2], method).WithAnnotation(ResultMayBeObservableAnnotation.Instance)));
569+
new GenericMethodCompiler((args, method) => BuildIndexer(args[1], args[2], method)));
566570
AddMethodTranslator(() => ImmutableArrayExtensions.ElementAtOrDefault(default(ImmutableArray<Generic.T>), 0),
567-
new GenericMethodCompiler((args, method) => BuildIndexer(args[1], args[2], method).WithAnnotation(ResultMayBeObservableAnnotation.Instance)));
571+
new GenericMethodCompiler((args, method) => BuildIndexer(args[1], args[2], method)));
568572

569-
var firstOrDefault = new GenericMethodCompiler((args, m) => BuildIndexer(args[1], new JsLiteral(0), m).WithAnnotation(MayBeNullAnnotation.Instance).WithAnnotation(ResultMayBeObservableAnnotation.Instance));
573+
var firstOrDefault = new GenericMethodCompiler((args, m) => BuildIndexer(args[1], new JsLiteral(0), m));
570574
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().FirstOrDefault(), firstOrDefault);
571575
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().First(), firstOrDefault);
572576
AddMethodTranslator(() => ImmutableArrayExtensions.FirstOrDefault(default(ImmutableArray<Generic.T>)), firstOrDefault);
573577
AddMethodTranslator(() => ImmutableArrayExtensions.First(default(ImmutableArray<Generic.T>)), firstOrDefault);
574578

575-
var firstOrDefaultPred = new GenericMethodCompiler(args =>
576-
args[1].Member("find").Invoke(args[2]).WithAnnotation(MayBeNullAnnotation.Instance).WithAnnotation(ResultMayBeObservableAnnotation.Instance));
579+
var firstOrDefaultPred = new GenericMethodCompiler((args, method) =>
580+
args[1].Member("find").Invoke(args[2])
581+
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.InstanceMethodTargetPath, objectPath: ImmutableArray.Create("Item")!)));
577582
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().FirstOrDefault(_ => true), firstOrDefaultPred);
578583
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().First(_ => true), firstOrDefaultPred);
579584
AddMethodTranslator(() => ImmutableArrayExtensions.FirstOrDefault(default(ImmutableArray<Generic.T>), _ => true), firstOrDefaultPred);
580585
AddMethodTranslator(() => ImmutableArrayExtensions.First(default(ImmutableArray<Generic.T>), _ => true), firstOrDefaultPred);
581586

582-
var lastOrDefault = new GenericMethodCompiler(args => args[1].Member("at").Invoke(new JsLiteral(-1)).WithAnnotation(MayBeNullAnnotation.Instance).WithAnnotation(ResultMayBeObservableAnnotation.Instance));
587+
var lastOrDefault = new GenericMethodCompiler((args, method) =>
588+
args[1].Member("at").Invoke(new JsLiteral(-1))
589+
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.InstanceMethodTargetPath, objectPath: ImmutableArray.Create("Item")!)));
583590
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().LastOrDefault(), lastOrDefault);
584591
AddMethodTranslator(() => ImmutableArrayExtensions.LastOrDefault(default(ImmutableArray<Generic.T>)), lastOrDefault);
585-
var lastOrDefaultPred = new GenericMethodCompiler(args =>
586-
args[1].Member("findLast").Invoke(args[2]).WithAnnotation(MayBeNullAnnotation.Instance).WithAnnotation(ResultMayBeObservableAnnotation.Instance));
592+
var lastOrDefaultPred = new GenericMethodCompiler((args, method) =>
593+
args[1].Member("findLast").Invoke(args[2])
594+
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.InstanceMethodTargetPath, objectPath: ImmutableArray.Create("Item")!)));
587595
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().LastOrDefault(_ => false), lastOrDefaultPred);
588596
AddMethodTranslator(() => ImmutableArrayExtensions.LastOrDefault(default(ImmutableArray<Generic.T>), _ => false), lastOrDefaultPred);
589597

590-
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().OrderBy(_ => Generic.Enum.Something), new GenericMethodCompiler((jArgs, dArgs) => new JsIdentifierExpression("dotvvm").Member("translations").Member("array").Member("orderBy")
591-
.Invoke(jArgs[1], jArgs[2], new JsLiteral((IsDelegateReturnTypeEnum(dArgs.Last().Type)) ? GetDelegateReturnTypeHash(dArgs.Last().Type) : null)),
598+
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().OrderBy(_ => Generic.Enum.Something), new GenericMethodCompiler((jArgs, dArgs, method) => new JsIdentifierExpression("dotvvm").Member("translations").Member("array").Member("orderBy")
599+
.Invoke(jArgs[1], jArgs[2], new JsLiteral((IsDelegateReturnTypeEnum(dArgs.Last().Type)) ? GetDelegateReturnTypeHash(dArgs.Last().Type) : null))
600+
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.FirstArgumentMethodTargetPath, isObservable: false, objectPath: ImmutableArray<string?>.Empty)),
592601
check: (method, _, arguments) => EnsureIsComparableInJavascript(method, arguments.Last().Type.GetGenericArguments().Last())));
593-
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().OrderByDescending(_ => Generic.Enum.Something), new GenericMethodCompiler((jArgs, dArgs) => new JsIdentifierExpression("dotvvm").Member("translations").Member("array").Member("orderByDesc")
594-
.Invoke(jArgs[1], jArgs[2], new JsLiteral((IsDelegateReturnTypeEnum(dArgs.Last().Type)) ? GetDelegateReturnTypeHash(dArgs.Last().Type) : null)),
602+
AddMethodTranslator(() => Enumerable.Empty<Generic.T>().OrderByDescending(_ => Generic.Enum.Something), new GenericMethodCompiler((jArgs, dArgs, method) => new JsIdentifierExpression("dotvvm").Member("translations").Member("array").Member("orderByDesc")
603+
.Invoke(jArgs[1], jArgs[2], new JsLiteral((IsDelegateReturnTypeEnum(dArgs.Last().Type)) ? GetDelegateReturnTypeHash(dArgs.Last().Type) : null))
604+
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.FirstArgumentMethodTargetPath, isObservable: false, objectPath: ImmutableArray<string?>.Empty)),
595605
check: (method, _, arguments) => EnsureIsComparableInJavascript(method, arguments.Last().Type.GetGenericArguments().Last())));
596606

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

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

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

@@ -700,7 +720,8 @@ private void AddDefaultDictionaryTranslations()
700720
var defaultValue =
701721
args.Length > 3 ? args[3] :
702722
new JsLiteral(ReflectionUtils.GetDefaultValue(method.GetGenericArguments().Last()));
703-
return new JsIdentifierExpression("dotvvm").Member("translations").Member("dictionary").Member("getItem").Invoke(args[1], args[2], defaultValue);
723+
return new JsIdentifierExpression("dotvvm").Member("translations").Member("dictionary").Member("getItem").Invoke(args[1], args[2], defaultValue)
724+
.WithAnnotation(new VMPropertyInfoAnnotation(method, targetPath: VMPropertyInfoAnnotation.FirstArgumentMethodTargetPath, isObservable: false));
704725
});
705726
#if DotNetCore
706727
AddMethodTranslator(() => default(IReadOnlyDictionary<Generic.T, Generic.T>)!.GetValueOrDefault(null!), getValueOrDefault);

0 commit comments

Comments
 (0)