Skip to content

Commit

Permalink
[Blazor] Bind after changes (#363)
Browse files Browse the repository at this point in the history
Due to the way we decided to implement bind get,set,after, customers can run into situations where their components render many more times than expected, which results in confusion and performance degradation.

This was due to the fact that we chose to rely on EventCallback to implement this feature and we did not realize there were some side effects associated with it.

The APIs we are adding bypass the whole EventCallback infrastructure (simplifying the implementation too) and fix the issue.

This change updates the compiler to rely on the new runtime APIs

(cherry picked from commit 9ce52f1afbfb819fc8499a590385200b97b13f33)
  • Loading branch information
javiercn authored and 333fred committed Feb 27, 2023
1 parent 8b3141d commit 10bdab9
Show file tree
Hide file tree
Showing 178 changed files with 3,855 additions and 210 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -855,21 +855,21 @@ private void RewriteNodesForComponentEventCallbackBind(
else if (setter == null && after != null)
{
// bind:after only
var afterToEventCallback = $"global::{ComponentsApi.EventCallback.FactoryAccessor}.{ComponentsApi.EventCallbackFactory.CreateMethod}(this, callback: {after.Content})";
var afterContentInvocation = $"{ComponentsApi.RuntimeHelpers.InvokeAsynchronousDelegate}(callback: {after.Content})";
changeExpressionTokens.Add(new IntermediateToken()
{
Content = $"{ComponentsApi.RuntimeHelpers.CreateInferredEventCallback}(this, callback: __value => {{ {original.Content} = __value; return {afterToEventCallback}.InvokeAsync(); }}, value: {original.Content})",
Content = $"{ComponentsApi.RuntimeHelpers.CreateInferredBindSetter}(callback: __value => {{ {original.Content} = __value; return {afterContentInvocation}; }}, value: {original.Content})",
Kind = TokenKind.CSharp
});
}
else
{
// bind:set and bind:after create the code even though we disallow this combination through a diagnostic
var setToEventCallback = $"{ComponentsApi.RuntimeHelpers.CreateInferredEventCallback}(this, callback: {setter.Content}, value: {original.Content})";
var afterToEventCallback = $"global::{ComponentsApi.EventCallback.FactoryAccessor}.{ComponentsApi.EventCallbackFactory.CreateMethod}(this, callback: {after.Content})";
var setToEventCallback = $"{ComponentsApi.RuntimeHelpers.CreateInferredBindSetter}(callback: {setter.Content}, value: {original.Content})";
var afterContentInvocation = $"{ComponentsApi.RuntimeHelpers.InvokeAsynchronousDelegate}(callback: {after.Content})";
changeExpressionTokens.Add(new IntermediateToken()
{
Content = $"{ComponentsApi.RuntimeHelpers.CreateInferredEventCallback}(this, callback: async __value => {{ await {setToEventCallback}.InvokeAsync(); await {afterToEventCallback}.InvokeAsync(); }}, value: {original.Content})",
Content = $"{ComponentsApi.RuntimeHelpers.CreateInferredEventCallback}(this, callback: async __value => {{ await {setToEventCallback}; await {afterContentInvocation}; }}, value: {original.Content})",
Kind = TokenKind.CSharp
});
}
Expand Down Expand Up @@ -966,28 +966,28 @@ private void RewriteNodesForElementEventCallbackBind(
// bind:set only
changeExpressionTokens.Add(new IntermediateToken()
{
Content = $"{ComponentsApi.RuntimeHelpers.CreateInferredEventCallback}(this, callback: {setter.Content}, value: {original.Content})",
Content = $"{ComponentsApi.RuntimeHelpers.CreateInferredBindSetter}(callback: {setter.Content}, value: {original.Content})",
Kind = TokenKind.CSharp
});
}
else if (setter == null && after != null)
{
// bind:after only
var afterToEventCallback = $"global::{ComponentsApi.EventCallback.FactoryAccessor}.{ComponentsApi.EventCallbackFactory.CreateMethod}(this, callback: {after.Content})";
var afterContentInvocation = $"{ComponentsApi.RuntimeHelpers.InvokeAsynchronousDelegate}(callback: {after.Content})";
changeExpressionTokens.Add(new IntermediateToken()
{
Content = $"{ComponentsApi.RuntimeHelpers.CreateInferredEventCallback}(this, callback: __value => {{ {original.Content} = __value; return {afterToEventCallback}.InvokeAsync(); }}, value: {original.Content})",
Content = $"{ComponentsApi.RuntimeHelpers.CreateInferredBindSetter}(callback: __value => {{ {original.Content} = __value; return {afterContentInvocation}; }}, value: {original.Content})",
Kind = TokenKind.CSharp
});
}
else
{
// bind:set and bind:after create the code even though we disallow this combination through a diagnostic
var setToEventCallback = $"{ComponentsApi.RuntimeHelpers.CreateInferredEventCallback}(this, callback: {setter.Content}, value: {original.Content})";
var afterToEventCallback = $"global::{ComponentsApi.EventCallback.FactoryAccessor}.{ComponentsApi.EventCallbackFactory.CreateMethod}(this, callback: {after.Content})";
var setterContentInvocation = $"{ComponentsApi.RuntimeHelpers.CreateInferredBindSetter}(callback: {setter.Content}, value: {original.Content})";
var afterContentInvocation = $"{ComponentsApi.RuntimeHelpers.InvokeAsynchronousDelegate}(callback: {after.Content})";
changeExpressionTokens.Add(new IntermediateToken()
{
Content = $"{ComponentsApi.RuntimeHelpers.CreateInferredEventCallback}(this, callback: async __value => {{ await {setToEventCallback}.InvokeAsync(); await {afterToEventCallback}.InvokeAsync(); }}, value: {original.Content})",
Content = $"{ComponentsApi.RuntimeHelpers.CreateInferredEventCallback}(this, callback: async __value => {{ await {setterContentInvocation}(); await {afterContentInvocation}; }}, value: {original.Content})",
Kind = TokenKind.CSharp
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ public static class RuntimeHelpers
{
public const string TypeCheck = "global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck";
public const string CreateInferredEventCallback = "global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.CreateInferredEventCallback";
public const string CreateInferredBindSetter = "global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.CreateInferredBindSetter";
public const string InvokeSynchronousDelegate = "global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.InvokeSynchronousDelegate";
public const string InvokeAsynchronousDelegate = "global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.InvokeAsynchronousDelegate";
}
Expand Down
Loading

0 comments on commit 10bdab9

Please sign in to comment.