Skip to content

Commit 68bd3b7

Browse files
committed
perf: Apply various workarounds for dotnet/runtime issue #56309
These changes are required to reduce the interpreter performance hit. The AOT compiler detects try/catch/finally containing methods and forces the interpreter, even if the try/catch is not in the execution path. Inner methods are created to reduce the size of the interpreter-executed code, and return to AOT as soon as possible.
1 parent 55273ee commit 68bd3b7

15 files changed

+971
-692
lines changed

src/Uno.UI/DataBinding/BindingExpression.cs

+91-57
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using Uno.UI.Converters;
1717
using Windows.UI.Xaml;
1818
using Windows.UI.Xaml.Data;
19+
using System.Runtime.CompilerServices;
1920

2021
namespace Windows.UI.Xaml.Data
2122
{
@@ -194,17 +195,7 @@ public void UpdateSource(object value)
194195

195196
if (ParentBinding.XBindBack != null)
196197
{
197-
try
198-
{
199-
ParentBinding.XBindBack(DataContext, value);
200-
}
201-
catch (Exception exception)
202-
{
203-
if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Error))
204-
{
205-
this.Log().Error($"Failed to set the source value for x:Bind path [{ParentBinding.Path}]", exception);
206-
}
207-
}
198+
UpdateSourceOnXBindBack(value);
208199
}
209200
else
210201
{
@@ -219,6 +210,27 @@ public void UpdateSource(object value)
219210
}
220211
}
221212

213+
/// <remarks>
214+
/// This method contains or is called by a try/catch containing method and
215+
/// can be significantly slower than other methods as a result on WebAssembly.
216+
/// See https://github.com/dotnet/runtime/issues/56309
217+
/// </remarks>
218+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
219+
private void UpdateSourceOnXBindBack(object value)
220+
{
221+
try
222+
{
223+
ParentBinding.XBindBack(DataContext, value);
224+
}
225+
catch (Exception exception)
226+
{
227+
if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Error))
228+
{
229+
this.Log().Error($"Failed to set the source value for x:Bind path [{ParentBinding.Path}]", exception);
230+
}
231+
}
232+
}
233+
222234
/// <summary>
223235
/// Sets the source value in the data context
224236
/// </summary>
@@ -511,39 +523,50 @@ void IValueChangedListener.OnValueChanged(object o)
511523
{
512524
if (ParentBinding.XBindSelector != null)
513525
{
514-
try
515-
{
516-
var canSetTarget = _updateSources?.None(s => s.ValueType == null) ?? true;
526+
SetTargetValueForXBindSelector();
527+
}
528+
else
529+
{
530+
SetTargetValueSafe(o);
531+
}
532+
}
517533

518-
if (canSetTarget)
519-
{
520-
SetTargetValueSafe(ParentBinding.XBindSelector(DataContext));
521-
}
522-
else
523-
{
524-
// x:Bind failed bindings don't change the target value
525-
// if no FallbackValue was specified.
526-
ApplyFallbackValue(useTypeDefaultValue: false);
534+
/// <remarks>
535+
/// This method contains or is called by a try/catch containing method and
536+
/// can be significantly slower than other methods as a result on WebAssembly.
537+
/// See https://github.com/dotnet/runtime/issues/56309
538+
/// </remarks>
539+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
540+
private void SetTargetValueForXBindSelector()
541+
{
542+
try
543+
{
544+
var canSetTarget = _updateSources?.None(s => s.ValueType == null) ?? true;
527545

528-
if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
529-
{
530-
this.Log().Debug($"Binding path does not provide a value [{TargetPropertyDetails}] on [{_targetOwnerType}], using fallback value");
531-
}
532-
}
546+
if (canSetTarget)
547+
{
548+
SetTargetValueSafe(ParentBinding.XBindSelector(DataContext));
533549
}
534-
catch (Exception e)
550+
else
535551
{
536-
ApplyFallbackValue();
552+
// x:Bind failed bindings don't change the target value
553+
// if no FallbackValue was specified.
554+
ApplyFallbackValue(useTypeDefaultValue: false);
537555

538-
if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Error))
556+
if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
539557
{
540-
this.Log().Error("Failed to apply binding to property [{0}] on [{1}] ({2})".InvariantCultureFormat(TargetPropertyDetails, _targetOwnerType, e.Message), e);
558+
this.Log().Debug($"Binding path does not provide a value [{TargetPropertyDetails}] on [{_targetOwnerType}], using fallback value");
541559
}
542560
}
543561
}
544-
else
562+
catch (Exception e)
545563
{
546-
SetTargetValueSafe(o);
564+
ApplyFallbackValue();
565+
566+
if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Error))
567+
{
568+
this.Log().Error("Failed to apply binding to property [{0}] on [{1}] ({2})".InvariantCultureFormat(TargetPropertyDetails, _targetOwnerType, e.Message), e);
569+
}
547570
}
548571
}
549572

