* Change event handler IDs to be longs * Update unit tests * Enable detailed errors for test app * CR: Explicitly check if we exceed Number.MAX_SAFE_INTEGER * Update ref assemblies
This commit is contained in:
parent
010ffe6121
commit
4e04b81415
|
|
@ -62,7 +62,7 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
|
|||
public override Microsoft.AspNetCore.Components.Dispatcher Dispatcher { get { throw null; } }
|
||||
public System.Threading.Tasks.Task AddComponentAsync(System.Type componentType, string domElementSelector) { throw null; }
|
||||
public System.Threading.Tasks.Task AddComponentAsync<TComponent>(string domElementSelector) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; }
|
||||
public override System.Threading.Tasks.Task DispatchEventAsync(int eventHandlerId, Microsoft.AspNetCore.Components.Rendering.EventFieldInfo eventFieldInfo, Microsoft.AspNetCore.Components.UIEventArgs eventArgs) { throw null; }
|
||||
public override System.Threading.Tasks.Task DispatchEventAsync(ulong eventHandlerId, Microsoft.AspNetCore.Components.Rendering.EventFieldInfo eventFieldInfo, Microsoft.AspNetCore.Components.UIEventArgs eventArgs) { throw null; }
|
||||
protected override void Dispose(bool disposing) { }
|
||||
protected override void HandleException(System.Exception exception) { }
|
||||
protected override System.Threading.Tasks.Task UpdateDisplayAsync(in Microsoft.AspNetCore.Components.Rendering.RenderBatch batch) { throw null; }
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task DispatchEventAsync(int eventHandlerId, EventFieldInfo eventFieldInfo, UIEventArgs eventArgs)
|
||||
public override Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo eventFieldInfo, UIEventArgs eventArgs)
|
||||
{
|
||||
// Be sure we only run one event handler at once. Although they couldn't run
|
||||
// simultaneously anyway (there's only one thread), they could run nested on
|
||||
|
|
@ -183,12 +183,12 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
|
|||
|
||||
readonly struct IncomingEventInfo
|
||||
{
|
||||
public readonly int EventHandlerId;
|
||||
public readonly ulong EventHandlerId;
|
||||
public readonly EventFieldInfo EventFieldInfo;
|
||||
public readonly UIEventArgs EventArgs;
|
||||
public readonly TaskCompletionSource<object> TaskCompletionSource;
|
||||
|
||||
public IncomingEventInfo(int eventHandlerId, EventFieldInfo eventFieldInfo, UIEventArgs eventArgs)
|
||||
public IncomingEventInfo(ulong eventHandlerId, EventFieldInfo eventFieldInfo, UIEventArgs eventArgs)
|
||||
{
|
||||
EventHandlerId = eventHandlerId;
|
||||
EventFieldInfo = eventFieldInfo;
|
||||
|
|
|
|||
|
|
@ -724,7 +724,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
{
|
||||
private readonly object _dummy;
|
||||
public Microsoft.AspNetCore.Components.RenderTree.ArrayRange<int> DisposedComponentIDs { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
|
||||
public Microsoft.AspNetCore.Components.RenderTree.ArrayRange<int> DisposedEventHandlerIDs { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
|
||||
public Microsoft.AspNetCore.Components.RenderTree.ArrayRange<ulong> DisposedEventHandlerIDs { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
|
||||
public Microsoft.AspNetCore.Components.RenderTree.ArrayRange<Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrame> ReferenceFrames { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
|
||||
public Microsoft.AspNetCore.Components.RenderTree.ArrayRange<Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiff> UpdatedComponents { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
|
||||
}
|
||||
|
|
@ -735,7 +735,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
public event System.UnhandledExceptionEventHandler UnhandledSynchronizationException { add { } remove { } }
|
||||
protected internal virtual void AddToRenderQueue(int componentId, Microsoft.AspNetCore.Components.RenderFragment renderFragment) { }
|
||||
protected internal int AssignRootComponentId(Microsoft.AspNetCore.Components.IComponent component) { throw null; }
|
||||
public virtual System.Threading.Tasks.Task DispatchEventAsync(int eventHandlerId, Microsoft.AspNetCore.Components.Rendering.EventFieldInfo fieldInfo, Microsoft.AspNetCore.Components.UIEventArgs eventArgs) { throw null; }
|
||||
public virtual System.Threading.Tasks.Task DispatchEventAsync(ulong eventHandlerId, Microsoft.AspNetCore.Components.Rendering.EventFieldInfo fieldInfo, Microsoft.AspNetCore.Components.UIEventArgs eventArgs) { throw null; }
|
||||
public void Dispose() { }
|
||||
protected virtual void Dispose(bool disposing) { }
|
||||
protected abstract void HandleException(System.Exception exception);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
|
|||
/// <summary>
|
||||
/// Represents an entry in a tree of user interface (UI) items.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 4)]
|
||||
public readonly struct RenderTreeFrame
|
||||
{
|
||||
// Note that the struct layout has to be valid in both 32-bit and 64-bit runtime platforms,
|
||||
|
|
@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
|
|||
/// 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 int AttributeEventHandlerId;
|
||||
[FieldOffset(8)] public readonly ulong AttributeEventHandlerId;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>,
|
||||
|
|
@ -267,7 +267,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
|
|||
}
|
||||
|
||||
// Attribute constructor
|
||||
private RenderTreeFrame(int sequence, string attributeName, object attributeValue, int attributeEventHandlerId, string attributeEventUpdatesAttributeName)
|
||||
private RenderTreeFrame(int sequence, string attributeName, object attributeValue, ulong attributeEventHandlerId, string attributeEventUpdatesAttributeName)
|
||||
: this()
|
||||
{
|
||||
FrameType = RenderTreeFrameType.Attribute;
|
||||
|
|
@ -337,7 +337,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
|
|||
internal RenderTreeFrame WithComponent(ComponentState componentState)
|
||||
=> new RenderTreeFrame(Sequence, componentSubtreeLength: ComponentSubtreeLength, ComponentType, componentState, ComponentKey);
|
||||
|
||||
internal RenderTreeFrame WithAttributeEventHandlerId(int eventHandlerId)
|
||||
internal RenderTreeFrame WithAttributeEventHandlerId(ulong eventHandlerId)
|
||||
=> new RenderTreeFrame(Sequence, attributeName: AttributeName, AttributeValue, eventHandlerId, AttributeEventUpdatesAttributeName);
|
||||
|
||||
internal RenderTreeFrame WithAttributeValue(object attributeValue)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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 Microsoft.AspNetCore.Components.RenderTree;
|
||||
|
|
@ -30,13 +30,13 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
/// <summary>
|
||||
/// Gets the IDs of the event handlers that were disposed.
|
||||
/// </summary>
|
||||
public ArrayRange<int> DisposedEventHandlerIDs { get; }
|
||||
public ArrayRange<ulong> DisposedEventHandlerIDs { get; }
|
||||
|
||||
internal RenderBatch(
|
||||
ArrayRange<RenderTreeDiff> updatedComponents,
|
||||
ArrayRange<RenderTreeFrame> referenceFrames,
|
||||
ArrayRange<int> disposedComponentIDs,
|
||||
ArrayRange<int> disposedEventHandlerIDs)
|
||||
ArrayRange<ulong> disposedEventHandlerIDs)
|
||||
{
|
||||
UpdatedComponents = updatedComponents;
|
||||
ReferenceFrames = referenceFrames;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
// Primary result data
|
||||
public ArrayBuilder<RenderTreeDiff> UpdatedComponentDiffs { get; } = new ArrayBuilder<RenderTreeDiff>();
|
||||
public ArrayBuilder<int> DisposedComponentIds { get; } = new ArrayBuilder<int>();
|
||||
public ArrayBuilder<int> DisposedEventHandlerIds { get; } = new ArrayBuilder<int>();
|
||||
public ArrayBuilder<ulong> DisposedEventHandlerIds { get; } = new ArrayBuilder<ulong>();
|
||||
|
||||
// Buffers referenced by UpdatedComponentDiffs
|
||||
public ArrayBuilder<RenderTreeEdit> EditsBuffer { get; } = new ArrayBuilder<RenderTreeEdit>(64);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
{
|
||||
internal class RenderTreeUpdater
|
||||
{
|
||||
public static void UpdateToMatchClientState(RenderTreeBuilder renderTreeBuilder, int eventHandlerId, object newFieldValue)
|
||||
public static void UpdateToMatchClientState(RenderTreeBuilder renderTreeBuilder, ulong eventHandlerId, object newFieldValue)
|
||||
{
|
||||
// We only allow the client to supply string or bool currently, since those are the only kinds of
|
||||
// values we output on attributes that go to the client
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
private static readonly Action<ILogger, int, Type, Exception> _disposingComponent =
|
||||
LoggerMessage.Define<int, Type>(LogLevel.Debug, new EventId(4, "DisposingComponent"), "Disposing component {ComponentId} of type {ComponentType}");
|
||||
|
||||
private static readonly Action<ILogger, int, string, Exception> _handlingEvent =
|
||||
LoggerMessage.Define<int, string>(LogLevel.Debug, new EventId(5, "HandlingEvent"), "Handling event {EventId} of type '{EventType}'");
|
||||
private static readonly Action<ILogger, ulong, string, Exception> _handlingEvent =
|
||||
LoggerMessage.Define<ulong, string>(LogLevel.Debug, new EventId(5, "HandlingEvent"), "Handling event {EventId} of type '{EventType}'");
|
||||
|
||||
public static void InitializingComponent(ILogger logger, ComponentState componentState, ComponentState parentComponentState)
|
||||
{
|
||||
|
|
@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
}
|
||||
}
|
||||
|
||||
internal static void HandlingEvent(ILogger<Renderer> logger, int eventHandlerId, UIEventArgs eventArgs)
|
||||
internal static void HandlingEvent(ILogger<Renderer> logger, ulong eventHandlerId, UIEventArgs eventArgs)
|
||||
{
|
||||
_handlingEvent(logger, eventHandlerId, eventArgs?.Type ?? "null", null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly Dictionary<int, ComponentState> _componentStateById = new Dictionary<int, ComponentState>();
|
||||
private readonly RenderBatchBuilder _batchBuilder = new RenderBatchBuilder();
|
||||
private readonly Dictionary<int, EventCallback> _eventBindings = new Dictionary<int, EventCallback>();
|
||||
private readonly Dictionary<int, int> _eventHandlerIdReplacements = new Dictionary<int, int>();
|
||||
private readonly Dictionary<ulong, EventCallback> _eventBindings = new Dictionary<ulong, EventCallback>();
|
||||
private readonly Dictionary<ulong, ulong> _eventHandlerIdReplacements = new Dictionary<ulong, ulong>();
|
||||
private readonly ILogger<Renderer> _logger;
|
||||
|
||||
private int _nextComponentId = 0; // TODO: change to 'long' when Mono .NET->JS interop supports it
|
||||
private bool _isBatchInProgress;
|
||||
private int _lastEventHandlerId = 0;
|
||||
private ulong _lastEventHandlerId;
|
||||
private List<Task> _pendingTasks;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -206,7 +206,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
/// A <see cref="Task"/> which will complete once all asynchronous processing related to the event
|
||||
/// has completed.
|
||||
/// </returns>
|
||||
public virtual Task DispatchEventAsync(int eventHandlerId, EventFieldInfo fieldInfo, UIEventArgs eventArgs)
|
||||
public virtual Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo fieldInfo, UIEventArgs eventArgs)
|
||||
{
|
||||
EnsureSynchronizationContext();
|
||||
|
||||
|
|
@ -354,7 +354,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
}
|
||||
}
|
||||
|
||||
internal void TrackReplacedEventHandlerId(int oldEventHandlerId, int newEventHandlerId)
|
||||
internal void TrackReplacedEventHandlerId(ulong oldEventHandlerId, ulong newEventHandlerId)
|
||||
{
|
||||
// Tracking the chain of old->new replacements allows us to interpret incoming EventFieldInfo
|
||||
// values even if they refer to an event handler ID that's since been superseded. This is essential
|
||||
|
|
@ -362,7 +362,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
_eventHandlerIdReplacements.Add(oldEventHandlerId, newEventHandlerId);
|
||||
}
|
||||
|
||||
private int FindLatestEventHandlerIdInChain(int eventHandlerId)
|
||||
private ulong FindLatestEventHandlerIdInChain(ulong eventHandlerId)
|
||||
{
|
||||
while (_eventHandlerIdReplacements.TryGetValue(eventHandlerId, out var replacementEventHandlerId))
|
||||
{
|
||||
|
|
@ -573,7 +573,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
}
|
||||
}
|
||||
|
||||
private void RemoveEventHandlerIds(ArrayRange<int> eventHandlerIds, Task afterTaskIgnoreErrors)
|
||||
private void RemoveEventHandlerIds(ArrayRange<ulong> eventHandlerIds, Task afterTaskIgnoreErrors)
|
||||
{
|
||||
if (eventHandlerIds.Count == 0)
|
||||
{
|
||||
|
|
@ -598,7 +598,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
|
||||
// Factor out the async part into a separate local method purely so, in the
|
||||
// synchronous case, there's no state machine or task construction
|
||||
async Task ContinueAfterTask(ArrayRange<int> eventHandlerIds, Task afterTaskIgnoreErrors)
|
||||
async Task ContinueAfterTask(ArrayRange<ulong> eventHandlerIds, Task afterTaskIgnoreErrors)
|
||||
{
|
||||
// We need to delay the actual removal (e.g., until we've confirmed the client
|
||||
// has processed the batch and hence can be sure not to reuse the handler IDs
|
||||
|
|
@ -637,7 +637,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
}
|
||||
}
|
||||
|
||||
private void UpdateRenderTreeToMatchClientState(int eventHandlerId, EventFieldInfo fieldInfo)
|
||||
private void UpdateRenderTreeToMatchClientState(ulong eventHandlerId, EventFieldInfo fieldInfo)
|
||||
{
|
||||
var componentState = GetOptionalComponentState(fieldInfo.ComponentId);
|
||||
if (componentState != null)
|
||||
|
|
|
|||
|
|
@ -859,7 +859,7 @@ namespace Microsoft.AspNetCore.Components.Test
|
|||
Assert.Equal(0, entry.ReferenceFrameIndex);
|
||||
});
|
||||
AssertFrame.Attribute(referenceFrames[0], "onbar", addedHandler);
|
||||
Assert.NotEqual(0, removedEventHandlerFrame.AttributeEventHandlerId);
|
||||
Assert.NotEqual(default, removedEventHandlerFrame.AttributeEventHandlerId);
|
||||
Assert.Equal(
|
||||
new[] { removedEventHandlerFrame.AttributeEventHandlerId },
|
||||
batchBuilder.DisposedEventHandlerIDs.AsEnumerable());
|
||||
|
|
@ -1592,7 +1592,7 @@ namespace Microsoft.AspNetCore.Components.Test
|
|||
Assert.Empty(result.Edits);
|
||||
AssertFrame.Attribute(oldAttributeFrame, "ontest", retainedHandler);
|
||||
AssertFrame.Attribute(newAttributeFrame, "ontest", retainedHandler);
|
||||
Assert.NotEqual(0, oldAttributeFrame.AttributeEventHandlerId);
|
||||
Assert.NotEqual(default, oldAttributeFrame.AttributeEventHandlerId);
|
||||
Assert.Equal(oldAttributeFrame.AttributeEventHandlerId, newAttributeFrame.AttributeEventHandlerId);
|
||||
Assert.Empty(batchBuilder.DisposedEventHandlerIDs.AsEnumerable());
|
||||
}
|
||||
|
|
@ -1619,7 +1619,7 @@ namespace Microsoft.AspNetCore.Components.Test
|
|||
Assert.Single(result.Edits);
|
||||
AssertFrame.Attribute(oldAttributeFrame, "ontest", retainedHandler);
|
||||
AssertFrame.Attribute(newAttributeFrame, "ontest", retainedHandler);
|
||||
Assert.NotEqual(0, oldAttributeFrame.AttributeEventHandlerId);
|
||||
Assert.NotEqual(default, oldAttributeFrame.AttributeEventHandlerId);
|
||||
Assert.Equal(oldAttributeFrame.AttributeEventHandlerId, newAttributeFrame.AttributeEventHandlerId);
|
||||
Assert.Empty(batchBuilder.DisposedEventHandlerIDs.AsEnumerable());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3313,7 +3313,7 @@ namespace Microsoft.AspNetCore.Components.Test
|
|||
Assert.Equal(RenderTreeEditType.SetAttribute, edit.Type);
|
||||
var attributeFrame = batch2.ReferenceFrames[edit.ReferenceFrameIndex];
|
||||
AssertFrame.Attribute(attributeFrame, "ontestevent", typeof(Action<UIChangeEventArgs>));
|
||||
Assert.NotEqual(0, attributeFrame.AttributeEventHandlerId);
|
||||
Assert.NotEqual(default, attributeFrame.AttributeEventHandlerId);
|
||||
Assert.NotEqual(eventHandlerId, attributeFrame.AttributeEventHandlerId);
|
||||
});
|
||||
}
|
||||
|
|
@ -3365,7 +3365,7 @@ namespace Microsoft.AspNetCore.Components.Test
|
|||
Assert.Equal(RenderTreeEditType.SetAttribute, edit.Type);
|
||||
var attributeFrame = latestBatch.ReferenceFrames[edit.ReferenceFrameIndex];
|
||||
AssertFrame.Attribute(attributeFrame, "ontestevent", typeof(Action<UIChangeEventArgs>));
|
||||
Assert.NotEqual(0, attributeFrame.AttributeEventHandlerId);
|
||||
Assert.NotEqual(default, attributeFrame.AttributeEventHandlerId);
|
||||
Assert.NotEqual(eventHandlerId, attributeFrame.AttributeEventHandlerId);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,16 +130,15 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
|
||||
void Write(in RenderTreeFrame frame)
|
||||
{
|
||||
// TODO: Change this to write as a short, saving 2 bytes per frame
|
||||
_binaryWriter.Write((int)frame.FrameType);
|
||||
|
||||
// We want each frame to take up the same number of bytes, so that the
|
||||
// recipient can index into the array directly instead of having to
|
||||
// walk through it.
|
||||
// Since we can fit every frame type into 3 ints, use that as the
|
||||
// Since we can fit every frame type into 16 bytes, use that as the
|
||||
// common size. For smaller frames, we add padding to expand it to
|
||||
// 12 bytes (i.e., 3 x 4-byte ints).
|
||||
// The total size then for each frame is 16 bytes (frame type, then
|
||||
// 3 other ints).
|
||||
// 16 bytes.
|
||||
switch (frame.FrameType)
|
||||
{
|
||||
case RenderTreeFrameType.Attribute:
|
||||
|
|
@ -160,41 +159,41 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
var attributeValueString = frame.AttributeValue as string;
|
||||
WriteString(attributeValueString, allowDeduplication: string.IsNullOrEmpty(attributeValueString));
|
||||
}
|
||||
_binaryWriter.Write(frame.AttributeEventHandlerId);
|
||||
_binaryWriter.Write(frame.AttributeEventHandlerId); // 8 bytes
|
||||
break;
|
||||
case RenderTreeFrameType.Component:
|
||||
_binaryWriter.Write(frame.ComponentSubtreeLength);
|
||||
_binaryWriter.Write(frame.ComponentId);
|
||||
WritePadding(_binaryWriter, 4);
|
||||
WritePadding(_binaryWriter, 8);
|
||||
break;
|
||||
case RenderTreeFrameType.ComponentReferenceCapture:
|
||||
// The client doesn't need to know about these. But we still have
|
||||
// to include them in the array otherwise the ReferenceFrameIndex
|
||||
// values in the edits data would be wrong.
|
||||
WritePadding(_binaryWriter, 12);
|
||||
WritePadding(_binaryWriter, 16);
|
||||
break;
|
||||
case RenderTreeFrameType.Element:
|
||||
_binaryWriter.Write(frame.ElementSubtreeLength);
|
||||
WriteString(frame.ElementName, allowDeduplication: true);
|
||||
WritePadding(_binaryWriter, 4);
|
||||
WritePadding(_binaryWriter, 8);
|
||||
break;
|
||||
case RenderTreeFrameType.ElementReferenceCapture:
|
||||
WriteString(frame.ElementReferenceCaptureId, allowDeduplication: false);
|
||||
WritePadding(_binaryWriter, 8);
|
||||
WritePadding(_binaryWriter, 12);
|
||||
break;
|
||||
case RenderTreeFrameType.Region:
|
||||
_binaryWriter.Write(frame.RegionSubtreeLength);
|
||||
WritePadding(_binaryWriter, 8);
|
||||
WritePadding(_binaryWriter, 12);
|
||||
break;
|
||||
case RenderTreeFrameType.Text:
|
||||
WriteString(
|
||||
frame.TextContent,
|
||||
allowDeduplication: string.IsNullOrWhiteSpace(frame.TextContent));
|
||||
WritePadding(_binaryWriter, 8);
|
||||
WritePadding(_binaryWriter, 12);
|
||||
break;
|
||||
case RenderTreeFrameType.Markup:
|
||||
WriteString(frame.MarkupContent, allowDeduplication: false);
|
||||
WritePadding(_binaryWriter, 8);
|
||||
WritePadding(_binaryWriter, 12);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported frame type: {frame.FrameType}");
|
||||
|
|
@ -216,6 +215,21 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
return startPos;
|
||||
}
|
||||
|
||||
int Write(in ArrayRange<ulong> numbers)
|
||||
{
|
||||
var startPos = (int)_binaryWriter.BaseStream.Position;
|
||||
_binaryWriter.Write(numbers.Count);
|
||||
|
||||
var array = numbers.Array;
|
||||
var count = numbers.Count;
|
||||
for (var index = 0; index < count; index++)
|
||||
{
|
||||
_binaryWriter.Write(array[index]);
|
||||
}
|
||||
|
||||
return startPos;
|
||||
}
|
||||
|
||||
void WriteString(string value, bool allowDeduplication)
|
||||
{
|
||||
if (value == null)
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.Components.Server
|
|||
new ArrayRange<RenderTreeDiff>(),
|
||||
new ArrayRange<RenderTreeFrame>(),
|
||||
new ArrayRange<int>(),
|
||||
new ArrayRange<int>(new[] { 123, int.MaxValue, int.MinValue, 456 }, 3) // Only use first 3 to show that param is respected
|
||||
new ArrayRange<ulong>(new ulong[] { 123, ulong.MaxValue, ulong.MinValue, 456 }, 3) // Only use first 3 to show that param is respected
|
||||
));
|
||||
|
||||
// Assert
|
||||
|
|
@ -84,15 +84,15 @@ namespace Microsoft.AspNetCore.Components.Server
|
|||
0, // Length of UpdatedComponents
|
||||
0, // Length of ReferenceFrames
|
||||
0, // Length of DisposedComponentIds
|
||||
3, 123, int.MaxValue, int.MinValue, // DisposedEventHandlerIds as length-prefixed array
|
||||
3, (ulong)123, ulong.MaxValue, ulong.MinValue, // DisposedEventHandlerIds as length-prefixed array
|
||||
|
||||
0, // Index of UpdatedComponents
|
||||
4, // Index of ReferenceFrames
|
||||
8, // Index of DisposedComponentIds
|
||||
12, // Index of DisposedEventHandlerIds
|
||||
28 // Index of strings
|
||||
40 // Index of strings
|
||||
);
|
||||
Assert.Equal(48, bytes.Length); // No other data
|
||||
Assert.Equal(60, bytes.Length); // No other data
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -200,7 +200,7 @@ namespace Microsoft.AspNetCore.Components.Server
|
|||
RenderTreeFrame.Attribute(123, "Attribute with string value", "String value"),
|
||||
RenderTreeFrame.Attribute(124, "Attribute with nonstring value", 1),
|
||||
RenderTreeFrame.Attribute(125, "Attribute with delegate value", new Action(() => { }))
|
||||
.WithAttributeEventHandlerId(789),
|
||||
.WithAttributeEventHandlerId(((ulong)uint.MaxValue) + 1),
|
||||
RenderTreeFrame.ChildComponent(126, typeof(object))
|
||||
.WithComponentSubtreeLength(5678)
|
||||
.WithComponent(new ComponentState(renderer, 2000, new FakeComponent(), null)),
|
||||
|
|
@ -230,22 +230,22 @@ namespace Microsoft.AspNetCore.Components.Server
|
|||
var referenceFramesStartIndex = ReadInt(bytes, bytes.Length - 16);
|
||||
AssertBinaryContents(bytes, referenceFramesStartIndex,
|
||||
16, // Number of frames
|
||||
RenderTreeFrameType.Attribute, "Attribute with string value", "String value", 0,
|
||||
RenderTreeFrameType.Attribute, "Attribute with nonstring value", NullStringMarker, 0,
|
||||
RenderTreeFrameType.Attribute, "Attribute with delegate value", NullStringMarker, 789,
|
||||
RenderTreeFrameType.Component, 5678, 2000, 0,
|
||||
RenderTreeFrameType.ComponentReferenceCapture, 0, 0, 0,
|
||||
RenderTreeFrameType.Element, 1234, "Some element", 0,
|
||||
RenderTreeFrameType.ElementReferenceCapture, "my unique ID", 0, 0,
|
||||
RenderTreeFrameType.Region, 1234, 0, 0,
|
||||
RenderTreeFrameType.Text, "Some text", 0, 0,
|
||||
RenderTreeFrameType.Markup, "Some markup", 0, 0,
|
||||
RenderTreeFrameType.Text, "\n\t ", 0, 0,
|
||||
RenderTreeFrameType.Attribute, "Attribute with string value", "String value", 0,
|
||||
RenderTreeFrameType.Element, 999, "Some element", 0,
|
||||
RenderTreeFrameType.Text, "Some text", 0, 0,
|
||||
RenderTreeFrameType.Markup, "Some markup", 0, 0,
|
||||
RenderTreeFrameType.Text, "\n\t ", 0, 0
|
||||
RenderTreeFrameType.Attribute, "Attribute with string value", "String value", 0, 0,
|
||||
RenderTreeFrameType.Attribute, "Attribute with nonstring value", NullStringMarker, 0, 0,
|
||||
RenderTreeFrameType.Attribute, "Attribute with delegate value", NullStringMarker, ((ulong)uint.MaxValue) + 1,
|
||||
RenderTreeFrameType.Component, 5678, 2000, 0, 0,
|
||||
RenderTreeFrameType.ComponentReferenceCapture, 0, 0, 0, 0,
|
||||
RenderTreeFrameType.Element, 1234, "Some element", 0, 0,
|
||||
RenderTreeFrameType.ElementReferenceCapture, "my unique ID", 0, 0, 0,
|
||||
RenderTreeFrameType.Region, 1234, 0, 0, 0,
|
||||
RenderTreeFrameType.Text, "Some text", 0, 0, 0,
|
||||
RenderTreeFrameType.Markup, "Some markup", 0, 0, 0,
|
||||
RenderTreeFrameType.Text, "\n\t ", 0, 0, 0,
|
||||
RenderTreeFrameType.Attribute, "Attribute with string value", "String value", 0, 0,
|
||||
RenderTreeFrameType.Element, 999, "Some element", 0, 0,
|
||||
RenderTreeFrameType.Text, "Some text", 0, 0, 0,
|
||||
RenderTreeFrameType.Markup, "Some markup", 0, 0, 0,
|
||||
RenderTreeFrameType.Text, "\n\t ", 0, 0, 0
|
||||
);
|
||||
|
||||
Assert.Equal(new[]
|
||||
|
|
@ -320,6 +320,10 @@ namespace Microsoft.AspNetCore.Components.Server
|
|||
{
|
||||
Assert.Equal(expectedInt, reader.ReadInt32());
|
||||
}
|
||||
else if (expectedEntry is ulong expectedUlong)
|
||||
{
|
||||
Assert.Equal(expectedUlong, reader.ReadUInt64());
|
||||
}
|
||||
else if (expectedEntry is string || expectedEntry == NullStringMarker)
|
||||
{
|
||||
// For strings, we have to look up the value in the table of strings
|
||||
|
|
|
|||
|
|
@ -60,10 +60,10 @@ namespace Microsoft.AspNetCore.Components.Test.Helpers
|
|||
public new Task RenderRootComponentAsync(int componentId, ParameterCollection parameters)
|
||||
=> Dispatcher.InvokeAsync(() => base.RenderRootComponentAsync(componentId, parameters));
|
||||
|
||||
public Task DispatchEventAsync(int eventHandlerId, UIEventArgs args)
|
||||
public Task DispatchEventAsync(ulong eventHandlerId, UIEventArgs args)
|
||||
=> Dispatcher.InvokeAsync(() => base.DispatchEventAsync(eventHandlerId, null, args));
|
||||
|
||||
public new Task DispatchEventAsync(int eventHandlerId, EventFieldInfo eventFieldInfo, UIEventArgs args)
|
||||
public new Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo eventFieldInfo, UIEventArgs args)
|
||||
=> Dispatcher.InvokeAsync(() => base.DispatchEventAsync(eventHandlerId, eventFieldInfo, args));
|
||||
|
||||
private static Task UnwrapTask(Task task)
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -13,6 +13,8 @@ let invoke_method: (method: MethodHandle, target: System_Object, argsArrayPtr: n
|
|||
let mono_string_get_utf8: (managedString: System_String) => Mono.Utf8Ptr;
|
||||
let mono_string: (jsString: string) => System_String;
|
||||
const appBinDirName = 'appBinDir';
|
||||
const uint64HighOrderShift = Math.pow(2, 32);
|
||||
const maxSafeNumberHighPart = Math.pow(2, 21) - 1; // The high-order int32 from Number.MAX_SAFE_INTEGER
|
||||
|
||||
export const monoPlatform: Platform = {
|
||||
start: function start(loadAssemblyUrls: string[]) {
|
||||
|
|
@ -122,6 +124,19 @@ export const monoPlatform: Platform = {
|
|||
return Module.getValue((baseAddress as any as number) + (fieldOffset || 0), 'i32');
|
||||
},
|
||||
|
||||
readUint64Field: function readHeapUint64(baseAddress: Pointer, fieldOffset?: number): number {
|
||||
// Module.getValue(..., 'i64') doesn't work because the implementation treats 'i64' as
|
||||
// being the same as 'i32'. Also we must take care to read both halves as unsigned.
|
||||
const address = (baseAddress as any as number) + (fieldOffset || 0);
|
||||
const heapU32Index = address >> 2;
|
||||
const highPart = Module.HEAPU32[heapU32Index + 1];
|
||||
if (highPart > maxSafeNumberHighPart) {
|
||||
throw new Error(`Cannot read uint64 with high order part ${highPart}, because the result would exceed Number.MAX_SAFE_INTEGER.`);
|
||||
}
|
||||
|
||||
return (highPart * uint64HighOrderShift) + Module.HEAPU32[heapU32Index];
|
||||
},
|
||||
|
||||
readFloatField: function readHeapFloat(baseAddress: Pointer, fieldOffset?: number): number {
|
||||
return Module.getValue((baseAddress as any as number) + (fieldOffset || 0), 'float');
|
||||
},
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export interface Platform {
|
|||
getObjectFieldsBaseAddress(referenceTypedObject: System_Object): Pointer;
|
||||
readInt16Field(baseAddress: Pointer, fieldOffset?: number): number;
|
||||
readInt32Field(baseAddress: Pointer, fieldOffset?: number): number;
|
||||
readUint64Field(baseAddress: Pointer, fieldOffset?: number): number;
|
||||
readFloatField(baseAddress: Pointer, fieldOffset?: number): number;
|
||||
readObjectField<T extends System_Object>(baseAddress: Pointer, fieldOffset?: number): T;
|
||||
readStringField(baseAddress: Pointer, fieldOffset?: number): string | null;
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ import { RenderBatch, ArrayRange, RenderTreeDiff, ArrayValues, RenderTreeEdit, E
|
|||
import { decodeUtf8 } from './Utf8Decoder';
|
||||
|
||||
const updatedComponentsEntryLength = 4; // Each is a single int32 giving the location of the data
|
||||
const referenceFramesEntryLength = 16; // 1 byte for frame type, then 3 bytes for type-specific data
|
||||
const referenceFramesEntryLength = 20; // 1 int for frame type, then 16 bytes for type-specific data
|
||||
const disposedComponentIdsEntryLength = 4; // Each is an int32 giving the ID
|
||||
const disposedEventHandlerIdsEntryLength = 4; // Each is an int32 giving the ID
|
||||
const disposedEventHandlerIdsEntryLength = 8; // Each is an int64 giving the ID
|
||||
const editsEntryLength = 16; // 4 ints
|
||||
const stringTableEntryLength = 4; // Each is an int32 giving the string data location, or -1 for null
|
||||
const uint64HighPartShift = Math.pow(2, 32);
|
||||
const maxSafeNumberHighPart = Math.pow(2, 21) - 1; // The high-order int32 from Number.MAX_SAFE_INTEGER
|
||||
|
||||
export class OutOfProcessRenderBatch implements RenderBatch {
|
||||
constructor(private batchData: Uint8Array) {
|
||||
|
|
@ -51,7 +53,7 @@ export class OutOfProcessRenderBatch implements RenderBatch {
|
|||
|
||||
disposedEventHandlerIdsEntry(values: ArrayValues<number>, index: number): number {
|
||||
const entryPos = (values as any) + index * disposedEventHandlerIdsEntryLength;
|
||||
return readInt32LE(this.batchData, entryPos);
|
||||
return readUint64LE(this.batchData, entryPos);
|
||||
}
|
||||
|
||||
diffReader: RenderTreeDiffReader;
|
||||
|
|
@ -160,7 +162,7 @@ class OutOfProcessRenderTreeFrameReader implements RenderTreeFrameReader {
|
|||
}
|
||||
|
||||
attributeEventHandlerId(frame: RenderTreeFrame) {
|
||||
return readInt32LE(this.batchDataUint8, frame as any + 12); // 4th int
|
||||
return readUint64LE(this.batchDataUint8, frame as any + 12); // Bytes 12-19
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -235,6 +237,24 @@ function readInt32LE(buffer: Uint8Array, position: number): any {
|
|||
| (buffer[position + 3] << 24);
|
||||
}
|
||||
|
||||
function readUint32LE(buffer: Uint8Array, position: number): any {
|
||||
return (buffer[position])
|
||||
+ (buffer[position + 1] << 8)
|
||||
+ (buffer[position + 2] << 16)
|
||||
+ ((buffer[position + 3] << 24) >>> 0); // The >>> 0 coerces the value to unsigned
|
||||
}
|
||||
|
||||
function readUint64LE(buffer: Uint8Array, position: number): any {
|
||||
// This cannot be done using bit-shift operators in JavaScript, because
|
||||
// those all implicitly convert to int32
|
||||
const highPart = readUint32LE(buffer, position + 4);
|
||||
if (highPart > maxSafeNumberHighPart) {
|
||||
throw new Error(`Cannot read uint64 with high order part ${highPart}, because the result would exceed Number.MAX_SAFE_INTEGER.`);
|
||||
}
|
||||
|
||||
return (highPart * uint64HighPartShift) + readUint32LE(buffer, position);
|
||||
}
|
||||
|
||||
function readLEB128(buffer: Uint8Array, position: number) {
|
||||
let result = 0;
|
||||
let shift = 0;
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ export class SharedMemoryRenderBatch implements RenderBatch {
|
|||
}
|
||||
|
||||
disposedEventHandlerIdsEntry(values: ArrayValues<number>, index: number) {
|
||||
const pointer = arrayValuesEntry(values, index, /* int length */ 4);
|
||||
return platform.readInt32Field(pointer as any as Pointer);
|
||||
const pointer = arrayValuesEntry(values, index, /* long length */ 8);
|
||||
return platform.readUint64Field(pointer as any as Pointer);
|
||||
}
|
||||
|
||||
arrayRangeReader = arrayRangeReader;
|
||||
|
|
@ -108,7 +108,7 @@ const frameReader = {
|
|||
markupContent: (frame: RenderTreeFrame) => platform.readStringField(frame as any, 16)!,
|
||||
attributeName: (frame: RenderTreeFrame) => platform.readStringField(frame as any, 16),
|
||||
attributeValue: (frame: RenderTreeFrame) => platform.readStringField(frame as any, 24),
|
||||
attributeEventHandlerId: (frame: RenderTreeFrame) => platform.readInt32Field(frame as any, 8),
|
||||
attributeEventHandlerId: (frame: RenderTreeFrame) => platform.readUint64Field(frame as any, 8),
|
||||
};
|
||||
|
||||
function arrayValuesEntry<T>(arrayValues: ArrayValues<T>, index: number, itemSize: number): T {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Components.Web
|
|||
public int BrowserRendererId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
public string EventArgsType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
public Microsoft.AspNetCore.Components.Rendering.EventFieldInfo EventFieldInfo { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
public int EventHandlerId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
public ulong EventHandlerId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ namespace Microsoft.AspNetCore.Components.Web
|
|||
/// <summary>
|
||||
/// For framework use only.
|
||||
/// </summary>
|
||||
public int EventHandlerId { get; set; }
|
||||
public ulong EventHandlerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For framework use only.
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ namespace Ignitor
|
|||
RenderTreeFrame.Attribute(123, "Attribute with string value", "String value"),
|
||||
RenderTreeFrame.Attribute(124, "Attribute with nonstring value", 1),
|
||||
RenderTreeFrame.Attribute(125, "Attribute with delegate value", new Action(() => { }))
|
||||
.WithAttributeEventHandlerId(789),
|
||||
.WithAttributeEventHandlerId((ulong)uint.MaxValue + 1),
|
||||
RenderTreeFrame.ChildComponent(126, typeof(object))
|
||||
.WithComponentSubtreeLength(5678)
|
||||
.WithComponent(new ComponentState(renderer, 2000, new FakeComponent(), null)),
|
||||
|
|
@ -180,22 +180,22 @@ namespace Ignitor
|
|||
var referenceFramesStartIndex = ReadInt(bytes, bytes.Length - 16);
|
||||
AssertBinaryContents(bytes, referenceFramesStartIndex,
|
||||
16, // Number of frames
|
||||
RenderTreeFrameType.Attribute, "Attribute with string value", "String value", 0,
|
||||
RenderTreeFrameType.Attribute, "Attribute with nonstring value", NullStringMarker, 0,
|
||||
RenderTreeFrameType.Attribute, "Attribute with delegate value", NullStringMarker, 789,
|
||||
RenderTreeFrameType.Component, 5678, 2000, 0,
|
||||
RenderTreeFrameType.ComponentReferenceCapture, 0, 0, 0,
|
||||
RenderTreeFrameType.Element, 1234, "Some element", 0,
|
||||
RenderTreeFrameType.ElementReferenceCapture, "my unique ID", 0, 0,
|
||||
RenderTreeFrameType.Region, 1234, 0, 0,
|
||||
RenderTreeFrameType.Text, "Some text", 0, 0,
|
||||
RenderTreeFrameType.Markup, "Some markup", 0, 0,
|
||||
RenderTreeFrameType.Text, "\n\t ", 0, 0,
|
||||
RenderTreeFrameType.Attribute, "Attribute with string value", "String value", 0,
|
||||
RenderTreeFrameType.Element, 999, "Some element", 0,
|
||||
RenderTreeFrameType.Text, "Some text", 0, 0,
|
||||
RenderTreeFrameType.Markup, "Some markup", 0, 0,
|
||||
RenderTreeFrameType.Text, "\n\t ", 0, 0
|
||||
RenderTreeFrameType.Attribute, "Attribute with string value", "String value", 0, 0,
|
||||
RenderTreeFrameType.Attribute, "Attribute with nonstring value", NullStringMarker, 0, 0,
|
||||
RenderTreeFrameType.Attribute, "Attribute with delegate value", NullStringMarker, (ulong)uint.MaxValue + 1,
|
||||
RenderTreeFrameType.Component, 5678, 2000, 0, 0,
|
||||
RenderTreeFrameType.ComponentReferenceCapture, 0, 0, 0, 0,
|
||||
RenderTreeFrameType.Element, 1234, "Some element", 0, 0,
|
||||
RenderTreeFrameType.ElementReferenceCapture, "my unique ID", 0, 0, 0,
|
||||
RenderTreeFrameType.Region, 1234, 0, 0, 0,
|
||||
RenderTreeFrameType.Text, "Some text", 0, 0, 0,
|
||||
RenderTreeFrameType.Markup, "Some markup", 0, 0, 0,
|
||||
RenderTreeFrameType.Text, "\n\t ", 0, 0, 0,
|
||||
RenderTreeFrameType.Attribute, "Attribute with string value", "String value", 0, 0,
|
||||
RenderTreeFrameType.Element, 999, "Some element", 0, 0,
|
||||
RenderTreeFrameType.Text, "Some text", 0, 0, 0,
|
||||
RenderTreeFrameType.Markup, "Some markup", 0, 0, 0,
|
||||
RenderTreeFrameType.Text, "\n\t ", 0, 0, 0
|
||||
);
|
||||
|
||||
Assert.Equal(new[]
|
||||
|
|
@ -279,6 +279,10 @@ namespace Ignitor
|
|||
{
|
||||
Assert.Equal(expectedInt, reader.ReadInt32());
|
||||
}
|
||||
else if (expectedEntry is ulong expectedUlong)
|
||||
{
|
||||
Assert.Equal(expectedUlong, reader.ReadUInt64());
|
||||
}
|
||||
else if (expectedEntry is string || expectedEntry == NullStringMarker)
|
||||
{
|
||||
// For strings, we have to look up the value in the table of strings
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@ namespace ComponentsApp.Server
|
|||
{
|
||||
services.AddMvc();
|
||||
services.AddSingleton<CircuitHandler, LoggingCircuitHandler>();
|
||||
services.AddServerSideBlazor();
|
||||
services.AddServerSideBlazor(options =>
|
||||
{
|
||||
options.JSInteropDetailedErrors = true;
|
||||
});
|
||||
|
||||
services.AddSingleton<WeatherForecastService, DefaultWeatherForecastService>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ namespace Ignitor
|
|||
|
||||
}
|
||||
|
||||
private void DisposeEventHandler(int eventHandlerId)
|
||||
private void DisposeEventHandler(ulong eventHandlerId)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ namespace Ignitor
|
|||
|
||||
public class ElementEventDescriptor
|
||||
{
|
||||
public ElementEventDescriptor(string eventName, int eventId)
|
||||
public ElementEventDescriptor(string eventName, ulong eventId)
|
||||
{
|
||||
EventName = eventName ?? throw new ArgumentNullException(nameof(eventName));
|
||||
EventId = eventId;
|
||||
|
|
@ -109,7 +109,7 @@ namespace Ignitor
|
|||
|
||||
public string EventName { get; }
|
||||
|
||||
public int EventId { get; }
|
||||
public ulong EventId { get; }
|
||||
}
|
||||
|
||||
public async Task ClickAsync(HubConnection connection)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ namespace Ignitor
|
|||
{
|
||||
public static class RenderBatchReader
|
||||
{
|
||||
private const int ReferenceFrameSize = 20;
|
||||
|
||||
private static readonly Renderer Renderer = new FakeRenderer();
|
||||
|
||||
public static RenderBatch Read(ReadOnlySpan<byte> data)
|
||||
|
|
@ -131,69 +133,67 @@ namespace Ignitor
|
|||
|
||||
private static ArrayRange<RenderTreeFrame> ReadReferenceFrames(ReadOnlySpan<byte> data, string[] strings)
|
||||
{
|
||||
var result = new RenderTreeFrame[data.Length / 16];
|
||||
var result = new RenderTreeFrame[data.Length / ReferenceFrameSize];
|
||||
|
||||
for (var i = 0; i < data.Length; i += 16)
|
||||
for (var i = 0; i < data.Length; i += ReferenceFrameSize)
|
||||
{
|
||||
var frameData = data.Slice(i, 16);
|
||||
var frameData = data.Slice(i, ReferenceFrameSize);
|
||||
|
||||
var type = (RenderTreeFrameType)BitConverter.ToInt32(frameData.Slice(0, 4));
|
||||
|
||||
// We want each frame to take up the same number of bytes, so that the
|
||||
// recipient can index into the array directly instead of having to
|
||||
// walk through it.
|
||||
// Since we can fit every frame type into 3 ints, use that as the
|
||||
// Since we can fit every frame type into 16 bytes, use that as the
|
||||
// common size. For smaller frames, we add padding to expand it to
|
||||
// 12 bytes (i.e., 3 x 4-byte ints).
|
||||
// The total size then for each frame is 16 bytes (frame type, then
|
||||
// 3 other ints).
|
||||
// 16 bytes.
|
||||
switch (type)
|
||||
{
|
||||
case RenderTreeFrameType.Attribute:
|
||||
var attributeName = ReadString(frameData.Slice(4, 4), strings);
|
||||
var attributeValue = ReadString(frameData.Slice(8, 4), strings);
|
||||
var attributeEventHandlerId = BitConverter.ToInt32(frameData.Slice(12, 4));
|
||||
result[i / 16] = RenderTreeFrame.Attribute(0, attributeName, attributeValue).WithAttributeEventHandlerId(attributeEventHandlerId);
|
||||
var attributeEventHandlerId = BitConverter.ToUInt64(frameData.Slice(12, 8));
|
||||
result[i / ReferenceFrameSize] = RenderTreeFrame.Attribute(0, attributeName, attributeValue).WithAttributeEventHandlerId(attributeEventHandlerId);
|
||||
break;
|
||||
|
||||
case RenderTreeFrameType.Component:
|
||||
var componentSubtreeLength = BitConverter.ToInt32(frameData.Slice(4, 4));
|
||||
var componentId = BitConverter.ToInt32(frameData.Slice(8, 4)); // Nowhere to put this without creating a ComponentState
|
||||
result[i / 16] = RenderTreeFrame.ChildComponent(0, componentType: null)
|
||||
result[i / ReferenceFrameSize] = RenderTreeFrame.ChildComponent(0, componentType: null)
|
||||
.WithComponentSubtreeLength(componentSubtreeLength)
|
||||
.WithComponent(new ComponentState(Renderer, componentId, new FakeComponent(), null));
|
||||
break;
|
||||
|
||||
case RenderTreeFrameType.ComponentReferenceCapture:
|
||||
// Client doesn't process these, skip.
|
||||
result[i / 16] = RenderTreeFrame.ComponentReferenceCapture(0, null, 0);
|
||||
result[i / ReferenceFrameSize] = RenderTreeFrame.ComponentReferenceCapture(0, null, 0);
|
||||
break;
|
||||
|
||||
case RenderTreeFrameType.Element:
|
||||
var elementSubtreeLength = BitConverter.ToInt32(frameData.Slice(4, 4));
|
||||
var elementName = ReadString(frameData.Slice(8, 4), strings);
|
||||
result[i / 16] = RenderTreeFrame.Element(0, elementName).WithElementSubtreeLength(elementSubtreeLength);
|
||||
result[i / ReferenceFrameSize] = RenderTreeFrame.Element(0, elementName).WithElementSubtreeLength(elementSubtreeLength);
|
||||
break;
|
||||
|
||||
case RenderTreeFrameType.ElementReferenceCapture:
|
||||
var referenceCaptureId = ReadString(frameData.Slice(4, 4), strings);
|
||||
result[i / 16] = RenderTreeFrame.ElementReferenceCapture(0, null)
|
||||
result[i / ReferenceFrameSize] = RenderTreeFrame.ElementReferenceCapture(0, null)
|
||||
.WithElementReferenceCaptureId(referenceCaptureId);
|
||||
break;
|
||||
|
||||
case RenderTreeFrameType.Region:
|
||||
var regionSubtreeLength = BitConverter.ToInt32(frameData.Slice(4, 4));
|
||||
result[i / 16] = RenderTreeFrame.Region(0).WithRegionSubtreeLength(regionSubtreeLength);
|
||||
result[i / ReferenceFrameSize] = RenderTreeFrame.Region(0).WithRegionSubtreeLength(regionSubtreeLength);
|
||||
break;
|
||||
|
||||
case RenderTreeFrameType.Text:
|
||||
var text = ReadString(frameData.Slice(4, 4), strings);
|
||||
result[i / 16] = RenderTreeFrame.Text(0, text);
|
||||
result[i / ReferenceFrameSize] = RenderTreeFrame.Text(0, text);
|
||||
break;
|
||||
|
||||
case RenderTreeFrameType.Markup:
|
||||
var markup = ReadString(frameData.Slice(4, 4), strings);
|
||||
result[i / 16] = RenderTreeFrame.Markup(0, markup);
|
||||
result[i / ReferenceFrameSize] = RenderTreeFrame.Markup(0, markup);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -209,9 +209,9 @@ namespace Ignitor
|
|||
return new ArrayRange<int>(Array.Empty<int>(), 0);
|
||||
}
|
||||
|
||||
private static ArrayRange<int> ReadDisposedEventHandlerIds(ReadOnlySpan<byte> data)
|
||||
private static ArrayRange<ulong> ReadDisposedEventHandlerIds(ReadOnlySpan<byte> data)
|
||||
{
|
||||
return new ArrayRange<int>(Array.Empty<int>(), 0);
|
||||
return new ArrayRange<ulong>(Array.Empty<ulong>(), 0);
|
||||
}
|
||||
|
||||
private static string ReadString(ReadOnlySpan<byte> data, string[] strings)
|
||||
|
|
@ -276,7 +276,7 @@ namespace Ignitor
|
|||
{
|
||||
// This is a count-prefixed contiguous array of RenderTreeFrame.
|
||||
var count = BitConverter.ToInt32(data.Slice(_referenceFrames, 4));
|
||||
return data.Slice(_referenceFrames + 4, count * 16);
|
||||
return data.Slice(_referenceFrames + 4, count * ReferenceFrameSize);
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> GetStringTableIndexes(ReadOnlySpan<byte> data)
|
||||
|
|
|
|||
Loading…
Reference in New Issue