mirror of
https://github.com/FabInfra/capnproto-dotnetcore_Runtime.git
synced 2025-03-12 06:41:50 +01:00
more fixes, more coverage
This commit is contained in:
parent
2369b4788a
commit
20c8523aae
@ -378,6 +378,10 @@ namespace Capnp.Net.Runtime.Tests
|
||||
var mb = MessageBuilder.Create();
|
||||
var root = mb.BuildRoot<TW>();
|
||||
obj.Serialize(root);
|
||||
using (var tr = new FrameTracing.RpcFrameTracer(Console.Out))
|
||||
{
|
||||
tr.TraceFrame(FrameTracing.FrameDirection.Tx, mb.Frame);
|
||||
}
|
||||
var d = (DeserializerState)root;
|
||||
var obj2 = new TD();
|
||||
obj2.Deserialize(d);
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Capnp.Net.Runtime.Tests.GenImpls;
|
||||
using Capnp.Rpc;
|
||||
using Capnproto_test.Capnp.Test;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
@ -229,7 +230,7 @@ namespace Capnp.Net.Runtime.Tests
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ListOfStructs()
|
||||
public void ListOfStructs1()
|
||||
{
|
||||
var b = MessageBuilder.Create();
|
||||
var list = b.CreateObject<ListOfStructsSerializer<SomeStruct.WRITER>>();
|
||||
@ -259,6 +260,19 @@ namespace Capnp.Net.Runtime.Tests
|
||||
Assert.AreEqual("3", list3[3].SomeText);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ListOfStructs2()
|
||||
{
|
||||
var b = MessageBuilder.Create();
|
||||
var list = b.CreateObject<DynamicSerializerState>();
|
||||
list.SetListOfStructs(3, 4, 5);
|
||||
list.SetListOfStructs(3, 4, 5);
|
||||
Assert.ThrowsException<InvalidOperationException>(() => list.SetListOfStructs(1, 4, 5));
|
||||
Assert.ThrowsException<InvalidOperationException>(() => list.SetListOfStructs(3, 1, 5));
|
||||
Assert.ThrowsException<InvalidOperationException>(() => list.SetListOfStructs(3, 4, 1));
|
||||
Assert.ThrowsException<InvalidOperationException>(() => list.StructWriteData(0, 1, 1));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ListOfText()
|
||||
{
|
||||
@ -741,5 +755,171 @@ namespace Capnp.Net.Runtime.Tests
|
||||
Assert.ThrowsException<IndexOutOfRangeException>(() => { var _ = list[0]; });
|
||||
Assert.AreEqual(0, list.ToArray().Length);
|
||||
}
|
||||
|
||||
class TestSerializerStateStruct11 : SerializerState
|
||||
{
|
||||
public TestSerializerStateStruct11()
|
||||
{
|
||||
SetStruct(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
class TestSerializerStateStruct20 : SerializerState
|
||||
{
|
||||
public TestSerializerStateStruct20()
|
||||
{
|
||||
SetStruct(2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
class TestSerializerStateStruct11X : SerializerState
|
||||
{
|
||||
public TestSerializerStateStruct11X()
|
||||
{
|
||||
SetStruct(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SerializerStateInvalidRewrap1()
|
||||
{
|
||||
var dss = new DynamicSerializerState(MessageBuilder.Create());
|
||||
dss.SetListOfValues(8, 1);
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.Rewrap<TestSerializerStateStruct11>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SerializerStateInvalidRewrap2()
|
||||
{
|
||||
var dss = new DynamicSerializerState();
|
||||
dss.SetStruct(1, 0);
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.Rewrap<TestSerializerStateStruct11>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SerializerStateInvalidRewrap3()
|
||||
{
|
||||
var dss = new DynamicSerializerState();
|
||||
dss.SetStruct(0, 1);
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.Rewrap<TestSerializerStateStruct11>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SerializerStateInvalidRewrap4()
|
||||
{
|
||||
var dss = new DynamicSerializerState();
|
||||
dss.SetStruct(1, 0);
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.Rewrap<ListOfTextSerializer>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UnboundSerializerState()
|
||||
{
|
||||
var dss = new DynamicSerializerState();
|
||||
dss.SetStruct(1, 0);
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.WriteData(0, 0));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void LinkBadUsage1()
|
||||
{
|
||||
var mb = MessageBuilder.Create();
|
||||
var dss = mb.CreateObject<DynamicSerializerState>();
|
||||
dss.SetStruct(0, 1);
|
||||
Assert.ThrowsException<ArgumentNullException>(() => dss.Link(0, null));
|
||||
Assert.ThrowsException<ArgumentOutOfRangeException>(() => dss.Link(-1, dss));
|
||||
Assert.ThrowsException<ArgumentOutOfRangeException>(() => dss.Link(1, dss));
|
||||
dss.Link(0, dss);
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.Link(0, mb.CreateObject<DynamicSerializerState>()));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void LinkBadUsage2()
|
||||
{
|
||||
var dss = new DynamicSerializerState(MessageBuilder.Create());
|
||||
dss.SetListOfPointers(1);
|
||||
Assert.ThrowsException<ArgumentNullException>(() => dss.Link(0, null));
|
||||
Assert.ThrowsException<ArgumentOutOfRangeException>(() => dss.Link(-1, dss));
|
||||
Assert.ThrowsException<ArgumentOutOfRangeException>(() => dss.Link(1, dss));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void StructReadCapNoCapTable()
|
||||
{
|
||||
var dss = new DynamicSerializerState(MessageBuilder.Create());
|
||||
dss.SetStruct(0, 1);
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.ReadCap(0));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void StructReadCap()
|
||||
{
|
||||
var dss = DynamicSerializerState.CreateForRpc();
|
||||
dss.SetStruct(0, 3);
|
||||
Assert.ThrowsException<ArgumentOutOfRangeException>(() => dss.ReadCap(-1));
|
||||
Assert.ThrowsException<ArgumentOutOfRangeException>(() => dss.ReadCap(100));
|
||||
Assert.IsTrue(dss.ReadCap(0).IsNull);
|
||||
dss.LinkToCapability(1, 99);
|
||||
dss.Link(2, dss);
|
||||
dss.Allocate();
|
||||
Assert.IsTrue(dss.ReadCap(0).IsNull);
|
||||
Assert.IsTrue(dss.ReadCap<ITestCallOrder>(0) is Proxy proxy && proxy.IsNull);
|
||||
Assert.ThrowsException<Rpc.RpcException>(() => dss.ReadCap(1));
|
||||
Assert.ThrowsException<Rpc.RpcException>(() => dss.ReadCap(2));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Rewrap()
|
||||
{
|
||||
var mb = MessageBuilder.Create();
|
||||
var list1 = mb.CreateObject<ListOfStructsSerializer<TestSerializerStateStruct11>>();
|
||||
list1.Init(3);
|
||||
var list2 = list1.Rewrap<ListOfStructsSerializer<TestSerializerStateStruct11X>>();
|
||||
list2[0].WriteData(0, 0);
|
||||
Assert.ThrowsException<InvalidOperationException>(() => list2.Rewrap<TestSerializerStateStruct11>());
|
||||
var obj = mb.CreateObject<TestSerializerStateStruct11>();
|
||||
var obj2 = obj.Rewrap<TestSerializerStateStruct11X>();
|
||||
Assert.ThrowsException<InvalidOperationException>(() => obj2.Rewrap<TestSerializerStateStruct20>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AllocatedNil()
|
||||
{
|
||||
var mb = MessageBuilder.Create();
|
||||
mb.InitCapTable();
|
||||
var dss = mb.CreateObject<DynamicSerializerState>();
|
||||
dss.Allocate();
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.SetCapability(0));
|
||||
dss.SetCapability(null);
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.SetListOfPointers(1));
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.SetListOfStructs(1, 1, 1));
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.SetListOfValues(8, 1));
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.SetObject(mb.CreateObject<TestSerializerStateStruct11>()));
|
||||
dss.SetObject(null);
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.SetStruct(1, 1));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SetCapability1()
|
||||
{
|
||||
var mb = MessageBuilder.Create();
|
||||
mb.InitCapTable();
|
||||
var dss = mb.CreateObject<DynamicSerializerState>();
|
||||
dss.SetStruct(1, 1);
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.SetCapability(null));
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.SetCapability(0));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SetCapability2()
|
||||
{
|
||||
var mb = MessageBuilder.Create();
|
||||
mb.InitCapTable();
|
||||
var dss = mb.CreateObject<DynamicSerializerState>();
|
||||
dss.SetCapability(7);
|
||||
dss.SetCapability(7);
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.SetCapability(8));
|
||||
Assert.ThrowsException<InvalidOperationException>(() => dss.SetCapability(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -571,12 +571,12 @@ namespace Capnp.Net.Runtime.Tests
|
||||
task1.Result.Dispose();
|
||||
|
||||
testbed.FlushCommunication();
|
||||
Assert.AreEqual(1, counters.HandleCount);
|
||||
Assert.IsTrue(SpinWait.SpinUntil(() => counters.HandleCount == 1, TestBase.ShortTimeout));
|
||||
|
||||
task2.Result.Dispose();
|
||||
|
||||
testbed.FlushCommunication();
|
||||
Assert.AreEqual(0, counters.HandleCount);
|
||||
Assert.IsTrue(SpinWait.SpinUntil(() => counters.HandleCount == 0, TestBase.ShortTimeout));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ namespace Capnp
|
||||
/// <summary>
|
||||
/// The capabilities imported from the capability table. Only valid in RPC context.
|
||||
/// </summary>
|
||||
public IList<Rpc.ConsumedCapability?>? Caps { get; set; }
|
||||
public IList<Rpc.ConsumedCapability>? Caps { get; set; }
|
||||
/// <summary>
|
||||
/// Current segment (essentially Segments[CurrentSegmentIndex])
|
||||
/// </summary>
|
||||
|
@ -79,7 +79,7 @@ namespace Capnp
|
||||
/// <item><description>This state does neither describe a struct, nor a list of pointers</description></item>
|
||||
/// <item><description>Another state is already linked to the specified position (sorry, no overwrite allowed)</description></item></list>
|
||||
/// </exception>
|
||||
public new void LinkToCapability(int slot, uint capabilityIndex) => base.LinkToCapability(slot, capabilityIndex);
|
||||
public new void LinkToCapability(int slot, uint? capabilityIndex) => base.LinkToCapability(slot, capabilityIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Determines the underlying object to be a struct.
|
||||
@ -89,6 +89,8 @@ namespace Capnp
|
||||
/// <exception cref="InvalidOperationException">The object type was already set to something different</exception>
|
||||
public new void SetStruct(ushort dataCount, ushort ptrCount) => base.SetStruct(dataCount, ptrCount);
|
||||
|
||||
public new void SetCapability(uint? capabilityIndex) => base.SetCapability(capabilityIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Determines the underlying object to be a list of (primitive) values.
|
||||
/// </summary>
|
||||
|
@ -10,7 +10,7 @@ namespace Capnp
|
||||
{
|
||||
readonly ISegmentAllocator _allocator;
|
||||
readonly DynamicSerializerState _rootPtrBuilder;
|
||||
List<Rpc.ConsumedCapability?>? _capTable;
|
||||
List<Rpc.ConsumedCapability>? _capTable;
|
||||
|
||||
MessageBuilder(ISegmentAllocator allocator)
|
||||
{
|
||||
@ -92,13 +92,13 @@ namespace Capnp
|
||||
if (_capTable != null)
|
||||
throw new InvalidOperationException("Capability table was already initialized");
|
||||
|
||||
_capTable = new List<Rpc.ConsumedCapability?>();
|
||||
_capTable = new List<Rpc.ConsumedCapability>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns this message builder's segment allocator.
|
||||
/// </summary>
|
||||
public ISegmentAllocator Allocator => _allocator;
|
||||
internal List<Rpc.ConsumedCapability?>? Caps => _capTable;
|
||||
internal List<Rpc.ConsumedCapability>? Caps => _capTable;
|
||||
}
|
||||
}
|
@ -180,7 +180,7 @@ namespace Capnp.Rpc
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<TInterface> Unwrap<TInterface>(this TInterface cap) where TInterface: class, IDisposable
|
||||
public static async Task<TInterface?> Unwrap<TInterface>(this TInterface cap) where TInterface: class, IDisposable
|
||||
{
|
||||
using var proxy = cap as Proxy;
|
||||
|
||||
@ -188,6 +188,9 @@ namespace Capnp.Rpc
|
||||
return cap;
|
||||
|
||||
var unwrapped = await proxy.ConsumedCap.Unwrap();
|
||||
if (unwrapped == null)
|
||||
return null;
|
||||
|
||||
return ((CapabilityReflection.CreateProxy<TInterface>(unwrapped)) as TInterface)!;
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,6 @@ namespace Capnp.Rpc
|
||||
return new LazyCapability(Task.FromCanceled<ConsumedCapability?>(token));
|
||||
}
|
||||
|
||||
public static LazyCapability Null => CreateBrokenCap("Null capability");
|
||||
|
||||
readonly Task<Proxy>? _proxyTask;
|
||||
|
||||
public LazyCapability(Task<ConsumedCapability?> capabilityTask)
|
||||
|
@ -108,7 +108,7 @@ namespace Capnp.Rpc
|
||||
try
|
||||
{
|
||||
var cap = aorcq.Answer.Caps![(int)cur.CapabilityIndex];
|
||||
proxy = new Proxy(cap ?? LazyCapability.Null);
|
||||
proxy = new Proxy(cap);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
|
@ -197,16 +197,13 @@ namespace Capnp.Rpc
|
||||
/// <param name="disposeThis">Whether to Dispose() this Proxy instance</param>
|
||||
/// <returns>Proxy for desired capability interface</returns>
|
||||
/// <exception cref="InvalidCapabilityInterfaceException"><typeparamref name="T"/> did not qualify as capability interface.</exception>
|
||||
/// <exception cref="InvalidOperationException">This capability is broken, or mismatch between generic type arguments (if capability interface is generic).</exception>
|
||||
/// <exception cref="InvalidOperationException">Mismatch between generic type arguments (if capability interface is generic).</exception>
|
||||
/// <exception cref="ArgumentException">Mismatch between generic type arguments (if capability interface is generic).</exception>
|
||||
/// <exception cref="System.Reflection.TargetInvocationException">Problem with instatiating the Proxy (constructor threw exception).</exception>
|
||||
/// <exception cref="MemberAccessException">Caller does not have permission to invoke the Proxy constructor.</exception>
|
||||
/// <exception cref="TypeLoadException">Problem with building the Proxy type, or problem with loading some dependent class.</exception>
|
||||
public T Cast<T>(bool disposeThis) where T: class
|
||||
{
|
||||
if (IsNull)
|
||||
throw new InvalidOperationException("Capability is broken");
|
||||
|
||||
using (disposeThis ? this : null)
|
||||
{
|
||||
return (CapabilityReflection.CreateProxy<T>(ConsumedCap) as T)!;
|
||||
|
@ -5,13 +5,11 @@ namespace Capnp.Rpc
|
||||
{
|
||||
static class ResolvingCapabilityExtensions
|
||||
{
|
||||
public static async Task<ConsumedCapability> Unwrap(this ConsumedCapability? cap)
|
||||
public static async Task<ConsumedCapability?> Unwrap(this ConsumedCapability? cap)
|
||||
{
|
||||
cap ??= LazyCapability.Null;
|
||||
|
||||
while (cap is IResolvingCapability resolving)
|
||||
{
|
||||
cap = await resolving.WhenResolved ?? LazyCapability.Null;
|
||||
cap = await resolving.WhenResolved;
|
||||
}
|
||||
|
||||
return cap;
|
||||
@ -66,7 +64,7 @@ namespace Capnp.Rpc
|
||||
switch (obj)
|
||||
{
|
||||
case Proxy proxy: return proxy;
|
||||
case null: return new Proxy(LazyCapability.Null);
|
||||
case null: return new Proxy(null);
|
||||
default: return BareProxy.FromImpl(obj);
|
||||
}
|
||||
}
|
||||
|
@ -1347,9 +1347,9 @@ namespace Capnp.Rpc
|
||||
}
|
||||
}
|
||||
|
||||
internal IList<ConsumedCapability?> ImportCapTable(Payload.READER payload)
|
||||
internal IList<ConsumedCapability> ImportCapTable(Payload.READER payload)
|
||||
{
|
||||
var list = new List<ConsumedCapability?>();
|
||||
var list = new List<ConsumedCapability>();
|
||||
|
||||
if (payload.CapTable != null)
|
||||
{
|
||||
@ -1378,16 +1378,8 @@ namespace Capnp.Rpc
|
||||
foreach (var cap in state.MsgBuilder.Caps)
|
||||
{
|
||||
var capDesc = payload.CapTable[i++];
|
||||
|
||||
if (cap == null)
|
||||
{
|
||||
LazyCapability.Null.Export(this, capDesc);
|
||||
}
|
||||
else
|
||||
{
|
||||
postAction += cap.Export(this, capDesc);
|
||||
cap.Release();
|
||||
}
|
||||
postAction += cap.Export(this, capDesc);
|
||||
cap.Release();
|
||||
}
|
||||
|
||||
Tx(state.MsgBuilder.Frame);
|
||||
|
@ -7,7 +7,7 @@ namespace Capnp.Rpc
|
||||
{
|
||||
class Vine : Skeleton
|
||||
{
|
||||
public static Skeleton Create(ConsumedCapability cap)
|
||||
public static Skeleton Create(ConsumedCapability? cap)
|
||||
{
|
||||
if (cap is LocalCapability lcap)
|
||||
return lcap.ProvidedCap;
|
||||
@ -15,9 +15,9 @@ namespace Capnp.Rpc
|
||||
return new Vine(cap);
|
||||
}
|
||||
|
||||
Vine(ConsumedCapability consumedCap)
|
||||
Vine(ConsumedCapability? consumedCap)
|
||||
{
|
||||
Proxy = new Proxy(consumedCap ?? throw new ArgumentNullException(nameof(consumedCap)));
|
||||
Proxy = new Proxy(consumedCap);
|
||||
|
||||
#if DebugFinalizers
|
||||
CreatorStackTrace = Environment.StackTrace;
|
||||
|
@ -30,7 +30,7 @@ namespace Capnp
|
||||
|
||||
internal MessageBuilder? MsgBuilder { get; set; }
|
||||
internal ISegmentAllocator? Allocator => MsgBuilder?.Allocator;
|
||||
internal List<Rpc.ConsumedCapability?>? Caps => MsgBuilder?.Caps;
|
||||
internal List<Rpc.ConsumedCapability>? Caps => MsgBuilder?.Caps;
|
||||
internal SerializerState? Owner { get; set; }
|
||||
internal int OwnerSlot { get; set; }
|
||||
internal uint SegmentIndex { get; set; }
|
||||
@ -156,7 +156,7 @@ namespace Capnp
|
||||
/// Returns the allocated memory slice (given this state already is allocated). Note that this definition is somewhat
|
||||
/// non-symmetric to <code>DeserializerState.RawData</code>. Never mind: You should not use it directly, anyway.
|
||||
/// </summary>
|
||||
public Span<ulong> RawData => SegmentSpan.Slice(Offset, (int)WordsAllocated);
|
||||
public Span<ulong> RawData => IsAllocated ? SegmentSpan.Slice(Offset, (int)WordsAllocated) : Span<ulong>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The kind of object this state currently represents.
|
||||
@ -170,12 +170,6 @@ namespace Capnp
|
||||
SegmentIndex = 0;
|
||||
Offset = 0;
|
||||
}
|
||||
else if (Owner?.Kind == ObjectKind.ListOfStructs)
|
||||
{
|
||||
Owner.Allocate();
|
||||
SegmentIndex = Owner.SegmentIndex;
|
||||
Offset = Owner.Offset + OwnerSlot + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Allocator == null)
|
||||
@ -242,25 +236,43 @@ namespace Capnp
|
||||
}
|
||||
}
|
||||
|
||||
internal void EncodePointer(int offset, SerializerState target, bool allowCopy)
|
||||
internal Rpc.ConsumedCapability? DecodeCapPointer(int offset)
|
||||
{
|
||||
if (target == null)
|
||||
throw new ArgumentNullException(nameof(target));
|
||||
if (Caps == null)
|
||||
throw new InvalidOperationException("Capbility table not set");
|
||||
|
||||
if (!target.IsAllocated)
|
||||
throw new InvalidOperationException("Target must be allocated before a pointer can be built");
|
||||
|
||||
if (MsgBuilder == null)
|
||||
throw Unbound();
|
||||
|
||||
try
|
||||
if (!IsAllocated)
|
||||
{
|
||||
if (SegmentSpan[offset] != 0)
|
||||
throw new InvalidOperationException("Won't replace an already allocated pointer to prevent memory leaks and security flaws");
|
||||
return null;
|
||||
}
|
||||
catch (IndexOutOfRangeException)
|
||||
|
||||
WirePointer pointer = RawData[offset];
|
||||
|
||||
if (pointer.IsNull)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (pointer.Kind != PointerKind.Other)
|
||||
{
|
||||
throw new Rpc.RpcException(
|
||||
"Expected a capability pointer, but got something different");
|
||||
}
|
||||
|
||||
if (pointer.CapabilityIndex >= Caps.Count)
|
||||
{
|
||||
throw new Rpc.RpcException(
|
||||
"Capability index out of range");
|
||||
}
|
||||
|
||||
return Caps[(int)pointer.CapabilityIndex];
|
||||
}
|
||||
|
||||
void EncodePointer(int offset, SerializerState target, bool allowCopy)
|
||||
{
|
||||
if (SegmentSpan[offset] != 0)
|
||||
{
|
||||
throw new InvalidOperationException("Won't replace an already allocated pointer to prevent memory leaks and security flaws");
|
||||
}
|
||||
|
||||
if (target.Allocator != null &&
|
||||
@ -375,31 +387,6 @@ namespace Capnp
|
||||
}
|
||||
}
|
||||
|
||||
internal Rpc.ConsumedCapability? DecodeCapPointer(int offset)
|
||||
{
|
||||
if (offset < 0)
|
||||
throw new IndexOutOfRangeException(nameof(offset));
|
||||
|
||||
if (Caps == null)
|
||||
throw new InvalidOperationException("Capbility table not set");
|
||||
|
||||
WirePointer pointer = RawData[offset];
|
||||
|
||||
if (pointer.Kind != PointerKind.Other)
|
||||
{
|
||||
throw new Rpc.RpcException(
|
||||
"Expected a capability pointer, but got something different");
|
||||
}
|
||||
|
||||
if (pointer.CapabilityIndex >= Caps.Count)
|
||||
{
|
||||
throw new Rpc.RpcException(
|
||||
"Capability index out of range");
|
||||
}
|
||||
|
||||
return Caps[(int)pointer.CapabilityIndex];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Links a sub-item (struct field or list element) of this state to another state. Usually, this operation is not necessary, since objects are constructed top-down.
|
||||
/// However, there might be some advanced scenarios where you want to reference the same object twice (also interesting for designing amplification attacks).
|
||||
@ -471,7 +458,7 @@ namespace Capnp
|
||||
/// <item><description>This state does neither describe a struct, nor a list of pointers</description></item>
|
||||
/// <item><description>Another state is already linked to the specified position (sorry, no overwrite allowed)</description></item></list>
|
||||
/// </exception>
|
||||
protected void LinkToCapability(int slot, uint capabilityIndex)
|
||||
protected void LinkToCapability(int slot, uint? capabilityIndex)
|
||||
{
|
||||
var cstate = new SerializerState();
|
||||
cstate.SetCapability(capabilityIndex);
|
||||
@ -511,19 +498,29 @@ namespace Capnp
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCapability(uint capabilityIndex)
|
||||
protected void SetCapability(uint? capabilityIndex)
|
||||
{
|
||||
if (Kind == ObjectKind.Nil)
|
||||
if (capabilityIndex.HasValue)
|
||||
{
|
||||
VerifyNotYetAllocated();
|
||||
if (Kind == ObjectKind.Nil)
|
||||
{
|
||||
VerifyNotYetAllocated();
|
||||
|
||||
Kind = ObjectKind.Capability;
|
||||
CapabilityIndex = capabilityIndex;
|
||||
Allocate();
|
||||
Kind = ObjectKind.Capability;
|
||||
CapabilityIndex = capabilityIndex.Value;
|
||||
Allocate();
|
||||
}
|
||||
else if (Kind != ObjectKind.Capability || CapabilityIndex != capabilityIndex)
|
||||
{
|
||||
throw AlreadySet();
|
||||
}
|
||||
}
|
||||
else if (Kind != ObjectKind.Capability || CapabilityIndex != capabilityIndex)
|
||||
else
|
||||
{
|
||||
throw AlreadySet();
|
||||
if (Kind != ObjectKind.Nil)
|
||||
{
|
||||
throw AlreadySet();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1225,10 +1222,13 @@ namespace Capnp
|
||||
/// Adds an entry to the capability table if the provided capability does not yet exist.
|
||||
/// </summary>
|
||||
/// <param name="capability">The low-level capability object to provide.</param>
|
||||
/// <returns>Index of the given capability in the capability table</returns>
|
||||
/// <returns>Index of the given capability in the capability table, null if capability is null</returns>
|
||||
/// <exception cref="InvalidOperationException">The underlying message builder was not configured for capability table support.</exception>
|
||||
public uint ProvideCapability(Rpc.ConsumedCapability? capability)
|
||||
public uint? ProvideCapability(Rpc.ConsumedCapability? capability)
|
||||
{
|
||||
if (capability == null)
|
||||
return null;
|
||||
|
||||
if (Caps == null)
|
||||
throw new InvalidOperationException("Underlying MessageBuilder was not enabled to support capabilities");
|
||||
|
||||
@ -1252,7 +1252,7 @@ namespace Capnp
|
||||
/// <exception cref="InvalidOperationException">The underlying message builder was not configured for capability table support.</exception>
|
||||
public uint ProvideCapability(Rpc.Skeleton capability)
|
||||
{
|
||||
return ProvideCapability(Rpc.LocalCapability.Create(capability));
|
||||
return ProvideCapability(Rpc.LocalCapability.Create(capability))!.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -1267,12 +1267,12 @@ namespace Capnp
|
||||
/// </list></param>
|
||||
/// <returns>Index of the given capability in the capability table</returns>
|
||||
/// <exception cref="InvalidOperationException">The underlying message builder was not configured for capability table support.</exception>
|
||||
public uint ProvideCapability(object? obj)
|
||||
public uint? ProvideCapability(object? obj)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case null:
|
||||
return ProvideCapability(default(Rpc.ConsumedCapability));
|
||||
return null;
|
||||
case Rpc.Proxy proxy: using (proxy)
|
||||
return ProvideCapability(proxy.ConsumedCap);
|
||||
case Rpc.ConsumedCapability consumedCapability:
|
||||
@ -1348,8 +1348,8 @@ namespace Capnp
|
||||
if (Kind != ObjectKind.Struct && Kind != ObjectKind.Nil)
|
||||
throw new InvalidOperationException("Allowed on structs only");
|
||||
|
||||
if (index >= StructPtrCount)
|
||||
return null;
|
||||
if (index < 0 || index >= StructPtrCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
return DecodeCapPointer(index + StructDataCount);
|
||||
}
|
||||
|
@ -219,10 +219,13 @@ namespace Capnp
|
||||
/// <summary>
|
||||
/// Encodes a capability pointer.
|
||||
/// </summary>
|
||||
/// <param name="index">capability index</param>
|
||||
public void SetCapability(uint index)
|
||||
/// <param name="index">capability index, 'null' means 'null pointer'</param>
|
||||
public void SetCapability(uint? index)
|
||||
{
|
||||
_ptrData = ((ulong)index << 32) | (ulong)PointerKind.Other;
|
||||
if (index.HasValue)
|
||||
_ptrData = ((ulong)index << 32) | (ulong)PointerKind.Other;
|
||||
else
|
||||
_ptrData = 0;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user