diff --git a/src/Components/Components/src/ParameterView.cs b/src/Components/Components/src/ParameterView.cs index 569b8ff2c2..f68fb60573 100644 --- a/src/Components/Components/src/ParameterView.cs +++ b/src/Components/Components/src/ParameterView.cs @@ -142,8 +142,8 @@ namespace Microsoft.AspNetCore.Components var oldIndex = oldParameters._ownerIndex; var newIndex = _ownerIndex; - var oldEndIndexExcl = oldIndex + oldParameters._frames[oldIndex].ComponentSubtreeLength; - var newEndIndexExcl = newIndex + _frames[newIndex].ComponentSubtreeLength; + var oldEndIndexExcl = oldIndex + oldParameters._frames[oldIndex].ComponentSubtreeLengthField; + var newEndIndexExcl = newIndex + _frames[newIndex].ComponentSubtreeLengthField; while (true) { // First, stop if we've reached the end of either subtree @@ -162,21 +162,21 @@ namespace Microsoft.AspNetCore.Components ref var newFrame = ref _frames[newIndex]; // Stop if we've reached the end of either subtree's sequence of attributes - oldFinished = oldFrame.FrameType != RenderTreeFrameType.Attribute; - newFinished = newFrame.FrameType != RenderTreeFrameType.Attribute; + oldFinished = oldFrame.FrameTypeField != RenderTreeFrameType.Attribute; + newFinished = newFrame.FrameTypeField != RenderTreeFrameType.Attribute; if (oldFinished || newFinished) { return oldFinished == newFinished; // Same only if we have same number of parameters } else { - if (!string.Equals(oldFrame.AttributeName, newFrame.AttributeName, StringComparison.Ordinal)) + if (!string.Equals(oldFrame.AttributeNameField, newFrame.AttributeNameField, StringComparison.Ordinal)) { return false; // Different names } - var oldValue = oldFrame.AttributeValue; - var newValue = newFrame.AttributeValue; + var oldValue = oldFrame.AttributeValueField; + var newValue = newFrame.AttributeValueField; if (ChangeDetection.MayHaveChanged(oldValue, newValue)) { return false; @@ -216,8 +216,8 @@ namespace Microsoft.AspNetCore.Components public static ParameterView FromDictionary(IDictionary parameters) { var frames = new RenderTreeFrame[parameters.Count + 1]; - frames[0] = RenderTreeFrame.Element(0, GeneratedParameterViewElementName) - .WithElementSubtreeLength(frames.Length); + frames[0] = RenderTreeFrame.Element(0, GeneratedParameterViewElementName); + frames[0].ElementSubtreeLengthField = frames.Length; var i = 0; foreach (var kvp in parameters) @@ -303,7 +303,7 @@ namespace Microsoft.AspNetCore.Components { _frames = frames; _ownerIndex = ownerIndex; - _ownerDescendantsEndIndexExcl = ownerIndex + _frames[ownerIndex].ElementSubtreeLength; + _ownerDescendantsEndIndexExcl = ownerIndex + _frames[ownerIndex].ElementSubtreeLengthField; _currentIndex = ownerIndex; _current = default; } @@ -321,7 +321,7 @@ namespace Microsoft.AspNetCore.Components // ... or if you get to its first non-attribute descendant (because attributes // are always before any other type of descendant) - if (_frames[nextIndex].FrameType != RenderTreeFrameType.Attribute) + if (_frames[nextIndex].FrameTypeField != RenderTreeFrameType.Attribute) { return false; } @@ -329,7 +329,7 @@ namespace Microsoft.AspNetCore.Components _currentIndex = nextIndex; ref var frame = ref _frames[_currentIndex]; - _current = new ParameterValue(frame.AttributeName, frame.AttributeValue, false); + _current = new ParameterValue(frame.AttributeNameField, frame.AttributeValueField, false); return true; } diff --git a/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs b/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs index aabd22217a..80bf99724e 100644 --- a/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs +++ b/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs @@ -78,7 +78,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree if (hasMoreOld) { ref var oldFrame = ref oldTree[oldStartIndex]; - oldSeq = oldFrame.Sequence; + oldSeq = oldFrame.SequenceField; oldKey = KeyValue(ref oldFrame); } else @@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree if (hasMoreNew) { ref var newFrame = ref newTree[newStartIndex]; - newSeq = newFrame.Sequence; + newSeq = newFrame.SequenceField; newKey = KeyValue(ref newFrame); } else @@ -195,7 +195,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree var newLoopsBackLater = false; for (var testIndex = newStartIndex + 1; testIndex < newEndIndexExcl; testIndex++) { - if (newTree[testIndex].Sequence < newSeq) + if (newTree[testIndex].SequenceField < newSeq) { newLoopsBackLater = true; break; @@ -218,7 +218,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree var oldLoopsBackLater = false; for (var testIndex = oldStartIndex + 1; testIndex < oldEndIndexExcl; testIndex++) { - if (oldTree[testIndex].Sequence < oldSeq) + if (oldTree[testIndex].SequenceField < oldSeq) { oldLoopsBackLater = true; break; @@ -350,13 +350,13 @@ namespace Microsoft.AspNetCore.Components.RenderTree private static void ThrowExceptionForDuplicateKey(object key, in RenderTreeFrame frame) { - switch (frame.FrameType) + switch (frame.FrameTypeField) { case RenderTreeFrameType.Component: - throw new InvalidOperationException($"More than one sibling of component '{frame.ComponentType}' has the same key value, '{key}'. Key values must be unique."); + throw new InvalidOperationException($"More than one sibling of component '{frame.ComponentTypeField}' has the same key value, '{key}'. Key values must be unique."); case RenderTreeFrameType.Element: - throw new InvalidOperationException($"More than one sibling of element '{frame.ElementName}' has the same key value, '{key}'. Key values must be unique."); + throw new InvalidOperationException($"More than one sibling of element '{frame.ElementNameField}' has the same key value, '{key}'. Key values must be unique."); default: throw new InvalidOperationException($"More than one sibling has the same key value, '{key}'. Key values must be unique."); @@ -365,12 +365,12 @@ namespace Microsoft.AspNetCore.Components.RenderTree private static object KeyValue(ref RenderTreeFrame frame) { - switch (frame.FrameType) + switch (frame.FrameTypeField) { case RenderTreeFrameType.Element: - return frame.ElementKey; + return frame.ElementKeyField; case RenderTreeFrameType.Component: - return frame.ComponentKey; + return frame.ComponentKeyField; default: return null; } @@ -405,10 +405,10 @@ namespace Microsoft.AspNetCore.Components.RenderTree while (hasMoreOld || hasMoreNew) { - var oldSeq = hasMoreOld ? oldTree[oldStartIndex].Sequence : int.MaxValue; - var newSeq = hasMoreNew ? newTree[newStartIndex].Sequence : int.MaxValue; - var oldAttributeName = oldTree[oldStartIndex].AttributeName; - var newAttributeName = newTree[newStartIndex].AttributeName; + var oldSeq = hasMoreOld ? oldTree[oldStartIndex].SequenceField : int.MaxValue; + var newSeq = hasMoreNew ? newTree[newStartIndex].SequenceField : int.MaxValue; + var oldAttributeName = oldTree[oldStartIndex].AttributeNameField; + var newAttributeName = newTree[newStartIndex].AttributeNameField; if (oldSeq == newSeq && string.Equals(oldAttributeName, newAttributeName, StringComparison.Ordinal)) @@ -481,12 +481,12 @@ namespace Microsoft.AspNetCore.Components.RenderTree // 3. iterate through the remaining attributes in the set and add them for (var i = newStartIndex; i < newEndIndexExcl; i++) { - diffContext.AttributeDiffSet[newTree[i].AttributeName] = i; + diffContext.AttributeDiffSet[newTree[i].AttributeNameField] = i; } for (var i = oldStartIndex; i < oldEndIndexExcl; i++) { - var oldName = oldTree[i].AttributeName; + var oldName = oldTree[i].AttributeNameField; if (diffContext.AttributeDiffSet.TryGetValue(oldName, out var matchIndex)) { // Has a match in the new tree, look for a diff @@ -519,10 +519,11 @@ namespace Microsoft.AspNetCore.Components.RenderTree var newTree = diffContext.NewTree; ref var oldComponentFrame = ref oldTree[oldComponentIndex]; ref var newComponentFrame = ref newTree[newComponentIndex]; - var componentState = oldComponentFrame.ComponentState; + var componentState = oldComponentFrame.ComponentStateField; // Preserve the actual componentInstance - newComponentFrame = newComponentFrame.WithComponent(componentState); + newComponentFrame.ComponentStateField = componentState; + newComponentFrame.ComponentIdField = componentState.ComponentId; // As an important rendering optimization, we want to skip parameter update // notifications if we know for sure they haven't changed/mutated. The @@ -545,14 +546,14 @@ namespace Microsoft.AspNetCore.Components.RenderTree private static int NextSiblingIndex(in RenderTreeFrame frame, int frameIndex) { - switch (frame.FrameType) + switch (frame.FrameTypeField) { case RenderTreeFrameType.Component: - return frameIndex + frame.ComponentSubtreeLength; + return frameIndex + frame.ComponentSubtreeLengthField; case RenderTreeFrameType.Element: - return frameIndex + frame.ElementSubtreeLength; + return frameIndex + frame.ElementSubtreeLengthField; case RenderTreeFrameType.Region: - return frameIndex + frame.RegionSubtreeLength; + return frameIndex + frame.RegionSubtreeLengthField; default: return frameIndex + 1; } @@ -570,8 +571,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree // This can't happen for sequence-matched frames from .razor components, but it can happen if you write your // builder logic manually or if two dissimilar frames matched by key. Treat as completely unrelated. - var newFrameType = newFrame.FrameType; - if (oldFrame.FrameType != newFrameType) + var newFrameType = newFrame.FrameTypeField; + if (oldFrame.FrameTypeField != newFrameType) { InsertNewFrame(ref diffContext, newFrameIndex); RemoveOldFrame(ref diffContext, oldFrameIndex); @@ -586,8 +587,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree { case RenderTreeFrameType.Text: { - var oldText = oldFrame.TextContent; - var newText = newFrame.TextContent; + var oldText = oldFrame.TextContentField; + var newText = newFrame.TextContentField; if (!string.Equals(oldText, newText, StringComparison.Ordinal)) { var referenceFrameIndex = diffContext.ReferenceFrames.Append(newFrame); @@ -599,8 +600,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree case RenderTreeFrameType.Markup: { - var oldMarkup = oldFrame.MarkupContent; - var newMarkup = newFrame.MarkupContent; + var oldMarkup = oldFrame.MarkupContentField; + var newMarkup = newFrame.MarkupContentField; if (!string.Equals(oldMarkup, newMarkup, StringComparison.Ordinal)) { var referenceFrameIndex = diffContext.ReferenceFrames.Append(newFrame); @@ -612,8 +613,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree case RenderTreeFrameType.Element: { - var oldElementName = oldFrame.ElementName; - var newElementName = newFrame.ElementName; + var oldElementName = oldFrame.ElementNameField; + var newElementName = newFrame.ElementNameField; if (string.Equals(oldElementName, newElementName, StringComparison.Ordinal)) { var oldFrameAttributesEndIndexExcl = GetAttributesEndIndexExclusive(oldTree, oldFrameIndex); @@ -626,8 +627,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree newFrameIndex + 1, newFrameAttributesEndIndexExcl); // Diff the children - var oldFrameChildrenEndIndexExcl = oldFrameIndex + oldFrame.ElementSubtreeLength; - var newFrameChildrenEndIndexExcl = newFrameIndex + newFrame.ElementSubtreeLength; + var oldFrameChildrenEndIndexExcl = oldFrameIndex + oldFrame.ElementSubtreeLengthField; + var newFrameChildrenEndIndexExcl = newFrameIndex + newFrame.ElementSubtreeLengthField; var hasChildrenToProcess = oldFrameChildrenEndIndexExcl > oldFrameAttributesEndIndexExcl || newFrameChildrenEndIndexExcl > newFrameAttributesEndIndexExcl; @@ -661,14 +662,14 @@ namespace Microsoft.AspNetCore.Components.RenderTree { AppendDiffEntriesForRange( ref diffContext, - oldFrameIndex + 1, oldFrameIndex + oldFrame.RegionSubtreeLength, - newFrameIndex + 1, newFrameIndex + newFrame.RegionSubtreeLength); + oldFrameIndex + 1, oldFrameIndex + oldFrame.RegionSubtreeLengthField, + newFrameIndex + 1, newFrameIndex + newFrame.RegionSubtreeLengthField); break; } case RenderTreeFrameType.Component: { - if (oldFrame.ComponentType == newFrame.ComponentType) + if (oldFrame.ComponentTypeField == newFrame.ComponentTypeField) { UpdateRetainedChildComponent( ref diffContext, @@ -698,7 +699,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree // We don't handle attributes here, they have their own diff logic. // See AppendDiffEntriesForAttributeFrame default: - throw new NotImplementedException($"Encountered unsupported frame type during diffing: {newTree[newFrameIndex].FrameType}"); + throw new NotImplementedException($"Encountered unsupported frame type during diffing: {newTree[newFrameIndex].FrameTypeField}"); } } @@ -715,7 +716,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree ref var newFrame = ref newTree[newFrameIndex]; // Using Equals to account for string comparisons, nulls, etc. - var valueChanged = !Equals(oldFrame.AttributeValue, newFrame.AttributeValue); + var valueChanged = !Equals(oldFrame.AttributeValueField, newFrame.AttributeValueField); if (valueChanged) { InitializeNewAttributeFrame(ref diffContext, ref newFrame); @@ -724,13 +725,13 @@ namespace Microsoft.AspNetCore.Components.RenderTree // If we're replacing an old event handler ID with a new one, register the old one for disposal, // plus keep track of the old->new chain until the old one is fully disposed - if (oldFrame.AttributeEventHandlerId > 0) + if (oldFrame.AttributeEventHandlerIdField > 0) { - diffContext.Renderer.TrackReplacedEventHandlerId(oldFrame.AttributeEventHandlerId, newFrame.AttributeEventHandlerId); - diffContext.BatchBuilder.DisposedEventHandlerIds.Append(oldFrame.AttributeEventHandlerId); + diffContext.Renderer.TrackReplacedEventHandlerId(oldFrame.AttributeEventHandlerIdField, newFrame.AttributeEventHandlerIdField); + diffContext.BatchBuilder.DisposedEventHandlerIds.Append(oldFrame.AttributeEventHandlerIdField); } } - else if (oldFrame.AttributeEventHandlerId > 0) + else if (oldFrame.AttributeEventHandlerIdField > 0) { // Retain the event handler ID by copying the old frame over the new frame. // this will prevent us from needing to dispose the old event handler @@ -743,7 +744,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree { var newTree = diffContext.NewTree; ref var newFrame = ref newTree[newFrameIndex]; - switch (newFrame.FrameType) + switch (newFrame.FrameTypeField) { case RenderTreeFrameType.Attribute: { @@ -756,7 +757,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree case RenderTreeFrameType.Element: { InitializeNewSubtree(ref diffContext, newFrameIndex); - var referenceFrameIndex = diffContext.ReferenceFrames.Append(newTree, newFrameIndex, newFrame.ElementSubtreeLength); + var referenceFrameIndex = diffContext.ReferenceFrames.Append(newTree, newFrameIndex, newFrame.ElementSubtreeLengthField); diffContext.Edits.Append(RenderTreeEdit.PrependFrame(diffContext.SiblingIndex, referenceFrameIndex)); diffContext.SiblingIndex++; break; @@ -764,7 +765,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree case RenderTreeFrameType.Region: { var regionChildFrameIndex = newFrameIndex + 1; - var regionChildFrameEndIndexExcl = newFrameIndex + newFrame.RegionSubtreeLength; + var regionChildFrameEndIndexExcl = newFrameIndex + newFrame.RegionSubtreeLengthField; while (regionChildFrameIndex < regionChildFrameEndIndexExcl) { InsertNewFrame(ref diffContext, regionChildFrameIndex); @@ -791,7 +792,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree break; } default: - throw new NotImplementedException($"Unexpected frame type during {nameof(InsertNewFrame)}: {newFrame.FrameType}"); + throw new NotImplementedException($"Unexpected frame type during {nameof(InsertNewFrame)}: {newFrame.FrameTypeField}"); } } @@ -799,21 +800,21 @@ namespace Microsoft.AspNetCore.Components.RenderTree { var oldTree = diffContext.OldTree; ref var oldFrame = ref oldTree[oldFrameIndex]; - switch (oldFrame.FrameType) + switch (oldFrame.FrameTypeField) { case RenderTreeFrameType.Attribute: { - diffContext.Edits.Append(RenderTreeEdit.RemoveAttribute(diffContext.SiblingIndex, oldFrame.AttributeName)); - if (oldFrame.AttributeEventHandlerId > 0) + diffContext.Edits.Append(RenderTreeEdit.RemoveAttribute(diffContext.SiblingIndex, oldFrame.AttributeNameField)); + if (oldFrame.AttributeEventHandlerIdField > 0) { - diffContext.BatchBuilder.DisposedEventHandlerIds.Append(oldFrame.AttributeEventHandlerId); + diffContext.BatchBuilder.DisposedEventHandlerIds.Append(oldFrame.AttributeEventHandlerIdField); } break; } case RenderTreeFrameType.Component: case RenderTreeFrameType.Element: { - var endIndexExcl = oldFrameIndex + oldFrame.ElementSubtreeLength; + var endIndexExcl = oldFrameIndex + oldFrame.ElementSubtreeLengthField; DisposeFramesInRange(diffContext.BatchBuilder, oldTree, oldFrameIndex, endIndexExcl); diffContext.Edits.Append(RenderTreeEdit.RemoveFrame(diffContext.SiblingIndex)); break; @@ -821,7 +822,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree case RenderTreeFrameType.Region: { var regionChildFrameIndex = oldFrameIndex + 1; - var regionChildFrameEndIndexExcl = oldFrameIndex + oldFrame.RegionSubtreeLength; + var regionChildFrameEndIndexExcl = oldFrameIndex + oldFrame.RegionSubtreeLengthField; while (regionChildFrameIndex < regionChildFrameEndIndexExcl) { RemoveOldFrame(ref diffContext, regionChildFrameIndex); @@ -836,17 +837,17 @@ namespace Microsoft.AspNetCore.Components.RenderTree break; } default: - throw new NotImplementedException($"Unexpected frame type during {nameof(RemoveOldFrame)}: {oldFrame.FrameType}"); + throw new NotImplementedException($"Unexpected frame type during {nameof(RemoveOldFrame)}: {oldFrame.FrameTypeField}"); } } private static int GetAttributesEndIndexExclusive(RenderTreeFrame[] tree, int rootIndex) { - var descendantsEndIndexExcl = rootIndex + tree[rootIndex].ElementSubtreeLength; + var descendantsEndIndexExcl = rootIndex + tree[rootIndex].ElementSubtreeLengthField; var index = rootIndex + 1; for (; index < descendantsEndIndexExcl; index++) { - if (tree[index].FrameType != RenderTreeFrameType.Attribute) + if (tree[index].FrameTypeField != RenderTreeFrameType.Attribute) { break; } @@ -872,11 +873,11 @@ namespace Microsoft.AspNetCore.Components.RenderTree private static void InitializeNewSubtree(ref DiffContext diffContext, int frameIndex) { var frames = diffContext.NewTree; - var endIndexExcl = frameIndex + frames[frameIndex].ElementSubtreeLength; + var endIndexExcl = frameIndex + frames[frameIndex].ElementSubtreeLengthField; for (var i = frameIndex; i < endIndexExcl; i++) { ref var frame = ref frames[i]; - switch (frame.FrameType) + switch (frame.FrameTypeField) { case RenderTreeFrameType.Component: InitializeNewComponentFrame(ref diffContext, i); @@ -899,14 +900,14 @@ namespace Microsoft.AspNetCore.Components.RenderTree var frames = diffContext.NewTree; ref var frame = ref frames[frameIndex]; - if (frame.ComponentState != null) + if (frame.ComponentStateField != null) { throw new InvalidOperationException($"Child component already exists during {nameof(InitializeNewComponentFrame)}"); } var parentComponentId = diffContext.ComponentId; diffContext.Renderer.InstantiateChildComponentOnFrame(ref frame, parentComponentId); - var childComponentState = frame.ComponentState; + var childComponentState = frame.ComponentStateField; // Set initial parameters var initialParametersLifetime = new ParameterViewLifetime(diffContext.BatchBuilder); @@ -920,9 +921,9 @@ namespace Microsoft.AspNetCore.Components.RenderTree // // We're following a simple heuristic here that's reflected in the ts runtime // based on the common usage of attributes for DOM events. - if ((newFrame.AttributeValue is MulticastDelegate || newFrame.AttributeValue is EventCallback) && - newFrame.AttributeName.Length >= 3 && - newFrame.AttributeName.StartsWith("on", StringComparison.Ordinal)) + if ((newFrame.AttributeValueField is MulticastDelegate || newFrame.AttributeValueField is EventCallback) && + newFrame.AttributeNameField.Length >= 3 && + newFrame.AttributeNameField.StartsWith("on", StringComparison.Ordinal)) { diffContext.Renderer.AssignEventHandlerId(ref newFrame); } @@ -931,14 +932,14 @@ namespace Microsoft.AspNetCore.Components.RenderTree private static void InitializeNewElementReferenceCaptureFrame(ref DiffContext diffContext, ref RenderTreeFrame newFrame) { var newElementReference = ElementReference.CreateWithUniqueId(diffContext.Renderer.ElementReferenceContext); - newFrame = newFrame.WithElementReferenceCaptureId(newElementReference.Id); - newFrame.ElementReferenceCaptureAction(newElementReference); + newFrame.ElementReferenceCaptureIdField = newElementReference.Id; + newFrame.ElementReferenceCaptureActionField(newElementReference); } private static void InitializeNewComponentReferenceCaptureFrame(ref DiffContext diffContext, ref RenderTreeFrame newFrame) { - ref var parentFrame = ref diffContext.NewTree[newFrame.ComponentReferenceCaptureParentFrameIndex]; - if (parentFrame.FrameType != RenderTreeFrameType.Component) + ref var parentFrame = ref diffContext.NewTree[newFrame.ComponentReferenceCaptureParentFrameIndexField]; + if (parentFrame.FrameTypeField != RenderTreeFrameType.Component) { // Should never happen, but will help with diagnosis if it does throw new InvalidOperationException($"{nameof(RenderTreeFrameType.ComponentReferenceCapture)} frame references invalid parent index."); @@ -951,7 +952,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree throw new InvalidOperationException($"Trying to initialize {nameof(RenderTreeFrameType.ComponentReferenceCapture)} frame before parent component was assigned."); } - newFrame.ComponentReferenceCaptureAction(componentInstance); + newFrame.ComponentReferenceCaptureActionField(componentInstance); } private static void DisposeFramesInRange(RenderBatchBuilder batchBuilder, RenderTreeFrame[] frames, int startIndex, int endIndexExcl) @@ -959,13 +960,13 @@ namespace Microsoft.AspNetCore.Components.RenderTree for (var i = startIndex; i < endIndexExcl; i++) { ref var frame = ref frames[i]; - if (frame.FrameType == RenderTreeFrameType.Component && frame.ComponentState != null) + if (frame.FrameTypeField == RenderTreeFrameType.Component && frame.ComponentStateField != null) { - batchBuilder.ComponentDisposalQueue.Enqueue(frame.ComponentId); + batchBuilder.ComponentDisposalQueue.Enqueue(frame.ComponentIdField); } - else if (frame.FrameType == RenderTreeFrameType.Attribute && frame.AttributeEventHandlerId > 0) + else if (frame.FrameTypeField == RenderTreeFrameType.Attribute && frame.AttributeEventHandlerIdField > 0) { - batchBuilder.DisposedEventHandlerIds.Append(frame.AttributeEventHandlerId); + batchBuilder.DisposedEventHandlerIds.Append(frame.AttributeEventHandlerIdField); } } } diff --git a/src/Components/Components/src/RenderTree/RenderTreeFrame.cs b/src/Components/Components/src/RenderTree/RenderTreeFrame.cs index 8fc59cfc7b..ba6d2e5320 100644 --- a/src/Components/Components/src/RenderTree/RenderTreeFrame.cs +++ b/src/Components/Components/src/RenderTree/RenderTreeFrame.cs @@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree // // Represents an entry in a tree of user interface (UI) items. [StructLayout(LayoutKind.Explicit, Pack = 4)] - public readonly struct RenderTreeFrame + public struct RenderTreeFrame { // Note that the struct layout has to be valid in both 32-bit and 64-bit runtime platforms, // which means that all reference-type fields need to take up 8 bytes (except for the last @@ -48,72 +48,86 @@ namespace Microsoft.AspNetCore.Components.RenderTree // Common // -------------------------------------------------------------------------------- + [FieldOffset(0)] internal int SequenceField; + [FieldOffset(4)] internal RenderTreeFrameType FrameTypeField; + /// /// Gets the sequence number of the frame. Sequence numbers indicate the relative source /// positions of the instructions that inserted the frames. Sequence numbers are only /// comparable within the same sequence (typically, the same source method). /// - [FieldOffset(0)] public readonly int Sequence; + public int Sequence => SequenceField; /// /// Describes the type of this frame. /// - [FieldOffset(4)] public readonly RenderTreeFrameType FrameType; + public RenderTreeFrameType FrameType => FrameTypeField; // -------------------------------------------------------------------------------- // RenderTreeFrameType.Element // -------------------------------------------------------------------------------- + [FieldOffset(8)] internal int ElementSubtreeLengthField; + [FieldOffset(16)] internal string ElementNameField; + [FieldOffset(24)] internal object ElementKeyField; + /// /// If the property equals /// gets the number of frames in the subtree for which this frame is the root. /// The value is zero if the frame has not yet been closed. /// - [FieldOffset(8)] public readonly int ElementSubtreeLength; + public int ElementSubtreeLength => ElementSubtreeLengthField; /// /// If the property equals , /// gets a name representing the type of the element. Otherwise, the value is undefined. /// - [FieldOffset(16)] public readonly string ElementName; + public string ElementName => ElementNameField; /// /// If the property equals , /// gets the element's diffing key, or null if none was specified. /// - [FieldOffset(24)] public readonly object ElementKey; + public object ElementKey => ElementKeyField; // -------------------------------------------------------------------------------- // RenderTreeFrameType.Text // -------------------------------------------------------------------------------- + [FieldOffset(16)] internal string TextContentField; + /// /// If the property equals , /// gets the content of the text frame. Otherwise, the value is undefined. /// - [FieldOffset(16)] public readonly string TextContent; + public string TextContent => TextContentField; // -------------------------------------------------------------------------------- // RenderTreeFrameType.Attribute // -------------------------------------------------------------------------------- + [FieldOffset(8)] internal ulong AttributeEventHandlerIdField; + [FieldOffset(16)] internal string AttributeNameField; + [FieldOffset(24)] internal object AttributeValueField; + [FieldOffset(32)] internal string AttributeEventUpdatesAttributeNameField; + /// /// If the property equals /// gets the ID of the corresponding event handler, if any. /// - [FieldOffset(8)] public readonly ulong AttributeEventHandlerId; + public ulong AttributeEventHandlerId => AttributeEventHandlerIdField; /// /// If the property equals , /// gets the attribute name. Otherwise, the value is undefined. /// - [FieldOffset(16)] public readonly string AttributeName; + public string AttributeName => AttributeNameField; /// /// If the property equals , /// gets the attribute value. Otherwise, the value is undefined. /// - [FieldOffset(24)] public readonly object AttributeValue; + public object AttributeValue => AttributeValueField; /// /// If the property equals , @@ -121,80 +135,94 @@ namespace Microsoft.AspNetCore.Components.RenderTree /// can be updated to represent the UI state prior to executing the event handler. This is /// primarily used in two-way bindings. /// - [FieldOffset(32)] public readonly string AttributeEventUpdatesAttributeName; + public string AttributeEventUpdatesAttributeName => AttributeEventUpdatesAttributeNameField; // -------------------------------------------------------------------------------- // RenderTreeFrameType.Component // -------------------------------------------------------------------------------- + [FieldOffset(8)] internal int ComponentSubtreeLengthField; + [FieldOffset(12)] internal int ComponentIdField; + [FieldOffset(16)] internal Type ComponentTypeField; + [FieldOffset(24)] internal ComponentState ComponentStateField; + [FieldOffset(32)] internal object ComponentKeyField; + /// /// If the property equals /// gets the number of frames in the subtree for which this frame is the root. /// The value is zero if the frame has not yet been closed. /// - [FieldOffset(8)] public readonly int ComponentSubtreeLength; + public int ComponentSubtreeLength => ComponentSubtreeLengthField; /// /// If the property equals , /// gets the child component instance identifier. /// - [FieldOffset(12)] public readonly int ComponentId; + public int ComponentId => ComponentIdField; /// /// If the property equals , /// gets the type of the child component. /// - [FieldOffset(16)] public readonly Type ComponentType; + public Type ComponentType => ComponentTypeField; /// /// If the property equals , /// gets the child component state object. Otherwise, the value is undefined. /// - [FieldOffset(24)] internal readonly ComponentState ComponentState; + internal ComponentState ComponentState => ComponentStateField; /// /// If the property equals , /// gets the component's diffing key, or null if none was specified. /// - [FieldOffset(32)] public readonly object ComponentKey; + public object ComponentKey => ComponentKeyField; /// /// If the property equals , /// gets the child component instance. Otherwise, the value is undefined. /// - public IComponent Component => ComponentState?.Component; + public IComponent Component => ComponentStateField?.Component; // -------------------------------------------------------------------------------- // RenderTreeFrameType.Region // -------------------------------------------------------------------------------- + [FieldOffset(8)] internal int RegionSubtreeLengthField; + /// /// If the property equals /// gets the number of frames in the subtree for which this frame is the root. /// The value is zero if the frame has not yet been closed. /// - [FieldOffset(8)] public readonly int RegionSubtreeLength; + public int RegionSubtreeLength => RegionSubtreeLengthField; // -------------------------------------------------------------------------------- // RenderTreeFrameType.ElementReferenceCapture // -------------------------------------------------------------------------------- + [FieldOffset(16)] internal string ElementReferenceCaptureIdField; + [FieldOffset(24)] internal Action ElementReferenceCaptureActionField; + /// /// If the property equals , /// gets the ID of the reference capture. Otherwise, the value is undefined. /// - [FieldOffset(16)] public readonly string ElementReferenceCaptureId; + public string ElementReferenceCaptureId => ElementReferenceCaptureIdField; /// /// If the property equals , /// gets the action that writes the reference to its target. Otherwise, the value is undefined. /// - [FieldOffset(24)] public readonly Action ElementReferenceCaptureAction; + public Action ElementReferenceCaptureAction => ElementReferenceCaptureActionField; // -------------------------------------------------------------------------------- // RenderTreeFrameType.ComponentReferenceCapture // -------------------------------------------------------------------------------- + [FieldOffset(8)] internal int ComponentReferenceCaptureParentFrameIndexField; + [FieldOffset(16)] internal Action ComponentReferenceCaptureActionField; + /// /// If the property equals , /// gets the index of the parent frame representing the component being captured. Otherwise, the value is undefined. @@ -205,49 +233,51 @@ namespace Microsoft.AspNetCore.Components.RenderTree /// initialization logic in RenderTreeDiffBuilder to walk the frames hierarchically, then it would know /// the parent index at the point where it wants to initialize the ComponentReferenceCapture frame. /// - [FieldOffset(8)] public readonly int ComponentReferenceCaptureParentFrameIndex; + public int ComponentReferenceCaptureParentFrameIndex => ComponentReferenceCaptureParentFrameIndexField; /// /// If the property equals , /// gets the action that writes the reference to its target. Otherwise, the value is undefined. /// - [FieldOffset(16)] public readonly Action ComponentReferenceCaptureAction; + public Action ComponentReferenceCaptureAction => ComponentReferenceCaptureActionField; // -------------------------------------------------------------------------------- // RenderTreeFrameType.Markup // -------------------------------------------------------------------------------- + [FieldOffset(16)] internal string MarkupContentField; + /// /// If the property equals , /// gets the content of the markup frame. Otherwise, the value is undefined. /// - [FieldOffset(16)] public readonly string MarkupContent; + public string MarkupContent => MarkupContentField; // Element constructor private RenderTreeFrame(int sequence, int elementSubtreeLength, string elementName, object elementKey) : this() { - Sequence = sequence; - FrameType = RenderTreeFrameType.Element; - ElementSubtreeLength = elementSubtreeLength; - ElementName = elementName; - ElementKey = elementKey; + SequenceField = sequence; + FrameTypeField = RenderTreeFrameType.Element; + ElementSubtreeLengthField = elementSubtreeLength; + ElementNameField = elementName; + ElementKeyField = elementKey; } // Component constructor private RenderTreeFrame(int sequence, int componentSubtreeLength, Type componentType, ComponentState componentState, object componentKey) : this() { - Sequence = sequence; - FrameType = RenderTreeFrameType.Component; - ComponentSubtreeLength = componentSubtreeLength; - ComponentType = componentType; - ComponentKey = componentKey; + SequenceField = sequence; + FrameTypeField = RenderTreeFrameType.Component; + ComponentSubtreeLengthField = componentSubtreeLength; + ComponentTypeField = componentType; + ComponentKeyField = componentKey; if (componentState != null) { - ComponentState = componentState; - ComponentId = componentState.ComponentId; + ComponentStateField = componentState; + ComponentIdField = componentState.ComponentId; } } @@ -255,25 +285,25 @@ namespace Microsoft.AspNetCore.Components.RenderTree private RenderTreeFrame(int sequence, int regionSubtreeLength) : this() { - Sequence = sequence; - FrameType = RenderTreeFrameType.Region; - RegionSubtreeLength = regionSubtreeLength; + SequenceField = sequence; + FrameTypeField = RenderTreeFrameType.Region; + RegionSubtreeLengthField = regionSubtreeLength; } // Text/markup constructor private RenderTreeFrame(int sequence, bool isMarkup, string textOrMarkup) : this() { - Sequence = sequence; + SequenceField = sequence; if (isMarkup) { - FrameType = RenderTreeFrameType.Markup; - MarkupContent = textOrMarkup; + FrameTypeField = RenderTreeFrameType.Markup; + MarkupContentField = textOrMarkup; } else { - FrameType = RenderTreeFrameType.Text; - TextContent = textOrMarkup; + FrameTypeField = RenderTreeFrameType.Text; + TextContentField = textOrMarkup; } } @@ -281,32 +311,32 @@ namespace Microsoft.AspNetCore.Components.RenderTree private RenderTreeFrame(int sequence, string attributeName, object attributeValue, ulong attributeEventHandlerId, string attributeEventUpdatesAttributeName) : this() { - FrameType = RenderTreeFrameType.Attribute; - Sequence = sequence; - AttributeName = attributeName; - AttributeValue = attributeValue; - AttributeEventHandlerId = attributeEventHandlerId; - AttributeEventUpdatesAttributeName = attributeEventUpdatesAttributeName; + FrameTypeField = RenderTreeFrameType.Attribute; + SequenceField = sequence; + AttributeNameField = attributeName; + AttributeValueField = attributeValue; + AttributeEventHandlerIdField = attributeEventHandlerId; + AttributeEventUpdatesAttributeNameField = attributeEventUpdatesAttributeName; } // Element reference capture constructor private RenderTreeFrame(int sequence, Action elementReferenceCaptureAction, string elementReferenceCaptureId) : this() { - FrameType = RenderTreeFrameType.ElementReferenceCapture; - Sequence = sequence; - ElementReferenceCaptureAction = elementReferenceCaptureAction; - ElementReferenceCaptureId = elementReferenceCaptureId; + FrameTypeField = RenderTreeFrameType.ElementReferenceCapture; + SequenceField = sequence; + ElementReferenceCaptureActionField = elementReferenceCaptureAction; + ElementReferenceCaptureIdField = elementReferenceCaptureId; } // Component reference capture constructor private RenderTreeFrame(int sequence, Action componentReferenceCaptureAction, int parentFrameIndex) : this() { - FrameType = RenderTreeFrameType.ComponentReferenceCapture; - Sequence = sequence; - ComponentReferenceCaptureAction = componentReferenceCaptureAction; - ComponentReferenceCaptureParentFrameIndex = parentFrameIndex; + FrameTypeField = RenderTreeFrameType.ComponentReferenceCapture; + SequenceField = sequence; + ComponentReferenceCaptureActionField = componentReferenceCaptureAction; + ComponentReferenceCaptureParentFrameIndexField = parentFrameIndex; } internal static RenderTreeFrame Element(int sequence, string elementName) @@ -337,61 +367,61 @@ namespace Microsoft.AspNetCore.Components.RenderTree => new RenderTreeFrame(sequence, componentReferenceCaptureAction: componentReferenceCaptureAction, parentFrameIndex: parentFrameIndex); internal RenderTreeFrame WithElementSubtreeLength(int elementSubtreeLength) - => new RenderTreeFrame(Sequence, elementSubtreeLength: elementSubtreeLength, ElementName, ElementKey); + => new RenderTreeFrame(SequenceField, elementSubtreeLength: elementSubtreeLength, ElementNameField, ElementKeyField); internal RenderTreeFrame WithComponentSubtreeLength(int componentSubtreeLength) - => new RenderTreeFrame(Sequence, componentSubtreeLength: componentSubtreeLength, ComponentType, ComponentState, ComponentKey); + => new RenderTreeFrame(SequenceField, componentSubtreeLength: componentSubtreeLength, ComponentTypeField, ComponentStateField, ComponentKeyField); internal RenderTreeFrame WithAttributeSequence(int sequence) - => new RenderTreeFrame(sequence, attributeName: AttributeName, AttributeValue, AttributeEventHandlerId, AttributeEventUpdatesAttributeName); + => new RenderTreeFrame(sequence, attributeName: AttributeNameField, AttributeValueField, AttributeEventHandlerIdField, AttributeEventUpdatesAttributeNameField); internal RenderTreeFrame WithComponent(ComponentState componentState) - => new RenderTreeFrame(Sequence, componentSubtreeLength: ComponentSubtreeLength, ComponentType, componentState, ComponentKey); + => new RenderTreeFrame(SequenceField, componentSubtreeLength: ComponentSubtreeLengthField, ComponentTypeField, componentState, ComponentKeyField); internal RenderTreeFrame WithAttributeEventHandlerId(ulong eventHandlerId) - => new RenderTreeFrame(Sequence, attributeName: AttributeName, AttributeValue, eventHandlerId, AttributeEventUpdatesAttributeName); + => new RenderTreeFrame(SequenceField, attributeName: AttributeNameField, AttributeValueField, eventHandlerId, AttributeEventUpdatesAttributeNameField); internal RenderTreeFrame WithAttributeValue(object attributeValue) - => new RenderTreeFrame(Sequence, attributeName: AttributeName, attributeValue, AttributeEventHandlerId, AttributeEventUpdatesAttributeName); + => new RenderTreeFrame(SequenceField, attributeName: AttributeNameField, attributeValue, AttributeEventHandlerIdField, AttributeEventUpdatesAttributeNameField); internal RenderTreeFrame WithAttributeEventUpdatesAttributeName(string attributeUpdatesAttributeName) - => new RenderTreeFrame(Sequence, attributeName: AttributeName, AttributeValue, AttributeEventHandlerId, attributeUpdatesAttributeName); + => new RenderTreeFrame(SequenceField, attributeName: AttributeNameField, AttributeValueField, AttributeEventHandlerIdField, attributeUpdatesAttributeName); internal RenderTreeFrame WithRegionSubtreeLength(int regionSubtreeLength) - => new RenderTreeFrame(Sequence, regionSubtreeLength: regionSubtreeLength); + => new RenderTreeFrame(SequenceField, regionSubtreeLength: regionSubtreeLength); internal RenderTreeFrame WithElementReferenceCaptureId(string elementReferenceCaptureId) - => new RenderTreeFrame(Sequence, elementReferenceCaptureAction: ElementReferenceCaptureAction, elementReferenceCaptureId); + => new RenderTreeFrame(SequenceField, elementReferenceCaptureAction: ElementReferenceCaptureActionField, elementReferenceCaptureId); internal RenderTreeFrame WithElementKey(object elementKey) - => new RenderTreeFrame(Sequence, elementSubtreeLength: ElementSubtreeLength, ElementName, elementKey); + => new RenderTreeFrame(SequenceField, elementSubtreeLength: ElementSubtreeLengthField, ElementNameField, elementKey); internal RenderTreeFrame WithComponentKey(object componentKey) - => new RenderTreeFrame(Sequence, componentSubtreeLength: ComponentSubtreeLength, ComponentType, ComponentState, componentKey); + => new RenderTreeFrame(SequenceField, componentSubtreeLength: ComponentSubtreeLengthField, ComponentTypeField, ComponentStateField, componentKey); /// // Just to be nice for debugging and unit tests. public override string ToString() { - switch (FrameType) + switch (FrameTypeField) { case RenderTreeFrameType.Attribute: return $"Attribute: (seq={Sequence}, id={AttributeEventHandlerId}) '{AttributeName}'='{AttributeValue}'"; case RenderTreeFrameType.Component: - return $"Component: (seq={Sequence}, key={ComponentKey ?? "(none)"}, len={ComponentSubtreeLength}) {ComponentType}"; + return $"Component: (seq={Sequence}, key={ComponentKeyField ?? "(none)"}, len={ComponentSubtreeLength}) {ComponentType}"; case RenderTreeFrameType.Element: - return $"Element: (seq={Sequence}, key={ElementKey ?? "(none)"}, len={ElementSubtreeLength}) {ElementName}"; + return $"Element: (seq={Sequence}, key={ElementKeyField ?? "(none)"}, len={ElementSubtreeLength}) {ElementName}"; case RenderTreeFrameType.Region: return $"Region: (seq={Sequence}, len={RegionSubtreeLength})"; case RenderTreeFrameType.Text: - return $"Text: (seq={Sequence}, len=n/a) {EscapeNewlines(TextContent)}"; + return $"Text: (seq={Sequence}, len=n/a) {EscapeNewlines(TextContentField)}"; case RenderTreeFrameType.Markup: - return $"Markup: (seq={Sequence}, len=n/a) {EscapeNewlines(TextContent)}"; + return $"Markup: (seq={Sequence}, len=n/a) {EscapeNewlines(TextContentField)}"; case RenderTreeFrameType.ElementReferenceCapture: return $"ElementReferenceCapture: (seq={Sequence}, len=n/a) {ElementReferenceCaptureAction}"; diff --git a/src/Components/Components/src/RenderTree/RenderTreeFrameArrayBuilder.cs b/src/Components/Components/src/RenderTree/RenderTreeFrameArrayBuilder.cs new file mode 100644 index 0000000000..817a0fd1a5 --- /dev/null +++ b/src/Components/Components/src/RenderTree/RenderTreeFrameArrayBuilder.cs @@ -0,0 +1,137 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Components.RenderTree +{ + /// + /// A special subclass of that contains methods optimized for appending entries. + /// + internal class RenderTreeFrameArrayBuilder : ArrayBuilder + { + // You may notice a repeated block at the top of each of these methods. This is intentionally inlined into each + // method because doing so improves intensive rendering scenarios by around 1% (based on the FastGrid benchmark). + + public void AppendElement(int sequence, string elementName) + { + if (_itemsInUse == _items.Length) + { + GrowBuffer(_items.Length * 2); + } + + _items[_itemsInUse++] = new RenderTreeFrame + { + SequenceField = sequence, + FrameTypeField = RenderTreeFrameType.Element, + ElementNameField = elementName, + }; + } + + public void AppendText(int sequence, string textContent) + { + if (_itemsInUse == _items.Length) + { + GrowBuffer(_items.Length * 2); + } + + _items[_itemsInUse++] = new RenderTreeFrame + { + SequenceField = sequence, + FrameTypeField = RenderTreeFrameType.Text, + TextContentField = textContent, + }; + } + + public void AppendMarkup(int sequence, string markupContent) + { + if (_itemsInUse == _items.Length) + { + GrowBuffer(_items.Length * 2); + } + + _items[_itemsInUse++] = new RenderTreeFrame + { + SequenceField = sequence, + FrameTypeField = RenderTreeFrameType.Markup, + MarkupContentField = markupContent, + }; + } + + public void AppendAttribute(int sequence, string attributeName, object? attributeValue) + { + if (_itemsInUse == _items.Length) + { + GrowBuffer(_items.Length * 2); + } + + _items[_itemsInUse++] = new RenderTreeFrame + { + SequenceField = sequence, + FrameTypeField = RenderTreeFrameType.Attribute, + AttributeNameField = attributeName, + AttributeValueField = attributeValue, + }; + } + + public void AppendComponent(int sequence, Type componentType) + { + if (_itemsInUse == _items.Length) + { + GrowBuffer(_items.Length * 2); + } + + _items[_itemsInUse++] = new RenderTreeFrame + { + SequenceField = sequence, + FrameTypeField = RenderTreeFrameType.Component, + ComponentTypeField = componentType, + }; + } + + public void AppendElementReferenceCapture(int sequence, Action elementReferenceCaptureAction) + { + if (_itemsInUse == _items.Length) + { + GrowBuffer(_items.Length * 2); + } + + _items[_itemsInUse++] = new RenderTreeFrame + { + SequenceField = sequence, + FrameTypeField = RenderTreeFrameType.ElementReferenceCapture, + ElementReferenceCaptureActionField = elementReferenceCaptureAction, + }; + } + + public void AppendComponentReferenceCapture(int sequence, Action componentReferenceCaptureAction, int parentFrameIndexValue) + { + if (_itemsInUse == _items.Length) + { + GrowBuffer(_items.Length * 2); + } + + _items[_itemsInUse++] = new RenderTreeFrame + { + SequenceField = sequence, + FrameTypeField = RenderTreeFrameType.ComponentReferenceCapture, + ComponentReferenceCaptureActionField = componentReferenceCaptureAction, + ComponentReferenceCaptureParentFrameIndexField = parentFrameIndexValue, + }; + } + + public void AppendRegion(int sequence) + { + if (_itemsInUse == _items.Length) + { + GrowBuffer(_items.Length * 2); + } + + _items[_itemsInUse++] = new RenderTreeFrame + { + SequenceField = sequence, + FrameTypeField = RenderTreeFrameType.Region, + }; + } + } +} diff --git a/src/Components/Components/src/RenderTree/Renderer.cs b/src/Components/Components/src/RenderTree/Renderer.cs index 6a501d1e04..e61e46f65d 100644 --- a/src/Components/Components/src/RenderTree/Renderer.cs +++ b/src/Components/Components/src/RenderTree/Renderer.cs @@ -292,19 +292,20 @@ namespace Microsoft.AspNetCore.Components.RenderTree internal void InstantiateChildComponentOnFrame(ref RenderTreeFrame frame, int parentComponentId) { - if (frame.FrameType != RenderTreeFrameType.Component) + if (frame.FrameTypeField != RenderTreeFrameType.Component) { throw new ArgumentException($"The frame's {nameof(RenderTreeFrame.FrameType)} property must equal {RenderTreeFrameType.Component}", nameof(frame)); } - if (frame.ComponentState != null) + if (frame.ComponentStateField != null) { throw new ArgumentException($"The frame already has a non-null component instance", nameof(frame)); } - var newComponent = InstantiateComponent(frame.ComponentType); + var newComponent = InstantiateComponent(frame.ComponentTypeField); var newComponentState = AttachAndInitComponent(newComponent, parentComponentId); - frame = frame.WithComponent(newComponentState); + frame.ComponentStateField = newComponentState; + frame.ComponentIdField = newComponentState.ComponentId; } internal void AddToPendingTasks(Task task) @@ -342,7 +343,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree { var id = ++_lastEventHandlerId; - if (frame.AttributeValue is EventCallback callback) + if (frame.AttributeValueField is EventCallback callback) { // We hit this case when a EventCallback object is produced that needs an explicit receiver. // Common cases for this are "chained bind" or "chained event handler" when a component @@ -352,7 +353,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree // the receiver. _eventBindings.Add(id, callback); } - else if (frame.AttributeValue is MulticastDelegate @delegate) + else if (frame.AttributeValueField is MulticastDelegate @delegate) { // This is the common case for a delegate, where the receiver of the event // is the same as delegate.Target. In this case since the receiver is implicit we can @@ -364,7 +365,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree // NOTE: we do not to handle EventCallback here. EventCallback is only used when passing // a callback to a component, and never when used to attaching a DOM event handler. - frame = frame.WithAttributeEventHandlerId(id); + frame.AttributeEventHandlerIdField = id; } /// diff --git a/src/Components/Components/src/Rendering/RenderTreeBuilder.cs b/src/Components/Components/src/Rendering/RenderTreeBuilder.cs index 566265c988..c3d1edcd50 100644 --- a/src/Components/Components/src/Rendering/RenderTreeBuilder.cs +++ b/src/Components/Components/src/Rendering/RenderTreeBuilder.cs @@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Components.Rendering private readonly static object BoxedFalse = false; private readonly static string ComponentReferenceCaptureInvalidParentMessage = $"Component reference captures may only be added as children of frames of type {RenderTreeFrameType.Component}"; - private readonly ArrayBuilder _entries = new ArrayBuilder(); + private readonly RenderTreeFrameArrayBuilder _entries = new RenderTreeFrameArrayBuilder(); private readonly Stack _openElementIndices = new Stack(); private RenderTreeFrameType? _lastNonAttributeFrameType; private bool _hasSeenAddMultipleAttributes; @@ -53,7 +53,8 @@ namespace Microsoft.AspNetCore.Components.Rendering } _openElementIndices.Push(_entries.Count); - Append(RenderTreeFrame.Element(sequence, elementName)); + _entries.AppendElement(sequence, elementName); + _lastNonAttributeFrameType = RenderTreeFrameType.Element; } /// @@ -71,8 +72,7 @@ namespace Microsoft.AspNetCore.Components.Rendering ProcessDuplicateAttributes(first: indexOfEntryBeingClosed + 1); } - ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed]; - entry = entry.WithElementSubtreeLength(_entries.Count - indexOfEntryBeingClosed); + _entries.Buffer[indexOfEntryBeingClosed].ElementSubtreeLengthField = _entries.Count - indexOfEntryBeingClosed; } /// @@ -82,7 +82,8 @@ namespace Microsoft.AspNetCore.Components.Rendering /// Content for the new markup frame. public void AddMarkupContent(int sequence, string? markupContent) { - Append(RenderTreeFrame.Markup(sequence, markupContent ?? string.Empty)); + _entries.AppendMarkup(sequence, markupContent ?? string.Empty); + _lastNonAttributeFrameType = RenderTreeFrameType.Markup; } /// @@ -92,7 +93,8 @@ namespace Microsoft.AspNetCore.Components.Rendering /// Content for the new text frame. public void AddContent(int sequence, string? textContent) { - Append(RenderTreeFrame.Text(sequence, textContent ?? string.Empty)); + _entries.AppendText(sequence, textContent ?? string.Empty); + _lastNonAttributeFrameType = RenderTreeFrameType.Text; } /// @@ -161,7 +163,8 @@ namespace Microsoft.AspNetCore.Components.Rendering throw new InvalidOperationException($"Valueless attributes may only be added immediately after frames of type {RenderTreeFrameType.Element}"); } - Append(RenderTreeFrame.Attribute(sequence, name, BoxedTrue)); + + _entries.AppendAttribute(sequence, name, BoxedTrue); } /// @@ -181,13 +184,13 @@ namespace Microsoft.AspNetCore.Components.Rendering AssertCanAddAttribute(); if (_lastNonAttributeFrameType == RenderTreeFrameType.Component) { - Append(RenderTreeFrame.Attribute(sequence, name, value ? BoxedTrue : BoxedFalse)); + _entries.AppendAttribute(sequence, name, value ? BoxedTrue : BoxedFalse); } else if (value) { // Don't add 'false' attributes for elements. We want booleans to map to the presence // or absence of an attribute, and false => "False" which isn't falsy in js. - Append(RenderTreeFrame.Attribute(sequence, name, BoxedTrue)); + _entries.AppendAttribute(sequence, name, BoxedTrue); } else { @@ -212,7 +215,7 @@ namespace Microsoft.AspNetCore.Components.Rendering AssertCanAddAttribute(); if (value != null || _lastNonAttributeFrameType == RenderTreeFrameType.Component) { - Append(RenderTreeFrame.Attribute(sequence, name, value)); + _entries.AppendAttribute(sequence, name, value); } else { @@ -237,7 +240,7 @@ namespace Microsoft.AspNetCore.Components.Rendering AssertCanAddAttribute(); if (value != null || _lastNonAttributeFrameType == RenderTreeFrameType.Component) { - Append(RenderTreeFrame.Attribute(sequence, name, value)); + _entries.AppendAttribute(sequence, name, value); } else { @@ -268,19 +271,19 @@ namespace Microsoft.AspNetCore.Components.Rendering { // Since this is a component, we need to preserve the type of the EventCallback, so we have // to box. - Append(RenderTreeFrame.Attribute(sequence, name, (object)value)); + _entries.AppendAttribute(sequence, name, value); } else if (value.RequiresExplicitReceiver) { // If we need to preserve the receiver, we just box the EventCallback // so we can get it out on the other side. - Append(RenderTreeFrame.Attribute(sequence, name, (object)value)); + _entries.AppendAttribute(sequence, name, value); } else if (value.HasDelegate) { // In the common case the receiver is also the delegate's target, so we // just need to retain the delegate. This allows us to avoid an allocation. - Append(RenderTreeFrame.Attribute(sequence, name, value.Delegate)); + _entries.AppendAttribute(sequence, name, value.Delegate); } else { @@ -312,19 +315,19 @@ namespace Microsoft.AspNetCore.Components.Rendering { // Since this is a component, we need to preserve the type of the EventCallback, so we have // to box. - Append(RenderTreeFrame.Attribute(sequence, name, (object)value)); + _entries.AppendAttribute(sequence, name, value); } else if (value.RequiresExplicitReceiver) { // If we need to preserve the receiver - we convert this to an untyped EventCallback. We don't // need to preserve the type of an EventCallback when it's invoked from the DOM. - Append(RenderTreeFrame.Attribute(sequence, name, (object)value.AsUntyped())); + _entries.AppendAttribute(sequence, name, value.AsUntyped()); } else if (value.HasDelegate) { // In the common case the receiver is also the delegate's target, so we // just need to retain the delegate. This allows us to avoid an allocation. - Append(RenderTreeFrame.Attribute(sequence, name, value.Delegate)); + _entries.AppendAttribute(sequence, name, value.Delegate); } else { @@ -357,7 +360,7 @@ namespace Microsoft.AspNetCore.Components.Rendering { if (boolValue) { - Append(RenderTreeFrame.Attribute(sequence, name, BoxedTrue)); + _entries.AppendAttribute(sequence, name, BoxedTrue); } else { @@ -369,7 +372,7 @@ namespace Microsoft.AspNetCore.Components.Rendering { if (callbackValue.HasDelegate) { - Append(RenderTreeFrame.Attribute(sequence, name, callbackValue.UnpackForRenderTree())); + _entries.AppendAttribute(sequence, name, callbackValue.UnpackForRenderTree()); } else { @@ -378,18 +381,18 @@ namespace Microsoft.AspNetCore.Components.Rendering } else if (value is MulticastDelegate) { - Append(RenderTreeFrame.Attribute(sequence, name, value)); + _entries.AppendAttribute(sequence, name, value); } else { // The value is either a string, or should be treated as a string. - Append(RenderTreeFrame.Attribute(sequence, name, value.ToString())); + _entries.AppendAttribute(sequence, name, value.ToString()); } } else if (_lastNonAttributeFrameType == RenderTreeFrameType.Component) { // If this is a component, we always want to preserve the original type. - Append(RenderTreeFrame.Attribute(sequence, name, value)); + _entries.AppendAttribute(sequence, name, value); } else { @@ -408,15 +411,16 @@ namespace Microsoft.AspNetCore.Components.Rendering /// /// An integer that represents the position of the instruction in the source code. /// A holding the name and value of the attribute. - public void AddAttribute(int sequence, in RenderTreeFrame frame) + public void AddAttribute(int sequence, RenderTreeFrame frame) { - if (frame.FrameType != RenderTreeFrameType.Attribute) + if (frame.FrameTypeField != RenderTreeFrameType.Attribute) { throw new ArgumentException($"The {nameof(frame.FrameType)} must be {RenderTreeFrameType.Attribute}."); } AssertCanAddAttribute(); - Append(frame.WithAttributeSequence(sequence)); + frame.SequenceField = sequence; + _entries.Append(frame); } /// @@ -464,12 +468,12 @@ namespace Microsoft.AspNetCore.Components.Rendering } ref var prevFrame = ref _entries.Buffer[_entries.Count - 1]; - if (prevFrame.FrameType != RenderTreeFrameType.Attribute) + if (prevFrame.FrameTypeField != RenderTreeFrameType.Attribute) { - throw new InvalidOperationException($"Incorrect frame type: '{prevFrame.FrameType}'"); + throw new InvalidOperationException($"Incorrect frame type: '{prevFrame.FrameTypeField}'"); } - prevFrame = prevFrame.WithAttributeEventUpdatesAttributeName(updatesAttributeName); + prevFrame.AttributeEventUpdatesAttributeNameField = updatesAttributeName; } /// @@ -516,16 +520,16 @@ namespace Microsoft.AspNetCore.Components.Rendering var parentFrameIndexValue = parentFrameIndex.Value; ref var parentFrame = ref _entries.Buffer[parentFrameIndexValue]; - switch (parentFrame.FrameType) + switch (parentFrame.FrameTypeField) { case RenderTreeFrameType.Element: - parentFrame = parentFrame.WithElementKey(value); // It's a ref var, so this writes to the array + parentFrame.ElementKeyField = value; // It's a ref var, so this writes to the array break; case RenderTreeFrameType.Component: - parentFrame = parentFrame.WithComponentKey(value); // It's a ref var, so this writes to the array + parentFrame.ComponentKeyField = value; // It's a ref var, so this writes to the array break; default: - throw new InvalidOperationException($"Cannot set a key on a frame of type {parentFrame.FrameType}."); + throw new InvalidOperationException($"Cannot set a key on a frame of type {parentFrame.FrameTypeField}."); } } @@ -540,7 +544,8 @@ namespace Microsoft.AspNetCore.Components.Rendering } _openElementIndices.Push(_entries.Count); - Append(RenderTreeFrame.ChildComponent(sequence, componentType)); + _entries.AppendComponent(sequence, componentType); + _lastNonAttributeFrameType = RenderTreeFrameType.Component; } /// @@ -558,8 +563,7 @@ namespace Microsoft.AspNetCore.Components.Rendering ProcessDuplicateAttributes(first: indexOfEntryBeingClosed + 1); } - ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed]; - entry = entry.WithComponentSubtreeLength(_entries.Count - indexOfEntryBeingClosed); + _entries.Buffer[indexOfEntryBeingClosed].ComponentSubtreeLengthField = _entries.Count - indexOfEntryBeingClosed; } /// @@ -574,7 +578,8 @@ namespace Microsoft.AspNetCore.Components.Rendering throw new InvalidOperationException($"Element reference captures may only be added as children of frames of type {RenderTreeFrameType.Element}"); } - Append(RenderTreeFrame.ElementReferenceCapture(sequence, elementReferenceCaptureAction)); + _entries.AppendElementReferenceCapture(sequence, elementReferenceCaptureAction); + _lastNonAttributeFrameType = RenderTreeFrameType.ElementReferenceCapture; } /// @@ -591,12 +596,13 @@ namespace Microsoft.AspNetCore.Components.Rendering } var parentFrameIndexValue = parentFrameIndex.Value; - if (_entries.Buffer[parentFrameIndexValue].FrameType != RenderTreeFrameType.Component) + if (_entries.Buffer[parentFrameIndexValue].FrameTypeField != RenderTreeFrameType.Component) { throw new InvalidOperationException(ComponentReferenceCaptureInvalidParentMessage); } - Append(RenderTreeFrame.ComponentReferenceCapture(sequence, componentReferenceCaptureAction, parentFrameIndexValue)); + _entries.AppendComponentReferenceCapture(sequence, componentReferenceCaptureAction, parentFrameIndexValue); + _lastNonAttributeFrameType = RenderTreeFrameType.ComponentReferenceCapture; } /// @@ -614,7 +620,8 @@ namespace Microsoft.AspNetCore.Components.Rendering } _openElementIndices.Push(_entries.Count); - Append(RenderTreeFrame.Region(sequence)); + _entries.AppendRegion(sequence); + _lastNonAttributeFrameType = RenderTreeFrameType.Region; } /// @@ -624,8 +631,7 @@ namespace Microsoft.AspNetCore.Components.Rendering public void CloseRegion() { var indexOfEntryBeingClosed = _openElementIndices.Pop(); - ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed]; - entry = entry.WithRegionSubtreeLength(_entries.Count - indexOfEntryBeingClosed); + _entries.Buffer[indexOfEntryBeingClosed].RegionSubtreeLengthField = _entries.Count - indexOfEntryBeingClosed; } private void AssertCanAddAttribute() @@ -644,7 +650,7 @@ namespace Microsoft.AspNetCore.Components.Rendering { var parentIndex = GetCurrentParentFrameIndex(); return parentIndex.HasValue - ? _entries.Buffer[parentIndex.Value].FrameType + ? _entries.Buffer[parentIndex.Value].FrameTypeField : (RenderTreeFrameType?)null; } @@ -680,17 +686,6 @@ namespace Microsoft.AspNetCore.Components.Rendering public ArrayRange GetFrames() => _entries.ToRange(); - private void Append(in RenderTreeFrame frame) - { - var frameType = frame.FrameType; - _entries.Append(frame); - - if (frameType != RenderTreeFrameType.Attribute) - { - _lastNonAttributeFrameType = frame.FrameType; - } - } - // Internal for testing internal void ProcessDuplicateAttributes(int first) { @@ -704,7 +699,7 @@ namespace Microsoft.AspNetCore.Components.Rendering for (var i = first; i <= last; i++) { - if (buffer[i].FrameType != RenderTreeFrameType.Attribute) + if (buffer[i].FrameTypeField != RenderTreeFrameType.Attribute) { last = i - 1; break; @@ -716,12 +711,12 @@ namespace Microsoft.AspNetCore.Components.Rendering for (var i = last; i >= first; i--) { ref var frame = ref buffer[i]; - Debug.Assert(frame.FrameType == RenderTreeFrameType.Attribute, $"Frame type is {frame.FrameType} at {i}"); + Debug.Assert(frame.FrameTypeField == RenderTreeFrameType.Attribute, $"Frame type is {frame.FrameTypeField} at {i}"); - if (!seenAttributeNames.TryGetValue(frame.AttributeName, out var index)) + if (!seenAttributeNames.TryGetValue(frame.AttributeNameField, out var index)) { // This is the first time seeing this attribute name. Add to the dictionary and move on. - seenAttributeNames.Add(frame.AttributeName, i); + seenAttributeNames.Add(frame.AttributeNameField, i); } else if (index < i) { @@ -729,7 +724,7 @@ namespace Microsoft.AspNetCore.Components.Rendering // This is the case for a null event handler, or bool false value. // // We need to update our tracking, in case the attribute appeared 3 or more times. - seenAttributeNames[frame.AttributeName] = i; + seenAttributeNames[frame.AttributeNameField] = i; } else if (index > i) { @@ -760,7 +755,7 @@ namespace Microsoft.AspNetCore.Components.Rendering for (var i = first; i < _entries.Count; i++) { ref var frame = ref buffer[i]; - if (frame.FrameType != RenderTreeFrameType.None) + if (frame.FrameTypeField != RenderTreeFrameType.None) { buffer[offset++] = frame; } diff --git a/src/Components/Components/src/Rendering/RenderTreeUpdater.cs b/src/Components/Components/src/Rendering/RenderTreeUpdater.cs index 139b3e09d7..2f5db8b146 100644 --- a/src/Components/Components/src/Rendering/RenderTreeUpdater.cs +++ b/src/Components/Components/src/Rendering/RenderTreeUpdater.cs @@ -24,21 +24,21 @@ namespace Microsoft.AspNetCore.Components.Rendering for (var frameIndex = 0; frameIndex < framesLength; frameIndex++) { ref var frame = ref framesArray[frameIndex]; - switch (frame.FrameType) + switch (frame.FrameTypeField) { case RenderTreeFrameType.Element: closestElementFrameIndex = frameIndex; break; case RenderTreeFrameType.Attribute: - if (frame.AttributeEventHandlerId == eventHandlerId) + if (frame.AttributeEventHandlerIdField == eventHandlerId) { - if (!string.IsNullOrEmpty(frame.AttributeEventUpdatesAttributeName)) + if (!string.IsNullOrEmpty(frame.AttributeEventUpdatesAttributeNameField)) { UpdateFrameToMatchClientState( renderTreeBuilder, framesArray, closestElementFrameIndex, - frame.AttributeEventUpdatesAttributeName, + frame.AttributeEventUpdatesAttributeNameField, newFieldValue); } @@ -55,20 +55,20 @@ namespace Microsoft.AspNetCore.Components.Rendering { // Find the attribute frame ref var elementFrame = ref framesArray[elementFrameIndex]; - var elementSubtreeEndIndexExcl = elementFrameIndex + elementFrame.ElementSubtreeLength; + var elementSubtreeEndIndexExcl = elementFrameIndex + elementFrame.ElementSubtreeLengthField; for (var attributeFrameIndex = elementFrameIndex + 1; attributeFrameIndex < elementSubtreeEndIndexExcl; attributeFrameIndex++) { ref var attributeFrame = ref framesArray[attributeFrameIndex]; - if (attributeFrame.FrameType != RenderTreeFrameType.Attribute) + if (attributeFrame.FrameTypeField != RenderTreeFrameType.Attribute) { // We're now looking at the descendants not attributes, so the search is over break; } - if (attributeFrame.AttributeName == attributeName) + if (attributeFrame.AttributeNameField == attributeName) { // Found an existing attribute we can update - attributeFrame = attributeFrame.WithAttributeValue(attributeValue); + attributeFrame.AttributeValueField = attributeValue; return; } } @@ -84,25 +84,25 @@ namespace Microsoft.AspNetCore.Components.Rendering for (var otherFrameIndex = elementFrameIndex; otherFrameIndex >= 0; otherFrameIndex--) { ref var otherFrame = ref framesArray[otherFrameIndex]; - switch (otherFrame.FrameType) + switch (otherFrame.FrameTypeField) { case RenderTreeFrameType.Element: { - var otherFrameSubtreeLength = otherFrame.ElementSubtreeLength; + var otherFrameSubtreeLength = otherFrame.ElementSubtreeLengthField; var otherFrameEndIndexExcl = otherFrameIndex + otherFrameSubtreeLength; if (otherFrameEndIndexExcl > elementFrameIndex) // i.e., contains the element we're inserting into { - otherFrame = otherFrame.WithElementSubtreeLength(otherFrameSubtreeLength + 1); + otherFrame.ElementSubtreeLengthField = otherFrameSubtreeLength + 1; } break; } case RenderTreeFrameType.Region: { - var otherFrameSubtreeLength = otherFrame.RegionSubtreeLength; + var otherFrameSubtreeLength = otherFrame.RegionSubtreeLengthField; var otherFrameEndIndexExcl = otherFrameIndex + otherFrameSubtreeLength; if (otherFrameEndIndexExcl > elementFrameIndex) // i.e., contains the element we're inserting into { - otherFrame = otherFrame.WithRegionSubtreeLength(otherFrameSubtreeLength + 1); + otherFrame.RegionSubtreeLengthField = otherFrameSubtreeLength + 1; } break; } diff --git a/src/Components/Shared/src/ArrayBuilder.cs b/src/Components/Shared/src/ArrayBuilder.cs index 8efa1c7baf..afb7b01987 100644 --- a/src/Components/Shared/src/ArrayBuilder.cs +++ b/src/Components/Shared/src/ArrayBuilder.cs @@ -28,8 +28,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree internal class ArrayBuilder : IDisposable { // The following fields are memory mapped to the WASM client. Do not re-order or use auto-properties. - private T[] _items; - private int _itemsInUse; + protected T[] _items; + protected int _itemsInUse; private static readonly T[] Empty = Array.Empty(); private readonly ArrayPool _arrayPool; @@ -139,7 +139,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree ThrowIndexOutOfBoundsException(); } - // Same expansion logic as elsewhere if (_itemsInUse == _items.Length) { GrowBuffer(_items.Length * 2); @@ -162,7 +161,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree _itemsInUse = 0; } - private void GrowBuffer(int desiredCapacity) + protected void GrowBuffer(int desiredCapacity) { // When we dispose, we set the count back to zero and return the array. //