Replace "DescendantsEndIndex" concept with "SubtreeLength"

In other words, use relative addressing so that frame data is
independent of its position in the array
This commit is contained in:
Steve Sanderson 2018-02-06 22:12:02 +00:00
parent c4412c9739
commit 33932f41fc
10 changed files with 79 additions and 74 deletions

View File

@ -115,15 +115,15 @@ export class BrowserRenderer {
insertNodeIntoDOM(newDomElement, parent, childIndex);
// Apply attributes
const descendantsEndIndex = renderTreeFrame.descendantsEndIndex(frame);
for (let descendantIndex = frameIndex + 1; descendantIndex <= descendantsEndIndex; descendantIndex++) {
const descendantsEndIndexExcl = frameIndex + renderTreeFrame.subtreeLength(frame);
for (let descendantIndex = frameIndex + 1; descendantIndex < descendantsEndIndexExcl; descendantIndex++) {
const descendantFrame = getTreeFramePtr(frames, descendantIndex);
if (renderTreeFrame.frameType(descendantFrame) === FrameType.attribute) {
this.applyAttribute(componentId, newDomElement, descendantFrame, descendantIndex);
} else {
// As soon as we see a non-attribute child, all the subsequent child frames are
// not attributes, so bail out and insert the remnants recursively
this.insertFrameRange(componentId, newDomElement, 0, frames, descendantIndex, descendantsEndIndex);
this.insertFrameRange(componentId, newDomElement, 0, frames, descendantIndex, descendantsEndIndexExcl);
break;
}
}
@ -197,16 +197,16 @@ export class BrowserRenderer {
}
}
insertFrameRange(componentId: number, parent: Element, childIndex: number, frames: System_Array<RenderTreeFramePointer>, startIndex: number, endIndex: number) {
for (let index = startIndex; index <= endIndex; index++) {
insertFrameRange(componentId: number, parent: Element, childIndex: number, frames: System_Array<RenderTreeFramePointer>, startIndex: number, endIndexExcl: number) {
for (let index = startIndex; index < endIndexExcl; index++) {
const frame = getTreeFramePtr(frames, index);
this.insertFrame(componentId, parent, childIndex, frames, frame, index);
childIndex++;
// Skip over any descendants, since they are already dealt with recursively
const descendantsEndIndex = renderTreeFrame.descendantsEndIndex(frame);
if (descendantsEndIndex > 0) {
index = descendantsEndIndex;
const subtreeLength = renderTreeFrame.subtreeLength(frame);
if (subtreeLength > 1) {
index += subtreeLength - 1;
}
}
}

View File

@ -13,7 +13,7 @@ export function getTreeFramePtr(renderTreeEntries: System_Array<RenderTreeFrameP
export const renderTreeFrame = {
// The properties and memory layout must be kept in sync with the .NET equivalent in RenderTreeFrame.cs
frameType: (frame: RenderTreeFramePointer) => platform.readInt32Field(frame, 4) as FrameType,
descendantsEndIndex: (frame: RenderTreeFramePointer) => platform.readInt32Field(frame, 8) as FrameType,
subtreeLength: (frame: RenderTreeFramePointer) => platform.readInt32Field(frame, 8) as FrameType,
componentId: (frame: RenderTreeFramePointer) => platform.readInt32Field(frame, 12),
elementName: (frame: RenderTreeFramePointer) => platform.readStringField(frame, 16),
textContent: (frame: RenderTreeFramePointer) => platform.readStringField(frame, 16),

View File

@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
{
var indexOfEntryBeingClosed = _openElementIndices.Pop();
ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed];
entry = entry.WithElementDescendantsEndIndex(_entries.Count - 1);
entry = entry.WithElementSubtreeLength(_entries.Count - indexOfEntryBeingClosed);
}
/// <summary>
@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
{
var indexOfEntryBeingClosed = _openElementIndices.Pop();
ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed];
entry = entry.WithComponentDescendantsEndIndex(_entries.Count - 1);
entry = entry.WithComponentSubtreeLength(_entries.Count - indexOfEntryBeingClosed);
}
/// <summary>

View File

@ -202,10 +202,10 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
// Now locate any added/changed/removed properties
var oldStartIndex = oldComponentIndex + 1;
var newStartIndex = newComponentIndex + 1;
var oldEndIndexIncl = oldComponentFrame.ElementDescendantsEndIndex;
var newEndIndexIncl = newComponentFrame.ElementDescendantsEndIndex;
var hasMoreOld = oldEndIndexIncl >= oldStartIndex;
var hasMoreNew = newEndIndexIncl >= newStartIndex;
var oldEndIndexExcl = oldComponentIndex + oldComponentFrame.ComponentSubtreeLength;
var newEndIndexExcl = newComponentIndex + newComponentFrame.ComponentSubtreeLength;
var hasMoreOld = oldEndIndexExcl > oldStartIndex;
var hasMoreNew = newEndIndexExcl > newStartIndex;
while (hasMoreOld || hasMoreNew)
{
var oldSeq = hasMoreOld ? oldTree[oldStartIndex].Sequence : int.MaxValue;
@ -240,8 +240,8 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
oldStartIndex++;
newStartIndex++;
hasMoreOld = oldEndIndexIncl >= oldStartIndex;
hasMoreNew = newEndIndexIncl >= newStartIndex;
hasMoreOld = oldEndIndexExcl > oldStartIndex;
hasMoreNew = newEndIndexExcl > newStartIndex;
}
else
{
@ -256,7 +256,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
SetChildComponentProperty(componentInstance, newFrame.AttributeName, newFrame.AttributeValue);
hasSetAnyProperty = true;
newStartIndex++;
hasMoreNew = newEndIndexIncl >= newStartIndex;
hasMoreNew = newEndIndexExcl > newStartIndex;
}
else
{
@ -264,7 +264,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
RemoveChildComponentProperty(componentInstance, oldFrame.AttributeName);
hasSetAnyProperty = true;
oldStartIndex++;
hasMoreOld = oldEndIndexIncl >= oldStartIndex;
hasMoreOld = oldEndIndexExcl > oldStartIndex;
}
}
}
@ -312,10 +312,13 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
return property;
}
private static int NextSiblingIndex(in RenderTreeFrame frame, int frameIndex)
private static int NextSiblingIndex(RenderTreeFrame frame, int frameIndex)
{
var descendantsEndIndex = frame.ElementDescendantsEndIndex;
return (descendantsEndIndex == 0 ? frameIndex : descendantsEndIndex) + 1;
var subtreeLength = frame.ElementSubtreeLength;
var distanceToNextSibling = subtreeLength == 0
? 1 // For frames that don't have a subtree length set, such as text frames
: subtreeLength; // For element or component frames
return frameIndex + distanceToNextSibling;
}
private void AppendDiffEntriesForFramesWithSameSequence(
@ -360,8 +363,8 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
ref siblingIndex);
// Diff the children
var oldFrameChildrenEndIndexExcl = oldFrame.ElementDescendantsEndIndex + 1;
var newFrameChildrenEndIndexExcl = newFrame.ElementDescendantsEndIndex + 1;
var oldFrameChildrenEndIndexExcl = oldFrameIndex + oldFrame.ElementSubtreeLength;
var newFrameChildrenEndIndexExcl = newFrameIndex + newFrame.ElementSubtreeLength;
var hasChildrenToProcess =
oldFrameChildrenEndIndexExcl > oldFrameAttributesEndIndexExcl ||
newFrameChildrenEndIndexExcl > newFrameAttributesEndIndexExcl;
@ -450,9 +453,9 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
private int GetAttributesEndIndexExclusive(RenderTreeFrame[] tree, int rootIndex)
{
var descendantsEndIndex = tree[rootIndex].ElementDescendantsEndIndex;
var descendantsEndIndexExcl = rootIndex + tree[rootIndex].ElementSubtreeLength;
var index = rootIndex + 1;
for (; index <= descendantsEndIndex; index++)
for (; index < descendantsEndIndexExcl; index++)
{
if (tree[index].FrameType != RenderTreeFrameType.Attribute)
{
@ -481,8 +484,8 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
private void InstantiateChildComponents(RenderBatchBuilder batchBuilder, RenderTreeFrame[] frames, int elementOrComponentIndex)
{
var endIndex = frames[elementOrComponentIndex].ElementDescendantsEndIndex;
for (var i = elementOrComponentIndex; i <= endIndex; i++)
var endIndexExcl = elementOrComponentIndex + frames[elementOrComponentIndex].ElementSubtreeLength;
for (var i = elementOrComponentIndex; i < endIndexExcl; i++)
{
ref var frame = ref frames[i];
if (frame.FrameType == RenderTreeFrameType.Component)
@ -496,8 +499,8 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
var childComponentInstance = frame.Component;
// All descendants of a component are its properties
var componentDescendantsEndIndex = frame.ElementDescendantsEndIndex;
for (var attributeFrameIndex = i + 1; attributeFrameIndex <= componentDescendantsEndIndex; attributeFrameIndex++)
var componentDescendantsEndIndexExcl = i + frame.ComponentSubtreeLength;
for (var attributeFrameIndex = i + 1; attributeFrameIndex < componentDescendantsEndIndexExcl; attributeFrameIndex++)
{
ref var attributeFrame = ref frames[attributeFrameIndex];
SetChildComponentProperty(
@ -530,8 +533,8 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
private void DisposeChildComponents(RenderBatchBuilder batchBuilder, RenderTreeFrame[] frames, int elementOrComponentIndex)
{
var endIndex = frames[elementOrComponentIndex].ElementDescendantsEndIndex;
for (var i = elementOrComponentIndex; i <= endIndex; i++)
var endIndexExcl = elementOrComponentIndex + frames[elementOrComponentIndex].ElementSubtreeLength;
for (var i = elementOrComponentIndex; i < endIndexExcl; i++)
{
ref var frame = ref frames[i];
if (frame.FrameType == RenderTreeFrameType.Component)

View File

@ -45,11 +45,11 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
// --------------------------------------------------------------------------------
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Element"/>,
/// gets the index of the final descendant frame in the tree. The value is
/// zero if the frame has not yet been closed.
/// 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 ElementDescendantsEndIndex;
[FieldOffset(8)] public readonly int ElementSubtreeLength;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Element"/>,
@ -88,11 +88,11 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
// --------------------------------------------------------------------------------
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
/// gets the index of the final descendant frame in the tree. The value is
/// zero if the frame has not yet been closed.
/// 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 ComponentDescendantsEndIndex;
[FieldOffset(8)] public readonly int ComponentSubtreeLength;
/// <summary>
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
@ -112,26 +112,26 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
/// </summary>
[FieldOffset(24)] public readonly IComponent Component;
private RenderTreeFrame(int sequence, string elementName, int descendantsEndIndex)
private RenderTreeFrame(int sequence, string elementName, int elementSubtreeLength)
: this()
{
FrameType = RenderTreeFrameType.Element;
Sequence = sequence;
ElementName = elementName;
ElementDescendantsEndIndex = descendantsEndIndex;
ElementSubtreeLength = elementSubtreeLength;
}
private RenderTreeFrame(int sequence, Type componentType, int descendantsEndIndex)
private RenderTreeFrame(int sequence, Type componentType, int componentSubtreeLength)
: this()
{
FrameType = RenderTreeFrameType.Component;
Sequence = sequence;
ComponentType = componentType;
ComponentDescendantsEndIndex = descendantsEndIndex;
ComponentSubtreeLength = componentSubtreeLength;
}
private RenderTreeFrame(int sequence, Type componentType, int descendantsEndIndex, int componentId, IComponent component)
: this(sequence, componentType, descendantsEndIndex)
private RenderTreeFrame(int sequence, Type componentType, int subtreeLength, int componentId, IComponent component)
: this(sequence, componentType, subtreeLength)
{
ComponentId = componentId;
Component = component;
@ -155,7 +155,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
}
internal static RenderTreeFrame Element(int sequence, string elementName)
=> new RenderTreeFrame(sequence, elementName: elementName, descendantsEndIndex: 0);
=> new RenderTreeFrame(sequence, elementName: elementName, elementSubtreeLength: 0);
internal static RenderTreeFrame Text(int sequence, string textContent)
=> new RenderTreeFrame(sequence, textContent: textContent);
@ -169,16 +169,16 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
internal static RenderTreeFrame ChildComponent<T>(int sequence) where T : IComponent
=> new RenderTreeFrame(sequence, typeof(T), 0);
internal RenderTreeFrame WithElementDescendantsEndIndex(int descendantsEndIndex)
=> new RenderTreeFrame(Sequence, elementName: ElementName, descendantsEndIndex: descendantsEndIndex);
internal RenderTreeFrame WithElementSubtreeLength(int elementSubtreeLength)
=> new RenderTreeFrame(Sequence, elementName: ElementName, elementSubtreeLength: elementSubtreeLength);
internal RenderTreeFrame WithComponentDescendantsEndIndex(int descendantsEndIndex)
=> new RenderTreeFrame(Sequence, componentType: ComponentType, descendantsEndIndex: descendantsEndIndex);
internal RenderTreeFrame WithComponentSubtreeLength(int componentSubtreeLength)
=> new RenderTreeFrame(Sequence, componentType: ComponentType, componentSubtreeLength: componentSubtreeLength);
internal RenderTreeFrame WithAttributeSequence(int sequence)
=> new RenderTreeFrame(sequence, attributeName: AttributeName, attributeValue: AttributeValue);
internal RenderTreeFrame WithComponentInstance(int componentId, IComponent component)
=> new RenderTreeFrame(Sequence, ComponentType, ComponentDescendantsEndIndex, componentId, component);
=> new RenderTreeFrame(Sequence, ComponentType, ComponentSubtreeLength, componentId, component);
}
}

View File

@ -157,7 +157,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
// Assert
Assert.Collection(GetRenderTree(component),
frame => AssertFrame.Element(frame, "myelem", 1, 0),
frame => AssertFrame.Element(frame, "myelem", 2, 0),
frame => AssertFrame.Text(frame, "Hello", 1));
}
@ -193,7 +193,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
// Assert
Assert.Collection(GetRenderTree(component),
frame => AssertFrame.Element(frame, "elem", 2, 0),
frame => AssertFrame.Element(frame, "elem", 3, 0),
frame => AssertFrame.Attribute(frame, "attrib-one", "Value 1", 1),
frame => AssertFrame.Attribute(frame, "a2", "v2", 2));
}
@ -208,7 +208,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
// Assert
Assert.Collection(GetRenderTree(component),
frame => AssertFrame.Element(frame, "elem", 1, 0),
frame => AssertFrame.Element(frame, "elem", 2, 0),
frame => AssertFrame.Attribute(frame, "attr", "My string", 1));
}
@ -222,7 +222,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
// Assert
Assert.Collection(GetRenderTree(component),
frame => AssertFrame.Element(frame, "elem", 1, 0),
frame => AssertFrame.Element(frame, "elem", 2, 0),
frame => AssertFrame.Attribute(frame, "attr", "123", 1));
}
@ -236,7 +236,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
// Assert
Assert.Collection(GetRenderTree(component),
frame => AssertFrame.Element(frame, "elem", 1, 0),
frame => AssertFrame.Element(frame, "elem", 2, 0),
frame => AssertFrame.Attribute(frame, "attr", "Hello, WORLD with number 246!", 1));
}
@ -259,7 +259,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
// Assert
Assert.False((bool)handlerWasCalledProperty.GetValue(component));
Assert.Collection(GetRenderTree(component),
frame => AssertFrame.Element(frame, "elem", 1, 0),
frame => AssertFrame.Element(frame, "elem", 2, 0),
frame =>
{
Assert.Equal(RenderTreeFrameType.Attribute, frame.FrameType);
@ -287,7 +287,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
// Assert
Assert.False((bool)didInvokeCodeProperty.GetValue(component));
Assert.Collection(frames,
frame => AssertFrame.Element(frame, "elem", 1, 0),
frame => AssertFrame.Element(frame, "elem", 2, 0),
frame =>
{
Assert.Equal(RenderTreeFrameType.Attribute, frame.FrameType);
@ -336,7 +336,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
// Assert
Assert.False((bool)didInvokeCodeProperty.GetValue(component));
Assert.Collection(GetRenderTree(component),
frame => AssertFrame.Element(frame, "elem", 1, 0),
frame => AssertFrame.Element(frame, "elem", 2, 0),
frame =>
{
Assert.Equal(RenderTreeFrameType.Attribute, frame.FrameType);

View File

@ -73,7 +73,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
}
[Fact]
public void UnclosedElementsHaveNoEndDescendantIndex()
public void UnclosedElementsHaveNoSubtreeLength()
{
// Arrange
var builder = new RenderTreeBuilder(new TestRenderer());
@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
}
[Fact]
public void ClosedEmptyElementsHaveSelfAsEndDescendantIndex()
public void ClosedEmptyElementsHaveSubtreeLengthOne()
{
// Arrange
var builder = new RenderTreeBuilder(new TestRenderer());
@ -104,7 +104,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
}
[Fact]
public void ClosedElementsHaveCorrectEndDescendantIndex()
public void ClosedElementsHaveCorrectSubtreeLength()
{
// Arrange
var builder = new RenderTreeBuilder(new TestRenderer());
@ -119,7 +119,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
// Assert
var frames = builder.GetFrames();
Assert.Equal(4, frames.Count);
AssertFrame.Element(frames.Array[0], "my element", 2);
AssertFrame.Element(frames.Array[0], "my element", 3);
}
[Fact]
@ -152,13 +152,13 @@ namespace Microsoft.AspNetCore.Blazor.Test
frame => AssertFrame.Element(frame, "root", 10),
frame => AssertFrame.Text(frame, "root text 1"),
frame => AssertFrame.Text(frame, "root text 2"),
frame => AssertFrame.Element(frame, "child", 8),
frame => AssertFrame.Element(frame, "child", 5),
frame => AssertFrame.Text(frame, "child text"),
frame => AssertFrame.Element(frame, "grandchild", 8),
frame => AssertFrame.Element(frame, "grandchild", 3),
frame => AssertFrame.Text(frame, "grandchild text 1"),
frame => AssertFrame.Text(frame, "grandchild text 2"),
frame => AssertFrame.Text(frame, "root text 3"),
frame => AssertFrame.Element(frame, "child 2", 10),
frame => AssertFrame.Element(frame, "child 2", 1),
frame => AssertFrame.Text(frame, "standalone text 2"));
}
@ -181,10 +181,10 @@ namespace Microsoft.AspNetCore.Blazor.Test
// Assert
Assert.Collection(builder.GetFrames(),
frame => AssertFrame.Element(frame, "myelement", 5),
frame => AssertFrame.Element(frame, "myelement", 6),
frame => AssertFrame.Attribute(frame, "attribute1", "value 1"),
frame => AssertFrame.Attribute(frame, "attribute2", "123"),
frame => AssertFrame.Element(frame, "child", 5),
frame => AssertFrame.Element(frame, "child", 3),
frame => AssertFrame.Attribute(frame, "childevent", eventHandler),
frame => AssertFrame.Text(frame, "some text"));
}
@ -264,7 +264,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
// Assert
Assert.Collection(builder.GetFrames(),
frame => AssertFrame.Element(frame, "parent", 5),
frame => AssertFrame.Element(frame, "parent", 6),
frame => AssertFrame.Component<TestComponent>(frame),
frame => AssertFrame.Attribute(frame, "child1attribute1", "A"),
frame => AssertFrame.Attribute(frame, "child1attribute2", "B"),

View File

@ -313,7 +313,9 @@ namespace Microsoft.AspNetCore.Blazor.Test
{
// Arrange
oldTree.OpenComponent<FakeComponent>(123);
oldTree.CloseComponent();
newTree.OpenComponent<FakeComponent2>(123);
newTree.CloseComponent();
// Act
var renderBatch = GetRenderedBatch();

View File

@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
// Assert
Assert.Collection(renderer.Batches.Single().RenderTreesByComponentId[componentId],
frame => AssertFrame.Element(frame, "my element", 1),
frame => AssertFrame.Element(frame, "my element", 2),
frame => AssertFrame.Text(frame, "some text"));
}

View File

@ -22,15 +22,15 @@ namespace Microsoft.AspNetCore.Blazor.Test.Shared
{
Assert.Equal(RenderTreeFrameType.Text, frame.FrameType);
Assert.Equal(textContent, frame.TextContent);
Assert.Equal(0, frame.ElementDescendantsEndIndex);
Assert.Equal(0, frame.ElementSubtreeLength);
AssertFrame.Sequence(frame, sequence);
}
public static void Element(RenderTreeFrame frame, string elementName, int descendantsEndIndex, int? sequence = null)
public static void Element(RenderTreeFrame frame, string elementName, int subtreeLength, int? sequence = null)
{
Assert.Equal(RenderTreeFrameType.Element, frame.FrameType);
Assert.Equal(elementName, frame.ElementName);
Assert.Equal(descendantsEndIndex, frame.ElementDescendantsEndIndex);
Assert.Equal(subtreeLength, frame.ElementSubtreeLength);
AssertFrame.Sequence(frame, sequence);
}