Optimize render tree building via RenderTreeFrameArrayBuilder (#24484)
This commit is contained in:
parent
749450ac7b
commit
38e166fd59
|
|
@ -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<string, object> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 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).
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public readonly int Sequence;
|
||||
public int Sequence => SequenceField;
|
||||
|
||||
/// <summary>
|
||||
/// Describes the type of this frame.
|
||||
/// </summary>
|
||||
[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;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Element"/>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public readonly int ElementSubtreeLength;
|
||||
public int ElementSubtreeLength => ElementSubtreeLengthField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Element"/>,
|
||||
/// gets a name representing the type of the element. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public readonly string ElementName;
|
||||
public string ElementName => ElementNameField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Element"/>,
|
||||
/// gets the element's diffing key, or null if none was specified.
|
||||
/// </summary>
|
||||
[FieldOffset(24)] public readonly object ElementKey;
|
||||
public object ElementKey => ElementKeyField;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// RenderTreeFrameType.Text
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
[FieldOffset(16)] internal string TextContentField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Text"/>,
|
||||
/// gets the content of the text frame. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[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;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>
|
||||
/// gets the ID of the corresponding event handler, if any.
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public readonly ulong AttributeEventHandlerId;
|
||||
public ulong AttributeEventHandlerId => AttributeEventHandlerIdField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>,
|
||||
/// gets the attribute name. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public readonly string AttributeName;
|
||||
public string AttributeName => AttributeNameField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>,
|
||||
/// gets the attribute value. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[FieldOffset(24)] public readonly object AttributeValue;
|
||||
public object AttributeValue => AttributeValueField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>,
|
||||
|
|
@ -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.
|
||||
/// </summary>
|
||||
[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;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public readonly int ComponentSubtreeLength;
|
||||
public int ComponentSubtreeLength => ComponentSubtreeLengthField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
|
||||
/// gets the child component instance identifier.
|
||||
/// </summary>
|
||||
[FieldOffset(12)] public readonly int ComponentId;
|
||||
public int ComponentId => ComponentIdField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
|
||||
/// gets the type of the child component.
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public readonly Type ComponentType;
|
||||
public Type ComponentType => ComponentTypeField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
|
||||
/// gets the child component state object. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[FieldOffset(24)] internal readonly ComponentState ComponentState;
|
||||
internal ComponentState ComponentState => ComponentStateField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
|
||||
/// gets the component's diffing key, or null if none was specified.
|
||||
/// </summary>
|
||||
[FieldOffset(32)] public readonly object ComponentKey;
|
||||
public object ComponentKey => ComponentKeyField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
|
||||
/// gets the child component instance. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
public IComponent Component => ComponentState?.Component;
|
||||
public IComponent Component => ComponentStateField?.Component;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// RenderTreeFrameType.Region
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
[FieldOffset(8)] internal int RegionSubtreeLengthField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Region"/>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public readonly int RegionSubtreeLength;
|
||||
public int RegionSubtreeLength => RegionSubtreeLengthField;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// RenderTreeFrameType.ElementReferenceCapture
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
[FieldOffset(16)] internal string ElementReferenceCaptureIdField;
|
||||
[FieldOffset(24)] internal Action<ElementReference> ElementReferenceCaptureActionField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.ElementReferenceCapture"/>,
|
||||
/// gets the ID of the reference capture. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public readonly string ElementReferenceCaptureId;
|
||||
public string ElementReferenceCaptureId => ElementReferenceCaptureIdField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.ElementReferenceCapture"/>,
|
||||
/// gets the action that writes the reference to its target. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[FieldOffset(24)] public readonly Action<ElementReference> ElementReferenceCaptureAction;
|
||||
public Action<ElementReference> ElementReferenceCaptureAction => ElementReferenceCaptureActionField;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// RenderTreeFrameType.ComponentReferenceCapture
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
[FieldOffset(8)] internal int ComponentReferenceCaptureParentFrameIndexField;
|
||||
[FieldOffset(16)] internal Action<object> ComponentReferenceCaptureActionField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.ComponentReferenceCapture"/>,
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public readonly int ComponentReferenceCaptureParentFrameIndex;
|
||||
public int ComponentReferenceCaptureParentFrameIndex => ComponentReferenceCaptureParentFrameIndexField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.ComponentReferenceCapture"/>,
|
||||
/// gets the action that writes the reference to its target. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public readonly Action<object> ComponentReferenceCaptureAction;
|
||||
public Action<object> ComponentReferenceCaptureAction => ComponentReferenceCaptureActionField;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// RenderTreeFrameType.Markup
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
[FieldOffset(16)] internal string MarkupContentField;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Markup"/>,
|
||||
/// gets the content of the markup frame. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[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<ElementReference> 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<object> 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);
|
||||
|
||||
/// <inheritdoc />
|
||||
// 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}";
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// A special subclass of <see cref="ArrayBuilder{T}"/> that contains methods optimized for appending <see cref="RenderTreeFrame"/> entries.
|
||||
/// </summary>
|
||||
internal class RenderTreeFrameArrayBuilder : ArrayBuilder<RenderTreeFrame>
|
||||
{
|
||||
// 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<ElementReference> 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<object?> 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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<T> here. EventCallback<T> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -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<RenderTreeFrame> _entries = new ArrayBuilder<RenderTreeFrame>();
|
||||
private readonly RenderTreeFrameArrayBuilder _entries = new RenderTreeFrameArrayBuilder();
|
||||
private readonly Stack<int> _openElementIndices = new Stack<int>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -82,7 +82,8 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
/// <param name="markupContent">Content for the new markup frame.</param>
|
||||
public void AddMarkupContent(int sequence, string? markupContent)
|
||||
{
|
||||
Append(RenderTreeFrame.Markup(sequence, markupContent ?? string.Empty));
|
||||
_entries.AppendMarkup(sequence, markupContent ?? string.Empty);
|
||||
_lastNonAttributeFrameType = RenderTreeFrameType.Markup;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -92,7 +93,8 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
/// <param name="textContent">Content for the new text frame.</param>
|
||||
public void AddContent(int sequence, string? textContent)
|
||||
{
|
||||
Append(RenderTreeFrame.Text(sequence, textContent ?? string.Empty));
|
||||
_entries.AppendText(sequence, textContent ?? string.Empty);
|
||||
_lastNonAttributeFrameType = RenderTreeFrameType.Text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -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<T> 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
|
|||
/// </summary>
|
||||
/// <param name="sequence">An integer that represents the position of the instruction in the source code.</param>
|
||||
/// <param name="frame">A <see cref="RenderTreeFrame"/> holding the name and value of the attribute.</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -614,7 +620,8 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
}
|
||||
|
||||
_openElementIndices.Push(_entries.Count);
|
||||
Append(RenderTreeFrame.Region(sequence));
|
||||
_entries.AppendRegion(sequence);
|
||||
_lastNonAttributeFrameType = RenderTreeFrameType.Region;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -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<RenderTreeFrame> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree
|
|||
internal class ArrayBuilder<T> : 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<T>();
|
||||
private readonly ArrayPool<T> _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.
|
||||
//
|
||||
|
|
|
|||
Loading…
Reference in New Issue