@@ -572,29 +595,7 @@ private void SetTargetValueSafe(object v, bool useTargetNullValue)
572595

573596
try
574597
{
575-
if (v is UnsetValue)
576-
{
577-
ApplyFallbackValue();
578-
}
579-
else
580-
{
581-
_IsCurrentlyPushing = true;
582-
// Get the source value and place it in the target property
583-
var convertedValue = ConvertValue(v);
584-
585-
if (convertedValue == DependencyProperty.UnsetValue)
586-
{
587-
ApplyFallbackValue();
588-
}
589-
else if (useTargetNullValue && convertedValue == null && ParentBinding.TargetNullValue != null)
590-
{
591-
SetTargetValue(ConvertValue(ParentBinding.TargetNullValue));
592-
}
593-
else
594-
{
595-
SetTargetValue(convertedValue);
596-
}
597-
}
598+
InnerSetTargetValueSafe(v, useTargetNullValue);
598599
}
599600
catch (Exception e)
600601
{
@@ -613,6 +614,39 @@ private void SetTargetValueSafe(object v, bool useTargetNullValue)
613614
}
614615
}
615616

617+
/// <remarks>
618+
/// This method contains or is called by a try/catch containing method and
619+
/// can be significantly slower than other methods as a result on WebAssembly.
620+
/// See https://github.com/dotnet/runtime/issues/56309
621+
/// </remarks>
622+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
623+
private void InnerSetTargetValueSafe(object v, bool useTargetNullValue)
624+
{
625+
if (v is UnsetValue)
626+
{
627+
ApplyFallbackValue();
628+
}
629+
else
630+
{
631+
_IsCurrentlyPushing = true;
632+
// Get the source value and place it in the target property
633+
var convertedValue = ConvertValue(v);
634+
635+
if (convertedValue == DependencyProperty.UnsetValue)
636+
{
637+
ApplyFallbackValue();
638+
}
639+
else if (useTargetNullValue && convertedValue == null && ParentBinding.TargetNullValue != null)
640+
{
641+
SetTargetValue(ConvertValue(ParentBinding.TargetNullValue));
642+
}
643+
else
644+
{
645+
SetTargetValue(convertedValue);
646+
}
647+
}
648+
}
649+
616650
private string GetCurrentCulture() => CultureInfo.CurrentCulture.ToString();
617651

618652

src/Uno.UI/DataBinding/BindingPropertyHelper.cs

+26-12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using Microsoft.Extensions.Logging;
1919
using Windows.UI.Xaml.Data;
2020
using System.Dynamic;
21+
using System.Runtime.CompilerServices;
2122

2223
namespace Uno.UI.DataBinding
2324
{
@@ -1066,24 +1067,37 @@ private static DependencyProperty FindDependencyProperty(Type ownerType, string
10661067
}
10671068
else if (t != typeof(object))
10681069
{
1069-
try
1070-
{
1071-
value = Conversion.To(value, t, CultureInfo.CurrentCulture);
1072-
}
1073-
catch (Exception)
1074-
{
1075-
// This is a temporary fallback solution.
1076-
// The problem is that we don't actually know which culture we must use in advance.
1077-
// Values can come from the xaml (invariant culture) or from a two way binding (current culture).
1078-
// The real solution would be to pass a culture or source when setting a value in a Dependency Property.
1079-
value = Conversion.To(value, t, CultureInfo.InvariantCulture);
1080-
}
1070+
value = ConvertWithConvertionExtension(value, t);
10811071
}
10821072
}
10831073
}
10841074
return value;
10851075
}
10861076

1077+
/// <remarks>
1078+
/// This method contains or is called by a try/catch containing method and
1079+
/// can be significantly slower than other methods as a result on WebAssembly.
1080+
/// See https://github.com/dotnet/runtime/issues/56309
1081+
/// </remarks>
1082+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1083+
private static object ConvertWithConvertionExtension(object? value, Type? t)
1084+
{
1085+
try
1086+
{
1087+
value = Conversion.To(value, t, CultureInfo.CurrentCulture);
1088+
}
1089+
catch (Exception)
1090+
{
1091+
// This is a temporary fallback solution.
1092+
// The problem is that we don't actually know which culture we must use in advance.
1093+
// Values can come from the xaml (invariant culture) or from a two way binding (current culture).
1094+
// The real solution would be to pass a culture or source when setting a value in a Dependency Property.
1095+
value = Conversion.To(value, t, CultureInfo.InvariantCulture);
1096+
}
1097+
1098+
return value;
1099+
}
1100+
10871101
private static object UnsetValueGetter(object unused)
10881102
=> DependencyProperty.UnsetValue;
10891103

0 commit comments

Comments
 (0)