From f6108bb38a3ae5335dc36ce9624e66b4781fa528 Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Tue, 14 Jan 2025 09:47:00 -0500 Subject: [PATCH] perf(wasm): Split methods containing try/catch/finally blocks Related to https://github.com/dotnet/runtime/issues/111281 --- .../Native/NativeDispatcher.cs | 36 +++++++---- .../UI/Xaml/Controls/Frame/Frame.legacy.cs | 28 ++++++--- .../UI/Xaml/Internal/EventManager.uno.cs | 25 +++++--- src/Uno.UI/UI/Xaml/Style/Style.cs | 62 +++++++++++-------- src/Uno.UI/UI/Xaml/UIElement.Pointers.cs | 36 ++++++++--- 5 files changed, 123 insertions(+), 64 deletions(-) diff --git a/src/Uno.UI.Dispatching/Native/NativeDispatcher.cs b/src/Uno.UI.Dispatching/Native/NativeDispatcher.cs index d6937067b54d..74e7cfa1b32a 100644 --- a/src/Uno.UI.Dispatching/Native/NativeDispatcher.cs +++ b/src/Uno.UI.Dispatching/Native/NativeDispatcher.cs @@ -120,32 +120,42 @@ private static void DispatchItems() } } + RunAction(@this, action); + + // Restore the priority to the default for native events + // (i.e. not dispatched by this running loop) + @this._currentPriority = NativeDispatcherPriority.Normal; + + if (!didEnqueue && @this.Rendering != null) + { + @this.DispatchWakeUp(); + } + } + + /// + /// This method runs in a separate method in order to workaround for the following issue: + /// https://github.com/dotnet/runtime/issues/111281 + /// which prevents AOT on WebAssembly when try/catch/finally are found in the same method. + /// + private static void RunAction(NativeDispatcher dispatcher, Action? action) + { if (action != null) { try { - using (@this._synchronizationContexts[(int)@this._currentPriority].Apply()) + using (dispatcher._synchronizationContexts[(int)dispatcher._currentPriority].Apply()) { action(); } } catch (Exception exception) { - @this.Log().Error("NativeDispatcher unhandled exception", exception); + dispatcher.Log().Error("NativeDispatcher unhandled exception", exception); } } - else if (@this.Rendering == null && @this.Log().IsEnabled(LogLevel.Debug)) - { - @this.Log().Error("Dispatch queue is empty."); - } - - // Restore the priority to the default for native events - // (i.e. not dispatched by this running loop) - @this._currentPriority = NativeDispatcherPriority.Normal; - - if (!didEnqueue && @this.Rendering != null) + else if (dispatcher.Rendering == null && dispatcher.Log().IsEnabled(LogLevel.Debug)) { - @this.DispatchWakeUp(); + dispatcher.Log().Error("Dispatch queue is empty."); } } diff --git a/src/Uno.UI/UI/Xaml/Controls/Frame/Frame.legacy.cs b/src/Uno.UI/UI/Xaml/Controls/Frame/Frame.legacy.cs index 58f4468fca55..648a406be8b2 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Frame/Frame.legacy.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Frame/Frame.legacy.cs @@ -140,20 +140,28 @@ private bool InnerNavigate(PageStackEntry entry, NavigationMode mode) try { - _isNavigating = true; + bool InternalNavigate(PageStackEntry entry, NavigationMode mode) + { + try + { + _isNavigating = true; - return InnerNavigateUnsafe(entry, mode); - } - catch (Exception exception) - { - NavigationFailed?.Invoke(this, new NavigationFailedEventArgs(entry.SourcePageType, exception)); + return InnerNavigateUnsafe(entry, mode); + } + catch (Exception exception) + { + NavigationFailed?.Invoke(this, new NavigationFailedEventArgs(entry.SourcePageType, exception)); - if (NavigationFailed == null) - { - Application.Current.RaiseRecoverableUnhandledException(new InvalidOperationException("Navigation failed", exception)); + if (NavigationFailed == null) + { + Application.Current.RaiseRecoverableUnhandledException(new InvalidOperationException("Navigation failed", exception)); + } + + throw; + } } - throw; + return InternalNavigate(entry, mode); } finally { diff --git a/src/Uno.UI/UI/Xaml/Internal/EventManager.uno.cs b/src/Uno.UI/UI/Xaml/Internal/EventManager.uno.cs index c2b0279c6c05..57da40b9bd83 100644 --- a/src/Uno.UI/UI/Xaml/Internal/EventManager.uno.cs +++ b/src/Uno.UI/UI/Xaml/Internal/EventManager.uno.cs @@ -129,15 +129,26 @@ public void RaiseSizeChangedEvents() //if (auto layoutStorage = item.m_pElement->GetLayoutStorage()) { var args = new SizeChangedEventArgs(item.Element, item.OldSize, item.Element.RenderSize); - // AddRef for args is done on the managed side - try - { - item.Element.RaiseSizeChanged(args); - } - catch + + /// + /// This method runs in a separate method in order to workaround for the following issue: + /// https://github.com/dotnet/runtime/issues/111281 + /// which prevents AOT on WebAssembly when try/catch/finally are found in the same method. + /// + static void RaiseItemSizeChanged(SizeChangedQueueItem item, SizeChangedEventArgs args) { - // Empty catch to have the same behavior as "IGNOREHR" in WinUI. + // AddRef for args is done on the managed side + try + { + item.Element.RaiseSizeChanged(args); + } + catch + { + // Empty catch to have the same behavior as "IGNOREHR" in WinUI. + } } + + RaiseItemSizeChanged(item, args); } //TraceIndividualSizeChangedEnd(UINT64(item.m_pElement)); diff --git a/src/Uno.UI/UI/Xaml/Style/Style.cs b/src/Uno.UI/UI/Xaml/Style/Style.cs index 479bef2151ab..1b8e1a6efe95 100644 --- a/src/Uno.UI/UI/Xaml/Style/Style.cs +++ b/src/Uno.UI/UI/Xaml/Style/Style.cs @@ -129,44 +129,56 @@ internal void ApplyTo(DependencyObject o, DependencyPropertyValuePrecedences pre try { - ResourceResolver.PushNewScope(_xamlScope); - localPrecedenceDisposable = DependencyObjectExtensions.OverrideLocalPrecedence(o, precedence); - - if (_flattenedSetters != null) + /// + /// This method runs in a separate method in order to workaround for the following issue: + /// https://github.com/dotnet/runtime/issues/111281 + /// which prevents AOT on WebAssembly when try/catch/finally are found in the same method. + /// + IDisposable? InnerApplyTo(DependencyObject o, DependencyPropertyValuePrecedences precedence) { - for (var i = 0; i < _flattenedSetters.Length; i++) + IDisposable? localPrecedenceDisposable; + ResourceResolver.PushNewScope(_xamlScope); + localPrecedenceDisposable = DependencyObjectExtensions.OverrideLocalPrecedence(o, precedence); + + if (_flattenedSetters != null) { - try + for (var i = 0; i < _flattenedSetters.Length; i++) { - if (TryGetAdjustedSetter(precedence, o, _flattenedSetters[i], out var adjustedSetter)) + try { - using (o.OverrideLocalPrecedence(DependencyPropertyValuePrecedences.ExplicitStyle)) + if (TryGetAdjustedSetter(precedence, o, _flattenedSetters[i], out var adjustedSetter)) { - adjustedSetter.ApplyTo(o); + using (o.OverrideLocalPrecedence(DependencyPropertyValuePrecedences.ExplicitStyle)) + { + adjustedSetter.ApplyTo(o); + } + } + else + { + _flattenedSetters[i].ApplyTo(o); } } - else - { - _flattenedSetters[i].ApplyTo(o); - } - } - catch (Exception ex) - { - // This empty catch is to keep parity with WinUI's IGNOREHR in - // https://github.com/microsoft/microsoft-ui-xaml/blob/93742a178db8f625ba9299f62c21f656e0b195ad/dxaml/xcp/core/core/elements/framework.cpp#L790 - if (this.Log().IsEnabled(LogLevel.Debug)) + catch (Exception ex) { - this.Log().LogDebug($"An exception occurred while applying style setter. {ex}"); + // This empty catch is to keep parity with WinUI's IGNOREHR in + // https://github.com/microsoft/microsoft-ui-xaml/blob/93742a178db8f625ba9299f62c21f656e0b195ad/dxaml/xcp/core/core/elements/framework.cpp#L790 + if (this.Log().IsEnabled(LogLevel.Debug)) + { + this.Log().LogDebug($"An exception occurred while applying style setter. {ex}"); + } } } } - } - localPrecedenceDisposable?.Dispose(); - localPrecedenceDisposable = null; + localPrecedenceDisposable?.Dispose(); + localPrecedenceDisposable = null; + + // Check tree for resource binding values, since some Setters may have set ThemeResource-backed values + (o as IDependencyObjectStoreProvider)!.Store.UpdateResourceBindings(ResourceUpdateReason.ResolvedOnLoading); + return localPrecedenceDisposable; + } - // Check tree for resource binding values, since some Setters may have set ThemeResource-backed values - (o as IDependencyObjectStoreProvider)!.Store.UpdateResourceBindings(ResourceUpdateReason.ResolvedOnLoading); + localPrecedenceDisposable = InnerApplyTo(o, precedence); } finally { diff --git a/src/Uno.UI/UI/Xaml/UIElement.Pointers.cs b/src/Uno.UI/UI/Xaml/UIElement.Pointers.cs index 198702b57468..515c28e77bac 100644 --- a/src/Uno.UI/UI/Xaml/UIElement.Pointers.cs +++ b/src/Uno.UI/UI/Xaml/UIElement.Pointers.cs @@ -1225,20 +1225,38 @@ private bool RaisePointerEvent(RoutedEvent evt, PointerRoutedEventArgs args, Bub } var originalPending = _pendingRaisedEvent; - try - { - _pendingRaisedEvent = (this, evt, args); - return RaiseEvent(evt, args, ctx); - } - catch (Exception e) + try { - if (this.Log().IsEnabled(LogLevel.Error)) + /// + /// This method runs in a separate method in order to workaround for the following issue: + /// https://github.com/dotnet/runtime/issues/111281 + /// which prevents AOT on WebAssembly when try/catch/finally are found in the same method. + /// + bool InnerRaiseEvent( + RoutedEvent evt, + PointerRoutedEventArgs args, + BubblingContext ctx, + ref (UIElement sender, RoutedEvent @event, PointerRoutedEventArgs args) pendingRaisedEvent) { - this.Log().Error($"Failed to raise '{evt.Name}': {e}"); + try + { + _pendingRaisedEvent = (this, evt, args); + + return RaiseEvent(evt, args, ctx); + } + catch (Exception e) + { + if (this.Log().IsEnabled(LogLevel.Error)) + { + this.Log().Error($"Failed to raise '{evt.Name}': {e}"); + } + + return false; + } } - return false; + return InnerRaiseEvent(evt, args, ctx, ref _pendingRaisedEvent); } finally {