Merge pull request #42 from c80k/nullable

Nullable support
This commit is contained in:
c80k 2020-02-09 12:11:48 +01:00 committed by GitHub
commit 7d4aefe37e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
135 changed files with 2462 additions and 1465 deletions

View File

@ -1,9 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;netcoreapp2.1</TargetFrameworks> <TargetFrameworks>netstandard2.0;netstandard2.1;netcoreapp2.1;netcoreapp3.0</TargetFrameworks>
<RootNamespace>Capnp</RootNamespace> <RootNamespace>Capnp</RootNamespace>
<LangVersion>7.2</LangVersion> <LangVersion>8.0</LangVersion>
<Nullable>Enable</Nullable>
<WarningsAsErrors>CS8600;CS8602;CS8603</WarningsAsErrors>
<AssemblyName>Capnp.Net.Runtime</AssemblyName> <AssemblyName>Capnp.Net.Runtime</AssemblyName>
<PackageId>Capnp.Net.Runtime</PackageId> <PackageId>Capnp.Net.Runtime</PackageId>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>

View File

@ -10,15 +10,15 @@ namespace Capnp
/// </summary> /// </summary>
public static class CapnpSerializable public static class CapnpSerializable
{ {
interface IConstructibleFromDeserializerState<out T> interface IConstructibleFromDeserializerState
{ {
T Create(DeserializerState state); object? Create(DeserializerState state);
} }
class FromStruct<T>: IConstructibleFromDeserializerState<T> class FromStruct<T>: IConstructibleFromDeserializerState
where T : ICapnpSerializable, new() where T : ICapnpSerializable, new()
{ {
public T Create(DeserializerState state) public object Create(DeserializerState state)
{ {
var result = new T(); var result = new T();
if (state.Kind != ObjectKind.Nil) if (state.Kind != ObjectKind.Nil)
@ -29,7 +29,7 @@ namespace Capnp
} }
} }
class FromList<T>: IConstructibleFromDeserializerState<IReadOnlyList<T>> class FromList<T>: IConstructibleFromDeserializerState
where T: class where T: class
{ {
readonly Func<DeserializerState, T> _elementSerializer; readonly Func<DeserializerState, T> _elementSerializer;
@ -39,23 +39,23 @@ namespace Capnp
_elementSerializer = (Func<DeserializerState, T>)GetSerializer(typeof(T)); _elementSerializer = (Func<DeserializerState, T>)GetSerializer(typeof(T));
} }
public IReadOnlyList<T> Create(DeserializerState state) public object Create(DeserializerState state)
{ {
return state.RequireList().Cast(_elementSerializer); return state.RequireList().Cast(_elementSerializer);
} }
} }
class FromCapability<T>: IConstructibleFromDeserializerState<T> class FromCapability<T>: IConstructibleFromDeserializerState
where T: class where T: class
{ {
public T Create(DeserializerState state) public object? Create(DeserializerState state)
{ {
return state.RequireCap<T>(); return state.RequireCap<T>();
} }
} }
static readonly ConditionalWeakTable<Type, Func<DeserializerState, object>> _typeMap = static readonly ConditionalWeakTable<Type, Func<DeserializerState, object?>> _typeMap =
new ConditionalWeakTable<Type, Func<DeserializerState, object>>(); new ConditionalWeakTable<Type, Func<DeserializerState, object?>>();
static CapnpSerializable() static CapnpSerializable()
{ {
@ -73,14 +73,14 @@ namespace Capnp
_typeMap.Add(typeof(IReadOnlyList<double>), d => d.RequireList().CastDouble()); _typeMap.Add(typeof(IReadOnlyList<double>), d => d.RequireList().CastDouble());
} }
static Func<DeserializerState, object> CreateSerializer(Type type) static Func<DeserializerState, object?> CreateSerializer(Type type)
{ {
if (typeof(ICapnpSerializable).IsAssignableFrom(type)) if (typeof(ICapnpSerializable).IsAssignableFrom(type))
{ {
try try
{ {
return ((IConstructibleFromDeserializerState<object>) return ((IConstructibleFromDeserializerState)
Activator.CreateInstance(typeof(FromStruct<>).MakeGenericType(type))).Create; Activator.CreateInstance(typeof(FromStruct<>).MakeGenericType(type))!).Create;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -94,12 +94,12 @@ namespace Capnp
try try
{ {
var elementType = type.GetGenericArguments()[0]; var elementType = type.GetGenericArguments()[0];
return ((IConstructibleFromDeserializerState<object>) return ((IConstructibleFromDeserializerState)
Activator.CreateInstance(typeof(FromList<>).MakeGenericType(elementType))).Create; Activator.CreateInstance(typeof(FromList<>).MakeGenericType(elementType))!).Create;
} }
catch (TargetInvocationException ex) catch (TargetInvocationException ex)
{ {
throw ex.InnerException; throw ex.InnerException!;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -123,8 +123,8 @@ namespace Capnp
try try
{ {
return ((IConstructibleFromDeserializerState<object>) return ((IConstructibleFromDeserializerState)
Activator.CreateInstance(typeof(FromCapability<>).MakeGenericType(type))).Create; Activator.CreateInstance(typeof(FromCapability<>).MakeGenericType(type))!).Create;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -135,7 +135,7 @@ namespace Capnp
} }
} }
static Func<DeserializerState, object> GetSerializer(Type type) static Func<DeserializerState, object?> GetSerializer(Type type)
{ {
return _typeMap.GetValue(type, CreateSerializer); return _typeMap.GetValue(type, CreateSerializer);
} }
@ -148,26 +148,28 @@ namespace Capnp
/// <item><description>Type implementing <see cref="ICapnpSerializable"/>. The type must must have a public parameterless constructor.</description></item> /// <item><description>Type implementing <see cref="ICapnpSerializable"/>. The type must must have a public parameterless constructor.</description></item>
/// <item><description>A capability interface (<seealso cref="Rpc.InvalidCapabilityInterfaceException"/> for further explanation)</description></item> /// <item><description>A capability interface (<seealso cref="Rpc.InvalidCapabilityInterfaceException"/> for further explanation)</description></item>
/// <item><description><see cref="String"/></description></item> /// <item><description><see cref="String"/></description></item>
/// <item><description><see cref="IReadOnlyList{Boolean}"/></description></item> /// <item><description><code>IReadOnlyList{Boolean}</code></description></item>
/// <item><description><see cref="IReadOnlyList{SByte}"/></description></item> /// <item><description><code>IReadOnlyList{SByte}"</code></description></item>
/// <item><description><see cref="IReadOnlyList{Byte}"/></description></item> /// <item><description><code>IReadOnlyList{Byte}"</code></description></item>
/// <item><description><see cref="IReadOnlyList{Int16}"/></description></item> /// <item><description><code>IReadOnlyList{Int16}"</code></description></item>
/// <item><description><see cref="IReadOnlyList{UInt16}"/></description></item> /// <item><description><code>IReadOnlyList{UInt16}"</code></description></item>
/// <item><description><see cref="IReadOnlyList{Int32}"/></description></item> /// <item><description><code>IReadOnlyList{Int32}"</code></description></item>
/// <item><description><see cref="IReadOnlyList{UInt32}"/></description></item> /// <item><description><code>IReadOnlyList{UInt32}"</code></description></item>
/// <item><description><see cref="IReadOnlyList{Int64}"/></description></item> /// <item><description><code>IReadOnlyList{Int64}"</code></description></item>
/// <item><description><see cref="IReadOnlyList{UInt64}"/></description></item> /// <item><description><code>IReadOnlyList{UInt64}"</code></description></item>
/// <item><description><see cref="IReadOnlyList{Single}"/></description></item> /// <item><description><code>IReadOnlyList{Single}"</code></description></item>
/// <item><description><see cref="IReadOnlyList{Double}"/></description></item> /// <item><description><code>IReadOnlyList{Double}"</code></description></item>
/// <item><description><see cref="IReadOnlyList{T}"/> whereby T is one of the things listed here.</description></item> /// <item><description><code>IReadOnlyList{T}</code> whereby T is one of the things listed here.</description></item>
/// </list> /// </list>
/// </typeparam> /// </typeparam>
/// <param name="state"></param> /// <param name="state">deserializer state to construct from</param>
/// <returns></returns> /// <returns>The domain object instance. Nullability note: The returned reference will be null if (and only if) <typeparamref name="T"/> is a capability interface and
public static T Create<T>(DeserializerState state) /// <paramref name="state"/> represents the nil object (obtained from a null pointer). For all other types, when the state is nil,
/// the method still constructs a valid but "empty" object instance (such as domain object without any properties set, empty string, empty list etc.)</returns>
public static T? Create<T>(DeserializerState state)
where T: class where T: class
{ {
return (T)GetSerializer(typeof(T))(state); return (T?)GetSerializer(typeof(T))(state);
} }
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace Capnp namespace Capnp
{ {
@ -47,7 +48,7 @@ namespace Capnp
/// <summary> /// <summary>
/// The capabilities imported from the capability table. Only valid in RPC context. /// The capabilities imported from the capability table. Only valid in RPC context.
/// </summary> /// </summary>
public IList<Rpc.ConsumedCapability> Caps { get; set; } public IList<Rpc.ConsumedCapability?>? Caps { get; set; }
/// <summary> /// <summary>
/// Current segment (essentially Segments[CurrentSegmentIndex] /// Current segment (essentially Segments[CurrentSegmentIndex]
/// </summary> /// </summary>
@ -83,11 +84,16 @@ namespace Capnp
/// The conversion is cheap, since it does not involve copying any payload. /// The conversion is cheap, since it does not involve copying any payload.
/// </summary> /// </summary>
/// <param name="state">The serializer state to be converted</param> /// <param name="state">The serializer state to be converted</param>
/// <exception cref="ArgumentNullException"><paramref name="state"/> is null</exception>
/// <exception cref="InvalidOperationException"><paramref name="state"/> is not bound to a MessageBuilder</exception>
public static implicit operator DeserializerState(SerializerState state) public static implicit operator DeserializerState(SerializerState state)
{ {
if (state == null) if (state == null)
throw new ArgumentNullException(nameof(state)); throw new ArgumentNullException(nameof(state));
if (state.MsgBuilder == null)
throw new InvalidOperationException("state is not bound to a MessageBuilder");
switch (state.Kind) switch (state.Kind)
{ {
case ObjectKind.ListOfBits: case ObjectKind.ListOfBits:
@ -100,7 +106,7 @@ namespace Capnp
case ObjectKind.ListOfStructs: case ObjectKind.ListOfStructs:
case ObjectKind.Nil: case ObjectKind.Nil:
case ObjectKind.Struct: case ObjectKind.Struct:
return new DeserializerState(state.Allocator.Segments) return new DeserializerState(state.Allocator!.Segments)
{ {
CurrentSegmentIndex = state.SegmentIndex, CurrentSegmentIndex = state.SegmentIndex,
Offset = state.Offset, Offset = state.Offset,
@ -112,7 +118,7 @@ namespace Capnp
}; };
case ObjectKind.Capability: case ObjectKind.Capability:
return new DeserializerState(state.Allocator.Segments) return new DeserializerState(state.Allocator!.Segments)
{ {
Kind = ObjectKind.Capability, Kind = ObjectKind.Capability,
Caps = state.Caps, Caps = state.Caps,
@ -376,11 +382,11 @@ namespace Capnp
/// the capability table. Does not mutate this state. /// the capability table. Does not mutate this state.
/// </summary> /// </summary>
/// <param name="offset">Offset relative to this.Offset within current segment</param> /// <param name="offset">Offset relative to this.Offset within current segment</param>
/// <returns>the low-level capability object</returns> /// <returns>the low-level capability object, or null if it is a null pointer</returns>
/// <exception cref="IndexOutOfRangeException">offset negative or out of range</exception> /// <exception cref="IndexOutOfRangeException">offset negative or out of range</exception>
/// <exception cref="InvalidOperationException">capability table not set</exception> /// <exception cref="InvalidOperationException">capability table not set</exception>
/// <exception cref="Rpc.RpcException">not a capability pointer or invalid capability index</exception> /// <exception cref="Rpc.RpcException">not a capability pointer or invalid capability index</exception>
internal Rpc.ConsumedCapability DecodeCapPointer(int offset) internal Rpc.ConsumedCapability? DecodeCapPointer(int offset)
{ {
if (offset < 0) if (offset < 0)
{ {
@ -490,7 +496,7 @@ namespace Capnp
return state; return state;
} }
internal Rpc.ConsumedCapability StructReadRawCap(int index) internal Rpc.ConsumedCapability? StructReadRawCap(int index)
{ {
if (Kind != ObjectKind.Struct && Kind != ObjectKind.Nil) if (Kind != ObjectKind.Struct && Kind != ObjectKind.Nil)
throw new InvalidOperationException("Allowed on structs only"); throw new InvalidOperationException("Allowed on structs only");
@ -567,7 +573,8 @@ namespace Capnp
/// <exception cref="IndexOutOfRangeException">negative index</exception> /// <exception cref="IndexOutOfRangeException">negative index</exception>
/// <exception cref="DeserializationException">state does not represent a struct, invalid pointer, /// <exception cref="DeserializationException">state does not represent a struct, invalid pointer,
/// non-list-of-bytes pointer, traversal limit exceeded</exception> /// non-list-of-bytes pointer, traversal limit exceeded</exception>
public string ReadText(int index, string defaultText = null) [return: NotNullIfNotNull("defaultText")]
public string? ReadText(int index, string? defaultText = null)
{ {
return StructReadPointer(index).RequireList().CastText() ?? defaultText; return StructReadPointer(index).RequireList().CastText() ?? defaultText;
} }
@ -646,7 +653,7 @@ namespace Capnp
/// <exception cref="IndexOutOfRangeException">negative index</exception> /// <exception cref="IndexOutOfRangeException">negative index</exception>
/// <exception cref="DeserializationException">state does not represent a struct, invalid pointer, /// <exception cref="DeserializationException">state does not represent a struct, invalid pointer,
/// non-capability pointer, traversal limit exceeded</exception> /// non-capability pointer, traversal limit exceeded</exception>
public T ReadCap<T>(int index, public T? ReadCap<T>(int index,
[System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0) where T: class [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0) where T: class
@ -677,7 +684,7 @@ namespace Capnp
/// <returns>capability instance or null if pointer was null</returns> /// <returns>capability instance or null if pointer was null</returns>
/// <exception cref="IndexOutOfRangeException">negative index</exception> /// <exception cref="IndexOutOfRangeException">negative index</exception>
/// <exception cref="DeserializationException">state does not represent a capability</exception> /// <exception cref="DeserializationException">state does not represent a capability</exception>
public T RequireCap<T>() where T: class public T? RequireCap<T>() where T: class
{ {
if (Kind == ObjectKind.Nil) if (Kind == ObjectKind.Nil)
return null; return null;
@ -685,7 +692,10 @@ namespace Capnp
if (Kind != ObjectKind.Capability) if (Kind != ObjectKind.Capability)
throw new DeserializationException("Expected a capability"); throw new DeserializationException("Expected a capability");
return Rpc.CapabilityReflection.CreateProxy<T>(Caps[(int)CapabilityIndex]) as T; if (Caps == null)
throw new InvalidOperationException("Capability table not set. This is a bug.");
return (Rpc.CapabilityReflection.CreateProxy<T>(Caps[(int)CapabilityIndex]) as T)!;
} }
} }
} }

View File

@ -144,7 +144,7 @@ namespace Capnp
/// <item><description>A <code><![CDATA[IReadOnlyList<object>]]></code> whereby each list item is one of the things listed here.</description></item> /// <item><description>A <code><![CDATA[IReadOnlyList<object>]]></code> whereby each list item is one of the things listed here.</description></item>
/// </list> /// </list>
/// </param> /// </param>
public void SetObject(object obj) public void SetObject(object? obj)
{ {
switch (obj) switch (obj)
{ {

View File

@ -2,9 +2,7 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Net.Sockets;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@ -22,7 +20,7 @@ namespace Capnp
int _disposing; int _disposing;
readonly Stream _stream; readonly Stream _stream;
readonly BinaryWriter _writer; readonly BinaryWriter? _writer;
readonly object _writeLock = new object(); readonly object _writeLock = new object();
readonly List<IFrameTracer> _tracers = new List<IFrameTracer>(); readonly List<IFrameTracer> _tracers = new List<IFrameTracer>();
@ -62,7 +60,7 @@ namespace Capnp
/// <summary> /// <summary>
/// Event handler for frame reception. /// Event handler for frame reception.
/// </summary> /// </summary>
public event Action<WireFrame> FrameReceived; public event Action<WireFrame>? FrameReceived;
/// <summary> /// <summary>
/// Sends a message over the stream. /// Sends a message over the stream.

View File

@ -10,7 +10,7 @@ namespace Capnp
{ {
static class GenericCasts<T> static class GenericCasts<T>
{ {
public static Func<ListDeserializer, T> CastFunc; public static Func<ListDeserializer, T>? CastFunc;
} }
static ListDeserializer() static ListDeserializer()

View File

@ -73,7 +73,7 @@ namespace Capnp
/// <param name="items">List content. Can be null in which case the list is simply not initialized.</param> /// <param name="items">List content. Can be null in which case the list is simply not initialized.</param>
/// <exception cref="InvalidOperationException">The list was already initialized</exception> /// <exception cref="InvalidOperationException">The list was already initialized</exception>
/// <exception cref="ArgumentOutOfRangeException">More than 2^29-1 items.</exception> /// <exception cref="ArgumentOutOfRangeException">More than 2^29-1 items.</exception>
public void Init(IReadOnlyList<bool> items) public void Init(IReadOnlyList<bool>? items)
{ {
if (items == null) if (items == null)
{ {

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
namespace Capnp namespace Capnp
{ {
@ -29,7 +28,7 @@ namespace Capnp
if (index < 0 || index >= Count) if (index < 0 || index >= Count)
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
return Rpc.CapabilityReflection.CreateProxy<T>(State.DecodeCapPointer(index)) as T; return (Rpc.CapabilityReflection.CreateProxy<T>(State.DecodeCapPointer(index)) as T)!;
} }
} }

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Diagnostics.CodeAnalysis;
namespace Capnp namespace Capnp
{ {
@ -11,7 +11,7 @@ namespace Capnp
/// <typeparam name="T">Capability interface</typeparam> /// <typeparam name="T">Capability interface</typeparam>
public class ListOfCapsSerializer<T> : public class ListOfCapsSerializer<T> :
SerializerState, SerializerState,
IReadOnlyList<T> IReadOnlyList<T?>
where T : class where T : class
{ {
/// <summary> /// <summary>
@ -32,9 +32,10 @@ namespace Capnp
/// <returns>Proxy object of capability at given element index</returns> /// <returns>Proxy object of capability at given element index</returns>
/// <exception cref="InvalidOperationException">List was not initialized, or attempting to overwrite an already set element.</exception> /// <exception cref="InvalidOperationException">List was not initialized, or attempting to overwrite an already set element.</exception>
/// <exception cref="IndexOutOfRangeException"><paramref name="index"/> is out of range.</exception> /// <exception cref="IndexOutOfRangeException"><paramref name="index"/> is out of range.</exception>
[AllowNull]
public T this[int index] public T this[int index]
{ {
get => Rpc.CapabilityReflection.CreateProxy<T>(DecodeCapPointer(index)) as T; get => (Rpc.CapabilityReflection.CreateProxy<T>(DecodeCapPointer(index)) as T)!;
set set
{ {
if (!IsAllocated) if (!IsAllocated)
@ -43,10 +44,7 @@ namespace Capnp
if (index < 0 || index >= RawData.Length) if (index < 0 || index >= RawData.Length)
throw new IndexOutOfRangeException("index out of range"); throw new IndexOutOfRangeException("index out of range");
uint id = ProvideCapability(value); RawData[index] = ProvideCapability(value);
WirePointer ptr = default;
ptr.SetCapability(id);
RawData[index] = id;
} }
} }
@ -67,7 +65,7 @@ namespace Capnp
/// <param name="caps">List content. Can be null in which case the list is simply not initialized.</param> /// <param name="caps">List content. Can be null in which case the list is simply not initialized.</param>
/// <exception cref="InvalidOperationException">The list was already initialized</exception> /// <exception cref="InvalidOperationException">The list was already initialized</exception>
/// <exception cref="ArgumentOutOfRangeException">More than 2^29-1 items.</exception> /// <exception cref="ArgumentOutOfRangeException">More than 2^29-1 items.</exception>
public void Init(IReadOnlyList<T> caps) public void Init(IReadOnlyList<T?>? caps)
{ {
if (caps == null) if (caps == null)
{ {

View File

@ -1,8 +1,6 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Capnp namespace Capnp
{ {
@ -12,7 +10,7 @@ namespace Capnp
/// <typeparam name="TS">SerializerState which represents the element type</typeparam> /// <typeparam name="TS">SerializerState which represents the element type</typeparam>
public class ListOfPointersSerializer<TS>: public class ListOfPointersSerializer<TS>:
SerializerState, SerializerState,
IReadOnlyList<TS> IReadOnlyList<TS?>
where TS: SerializerState, new() where TS: SerializerState, new()
{ {
/// <summary> /// <summary>
@ -51,7 +49,7 @@ namespace Capnp
/// </summary> /// </summary>
public int Count => ListElementCount; public int Count => ListElementCount;
IEnumerable<TS> Enumerate() IEnumerable<TS?> Enumerate()
{ {
int count = Count; int count = Count;
@ -64,7 +62,7 @@ namespace Capnp
/// <summary> /// <summary>
/// Implements <see cref="IEnumerable{TS}"/>. /// Implements <see cref="IEnumerable{TS}"/>.
/// </summary> /// </summary>
public IEnumerator<TS> GetEnumerator() public IEnumerator<TS?> GetEnumerator()
{ {
return Enumerate().GetEnumerator(); return Enumerate().GetEnumerator();
} }
@ -94,7 +92,7 @@ namespace Capnp
/// <param name="init">Serialization action to transfer a particular item into the serializer state.</param> /// <param name="init">Serialization action to transfer a particular item into the serializer state.</param>
/// <exception cref="InvalidOperationException">The list was already initialized</exception> /// <exception cref="InvalidOperationException">The list was already initialized</exception>
/// <exception cref="ArgumentOutOfRangeException">More than 2^29-1 items.</exception> /// <exception cref="ArgumentOutOfRangeException">More than 2^29-1 items.</exception>
public void Init<T>(IReadOnlyList<T> items, Action<TS, T> init) public void Init<T>(IReadOnlyList<T>? items, Action<TS, T> init)
{ {
if (items == null) if (items == null)
{ {
@ -115,4 +113,3 @@ namespace Capnp
} }
} }
} }

View File

@ -2,7 +2,6 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
namespace Capnp namespace Capnp
{ {
@ -70,7 +69,7 @@ namespace Capnp
/// <param name="items">List content. Can be null in which case the list is simply not initialized.</param> /// <param name="items">List content. Can be null in which case the list is simply not initialized.</param>
/// <exception cref="InvalidOperationException">The list was already initialized</exception> /// <exception cref="InvalidOperationException">The list was already initialized</exception>
/// <exception cref="ArgumentOutOfRangeException">More than 2^29-1 items.</exception> /// <exception cref="ArgumentOutOfRangeException">More than 2^29-1 items.</exception>
public void Init(IReadOnlyList<T> items) public void Init(IReadOnlyList<T>? items)
{ {
if (items == null) if (items == null)
{ {

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Capnp namespace Capnp
{ {

View File

@ -72,7 +72,7 @@ namespace Capnp
/// <param name="init">Serialization action to transfer a particular item into the serializer state.</param> /// <param name="init">Serialization action to transfer a particular item into the serializer state.</param>
/// <exception cref="InvalidOperationException">The list was already initialized</exception> /// <exception cref="InvalidOperationException">The list was already initialized</exception>
/// <exception cref="ArgumentOutOfRangeException">More than 2^29-1 items.</exception> /// <exception cref="ArgumentOutOfRangeException">More than 2^29-1 items.</exception>
public void Init<T>(IReadOnlyList<T> items, Action<TS, T> init) public void Init<T>(IReadOnlyList<T>? items, Action<TS, T> init)
{ {
if (items == null) if (items == null)
{ {

View File

@ -9,7 +9,7 @@ namespace Capnp
/// </summary> /// </summary>
public class ListOfTextSerializer : public class ListOfTextSerializer :
SerializerState, SerializerState,
IReadOnlyList<string> IReadOnlyList<string?>
{ {
/// <summary> /// <summary>
/// Gets or sets the text at given index. Once an element is set, it cannot be overwritten. /// Gets or sets the text at given index. Once an element is set, it cannot be overwritten.
@ -18,7 +18,7 @@ namespace Capnp
/// <exception cref="InvalidOperationException">List is not initialized</exception> /// <exception cref="InvalidOperationException">List is not initialized</exception>
/// <exception cref="IndexOutOfRangeException"><paramref name="index"/> is out of range.</exception> /// <exception cref="IndexOutOfRangeException"><paramref name="index"/> is out of range.</exception>
/// <exception cref="ArgumentOutOfRangeException">UTF-8 encoding exceeds 2^29-2 bytes</exception> /// <exception cref="ArgumentOutOfRangeException">UTF-8 encoding exceeds 2^29-2 bytes</exception>
public string this[int index] public string? this[int index]
{ {
get get
{ {
@ -47,7 +47,7 @@ namespace Capnp
/// </summary> /// </summary>
public int Count => ListElementCount; public int Count => ListElementCount;
IEnumerable<string> Enumerate() IEnumerable<string?> Enumerate()
{ {
int count = Count; int count = Count;
@ -60,7 +60,7 @@ namespace Capnp
/// <summary> /// <summary>
/// Implementation of <see cref="IEnumerable{String}"/>/> /// Implementation of <see cref="IEnumerable{String}"/>/>
/// </summary> /// </summary>
public IEnumerator<string> GetEnumerator() public IEnumerator<string?> GetEnumerator()
{ {
return Enumerate().GetEnumerator(); return Enumerate().GetEnumerator();
} }
@ -88,7 +88,7 @@ namespace Capnp
/// <param name="items">List content. Can be null in which case the list is simply not initialized.</param> /// <param name="items">List content. Can be null in which case the list is simply not initialized.</param>
/// <exception cref="InvalidOperationException">The list was already initialized</exception> /// <exception cref="InvalidOperationException">The list was already initialized</exception>
/// <exception cref="ArgumentOutOfRangeException">More than 2^29-1 items, or the UTF-8 encoding of an individual string requires more than 2^29-2 bytes.</exception> /// <exception cref="ArgumentOutOfRangeException">More than 2^29-1 items, or the UTF-8 encoding of an individual string requires more than 2^29-2 bytes.</exception>
public void Init(IReadOnlyList<string> items) public void Init(IReadOnlyList<string?>? items)
{ {
if (items == null) if (items == null)
{ {
@ -109,4 +109,3 @@ namespace Capnp
} }
} }
} }

View File

@ -1,5 +1,4 @@
using System; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
namespace Capnp namespace Capnp
{ {

View File

@ -1,7 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
namespace Capnp namespace Capnp
{ {
@ -12,7 +10,7 @@ namespace Capnp
{ {
readonly ISegmentAllocator _allocator; readonly ISegmentAllocator _allocator;
readonly DynamicSerializerState _rootPtrBuilder; readonly DynamicSerializerState _rootPtrBuilder;
List<Rpc.ConsumedCapability> _capTable; List<Rpc.ConsumedCapability?>? _capTable;
MessageBuilder(ISegmentAllocator allocator) MessageBuilder(ISegmentAllocator allocator)
{ {
@ -56,10 +54,11 @@ namespace Capnp
/// Gets or sets the root object. The root object must be set exactly once per message. /// Gets or sets the root object. The root object must be set exactly once per message.
/// Setting it manually is only required (and allowed) when it was created with <see cref="CreateObject{TS}"/>. /// Setting it manually is only required (and allowed) when it was created with <see cref="CreateObject{TS}"/>.
/// </summary> /// </summary>
public SerializerState Root /// <exception cref="ArgumentNullException">Attempt to set null reference</exception>
public SerializerState? Root
{ {
get => _rootPtrBuilder.TryGetPointer(0); get => _rootPtrBuilder.TryGetPointer(0);
set => _rootPtrBuilder.Link(0, value); set => _rootPtrBuilder.Link(0, value ?? throw new ArgumentNullException(nameof(value)));
} }
/// <summary> /// <summary>
@ -90,13 +89,13 @@ namespace Capnp
if (_capTable != null) if (_capTable != null)
throw new InvalidOperationException("Capability table was already initialized"); throw new InvalidOperationException("Capability table was already initialized");
_capTable = new List<Rpc.ConsumedCapability>(); _capTable = new List<Rpc.ConsumedCapability?>();
} }
/// <summary> /// <summary>
/// Returns this message builder's segment allocator. /// Returns this message builder's segment allocator.
/// </summary> /// </summary>
public ISegmentAllocator Allocator => _allocator; public ISegmentAllocator Allocator => _allocator;
internal List<Rpc.ConsumedCapability> Caps => _capTable; internal List<Rpc.ConsumedCapability?>? Caps => _capTable;
} }
} }

View File

@ -0,0 +1,140 @@
#pragma warning disable MA0048 // File name must match type name
#define INTERNAL_NULLABLE_ATTRIBUTES
#if NETSTANDARD2_0 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NET45 || NET451 || NET452 || NET6 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48
// https://github.com/dotnet/corefx/blob/48363ac826ccf66fbe31a5dcb1dc2aab9a7dd768/src/Common/src/CoreLib/System/Diagnostics/CodeAnalysis/NullableAttributes.cs
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace System.Diagnostics.CodeAnalysis
{
/// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class AllowNullAttribute : Attribute
{ }
/// <summary>Specifies that null is disallowed as an input even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class DisallowNullAttribute : Attribute
{ }
/// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class MaybeNullAttribute : Attribute
{ }
/// <summary>Specifies that an output will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class NotNullAttribute : Attribute
{ }
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class MaybeNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter may be null.
/// </param>
public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class NotNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter will not be null.
/// </param>
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}
/// <summary>Specifies that the output will be non-null if the named parameter is non-null.</summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class NotNullIfNotNullAttribute : Attribute
{
/// <summary>Initializes the attribute with the associated parameter name.</summary>
/// <param name="parameterName">
/// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.
/// </param>
public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName;
/// <summary>Gets the associated parameter name.</summary>
public string ParameterName { get; }
}
/// <summary>Applied to a method that will never return under any circumstance.</summary>
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class DoesNotReturnAttribute : Attribute
{ }
/// <summary>Specifies that the method will not return if the associated Boolean parameter is passed the specified value.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class DoesNotReturnIfAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified parameter value.</summary>
/// <param name="parameterValue">
/// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to
/// the associated parameter matches this value.
/// </param>
public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue;
/// <summary>Gets the condition parameter value.</summary>
public bool ParameterValue { get; }
}
}
#endif

View File

@ -1,6 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
namespace Capnp namespace Capnp
{ {
@ -8,7 +6,7 @@ namespace Capnp
{ {
class Coder<T> class Coder<T>
{ {
public static Func<T, T, T> Fn { get; set; } public static Func<T, T, T>? Fn { get; set; }
} }
static PrimitiveCoder() static PrimitiveCoder()

View File

@ -2,7 +2,6 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
namespace Capnp namespace Capnp
{ {

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
namespace Capnp namespace Capnp
{ {

View File

@ -33,11 +33,11 @@
/// <summary> /// <summary>
/// SerializerState, if applicable /// SerializerState, if applicable
/// </summary> /// </summary>
public SerializerState Answer => _obj as SerializerState; public SerializerState? Answer => _obj as SerializerState;
/// <summary> /// <summary>
/// PendingQuestion, if applicable /// PendingQuestion, if applicable
/// </summary> /// </summary>
public PendingQuestion Counterquestion => _obj as PendingQuestion; public PendingQuestion? Counterquestion => _obj as PendingQuestion;
} }
} }

View File

@ -33,7 +33,7 @@
/// Constructs an instance and binds it to the given low-level capability. /// Constructs an instance and binds it to the given low-level capability.
/// </summary> /// </summary>
/// <param name="cap">low-level capability</param> /// <param name="cap">low-level capability</param>
public BareProxy(ConsumedCapability cap): base(cap) public BareProxy(ConsumedCapability? cap): base(cap)
{ {
} }

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -110,7 +109,7 @@ namespace Capnp.Rpc
return (SkeletonFactory)Activator.CreateInstance( return (SkeletonFactory)Activator.CreateInstance(
typeof(SkeletonFactory<>) typeof(SkeletonFactory<>)
.MakeGenericType(skeletonClass)); .MakeGenericType(skeletonClass))!;
} }
static SkeletonFactory GetSkeletonFactory(Type type) static SkeletonFactory GetSkeletonFactory(Type type)
@ -190,7 +189,7 @@ namespace Capnp.Rpc
return (ProxyFactory)Activator.CreateInstance( return (ProxyFactory)Activator.CreateInstance(
typeof(ProxyFactory<>) typeof(ProxyFactory<>)
.MakeGenericType(proxyClass)); .MakeGenericType(proxyClass))!;
} }
else else
{ {
@ -263,7 +262,7 @@ namespace Capnp.Rpc
/// <exception cref="System.Reflection.TargetInvocationException">Problem with instatiating the Proxy (constructor threw exception).</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="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> /// <exception cref="TypeLoadException">Problem with building the Proxy type, or problem with loading some dependent class.</exception>
public static Proxy CreateProxy<TInterface>(ConsumedCapability cap, public static Proxy CreateProxy<TInterface>(ConsumedCapability? cap,
[System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0) [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)

View File

@ -1,8 +1,4 @@
using System; namespace Capnp.Rpc
using System.Threading;
using System.Threading.Tasks;
namespace Capnp.Rpc
{ {
/// <summary> /// <summary>
/// Base class for a low-level capability at consumer side. It is created by the <see cref="RpcEngine"/>. An application does not directly interact with it /// Base class for a low-level capability at consumer side. It is created by the <see cref="RpcEngine"/>. An application does not directly interact with it
@ -18,7 +14,7 @@ namespace Capnp.Rpc
/// </summary> /// </summary>
protected abstract void ReleaseRemotely(); protected abstract void ReleaseRemotely();
internal abstract void Export(IRpcEndpoint endpoint, CapDescriptor.WRITER writer); internal abstract void Export(IRpcEndpoint endpoint, CapDescriptor.WRITER writer);
internal abstract void Freeze(out IRpcEndpoint boundEndpoint); internal abstract void Freeze(out IRpcEndpoint? boundEndpoint);
internal abstract void Unfreeze(); internal abstract void Unfreeze();
internal abstract void AddRef(); internal abstract void AddRef();

View File

@ -22,6 +22,6 @@ namespace Capnp.Rpc
/// </summary> /// </summary>
/// <param name="access">Path to the desired capability inside the result struct.</param> /// <param name="access">Path to the desired capability inside the result struct.</param>
/// <returns>Pipelined low-level capability</returns> /// <returns>Pipelined low-level capability</returns>
ConsumedCapability Access(MemberAccessPath access); ConsumedCapability? Access(MemberAccessPath access);
} }
} }

View File

@ -1,6 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Capnp.Rpc namespace Capnp.Rpc

View File

@ -11,7 +11,7 @@ namespace Capnp.Rpc
public static class Impatient public static class Impatient
{ {
static readonly ConditionalWeakTable<Task, IPromisedAnswer> _taskTable = new ConditionalWeakTable<Task, IPromisedAnswer>(); static readonly ConditionalWeakTable<Task, IPromisedAnswer> _taskTable = new ConditionalWeakTable<Task, IPromisedAnswer>();
static readonly ThreadLocal<IRpcEndpoint> _askingEndpoint = new ThreadLocal<IRpcEndpoint>(); static readonly ThreadLocal<IRpcEndpoint?> _askingEndpoint = new ThreadLocal<IRpcEndpoint?>();
/// <summary> /// <summary>
/// Attaches a continuation to the given promise and registers the resulting task for pipelining. /// Attaches a continuation to the given promise and registers the resulting task for pipelining.
@ -51,7 +51,7 @@ namespace Capnp.Rpc
} }
else if (rtask.IsFaulted) else if (rtask.IsFaulted)
{ {
rtask = Task.FromException<T>(rtask.Exception.InnerException); rtask = Task.FromException<T>(rtask.Exception!.InnerException!);
} }
else else
{ {
@ -86,7 +86,7 @@ namespace Capnp.Rpc
return answer; return answer;
} }
internal static IPromisedAnswer TryGetAnswer(Task task) internal static IPromisedAnswer? TryGetAnswer(Task task)
{ {
_taskTable.TryGetValue(task, out var answer); _taskTable.TryGetValue(task, out var answer);
return answer; return answer;
@ -102,10 +102,10 @@ namespace Capnp.Rpc
return proxy; return proxy;
case null: case null:
return null; return CapabilityReflection.CreateProxy<T>(null);
} }
var skel = Skeleton.GetOrCreateSkeleton(item, false); var skel = Skeleton.GetOrCreateSkeleton(item!, false);
var localCap = LocalCapability.Create(skel); var localCap = LocalCapability.Create(skel);
return CapabilityReflection.CreateProxy<T>(localCap); return CapabilityReflection.CreateProxy<T>(localCap);
} }
@ -131,7 +131,7 @@ namespace Capnp.Rpc
where TInterface : class where TInterface : class
{ {
var lazyCap = new LazyCapability(AwaitProxy(task)); var lazyCap = new LazyCapability(AwaitProxy(task));
return CapabilityReflection.CreateProxy<TInterface>(lazyCap, memberName, sourceFilePath, sourceLineNumber) as TInterface; return (CapabilityReflection.CreateProxy<TInterface>(lazyCap, memberName, sourceFilePath, sourceLineNumber) as TInterface)!;
} }
static readonly MemberAccessPath Path_OneAndOnly = new MemberAccessPath(0U); static readonly MemberAccessPath Path_OneAndOnly = new MemberAccessPath(0U);
@ -168,15 +168,15 @@ namespace Capnp.Rpc
} }
var lazyCap = new LazyCapability(AwaitProxy(task)); var lazyCap = new LazyCapability(AwaitProxy(task));
return CapabilityReflection.CreateProxy<TInterface>(lazyCap) as TInterface; return (CapabilityReflection.CreateProxy<TInterface>(lazyCap) as TInterface)!;
} }
else else
{ {
return CapabilityReflection.CreateProxy<TInterface>(answer.Access(Path_OneAndOnly)) as TInterface; return (CapabilityReflection.CreateProxy<TInterface>(answer.Access(Path_OneAndOnly)) as TInterface)!;
} }
} }
internal static IRpcEndpoint AskingEndpoint internal static IRpcEndpoint? AskingEndpoint
{ {
get => _askingEndpoint.Value; get => _askingEndpoint.Value;
set { _askingEndpoint.Value = value; } set { _askingEndpoint.Value = value; }

View File

@ -1,6 +1,4 @@
using System.Threading.Tasks; namespace Capnp.Rpc
namespace Capnp.Rpc
{ {
/// <summary> /// <summary>
/// Low-level capability which as imported from a remote peer. /// Low-level capability which as imported from a remote peer.

View File

@ -1,7 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -34,7 +31,7 @@ namespace Capnp.Rpc.Interception
return new Proxy(Access(access)); return new Proxy(Access(access));
} }
public ConsumedCapability Access(MemberAccessPath access) public ConsumedCapability? Access(MemberAccessPath access)
{ {
if (_futureResult.Task.IsCompleted) if (_futureResult.Task.IsCompleted)
{ {
@ -44,7 +41,7 @@ namespace Capnp.Rpc.Interception
} }
catch (AggregateException exception) catch (AggregateException exception)
{ {
throw exception.InnerException; throw exception.InnerException!;
} }
} }
else else
@ -107,7 +104,7 @@ namespace Capnp.Rpc.Interception
/// <summary> /// <summary>
/// Input arguments /// Input arguments
/// </summary> /// </summary>
public SerializerState InArgs { get; set; } public SerializerState? InArgs { get; set; }
/// <summary> /// <summary>
/// Output arguments ("return value") /// Output arguments ("return value")
@ -117,7 +114,7 @@ namespace Capnp.Rpc.Interception
/// <summary> /// <summary>
/// Exception text, or null if there is no exception /// Exception text, or null if there is no exception
/// </summary> /// </summary>
public string Exception { get; set; } public string? Exception { get; set; }
/// <summary> /// <summary>
/// Whether the call should return in canceled state to Alice (the original caller). /// Whether the call should return in canceled state to Alice (the original caller).
@ -156,7 +153,7 @@ namespace Capnp.Rpc.Interception
/// <item><description>null</description></item> /// <item><description>null</description></item>
/// </list> /// </list>
/// </summary> /// </summary>
public object Bob public object? Bob
{ {
get => _bob; get => _bob;
set set
@ -196,11 +193,11 @@ namespace Capnp.Rpc.Interception
} }
} }
internal Proxy BobProxy { get; private set; } internal Proxy? BobProxy { get; private set; }
readonly CensorCapability _censorCapability; readonly CensorCapability _censorCapability;
PromisedAnswer _promisedAnswer; PromisedAnswer _promisedAnswer;
object _bob; object? _bob;
internal IPromisedAnswer Answer => _promisedAnswer; internal IPromisedAnswer Answer => _promisedAnswer;
@ -224,8 +221,13 @@ namespace Capnp.Rpc.Interception
{ {
for (int i = 0; i < state.Caps.Count; i++) for (int i = 0; i < state.Caps.Count; i++)
{ {
state.Caps[i] = policy.Attach(state.Caps[i]); var cap = state.Caps[i];
state.Caps[i].AddRef(); if (cap != null)
{
cap = policy.Attach(cap);
state.Caps[i] = cap;
cap.AddRef();
}
} }
} }
} }
@ -236,8 +238,13 @@ namespace Capnp.Rpc.Interception
{ {
for (int i = 0; i < state.Caps.Count; i++) for (int i = 0; i < state.Caps.Count; i++)
{ {
state.Caps[i] = policy.Detach(state.Caps[i]); var cap = state.Caps[i];
state.Caps[i].AddRef(); if (cap != null)
{
cap = policy.Detach(cap);
state.Caps[i] = cap;
cap.AddRef();
}
} }
} }
} }
@ -246,8 +253,12 @@ namespace Capnp.Rpc.Interception
/// Intercepts all capabilies inside the input arguments /// Intercepts all capabilies inside the input arguments
/// </summary> /// </summary>
/// <param name="policyOverride">Policy to use, or null to further use present policy</param> /// <param name="policyOverride">Policy to use, or null to further use present policy</param>
public void InterceptInCaps(IInterceptionPolicy policyOverride = null) /// <exception cref="InvalidOperationException">InArgs not set</exception>
public void InterceptInCaps(IInterceptionPolicy? policyOverride = null)
{ {
if (InArgs == null)
throw new InvalidOperationException("InArgs not set");
InterceptCaps(InArgs, policyOverride ?? _censorCapability.Policy); InterceptCaps(InArgs, policyOverride ?? _censorCapability.Policy);
} }
@ -255,7 +266,7 @@ namespace Capnp.Rpc.Interception
/// Intercepts all capabilies inside the output arguments /// Intercepts all capabilies inside the output arguments
/// </summary> /// </summary>
/// <param name="policyOverride">Policy to use, or null to further use present policy</param> /// <param name="policyOverride">Policy to use, or null to further use present policy</param>
public void InterceptOutCaps(IInterceptionPolicy policyOverride = null) public void InterceptOutCaps(IInterceptionPolicy? policyOverride = null)
{ {
InterceptCaps(OutArgs, policyOverride ?? _censorCapability.Policy); InterceptCaps(OutArgs, policyOverride ?? _censorCapability.Policy);
} }
@ -264,8 +275,12 @@ namespace Capnp.Rpc.Interception
/// Unintercepts all capabilies inside the input arguments /// Unintercepts all capabilies inside the input arguments
/// </summary> /// </summary>
/// <param name="policyOverride">Policy to remove, or null to remove present policy</param> /// <param name="policyOverride">Policy to remove, or null to remove present policy</param>
public void UninterceptInCaps(IInterceptionPolicy policyOverride = null) /// <exception cref="InvalidOperationException">InArgs not set</exception>
public void UninterceptInCaps(IInterceptionPolicy? policyOverride = null)
{ {
if (InArgs == null)
throw new InvalidOperationException("InArgs not set");
UninterceptCaps(InArgs, policyOverride ?? _censorCapability.Policy); UninterceptCaps(InArgs, policyOverride ?? _censorCapability.Policy);
} }
@ -273,7 +288,7 @@ namespace Capnp.Rpc.Interception
/// Unintercepts all capabilies inside the output arguments /// Unintercepts all capabilies inside the output arguments
/// </summary> /// </summary>
/// <param name="policyOverride">Policy to remove, or null to remove present policy</param> /// <param name="policyOverride">Policy to remove, or null to remove present policy</param>
public void UninterceptOutCaps(IInterceptionPolicy policyOverride = null) public void UninterceptOutCaps(IInterceptionPolicy? policyOverride = null)
{ {
UninterceptCaps(OutArgs, policyOverride ?? _censorCapability.Policy); UninterceptCaps(OutArgs, policyOverride ?? _censorCapability.Policy);
} }
@ -281,14 +296,16 @@ namespace Capnp.Rpc.Interception
/// <summary> /// <summary>
/// Forwards this intercepted call to the target capability ("Bob"). /// Forwards this intercepted call to the target capability ("Bob").
/// </summary> /// </summary>
/// <exception cref="InvalidOperationException">Bob/InArgs not set</exception>
public void ForwardToBob() public void ForwardToBob()
{ {
if (Bob == null) if (Bob == null)
{
throw new InvalidOperationException("Bob is null"); throw new InvalidOperationException("Bob is null");
}
var answer = BobProxy.Call(InterfaceId, MethodId, InArgs.Rewrap<DynamicSerializerState>(), default, CancelToBob); if (InArgs == null)
throw new InvalidOperationException("InArgs not set");
var answer = BobProxy!.Call(InterfaceId, MethodId, InArgs.Rewrap<DynamicSerializerState>(), default, CancelToBob);
State = InterceptionState.ForwardedToBob; State = InterceptionState.ForwardedToBob;

View File

@ -32,7 +32,7 @@
writer.SenderHosted = endpoint.AllocateExport(MyVine, out bool _); writer.SenderHosted = endpoint.AllocateExport(MyVine, out bool _);
} }
internal override void Freeze(out IRpcEndpoint boundEndpoint) internal override void Freeze(out IRpcEndpoint? boundEndpoint)
{ {
boundEndpoint = null; boundEndpoint = null;
} }

View File

@ -45,16 +45,16 @@ namespace Capnp.Rpc.Interception
switch (cap) switch (cap)
{ {
case Proxy proxy: case Proxy proxy:
return CapabilityReflection.CreateProxy<TCap>(Attach(policy, proxy.ConsumedCap)) as TCap; return (CapabilityReflection.CreateProxy<TCap>(Attach(policy, proxy.ConsumedCap!)) as TCap)!;
case ConsumedCapability ccap: case ConsumedCapability ccap:
return new CensorCapability(ccap, policy) as TCap; return (new CensorCapability(ccap, policy) as TCap)!;
default: default:
return Attach(policy, return (Attach(policy,
CapabilityReflection.CreateProxy<TCap>( (CapabilityReflection.CreateProxy<TCap>(
LocalCapability.Create( LocalCapability.Create(
Skeleton.GetOrCreateSkeleton(cap, false))) as TCap); Skeleton.GetOrCreateSkeleton(cap, false))) as TCap)!));
} }
} }
@ -79,11 +79,11 @@ namespace Capnp.Rpc.Interception
switch (cap) switch (cap)
{ {
case Proxy proxy: case Proxy proxy:
return CapabilityReflection.CreateProxy<TCap>(Detach(policy, proxy.ConsumedCap)) as TCap; return (CapabilityReflection.CreateProxy<TCap>(Detach(policy, proxy.ConsumedCap!)) as TCap)!;
case CensorCapability ccap: case CensorCapability ccap:
{ {
var cur = ccap; CensorCapability? cur = ccap;
var stk = new Stack<IInterceptionPolicy>(); var stk = new Stack<IInterceptionPolicy>();
do do
@ -96,7 +96,7 @@ namespace Capnp.Rpc.Interception
{ {
cur2 = p.Attach(cur2); cur2 = p.Attach(cur2);
} }
return cur2 as TCap; return (cur2 as TCap)!;
} }
stk.Push(cur.Policy); stk.Push(cur.Policy);
@ -104,7 +104,7 @@ namespace Capnp.Rpc.Interception
} }
while (cur != null); while (cur != null);
return ccap as TCap; return (ccap as TCap)!;
} }
default: default:

View File

@ -27,7 +27,7 @@ namespace Capnp.Rpc
WhenResolved = capabilityTask; WhenResolved = capabilityTask;
} }
internal override void Freeze(out IRpcEndpoint boundEndpoint) internal override void Freeze(out IRpcEndpoint? boundEndpoint)
{ {
if (WhenResolved.IsCompleted) if (WhenResolved.IsCompleted)
{ {
@ -37,7 +37,7 @@ namespace Capnp.Rpc
} }
catch (AggregateException exception) catch (AggregateException exception)
{ {
throw exception.InnerException; throw exception.InnerException!;
} }
} }
else else

View File

@ -15,7 +15,7 @@ namespace Capnp.Rpc
_access = access; _access = access;
} }
internal override void Freeze(out IRpcEndpoint boundEndpoint) internal override void Freeze(out IRpcEndpoint? boundEndpoint)
{ {
boundEndpoint = null; boundEndpoint = null;
} }
@ -43,7 +43,7 @@ namespace Capnp.Rpc
} }
catch (AggregateException exception) catch (AggregateException exception)
{ {
throw exception.InnerException; throw exception.InnerException!;
} }
using (var proxy = new Proxy(_access.Eval(result))) using (var proxy = new Proxy(_access.Eval(result)))

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -14,7 +13,7 @@ namespace Capnp.Rpc
public static ConsumedCapability Create(Skeleton skeleton) public static ConsumedCapability Create(Skeleton skeleton)
{ {
if (skeleton is Vine vine) if (skeleton is Vine vine)
return vine.Proxy.ConsumedCap; return vine.Proxy.ConsumedCap!;
else else
return _localCaps.GetValue(skeleton, _ => new LocalCapability(_)); return _localCaps.GetValue(skeleton, _ => new LocalCapability(_));
} }
@ -22,7 +21,7 @@ namespace Capnp.Rpc
static async Task<DeserializerState> AwaitAnswer(Task<AnswerOrCounterquestion> call) static async Task<DeserializerState> AwaitAnswer(Task<AnswerOrCounterquestion> call)
{ {
var aorcq = await call; var aorcq = await call;
return aorcq.Answer ?? await aorcq.Counterquestion.WhenReturned; return aorcq.Answer ?? await aorcq.Counterquestion!.WhenReturned;
} }
public Skeleton ProvidedCap { get; } public Skeleton ProvidedCap { get; }
@ -55,7 +54,7 @@ namespace Capnp.Rpc
capDesc.SenderHosted = endpoint.AllocateExport(ProvidedCap, out bool _); capDesc.SenderHosted = endpoint.AllocateExport(ProvidedCap, out bool _);
} }
internal override void Freeze(out IRpcEndpoint boundEndpoint) internal override void Freeze(out IRpcEndpoint? boundEndpoint)
{ {
boundEndpoint = null; boundEndpoint = null;
} }

View File

@ -169,7 +169,7 @@ namespace Capnp.Rpc
/// <param name="rpcState">The object (usually "params struct") on which to evaluate this path.</param> /// <param name="rpcState">The object (usually "params struct") on which to evaluate this path.</param>
/// <returns>Resulting low-level capability</returns> /// <returns>Resulting low-level capability</returns>
/// <exception cref="DeserializationException">Evaluation of this path did not give a capability</exception> /// <exception cref="DeserializationException">Evaluation of this path did not give a capability</exception>
public ConsumedCapability Eval(DeserializerState rpcState) public ConsumedCapability? Eval(DeserializerState rpcState)
{ {
var cur = rpcState; var cur = rpcState;
@ -184,7 +184,7 @@ namespace Capnp.Rpc
return null; return null;
case ObjectKind.Capability: case ObjectKind.Capability:
return rpcState.Caps[(int)cur.CapabilityIndex]; return rpcState.Caps![(int)cur.CapabilityIndex];
default: default:
throw new DeserializationException("Access path did not result in a capability"); throw new DeserializationException("Access path did not result in a capability");

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Diagnostics;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -8,14 +7,14 @@ namespace Capnp.Rpc
class PendingAnswer: IDisposable class PendingAnswer: IDisposable
{ {
readonly object _reentrancyBlocker = new object(); readonly object _reentrancyBlocker = new object();
readonly CancellationTokenSource _cts; readonly CancellationTokenSource? _cts;
readonly TaskCompletionSource<int> _whenCanceled; readonly TaskCompletionSource<int> _whenCanceled;
Task<AnswerOrCounterquestion> _callTask; Task<AnswerOrCounterquestion> _callTask;
Task _initialTask; Task? _initialTask;
Task _chainedTask; Task? _chainedTask;
bool _disposed; bool _disposed;
public PendingAnswer(Task<AnswerOrCounterquestion> callTask, CancellationTokenSource cts) public PendingAnswer(Task<AnswerOrCounterquestion> callTask, CancellationTokenSource? cts)
{ {
_cts = cts; _cts = cts;
_callTask = callTask ?? throw new ArgumentNullException(nameof(callTask)); _callTask = callTask ?? throw new ArgumentNullException(nameof(callTask));
@ -138,7 +137,7 @@ namespace Capnp.Rpc
case ObjectKind.Capability: case ObjectKind.Capability:
try try
{ {
var cap = aorcq.Answer.Caps[(int)cur.CapabilityIndex]; var cap = aorcq.Answer.Caps![(int)cur.CapabilityIndex];
proxy = new Proxy(cap ?? LazyCapability.Null); proxy = new Proxy(cap ?? LazyCapability.Null);
} }
catch (ArgumentOutOfRangeException) catch (ArgumentOutOfRangeException)
@ -154,7 +153,7 @@ namespace Capnp.Rpc
else else
{ {
var path = MemberAccessPath.Deserialize(rd); var path = MemberAccessPath.Deserialize(rd);
var cap = new RemoteAnswerCapability(aorcq.Counterquestion, path); var cap = new RemoteAnswerCapability(aorcq.Counterquestion!, path);
return new Proxy(cap); return new Proxy(cap);
} }
} }
@ -169,7 +168,7 @@ namespace Capnp.Rpc
{ {
if (_cts != null) if (_cts != null)
{ {
Task chainedTask; Task? chainedTask;
lock (_reentrancyBlocker) lock (_reentrancyBlocker)
{ {

View File

@ -1,7 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Capnp.Rpc namespace Capnp.Rpc
@ -61,11 +59,11 @@ namespace Capnp.Rpc
readonly TaskCompletionSource<DeserializerState> _tcs = new TaskCompletionSource<DeserializerState>(); readonly TaskCompletionSource<DeserializerState> _tcs = new TaskCompletionSource<DeserializerState>();
readonly uint _questionId; readonly uint _questionId;
ConsumedCapability _target; ConsumedCapability? _target;
SerializerState _inParams; SerializerState? _inParams;
int _inhibitFinishCounter; int _inhibitFinishCounter;
internal PendingQuestion(IRpcEndpoint ep, uint id, ConsumedCapability target, SerializerState inParams) internal PendingQuestion(IRpcEndpoint ep, uint id, ConsumedCapability? target, SerializerState? inParams)
{ {
RpcEndpoint = ep ?? throw new ArgumentNullException(nameof(ep)); RpcEndpoint = ep ?? throw new ArgumentNullException(nameof(ep));
_questionId = id; _questionId = id;
@ -75,7 +73,7 @@ namespace Capnp.Rpc
if (inParams != null) if (inParams != null)
{ {
foreach (var cap in inParams.Caps) foreach (var cap in inParams.Caps!)
{ {
cap?.AddRef(); cap?.AddRef();
} }
@ -236,7 +234,7 @@ namespace Capnp.Rpc
/// <param name="access">Access path</param> /// <param name="access">Access path</param>
/// <returns>Low-level capability</returns> /// <returns>Low-level capability</returns>
/// <exception cref="DeserializationException">The referenced member does not exist or does not resolve to a capability pointer.</exception> /// <exception cref="DeserializationException">The referenced member does not exist or does not resolve to a capability pointer.</exception>
public ConsumedCapability Access(MemberAccessPath access) public ConsumedCapability? Access(MemberAccessPath access)
{ {
lock (ReentrancyBlocker) lock (ReentrancyBlocker)
{ {
@ -249,7 +247,7 @@ namespace Capnp.Rpc
} }
catch (AggregateException exception) catch (AggregateException exception)
{ {
throw exception.InnerException; throw exception.InnerException!;
} }
} }
else else
@ -259,11 +257,11 @@ namespace Capnp.Rpc
} }
} }
static void ReleaseCaps(ConsumedCapability target, SerializerState inParams) static void ReleaseCaps(ConsumedCapability? target, SerializerState? inParams)
{ {
if (inParams != null) if (inParams != null)
{ {
foreach (var cap in inParams.Caps) foreach (var cap in inParams.Caps!)
{ {
cap?.Release(); cap?.Release();
} }
@ -277,7 +275,7 @@ namespace Capnp.Rpc
static void ReleaseOutCaps(DeserializerState outParams) static void ReleaseOutCaps(DeserializerState outParams)
{ {
foreach (var cap in outParams.Caps) foreach (var cap in outParams.Caps!)
{ {
cap?.Release(); cap?.Release();
} }
@ -285,12 +283,13 @@ namespace Capnp.Rpc
internal void Send() internal void Send()
{ {
SerializerState inParams; SerializerState? inParams;
ConsumedCapability target; ConsumedCapability? target;
lock (ReentrancyBlocker) lock (ReentrancyBlocker)
{ {
Debug.Assert(!StateFlags.HasFlag(State.Sent)); if (StateFlags.HasFlag(State.Sent))
throw new InvalidOperationException("Already sent");
inParams = _inParams; inParams = _inParams;
_inParams = null; _inParams = null;
@ -299,7 +298,7 @@ namespace Capnp.Rpc
StateFlags |= State.Sent; StateFlags |= State.Sent;
} }
var msg = (Message.WRITER)inParams.MsgBuilder.Root; var msg = (Message.WRITER)inParams!.MsgBuilder!.Root!;
Debug.Assert(msg.Call.Target.which != MessageTarget.WHICH.undefined); Debug.Assert(msg.Call.Target.which != MessageTarget.WHICH.undefined);
var call = msg.Call; var call = msg.Call;
call.QuestionId = QuestionId; call.QuestionId = QuestionId;
@ -316,15 +315,15 @@ namespace Capnp.Rpc
OnException(exception); OnException(exception);
} }
ReleaseCaps(target, inParams); ReleaseCaps(target!, inParams);
} }
#region IDisposable Support #region IDisposable Support
void Dispose(bool disposing) void Dispose(bool disposing)
{ {
SerializerState inParams; SerializerState? inParams;
ConsumedCapability target; ConsumedCapability? target;
bool justDisposed = false; bool justDisposed = false;
lock (ReentrancyBlocker) lock (ReentrancyBlocker)

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Capnp.Rpc namespace Capnp.Rpc
@ -19,7 +18,7 @@ namespace Capnp.Rpc
public override Task<Proxy> WhenResolved => _resolvedCap.Task; public override Task<Proxy> WhenResolved => _resolvedCap.Task;
internal override void Freeze(out IRpcEndpoint boundEndpoint) internal override void Freeze(out IRpcEndpoint? boundEndpoint)
{ {
lock (_reentrancyBlocker) lock (_reentrancyBlocker)
{ {
@ -31,7 +30,7 @@ namespace Capnp.Rpc
} }
catch (AggregateException exception) catch (AggregateException exception)
{ {
throw exception.InnerException; throw exception.InnerException!;
} }
} }
else else
@ -148,7 +147,7 @@ namespace Capnp.Rpc
} }
} }
protected override Proxy ResolvedCap protected override Proxy? ResolvedCap
{ {
get get
{ {
@ -158,7 +157,7 @@ namespace Capnp.Rpc
} }
catch (AggregateException exception) catch (AggregateException exception)
{ {
throw exception.InnerException; throw exception.InnerException!;
} }
} }
} }

View File

@ -1,5 +1,4 @@
using Microsoft.Extensions.Logging; using System;
using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -38,7 +37,7 @@ namespace Capnp.Rpc
/// <summary> /// <summary>
/// Underlying low-level capability /// Underlying low-level capability
/// </summary> /// </summary>
protected internal ConsumedCapability ConsumedCap { get; private set; } protected internal ConsumedCapability? ConsumedCap { get; private set; }
/// <summary> /// <summary>
/// Whether is this a broken capability. /// Whether is this a broken capability.
@ -99,12 +98,12 @@ namespace Capnp.Rpc
{ {
} }
internal Proxy(ConsumedCapability cap) internal Proxy(ConsumedCapability? cap)
{ {
Bind(cap); Bind(cap);
} }
internal void Bind(ConsumedCapability cap) internal void Bind(ConsumedCapability? cap)
{ {
if (ConsumedCap != null) if (ConsumedCap != null)
throw new InvalidOperationException("Proxy was already bound"); throw new InvalidOperationException("Proxy was already bound");
@ -116,7 +115,7 @@ namespace Capnp.Rpc
cap.AddRef(); cap.AddRef();
} }
internal IProvidedCapability GetProvider() internal IProvidedCapability? GetProvider()
{ {
switch (ConsumedCap) switch (ConsumedCap)
{ {
@ -200,7 +199,7 @@ namespace Capnp.Rpc
using (disposeThis ? this : null) using (disposeThis ? this : null)
{ {
return CapabilityReflection.CreateProxy<T>(ConsumedCap) as T; return (CapabilityReflection.CreateProxy<T>(ConsumedCap) as T)!;
} }
} }
@ -215,7 +214,7 @@ namespace Capnp.Rpc
ConsumedCap.Export(endpoint, writer); ConsumedCap.Export(endpoint, writer);
} }
internal void Freeze(out IRpcEndpoint boundEndpoint) internal void Freeze(out IRpcEndpoint? boundEndpoint)
{ {
if (_disposedValue) if (_disposedValue)
throw new ObjectDisposedException(nameof(Proxy)); throw new ObjectDisposedException(nameof(Proxy));

View File

@ -1,6 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
namespace Capnp.Rpc namespace Capnp.Rpc
{ {

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Capnp.Rpc namespace Capnp.Rpc

View File

@ -1,6 +1,4 @@
using Microsoft.Extensions.Logging; using System;
using System;
using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Capnp.Rpc namespace Capnp.Rpc
@ -18,7 +16,7 @@ namespace Capnp.Rpc
readonly PendingQuestion _question; readonly PendingQuestion _question;
readonly MemberAccessPath _access; readonly MemberAccessPath _access;
Proxy _resolvedCap; Proxy? _resolvedCap;
public RemoteAnswerCapability(PendingQuestion question, MemberAccessPath access): base(question.RpcEndpoint) public RemoteAnswerCapability(PendingQuestion question, MemberAccessPath access): base(question.RpcEndpoint)
{ {
@ -59,7 +57,7 @@ namespace Capnp.Rpc
} }
} }
protected override Proxy ResolvedCap protected override Proxy? ResolvedCap
{ {
get get
{ {
@ -74,7 +72,7 @@ namespace Capnp.Rpc
} }
catch (AggregateException exception) catch (AggregateException exception)
{ {
throw exception.InnerException; throw exception.InnerException!;
} }
_resolvedCap = new Proxy(_access.Eval(result)); _resolvedCap = new Proxy(_access.Eval(result));
@ -87,7 +85,11 @@ namespace Capnp.Rpc
async Task<Proxy> AwaitWhenResolved() async Task<Proxy> AwaitWhenResolved()
{ {
await _question.WhenReturned; await _question.WhenReturned;
return ResolvedCap;
if (_question.IsTailCall)
throw new InvalidOperationException("Question is a tail call, so won't resolve back.");
return ResolvedCap!;
} }
public override Task<Proxy> WhenResolved => AwaitWhenResolved(); public override Task<Proxy> WhenResolved => AwaitWhenResolved();
@ -168,7 +170,7 @@ namespace Capnp.Rpc
return call; return call;
} }
internal override void Freeze(out IRpcEndpoint boundEndpoint) internal override void Freeze(out IRpcEndpoint? boundEndpoint)
{ {
lock (_question.ReentrancyBlocker) lock (_question.ReentrancyBlocker)
{ {
@ -216,7 +218,7 @@ namespace Capnp.Rpc
if (_question.StateFlags.HasFlag(PendingQuestion.State.Returned)) if (_question.StateFlags.HasFlag(PendingQuestion.State.Returned))
{ {
ResolvedCap.Export(endpoint, writer); ResolvedCap?.Export(endpoint, writer);
} }
else else
{ {

View File

@ -1,7 +1,5 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace Capnp.Rpc namespace Capnp.Rpc
{ {
@ -24,6 +22,9 @@ namespace Capnp.Rpc
protected virtual Call.WRITER SetupMessage(DynamicSerializerState args, ulong interfaceId, ushort methodId) protected virtual Call.WRITER SetupMessage(DynamicSerializerState args, ulong interfaceId, ushort methodId)
{ {
if (args.MsgBuilder == null)
throw new ArgumentException("Unbound serializer state", nameof(args));
var callMsg = args.MsgBuilder.BuildRoot<Message.WRITER>(); var callMsg = args.MsgBuilder.BuildRoot<Message.WRITER>();
callMsg.which = Message.WHICH.Call; callMsg.which = Message.WHICH.Call;

View File

@ -23,14 +23,17 @@ namespace Capnp.Rpc
} }
protected int _pendingCallsOnPromise; protected int _pendingCallsOnPromise;
Task _disembargo; Task? _disembargo;
protected abstract Proxy ResolvedCap { get; } protected abstract Proxy? ResolvedCap { get; }
protected abstract void GetMessageTarget(MessageTarget.WRITER wr); protected abstract void GetMessageTarget(MessageTarget.WRITER wr);
protected IPromisedAnswer CallOnResolution(ulong interfaceId, ushort methodId, DynamicSerializerState args) protected IPromisedAnswer CallOnResolution(ulong interfaceId, ushort methodId, DynamicSerializerState args)
{ {
if (ResolvedCap == null)
throw new InvalidOperationException("Capability not yet resolved, calling on resolution not possible");
try try
{ {
ResolvedCap.Freeze(out var resolvedCapEndpoint); ResolvedCap.Freeze(out var resolvedCapEndpoint);

View File

@ -19,7 +19,7 @@
{ {
var resolvedCap = await cap.WhenResolved; var resolvedCap = await cap.WhenResolved;
endpoint.Resolve(preliminaryId, vine, () => resolvedCap.ConsumedCap); endpoint.Resolve(preliminaryId, vine, () => resolvedCap.ConsumedCap!);
} }
catch (System.Exception exception) catch (System.Exception exception)
{ {

View File

@ -4,9 +4,6 @@ using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -65,8 +62,8 @@ namespace Capnp.Rpc
internal class RpcEndpoint : IEndpoint, IRpcEndpoint internal class RpcEndpoint : IEndpoint, IRpcEndpoint
{ {
static readonly ThreadLocal<Action> _exportCapTablePostActions = new ThreadLocal<Action>(); static readonly ThreadLocal<Action?> _exportCapTablePostActions = new ThreadLocal<Action?>();
static readonly ThreadLocal<PendingQuestion> _tailCall = new ThreadLocal<PendingQuestion>(); static readonly ThreadLocal<PendingQuestion?> _tailCall = new ThreadLocal<PendingQuestion?>();
static readonly ThreadLocal<bool> _canDeferCalls = new ThreadLocal<bool>(); static readonly ThreadLocal<bool> _canDeferCalls = new ThreadLocal<bool>();
ILogger Logger { get; } = Logging.CreateLogger<RpcEndpoint>(); ILogger Logger { get; } = Logging.CreateLogger<RpcEndpoint>();
@ -145,8 +142,7 @@ namespace Capnp.Rpc
Tx(mb.Frame); Tx(mb.Frame);
} }
void IRpcEndpoint.Resolve(uint preliminaryId, Skeleton preliminaryCap, void IRpcEndpoint.Resolve(uint preliminaryId, Skeleton preliminaryCap, Func<ConsumedCapability> resolvedCapGetter)
Func<ConsumedCapability> resolvedCapGetter)
{ {
lock (_reentrancyBlocker) lock (_reentrancyBlocker)
{ {
@ -232,7 +228,7 @@ namespace Capnp.Rpc
return AllocateExport(providedCapability, out first); return AllocateExport(providedCapability, out first);
} }
PendingQuestion AllocateQuestion(ConsumedCapability target, SerializerState inParams) PendingQuestion AllocateQuestion(ConsumedCapability? target, SerializerState? inParams)
{ {
lock (_reentrancyBlocker) lock (_reentrancyBlocker)
{ {
@ -293,7 +289,7 @@ namespace Capnp.Rpc
uint q = req.QuestionId; uint q = req.QuestionId;
var bootstrap = DynamicSerializerState.CreateForRpc(); var bootstrap = DynamicSerializerState.CreateForRpc();
var ans = bootstrap.MsgBuilder.BuildRoot<Message.WRITER>(); var ans = bootstrap.MsgBuilder!.BuildRoot<Message.WRITER>();
ans.which = Message.WHICH.Return; ans.which = Message.WHICH.Return;
var ret = ans.Return; var ret = ans.Return;
@ -305,7 +301,7 @@ namespace Capnp.Rpc
if (bootstrapCap != null) if (bootstrapCap != null)
{ {
ret.which = Return.WHICH.Results; ret.which = Return.WHICH.Results;
bootstrap.SetCapability(bootstrap.ProvideCapability(LocalCapability.Create(_host.BootstrapCap))); bootstrap.SetCapability(bootstrap.ProvideCapability(LocalCapability.Create(bootstrapCap)));
ret.Results.Content = bootstrap; ret.Results.Content = bootstrap;
bootstrapTask = Task.FromResult<AnswerOrCounterquestion>(bootstrap); bootstrapTask = Task.FromResult<AnswerOrCounterquestion>(bootstrap);
@ -371,12 +367,12 @@ namespace Capnp.Rpc
} }
catch (RpcException exception) catch (RpcException exception)
{ {
Logger.LogWarning($"Unable to return call: {exception.InnerException.Message}"); Logger.LogWarning($"Unable to return call: {exception.InnerException?.Message ?? exception.Message}");
} }
} }
IProvidedCapability cap; IProvidedCapability? cap;
PendingAnswer pendingAnswer = null; PendingAnswer pendingAnswer;
bool releaseParamCaps = false; bool releaseParamCaps = false;
void AwaitAnswerAndReply() void AwaitAnswerAndReply()
@ -414,8 +410,8 @@ namespace Capnp.Rpc
} }
else if (aorcq.Answer != null || aorcq.Counterquestion != _tailCall.Value) else if (aorcq.Answer != null || aorcq.Counterquestion != _tailCall.Value)
{ {
var results = aorcq.Answer ?? (DynamicSerializerState)(await aorcq.Counterquestion.WhenReturned); var results = aorcq.Answer ?? (DynamicSerializerState)(await aorcq.Counterquestion!.WhenReturned);
var ret = SetupReturn(results.MsgBuilder); var ret = SetupReturn(results.MsgBuilder!);
switch (req.SendResultsTo.which) switch (req.SendResultsTo.which)
{ {
@ -569,7 +565,7 @@ namespace Capnp.Rpc
case MessageTarget.WHICH.PromisedAnswer: case MessageTarget.WHICH.PromisedAnswer:
{ {
bool exists; bool exists;
PendingAnswer previousAnswer; PendingAnswer? previousAnswer;
lock (_reentrancyBlocker) lock (_reentrancyBlocker)
{ {
@ -578,7 +574,7 @@ namespace Capnp.Rpc
if (exists) if (exists)
{ {
previousAnswer.Chain( previousAnswer!.Chain(
false, false,
req.Target.PromisedAnswer, req.Target.PromisedAnswer,
async t => async t =>
@ -633,7 +629,7 @@ namespace Capnp.Rpc
void ProcessReturn(Return.READER req) void ProcessReturn(Return.READER req)
{ {
PendingQuestion question; PendingQuestion? question;
lock (_reentrancyBlocker) lock (_reentrancyBlocker)
{ {
@ -674,7 +670,7 @@ namespace Capnp.Rpc
case Return.WHICH.TakeFromOtherQuestion: case Return.WHICH.TakeFromOtherQuestion:
{ {
bool exists; bool exists;
PendingAnswer pendingAnswer; PendingAnswer? pendingAnswer;
lock (_reentrancyBlocker) lock (_reentrancyBlocker)
{ {
@ -683,7 +679,7 @@ namespace Capnp.Rpc
if (exists) if (exists)
{ {
pendingAnswer.Chain(false, async t => pendingAnswer!.Chain(false, async t =>
{ {
try try
{ {
@ -747,6 +743,8 @@ namespace Capnp.Rpc
lock (_reentrancyBlocker) lock (_reentrancyBlocker)
{ {
var resolvedCap = ImportCap(resolve.Cap); var resolvedCap = ImportCap(resolve.Cap);
if (resolvedCap == null)
resolvedCap = LazyCapability.CreateBrokenCap("Failed to resolve this capability");
resolvableCap.ResolveTo(resolvedCap); resolvableCap.ResolveTo(resolvedCap);
} }
break; break;
@ -935,7 +933,7 @@ namespace Capnp.Rpc
{ {
foreach (var cap in results.Caps) foreach (var cap in results.Caps)
{ {
cap.Release(); cap?.Release();
} }
} }
}); });
@ -984,8 +982,8 @@ namespace Capnp.Rpc
try try
{ {
int icount = checked((int)count); int icount = checked((int)count);
rc.Release(icount); rc!.Release(icount);
rc.Cap.Relinquish(icount); rc!.Cap.Relinquish(icount);
if (rc.RefCount == 0) if (rc.RefCount == 0)
{ {
@ -1171,7 +1169,7 @@ namespace Capnp.Rpc
} }
} }
ConsumedCapability ImportCap(CapDescriptor.READER capDesc) ConsumedCapability? ImportCap(CapDescriptor.READER capDesc)
{ {
lock (_reentrancyBlocker) lock (_reentrancyBlocker)
{ {
@ -1192,8 +1190,7 @@ namespace Capnp.Rpc
rcw.Cap.SetTarget(impCap); rcw.Cap.SetTarget(impCap);
} }
Debug.Assert(impCap != null); return impCap!;
return impCap;
} }
else else
{ {
@ -1236,7 +1233,6 @@ namespace Capnp.Rpc
case CapDescriptor.WHICH.ReceiverHosted: case CapDescriptor.WHICH.ReceiverHosted:
if (_exportTable.TryGetValue(capDesc.ReceiverHosted, out var rc)) if (_exportTable.TryGetValue(capDesc.ReceiverHosted, out var rc))
{ {
Debug.Assert(rc.Cap != null);
return LocalCapability.Create(rc.Cap); return LocalCapability.Create(rc.Cap);
} }
else else
@ -1311,9 +1307,9 @@ namespace Capnp.Rpc
} }
} }
public IList<ConsumedCapability> ImportCapTable(Payload.READER payload) public IList<ConsumedCapability?> ImportCapTable(Payload.READER payload)
{ {
var list = new List<ConsumedCapability>(); var list = new List<ConsumedCapability?>();
if (payload.CapTable != null) if (payload.CapTable != null)
{ {
@ -1341,7 +1337,7 @@ namespace Capnp.Rpc
Debug.Assert(_exportCapTablePostActions.Value == null); Debug.Assert(_exportCapTablePostActions.Value == null);
_exportCapTablePostActions.Value = null; _exportCapTablePostActions.Value = null;
payload.CapTable.Init(state.MsgBuilder.Caps.Count); payload.CapTable.Init(state.MsgBuilder!.Caps!.Count);
int i = 0; int i = 0;
foreach (var cap in state.MsgBuilder.Caps) foreach (var cap in state.MsgBuilder.Caps)
@ -1454,7 +1450,7 @@ namespace Capnp.Rpc
} }
catch (RpcException exception) catch (RpcException exception)
{ {
Logger.LogWarning($"Unable to release import: {exception.InnerException.Message}"); Logger.LogWarning($"Unable to release import: {exception.InnerException?.Message ?? exception.Message}");
} }
} }
} }
@ -1492,12 +1488,12 @@ namespace Capnp.Rpc
return inboundEndpoint; return inboundEndpoint;
} }
Skeleton _bootstrapCap; Skeleton? _bootstrapCap;
/// <summary> /// <summary>
/// Gets or sets the bootstrap capability. /// Gets or sets the bootstrap capability.
/// </summary> /// </summary>
public Skeleton BootstrapCap public Skeleton? BootstrapCap
{ {
get => _bootstrapCap; get => _bootstrapCap;
set set

View File

@ -1,8 +1,5 @@
using Microsoft.Extensions.Logging; using System;
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -179,8 +176,8 @@ namespace Capnp.Rpc
ILogger Logger { get; } = Logging.CreateLogger<Skeleton<T>>(); ILogger Logger { get; } = Logging.CreateLogger<Skeleton<T>>();
#endif #endif
Func<DeserializerState, CancellationToken, Task<AnswerOrCounterquestion>>[] _methods; Func<DeserializerState, CancellationToken, Task<AnswerOrCounterquestion>>[] _methods = null!;
CancellationTokenSource _disposed = new CancellationTokenSource(); CancellationTokenSource? _disposed = new CancellationTokenSource();
readonly object _reentrancyBlocker = new object(); readonly object _reentrancyBlocker = new object();
int _pendingCalls; int _pendingCalls;
@ -204,7 +201,7 @@ namespace Capnp.Rpc
/// <summary> /// <summary>
/// Gets the underlying capability implementation. /// Gets the underlying capability implementation.
/// </summary> /// </summary>
protected T Impl { get; private set; } protected T Impl { get; private set; } = default!;
/// <summary> /// <summary>
/// Gets the ID of the implemented interface. /// Gets the ID of the implemented interface.

View File

@ -1,13 +1,9 @@
using Capnp.FrameTracing; using Capnp.FrameTracing;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -46,17 +42,17 @@ namespace Capnp.Rpc
readonly RpcEngine _rpcEngine; readonly RpcEngine _rpcEngine;
readonly TcpClient _client; readonly TcpClient _client;
Func<Stream, Stream> _createLayers = _ => _; Func<Stream, Stream> _createLayers = _ => _;
RpcEngine.RpcEndpoint _inboundEndpoint; RpcEngine.RpcEndpoint? _inboundEndpoint;
OutboundTcpEndpoint _outboundEndpoint; OutboundTcpEndpoint? _outboundEndpoint;
FramePump _pump; FramePump? _pump;
Thread _pumpThread; Thread? _pumpThread;
Action _attachTracerAction; Action? _attachTracerAction;
/// <summary> /// <summary>
/// Gets a Task which completes when TCP is connected. Will be /// Gets a Task which completes when TCP is connected. Will be
/// null until connection is actually requested (either by calling Connect or using appropriate constructor). /// null until connection is actually requested (either by calling Connect or using appropriate constructor).
/// </summary> /// </summary>
public Task WhenConnected { get; private set; } public Task? WhenConnected { get; private set; }
async Task ConnectAsync(string host, int port) async Task ConnectAsync(string host, int port)
{ {
@ -156,8 +152,14 @@ namespace Capnp.Rpc
/// </summary> /// </summary>
/// <typeparam name="TProxy">Bootstrap capability interface</typeparam> /// <typeparam name="TProxy">Bootstrap capability interface</typeparam>
/// <returns>A proxy for the bootstrap capability</returns> /// <returns>A proxy for the bootstrap capability</returns>
/// <exception cref="InvalidOperationException">Not connected</exception>
public TProxy GetMain<TProxy>() where TProxy: class public TProxy GetMain<TProxy>() where TProxy: class
{ {
if (WhenConnected == null)
{
throw new InvalidOperationException("Not connecting");
}
if (!WhenConnected.IsCompleted) if (!WhenConnected.IsCompleted)
{ {
throw new InvalidOperationException("Connection not yet established"); throw new InvalidOperationException("Connection not yet established");
@ -168,9 +170,7 @@ namespace Capnp.Rpc
throw new InvalidOperationException("Connection not successfully established"); throw new InvalidOperationException("Connection not successfully established");
} }
Debug.Assert(_inboundEndpoint != null); return (CapabilityReflection.CreateProxy<TProxy>(_inboundEndpoint!.QueryMain()) as TProxy)!;
return CapabilityReflection.CreateProxy<TProxy>(_inboundEndpoint.QueryMain()) as TProxy;
} }
/// <summary> /// <summary>
@ -182,7 +182,7 @@ namespace Capnp.Rpc
try try
{ {
if (!WhenConnected.Wait(500)) if (WhenConnected != null && !WhenConnected.Wait(500))
{ {
Logger.LogError("Unable to join connection task within timeout"); Logger.LogError("Unable to join connection task within timeout");
} }
@ -218,7 +218,7 @@ namespace Capnp.Rpc
_attachTracerAction += () => _attachTracerAction += () =>
{ {
_pump.AttachTracer(tracer); _pump?.AttachTracer(tracer);
}; };
} }
@ -257,33 +257,33 @@ namespace Capnp.Rpc
/// <summary> /// <summary>
/// Gets the number of RPC protocol messages sent by this client so far. /// Gets the number of RPC protocol messages sent by this client so far.
/// </summary> /// </summary>
public long SendCount => _inboundEndpoint.SendCount; public long SendCount => _inboundEndpoint?.SendCount ?? 0;
/// <summary> /// <summary>
/// Gets the number of RPC protocol messages received by this client so far. /// Gets the number of RPC protocol messages received by this client so far.
/// </summary> /// </summary>
public long RecvCount => _inboundEndpoint.RecvCount; public long RecvCount => _inboundEndpoint?.RecvCount ?? 0;
/// <summary> /// <summary>
/// Gets the remote port number which this client is connected to, /// Gets the remote port number which this client is connected to,
/// or null if the connection is not yet established. /// or null if the connection is not yet established.
/// </summary> /// </summary>
public int? RemotePort => ((IPEndPoint)_client.Client?.RemoteEndPoint)?.Port; public int? RemotePort => ((IPEndPoint)_client.Client.RemoteEndPoint)?.Port;
/// <summary> /// <summary>
/// Gets the local port number which this client using, /// Gets the local port number which this client using,
/// or null if the connection is not yet established. /// or null if the connection is not yet established.
/// </summary> /// </summary>
public int? LocalPort => ((IPEndPoint)_client.Client?.LocalEndPoint)?.Port; public int? LocalPort => ((IPEndPoint)_client.Client.LocalEndPoint)?.Port;
/// <summary> /// <summary>
/// Whether the I/O thread is currently running /// Whether the I/O thread is currently running
/// </summary> /// </summary>
public bool IsComputing => _pumpThread.ThreadState == System.Threading.ThreadState.Running; public bool IsComputing => _pumpThread?.ThreadState == System.Threading.ThreadState.Running;
/// <summary> /// <summary>
/// Whether the I/O thread is waiting for data to receive /// Whether the I/O thread is waiting for data to receive
/// </summary> /// </summary>
public bool IsWaitingForData => _pump.IsWaitingForData; public bool IsWaitingForData => _pump?.IsWaitingForData ?? false;
} }
} }

View File

@ -109,12 +109,12 @@ namespace Capnp.Rpc
public FramePump Pump { get; private set; } public FramePump Pump { get; private set; }
public OutboundTcpEndpoint OutboundEp { get; private set; } public OutboundTcpEndpoint OutboundEp { get; private set; }
public RpcEngine.RpcEndpoint InboundEp { get; private set; } public RpcEngine.RpcEndpoint InboundEp { get; private set; }
public Thread PumpRunner { get; private set; } public Thread? PumpRunner { get; private set; }
public int? LocalPort => ((IPEndPoint)Client.Client.LocalEndPoint)?.Port; public int? LocalPort => ((IPEndPoint)Client.Client.LocalEndPoint)?.Port;
public int? RemotePort => ((IPEndPoint)Client.Client.RemoteEndPoint)?.Port; public int? RemotePort => ((IPEndPoint)Client.Client.RemoteEndPoint)?.Port;
public long RecvCount => InboundEp.RecvCount; public long RecvCount => InboundEp.RecvCount;
public long SendCount => InboundEp.SendCount; public long SendCount => InboundEp.SendCount;
public bool IsComputing => PumpRunner.ThreadState == ThreadState.Running; public bool IsComputing => PumpRunner?.ThreadState == ThreadState.Running;
public bool IsWaitingForData => Pump.IsWaitingForData; public bool IsWaitingForData => Pump.IsWaitingForData;
public void AttachTracer(IFrameTracer tracer) public void AttachTracer(IFrameTracer tracer)
@ -184,7 +184,7 @@ namespace Capnp.Rpc
connection.Start(); connection.Start();
} }
connection.PumpRunner.Start(); connection.PumpRunner!.Start();
} }
} }
catch (SocketException) catch (SocketException)
@ -199,8 +199,13 @@ namespace Capnp.Rpc
} }
} }
void SafeJoin(Thread thread) void SafeJoin(Thread? thread)
{ {
if (thread == null)
{
return;
}
for (int retry = 0; retry < 5; ++retry) for (int retry = 0; retry < 5; ++retry)
{ {
try try
@ -332,6 +337,6 @@ namespace Capnp.Rpc
/// <summary> /// <summary>
/// Fires when a new incoming connection was accepted, or when an active connection is closed. /// Fires when a new incoming connection was accepted, or when an active connection is closed.
/// </summary> /// </summary>
public event Action<object, ConnectionEventArgs> OnConnectionChanged; public event Action<object, ConnectionEventArgs>? OnConnectionChanged;
} }
} }

View File

@ -1,6 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;

View File

@ -1,4 +1,5 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
#nullable disable
using Capnp; using Capnp;
using Capnp.Rpc; using Capnp.Rpc;
@ -2641,3 +2642,5 @@ namespace Capnp.Rpc
} }
} }
} }
#nullable restore

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
namespace Capnp namespace Capnp
{ {

View File

@ -1,6 +1,4 @@
using System; namespace Capnp
namespace Capnp
{ {
/// <summary> /// <summary>
/// Helper struct to represent the tuple (segment index, offset) /// Helper struct to represent the tuple (segment index, offset)

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -29,10 +28,10 @@ namespace Capnp
return s; return s;
} }
internal MessageBuilder MsgBuilder { get; set; } internal MessageBuilder? MsgBuilder { get; set; }
internal ISegmentAllocator Allocator => MsgBuilder?.Allocator; 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 SerializerState? Owner { get; set; }
internal int OwnerSlot { get; set; } internal int OwnerSlot { get; set; }
internal uint SegmentIndex { get; set; } internal uint SegmentIndex { get; set; }
internal int Offset { get; set; } internal int Offset { get; set; }
@ -43,7 +42,7 @@ namespace Capnp
internal ObjectKind Kind { get; set; } internal ObjectKind Kind { get; set; }
internal uint CapabilityIndex { get; set; } internal uint CapabilityIndex { get; set; }
SerializerState[] _linkedStates; SerializerState[]? _linkedStates;
/// <summary> /// <summary>
/// Constructs an unbound serializer state. /// Constructs an unbound serializer state.
@ -129,7 +128,7 @@ namespace Capnp
if (Owner != null) if (Owner != null)
ts.Bind(Owner, OwnerSlot); ts.Bind(Owner, OwnerSlot);
else else
ts.Bind(MsgBuilder); ts.Bind(MsgBuilder ?? throw Unbound());
return ts; return ts;
} }
@ -152,8 +151,8 @@ namespace Capnp
/// </summary> /// </summary>
public bool IsAllocated => Offset >= 0; public bool IsAllocated => Offset >= 0;
Span<ulong> SegmentSpan => IsAllocated ? Allocator.Segments[(int)SegmentIndex].Span : Span<ulong>.Empty; Span<ulong> SegmentSpan => IsAllocated && Allocator != null ? Allocator.Segments[(int)SegmentIndex].Span : Span<ulong>.Empty;
Span<ulong> FarSpan(uint index) => Allocator.Segments[(int)index].Span; Span<ulong> FarSpan(uint index) => Allocator!.Segments[(int)index].Span;
/// <summary> /// <summary>
/// Given this state describes a struct and is allocated, returns the struct's data section. /// Given this state describes a struct and is allocated, returns the struct's data section.
@ -181,6 +180,9 @@ namespace Capnp
} }
else else
{ {
if (Allocator == null)
throw Unbound();
SegmentIndex = Owner?.SegmentIndex ?? SegmentIndex; SegmentIndex = Owner?.SegmentIndex ?? SegmentIndex;
Allocator.Allocate(count, SegmentIndex, out var slice, false); Allocator.Allocate(count, SegmentIndex, out var slice, false);
SegmentIndex = slice.SegmentIndex; SegmentIndex = slice.SegmentIndex;
@ -191,7 +193,7 @@ namespace Capnp
} }
/// <summary> /// <summary>
/// Allocates storage for the underlying object. Does nothing if it is already allocated. From the point the object is allocated, its type cannot by changed /// Allocates storage for the underlying object. Does nothing if it is already allocated. From the point the object is allocated, its type cannot be changed
/// anymore (e.g. changing from struct to list, or modifying the struct's section sizes). /// anymore (e.g. changing from struct to list, or modifying the struct's section sizes).
/// </summary> /// </summary>
public void Allocate() public void Allocate()
@ -250,6 +252,9 @@ namespace Capnp
if (!target.IsAllocated) if (!target.IsAllocated)
throw new InvalidOperationException("Target must be allocated before a pointer can be built"); throw new InvalidOperationException("Target must be allocated before a pointer can be built");
if (MsgBuilder == null)
throw Unbound();
try try
{ {
if (SegmentSpan[offset] != 0) if (SegmentSpan[offset] != 0)
@ -345,7 +350,7 @@ namespace Capnp
{ {
WirePointer farPtr = default; WirePointer farPtr = default;
if (Allocator.Allocate(1, target.SegmentIndex, out var landingPadSlice, true)) if (Allocator!.Allocate(1, target.SegmentIndex, out var landingPadSlice, true))
{ {
farPtr.SetFarPointer(target.SegmentIndex, landingPadSlice.Offset, false); farPtr.SetFarPointer(target.SegmentIndex, landingPadSlice.Offset, false);
SegmentSpan[offset] = farPtr; SegmentSpan[offset] = farPtr;
@ -372,7 +377,7 @@ namespace Capnp
} }
} }
internal Rpc.ConsumedCapability DecodeCapPointer(int offset) internal Rpc.ConsumedCapability? DecodeCapPointer(int offset)
{ {
if (offset < 0) if (offset < 0)
throw new IndexOutOfRangeException(nameof(offset)); throw new IndexOutOfRangeException(nameof(offset));
@ -455,7 +460,7 @@ namespace Capnp
throw new InvalidOperationException("This object cannot own pointers to sub-objects"); throw new InvalidOperationException("This object cannot own pointers to sub-objects");
} }
_linkedStates[slot] = target; _linkedStates![slot] = target;
} }
/// <summary> /// <summary>
@ -476,6 +481,7 @@ namespace Capnp
} }
static InvalidOperationException AlreadySet() => new InvalidOperationException("The object type was already set"); static InvalidOperationException AlreadySet() => new InvalidOperationException("The object type was already set");
static InvalidOperationException Unbound() => new InvalidOperationException("This state is not bound to a MessageBuilder");
void VerifyNotYetAllocated() void VerifyNotYetAllocated()
{ {
@ -654,7 +660,13 @@ namespace Capnp
/// <exception cref="EncoderFallbackException">Trying to obtain the UTF-8 encoding might throw this exception.</exception> /// <exception cref="EncoderFallbackException">Trying to obtain the UTF-8 encoding might throw this exception.</exception>
/// <exception cref="InvalidOperationException">The object type was already set to something different</exception> /// <exception cref="InvalidOperationException">The object type was already set to something different</exception>
/// <exception cref="ArgumentOutOfRangeException">UTF-8 encoding exceeds 2^29-2 bytes</exception> /// <exception cref="ArgumentOutOfRangeException">UTF-8 encoding exceeds 2^29-2 bytes</exception>
protected void WriteText(string text) protected void WriteText(string? text)
{
if (text == null)
{
VerifyNotYetAllocated();
}
else
{ {
byte[] srcBytes = Encoding.UTF8.GetBytes(text); byte[] srcBytes = Encoding.UTF8.GetBytes(text);
SetListOfValues(8, srcBytes.Length + 1); SetListOfValues(8, srcBytes.Length + 1);
@ -663,6 +675,7 @@ namespace Capnp
dstSpan = dstSpan.Slice(0, dstSpan.Length - 1); dstSpan = dstSpan.Slice(0, dstSpan.Length - 1);
srcSpan.CopyTo(dstSpan); srcSpan.CopyTo(dstSpan);
} }
}
/// <summary> /// <summary>
/// Writes data (up to 64 bits) into the underlying struct's data section. /// Writes data (up to 64 bits) into the underlying struct's data section.
@ -761,7 +774,7 @@ namespace Capnp
if (Kind != ObjectKind.Struct && Kind != ObjectKind.ListOfPointers) if (Kind != ObjectKind.Struct && Kind != ObjectKind.ListOfPointers)
throw new InvalidOperationException("This is not a struct or list of pointers"); throw new InvalidOperationException("This is not a struct or list of pointers");
ref var state = ref _linkedStates[index]; ref var state = ref _linkedStates![index];
if (state == null) if (state == null)
{ {
@ -784,12 +797,12 @@ namespace Capnp
/// <item><description>Object at given position is not compatible with the desired target serializer type.</description></item> /// <item><description>Object at given position is not compatible with the desired target serializer type.</description></item>
/// </list></exception> /// </list></exception>
/// <exception cref="IndexOutOfRangeException"><paramref name="index"/> is out of bounds.</exception> /// <exception cref="IndexOutOfRangeException"><paramref name="index"/> is out of bounds.</exception>
public TS TryGetPointer<TS>(int index) where TS : SerializerState, new() public TS? TryGetPointer<TS>(int index) where TS : SerializerState, new()
{ {
if (Kind != ObjectKind.Struct && Kind != ObjectKind.ListOfPointers) if (Kind != ObjectKind.Struct && Kind != ObjectKind.ListOfPointers)
throw new InvalidOperationException("This is not a struct or list of pointers"); throw new InvalidOperationException("This is not a struct or list of pointers");
var state = _linkedStates[index]; var state = _linkedStates![index];
if (state == null) return null; if (state == null) return null;
@ -820,7 +833,7 @@ namespace Capnp
/// <item><description>Object at given position is not compatible with the desired target serializer type.</description></item> /// <item><description>Object at given position is not compatible with the desired target serializer type.</description></item>
/// </list></exception> /// </list></exception>
/// <exception cref="IndexOutOfRangeException"><paramref name="index"/> is out of bounds.</exception> /// <exception cref="IndexOutOfRangeException"><paramref name="index"/> is out of bounds.</exception>
public SerializerState TryGetPointer(int index) => TryGetPointer<SerializerState>(index); public SerializerState? TryGetPointer(int index) => TryGetPointer<SerializerState>(index);
/// <summary> /// <summary>
/// Reads text from a struct field or list element. /// Reads text from a struct field or list element.
@ -829,7 +842,7 @@ namespace Capnp
/// If the underlying object is a list of pointers: Element index</param> /// If the underlying object is a list of pointers: Element index</param>
/// <param name="defaultText">String to return in case of null</param> /// <param name="defaultText">String to return in case of null</param>
/// <returns>The decoded text</returns> /// <returns>The decoded text</returns>
public string ReadText(int index, string defaultText = null) public string? ReadText(int index, string? defaultText = null)
{ {
var b = BuildPointer(index); var b = BuildPointer(index);
@ -851,7 +864,7 @@ namespace Capnp
/// <item><description>Object at given position was already set.</description></item> /// <item><description>Object at given position was already set.</description></item>
/// </list></exception> /// </list></exception>
/// <exception cref="IndexOutOfRangeException"><paramref name="index"/> is out of bounds.</exception> /// <exception cref="IndexOutOfRangeException"><paramref name="index"/> is out of bounds.</exception>
public void WriteText(int index, string text) public void WriteText(int index, string? text)
{ {
BuildPointer(index).WriteText(text); BuildPointer(index).WriteText(text);
} }
@ -869,7 +882,7 @@ namespace Capnp
/// <item><description>Object at given position was already set.</description></item> /// <item><description>Object at given position was already set.</description></item>
/// </list></exception> /// </list></exception>
/// <exception cref="IndexOutOfRangeException"><paramref name="index"/> is out of bounds.</exception> /// <exception cref="IndexOutOfRangeException"><paramref name="index"/> is out of bounds.</exception>
public void WriteText(int index, string text, string defaultText) public void WriteText(int index, string? text, string defaultText)
{ {
BuildPointer(index).WriteText(text ?? defaultText); BuildPointer(index).WriteText(text ?? defaultText);
} }
@ -886,11 +899,11 @@ namespace Capnp
if (Kind != ObjectKind.ListOfStructs) if (Kind != ObjectKind.ListOfStructs)
throw new InvalidOperationException("This is not a list of structs"); throw new InvalidOperationException("This is not a list of structs");
ref var state = ref _linkedStates[index]; ref var state = ref _linkedStates![index];
if (state == null) if (state == null)
{ {
state = new SerializerState(MsgBuilder); state = new SerializerState(MsgBuilder!);
state.SetStruct(StructDataCount, StructPtrCount); state.SetStruct(StructDataCount, StructPtrCount);
state.SegmentIndex = SegmentIndex; state.SegmentIndex = SegmentIndex;
state.Offset = Offset + 1 + index * (StructDataCount + StructPtrCount); state.Offset = Offset + 1 + index * (StructDataCount + StructPtrCount);
@ -913,12 +926,12 @@ namespace Capnp
if (Kind != ObjectKind.ListOfStructs) if (Kind != ObjectKind.ListOfStructs)
throw new InvalidOperationException("This is not a list of structs"); throw new InvalidOperationException("This is not a list of structs");
ref var state = ref _linkedStates[index]; ref var state = ref _linkedStates![index];
if (state == null) if (state == null)
{ {
state = new TS(); state = new TS();
state.Bind(MsgBuilder); state.Bind(MsgBuilder!);
state.SetStruct(StructDataCount, StructPtrCount); state.SetStruct(StructDataCount, StructPtrCount);
state.SegmentIndex = SegmentIndex; state.SegmentIndex = SegmentIndex;
int stride = StructDataCount + StructPtrCount; int stride = StructDataCount + StructPtrCount;
@ -942,19 +955,19 @@ namespace Capnp
for (int offset = minOffset; offset < maxOffset; offset++) for (int offset = minOffset; offset < maxOffset; offset++)
{ {
ref var state = ref _linkedStates[offset - minOffset]; ref var state = ref _linkedStates![offset - minOffset];
if (state == null) if (state == null)
{ {
state = new TS(); state = new TS();
state.Bind(MsgBuilder); state.Bind(MsgBuilder!);
state.SetStruct(StructDataCount, StructPtrCount); state.SetStruct(StructDataCount, StructPtrCount);
state.SegmentIndex = SegmentIndex; state.SegmentIndex = SegmentIndex;
state.Offset = offset; state.Offset = offset;
} }
} }
return _linkedStates.LazyListSelect(ts => (TS)ts); return _linkedStates!.LazyListSelect(ts => (TS)ts);
} }
/// <summary> /// <summary>
@ -1240,7 +1253,7 @@ namespace Capnp
/// <param name="capability">The low-level capability object to provide.</param> /// <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</returns>
/// <exception cref="InvalidOperationException">The underlying message builder was not configured for capability table support.</exception> /// <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 (Caps == null) if (Caps == null)
throw new InvalidOperationException("Underlying MessageBuilder was not enabled to support capabilities"); throw new InvalidOperationException("Underlying MessageBuilder was not enabled to support capabilities");
@ -1279,7 +1292,7 @@ namespace Capnp
/// </list></param> /// </list></param>
/// <returns>Index of the given capability in the capability table</returns> /// <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> /// <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)
{ {
if (obj == null) if (obj == null)
return ProvideCapability(default(Rpc.ConsumedCapability)); return ProvideCapability(default(Rpc.ConsumedCapability));
@ -1352,7 +1365,7 @@ namespace Capnp
} }
} }
internal Rpc.ConsumedCapability StructReadRawCap(int index) internal Rpc.ConsumedCapability? StructReadRawCap(int index)
{ {
if (Kind != ObjectKind.Struct && Kind != ObjectKind.Nil) if (Kind != ObjectKind.Struct && Kind != ObjectKind.Nil)
throw new InvalidOperationException("Allowed on structs only"); throw new InvalidOperationException("Allowed on structs only");
@ -1372,7 +1385,7 @@ namespace Capnp
/// <exception cref="ArgumentOutOfRangeException"><paramref name="slot"/> is out of range.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="slot"/> is out of range.</exception>
/// <exception cref="ArgumentException">The desired interface does not qualify as capability interface (<see cref="Rpc.ProxyAttribute"/>)</exception> /// <exception cref="ArgumentException">The desired interface does not qualify as capability interface (<see cref="Rpc.ProxyAttribute"/>)</exception>
/// <exception cref="InvalidOperationException">This state does not represent a struct.</exception> /// <exception cref="InvalidOperationException">This state does not represent a struct.</exception>
public T ReadCap<T>(int slot) where T : class public T? ReadCap<T>(int slot) where T : class
{ {
var cap = StructReadRawCap(slot); var cap = StructReadRawCap(slot);
return Rpc.CapabilityReflection.CreateProxy<T>(cap) as T; return Rpc.CapabilityReflection.CreateProxy<T>(cap) as T;

View File

@ -23,7 +23,7 @@ namespace Capnp
return true; return true;
} }
#else #else
public static bool ReplacementTryAdd<K, V>(this Dictionary<K, V> thisDict, K key, V value) => thisDict.TryAdd(key, value); public static bool ReplacementTryAdd<K, V>(this Dictionary<K, V> thisDict, K key, V value) where K: struct => thisDict.TryAdd(key, value);
#endif #endif
/// <summary> /// <summary>
@ -40,14 +40,16 @@ namespace Capnp
{ {
if (!thisDict.ContainsKey(key)) if (!thisDict.ContainsKey(key))
{ {
value = default; value = default!; // OK here since .NET Standard 2.0 does not support (non-)nullability anyway.
return false; return false;
} }
value = thisDict[key]; value = thisDict[key];
return thisDict.Remove(key); return thisDict.Remove(key);
} }
#else #else
#pragma warning disable CS8714
public static bool ReplacementTryRemove<K, V>(this Dictionary<K, V> thisDict, K key, out V value) => thisDict.Remove(key, out value); public static bool ReplacementTryRemove<K, V>(this Dictionary<K, V> thisDict, K key, out V value) => thisDict.Remove(key, out value);
#pragma warning restore CS8714
#endif #endif
/// <summary> /// <summary>

View File

@ -1,6 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
namespace Capnp namespace Capnp
{ {

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
<RootNamespace>CapnpC.CSharp.Generator.Tests</RootNamespace> <RootNamespace>CapnpC.CSharp.Generator.Tests</RootNamespace>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
@ -10,15 +10,15 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.3.1" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.4.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.3.1" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.4.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="Mono.Cecil" Version="0.11.0" /> <PackageReference Include="Mono.Cecil" Version="0.11.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" /> <PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
<PackageReference Include="MSTest.TestFramework" Version="1.4.0" /> <PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
<PackageReference Include="SpecFlow" Version="3.0.225" /> <PackageReference Include="SpecFlow" Version="3.1.78" />
<PackageReference Include="SpecFlow.MsTest" Version="3.0.225" /> <PackageReference Include="SpecFlow.MsTest" Version="3.1.78" />
<PackageReference Include="SpecFlow.Tools.MsBuild.Generation" Version="3.0.225" /> <PackageReference Include="SpecFlow.Tools.MsBuild.Generation" Version="3.1.78" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -49,11 +49,25 @@ Scenario: Multiple errors
Scenario Outline: Valid generator output Scenario Outline: Valid generator output
Given I have a binary code generator request <bin> Given I have a binary code generator request <bin>
And I enable generation of nullable reference types according to <nullablegen>
And I enable the compiler support of nullable reference types according to <nullablesupp>
When I invoke capnpc-csharp When I invoke capnpc-csharp
Then the invocation must succeed and the generated code must compile Then the invocation must succeed and attempting to compile the generated code gives <outcome>
Examples: Examples:
| bin | | bin | nullablegen | nullablesupp | outcome |
| Issue19.capnp.bin | | test.capnp.bin | false | false | success |
| Issue21.capnp.bin | | test.capnp.bin | true | false | errors |
| Issue22.capnp.bin | | test.capnp.bin | false | true | warnings |
| test.capnp.bin | true | true | success |
| Issue19.capnp.bin | false | false | success |
| Issue21.capnp.bin | false | false | success |
| Issue22.capnp.bin | false | false | success |
| NullableDisable.capnp.bin | true | false | success |
| NullableDisable.capnp.bin | true | true | warnings |
| NullableEnable.capnp.bin | false | true | success |
| NullableEnable.capnp.bin | false | false | errors |
| NullableDisable2.capnp.bin | false | false | errors |
| NullableDisable2.capnp.bin | false | true | success |
| NullableEnable2.capnp.bin | false | false | errors |
| NullableEnable2.capnp.bin | false | true | success |

View File

@ -1,8 +1,8 @@
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// This code was generated by SpecFlow (http://www.specflow.org/). // This code was generated by SpecFlow (http://www.specflow.org/).
// SpecFlow Version:3.0.0.0 // SpecFlow Version:3.1.0.0
// SpecFlow Generator Version:3.0.0.0 // SpecFlow Generator Version:3.1.0.0
// //
// Changes to this file may cause incorrect behavior and will be lost if // Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated. // the code is regenerated.
@ -13,9 +13,11 @@
namespace CapnpC.CSharp.Generator.Tests namespace CapnpC.CSharp.Generator.Tests
{ {
using TechTalk.SpecFlow; using TechTalk.SpecFlow;
using System;
using System.Linq;
[System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.0.0.0")] [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.1.0.0")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute()] [Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute()]
public partial class CodeGeneratorFeature public partial class CodeGeneratorFeature
@ -25,6 +27,8 @@ namespace CapnpC.CSharp.Generator.Tests
private Microsoft.VisualStudio.TestTools.UnitTesting.TestContext _testContext; private Microsoft.VisualStudio.TestTools.UnitTesting.TestContext _testContext;
private string[] _featureTags = ((string[])(null));
#line 1 "CodeGenerator.feature" #line 1 "CodeGenerator.feature"
#line hidden #line hidden
@ -68,7 +72,7 @@ namespace CapnpC.CSharp.Generator.Tests
} }
[Microsoft.VisualStudio.TestTools.UnitTesting.TestCleanupAttribute()] [Microsoft.VisualStudio.TestTools.UnitTesting.TestCleanupAttribute()]
public virtual void ScenarioTearDown() public virtual void TestTearDown()
{ {
testRunner.OnScenarioEnd(); testRunner.OnScenarioEnd();
} }
@ -94,35 +98,78 @@ namespace CapnpC.CSharp.Generator.Tests
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")] [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
public virtual void ComparingBackendOutputWithReference() public virtual void ComparingBackendOutputWithReference()
{ {
string[] tagsOfScenario = ((string[])(null));
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Comparing backend output with reference", null, ((string[])(null))); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Comparing backend output with reference", null, ((string[])(null)));
#line 6 #line 6
this.ScenarioInitialize(scenarioInfo); this.ScenarioInitialize(scenarioInfo);
#line hidden
bool isScenarioIgnored = default(bool);
bool isFeatureIgnored = default(bool);
if ((tagsOfScenario != null))
{
isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
}
if ((this._featureTags != null))
{
isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
}
if ((isScenarioIgnored || isFeatureIgnored))
{
testRunner.SkipScenario();
}
else
{
this.ScenarioStart(); this.ScenarioStart();
#line 7 #line 7
testRunner.Given("I have a binary code generator request \"test.capnp.bin\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); testRunner.Given("I have a binary code generator request \"test.capnp.bin\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line hidden
#line 8 #line 8
testRunner.And("my reference output is \"test.cs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); testRunner.And("my reference output is \"test.cs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
#line 9 #line 9
testRunner.When("I invoke capnpc-csharp", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); testRunner.When("I invoke capnpc-csharp", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line hidden
#line 10 #line 10
testRunner.Then("the generated output must match the reference", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); testRunner.Then("the generated output must match the reference", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden #line hidden
}
this.ScenarioCleanup(); this.ScenarioCleanup();
} }
public virtual void InvalidBinaryCodeGeneratorRequests(string bin, string[] exampleTags) public virtual void InvalidBinaryCodeGeneratorRequests(string bin, string[] exampleTags)
{ {
string[] tagsOfScenario = exampleTags;
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Invalid binary code generator requests", null, exampleTags); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Invalid binary code generator requests", null, exampleTags);
#line 12 #line 12
this.ScenarioInitialize(scenarioInfo); this.ScenarioInitialize(scenarioInfo);
#line hidden
bool isScenarioIgnored = default(bool);
bool isFeatureIgnored = default(bool);
if ((tagsOfScenario != null))
{
isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
}
if ((this._featureTags != null))
{
isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
}
if ((isScenarioIgnored || isFeatureIgnored))
{
testRunner.SkipScenario();
}
else
{
this.ScenarioStart(); this.ScenarioStart();
#line 13 #line 13
testRunner.Given(string.Format("I have a binary code generator request {0}", bin), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); testRunner.Given(string.Format("I have a binary code generator request {0}", bin), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line hidden
#line 14 #line 14
testRunner.When("I invoke capnpc-csharp", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); testRunner.When("I invoke capnpc-csharp", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line hidden
#line 15 #line 15
testRunner.Then("the invocation must fail", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); testRunner.Then("the invocation must fail", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden #line hidden
}
this.ScenarioCleanup(); this.ScenarioCleanup();
} }
@ -155,19 +202,41 @@ this.InvalidBinaryCodeGeneratorRequests("test.cs", ((string[])(null)));
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")] [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
public virtual void CombiningFrontendAndBackend() public virtual void CombiningFrontendAndBackend()
{ {
string[] tagsOfScenario = ((string[])(null));
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Combining frontend and backend", null, ((string[])(null))); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Combining frontend and backend", null, ((string[])(null)));
#line 22 #line 22
this.ScenarioInitialize(scenarioInfo); this.ScenarioInitialize(scenarioInfo);
#line hidden
bool isScenarioIgnored = default(bool);
bool isFeatureIgnored = default(bool);
if ((tagsOfScenario != null))
{
isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
}
if ((this._featureTags != null))
{
isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
}
if ((isScenarioIgnored || isFeatureIgnored))
{
testRunner.SkipScenario();
}
else
{
this.ScenarioStart(); this.ScenarioStart();
#line 23 #line 23
testRunner.Given("capnp.exe is installed on my system", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); testRunner.Given("capnp.exe is installed on my system", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line hidden
#line 24 #line 24
testRunner.And("I have a schema \"UnitTest1.capnp\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); testRunner.And("I have a schema \"UnitTest1.capnp\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
#line 25 #line 25
testRunner.When("I try to generate code from that schema", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); testRunner.When("I try to generate code from that schema", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line hidden
#line 26 #line 26
testRunner.Then("code generation must succeed", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); testRunner.Then("code generation must succeed", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden #line hidden
}
this.ScenarioCleanup(); this.ScenarioCleanup();
} }
@ -176,19 +245,41 @@ this.ScenarioInitialize(scenarioInfo);
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")] [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
public virtual void MissingFrontend() public virtual void MissingFrontend()
{ {
string[] tagsOfScenario = ((string[])(null));
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Missing frontend", null, ((string[])(null))); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Missing frontend", null, ((string[])(null)));
#line 28 #line 28
this.ScenarioInitialize(scenarioInfo); this.ScenarioInitialize(scenarioInfo);
#line hidden
bool isScenarioIgnored = default(bool);
bool isFeatureIgnored = default(bool);
if ((tagsOfScenario != null))
{
isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
}
if ((this._featureTags != null))
{
isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
}
if ((isScenarioIgnored || isFeatureIgnored))
{
testRunner.SkipScenario();
}
else
{
this.ScenarioStart(); this.ScenarioStart();
#line 29 #line 29
testRunner.Given("capnp.exe is not installed on my system", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); testRunner.Given("capnp.exe is not installed on my system", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line hidden
#line 30 #line 30
testRunner.And("I have a schema \"UnitTest1.capnp\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); testRunner.And("I have a schema \"UnitTest1.capnp\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
#line 31 #line 31
testRunner.When("I try to generate code from that schema", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); testRunner.When("I try to generate code from that schema", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line hidden
#line 32 #line 32
testRunner.Then("the invocation must fail", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); testRunner.Then("the invocation must fail", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden #line hidden
}
this.ScenarioCleanup(); this.ScenarioCleanup();
} }
@ -197,23 +288,47 @@ this.ScenarioInitialize(scenarioInfo);
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")] [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
public virtual void SchemaWithoutID() public virtual void SchemaWithoutID()
{ {
string[] tagsOfScenario = ((string[])(null));
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Schema without ID", null, ((string[])(null))); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Schema without ID", null, ((string[])(null)));
#line 34 #line 34
this.ScenarioInitialize(scenarioInfo); this.ScenarioInitialize(scenarioInfo);
#line hidden
bool isScenarioIgnored = default(bool);
bool isFeatureIgnored = default(bool);
if ((tagsOfScenario != null))
{
isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
}
if ((this._featureTags != null))
{
isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
}
if ((isScenarioIgnored || isFeatureIgnored))
{
testRunner.SkipScenario();
}
else
{
this.ScenarioStart(); this.ScenarioStart();
#line 35 #line 35
testRunner.Given("capnp.exe is installed on my system", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); testRunner.Given("capnp.exe is installed on my system", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line hidden
#line 36 #line 36
testRunner.And("I have a schema \"Empty1.capnp\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); testRunner.And("I have a schema \"Empty1.capnp\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
#line 37 #line 37
testRunner.When("I try to generate code from that schema", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); testRunner.When("I try to generate code from that schema", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line hidden
#line 38 #line 38
testRunner.Then("the invocation must fail", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); testRunner.Then("the invocation must fail", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
#line 39 #line 39
testRunner.And("the reason must be bad input", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); testRunner.And("the reason must be bad input", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
#line 40 #line 40
testRunner.And("the error output must contain \"File does not declare an ID\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); testRunner.And("the error output must contain \"File does not declare an ID\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden #line hidden
}
this.ScenarioCleanup(); this.ScenarioCleanup();
} }
@ -222,75 +337,316 @@ this.ScenarioInitialize(scenarioInfo);
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")] [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
public virtual void MultipleErrors() public virtual void MultipleErrors()
{ {
string[] tagsOfScenario = ((string[])(null));
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Multiple errors", null, ((string[])(null))); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Multiple errors", null, ((string[])(null)));
#line 42 #line 42
this.ScenarioInitialize(scenarioInfo); this.ScenarioInitialize(scenarioInfo);
#line hidden
bool isScenarioIgnored = default(bool);
bool isFeatureIgnored = default(bool);
if ((tagsOfScenario != null))
{
isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
}
if ((this._featureTags != null))
{
isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
}
if ((isScenarioIgnored || isFeatureIgnored))
{
testRunner.SkipScenario();
}
else
{
this.ScenarioStart(); this.ScenarioStart();
#line 43 #line 43
testRunner.Given("capnp.exe is installed on my system", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); testRunner.Given("capnp.exe is installed on my system", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line hidden
#line 44 #line 44
testRunner.And("I have a schema \"invalid.capnp\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); testRunner.And("I have a schema \"invalid.capnp\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
#line 45 #line 45
testRunner.When("I try to generate code from that schema", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); testRunner.When("I try to generate code from that schema", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line hidden
#line 46 #line 46
testRunner.Then("the invocation must fail", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); testRunner.Then("the invocation must fail", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
#line 47 #line 47
testRunner.And("the reason must be bad input", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); testRunner.And("the reason must be bad input", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
#line 48 #line 48
testRunner.And("the error output must contain multiple messages", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); testRunner.And("the error output must contain multiple messages", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden #line hidden
}
this.ScenarioCleanup(); this.ScenarioCleanup();
} }
public virtual void ValidGeneratorOutput(string bin, string[] exampleTags) public virtual void ValidGeneratorOutput(string bin, string nullablegen, string nullablesupp, string outcome, string[] exampleTags)
{ {
string[] tagsOfScenario = exampleTags;
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Valid generator output", null, exampleTags); TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Valid generator output", null, exampleTags);
#line 50 #line 50
this.ScenarioInitialize(scenarioInfo); this.ScenarioInitialize(scenarioInfo);
#line hidden
bool isScenarioIgnored = default(bool);
bool isFeatureIgnored = default(bool);
if ((tagsOfScenario != null))
{
isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
}
if ((this._featureTags != null))
{
isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
}
if ((isScenarioIgnored || isFeatureIgnored))
{
testRunner.SkipScenario();
}
else
{
this.ScenarioStart(); this.ScenarioStart();
#line 51 #line 51
testRunner.Given(string.Format("I have a binary code generator request {0}", bin), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); testRunner.Given(string.Format("I have a binary code generator request {0}", bin), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 52
testRunner.When("I invoke capnpc-csharp", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 53
testRunner.Then("the invocation must succeed and the generated code must compile", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden #line hidden
#line 52
testRunner.And(string.Format("I enable generation of nullable reference types according to {0}", nullablegen), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
#line 53
testRunner.And(string.Format("I enable the compiler support of nullable reference types according to {0}", nullablesupp), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
#line 54
testRunner.When("I invoke capnpc-csharp", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line hidden
#line 55
testRunner.Then(string.Format("the invocation must succeed and attempting to compile the generated code gives {0" +
"}", outcome), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
}
this.ScenarioCleanup(); this.ScenarioCleanup();
} }
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Issue19.capnp.bin")] [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Variant 0")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")] [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Issue19.capnp.bin")] [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Variant 0")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "test.capnp.bin")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablegen", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablesupp", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:outcome", "success")]
public virtual void ValidGeneratorOutput_Variant0()
{
#line 50
this.ValidGeneratorOutput("test.capnp.bin", "false", "false", "success", ((string[])(null)));
#line hidden
}
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Variant 1")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Variant 1")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "test.capnp.bin")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablegen", "true")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablesupp", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:outcome", "errors")]
public virtual void ValidGeneratorOutput_Variant1()
{
#line 50
this.ValidGeneratorOutput("test.capnp.bin", "true", "false", "errors", ((string[])(null)));
#line hidden
}
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Variant 2")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Variant 2")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "test.capnp.bin")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablegen", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablesupp", "true")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:outcome", "warnings")]
public virtual void ValidGeneratorOutput_Variant2()
{
#line 50
this.ValidGeneratorOutput("test.capnp.bin", "false", "true", "warnings", ((string[])(null)));
#line hidden
}
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Variant 3")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Variant 3")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "test.capnp.bin")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablegen", "true")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablesupp", "true")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:outcome", "success")]
public virtual void ValidGeneratorOutput_Variant3()
{
#line 50
this.ValidGeneratorOutput("test.capnp.bin", "true", "true", "success", ((string[])(null)));
#line hidden
}
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Variant 4")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Variant 4")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "Issue19.capnp.bin")] [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "Issue19.capnp.bin")]
public virtual void ValidGeneratorOutput_Issue19_Capnp_Bin() [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablegen", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablesupp", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:outcome", "success")]
public virtual void ValidGeneratorOutput_Variant4()
{ {
#line 50 #line 50
this.ValidGeneratorOutput("Issue19.capnp.bin", ((string[])(null))); this.ValidGeneratorOutput("Issue19.capnp.bin", "false", "false", "success", ((string[])(null)));
#line hidden #line hidden
} }
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Issue21.capnp.bin")] [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Variant 5")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")] [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Issue21.capnp.bin")] [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Variant 5")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "Issue21.capnp.bin")] [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "Issue21.capnp.bin")]
public virtual void ValidGeneratorOutput_Issue21_Capnp_Bin() [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablegen", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablesupp", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:outcome", "success")]
public virtual void ValidGeneratorOutput_Variant5()
{ {
#line 50 #line 50
this.ValidGeneratorOutput("Issue21.capnp.bin", ((string[])(null))); this.ValidGeneratorOutput("Issue21.capnp.bin", "false", "false", "success", ((string[])(null)));
#line hidden #line hidden
} }
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Issue22.capnp.bin")] [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Variant 6")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")] [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Issue22.capnp.bin")] [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Variant 6")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "Issue22.capnp.bin")] [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "Issue22.capnp.bin")]
public virtual void ValidGeneratorOutput_Issue22_Capnp_Bin() [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablegen", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablesupp", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:outcome", "success")]
public virtual void ValidGeneratorOutput_Variant6()
{ {
#line 50 #line 50
this.ValidGeneratorOutput("Issue22.capnp.bin", ((string[])(null))); this.ValidGeneratorOutput("Issue22.capnp.bin", "false", "false", "success", ((string[])(null)));
#line hidden
}
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Variant 7")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Variant 7")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "NullableDisable.capnp.bin")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablegen", "true")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablesupp", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:outcome", "success")]
public virtual void ValidGeneratorOutput_Variant7()
{
#line 50
this.ValidGeneratorOutput("NullableDisable.capnp.bin", "true", "false", "success", ((string[])(null)));
#line hidden
}
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Variant 8")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Variant 8")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "NullableDisable.capnp.bin")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablegen", "true")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablesupp", "true")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:outcome", "warnings")]
public virtual void ValidGeneratorOutput_Variant8()
{
#line 50
this.ValidGeneratorOutput("NullableDisable.capnp.bin", "true", "true", "warnings", ((string[])(null)));
#line hidden
}
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Variant 9")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Variant 9")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "NullableEnable.capnp.bin")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablegen", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablesupp", "true")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:outcome", "success")]
public virtual void ValidGeneratorOutput_Variant9()
{
#line 50
this.ValidGeneratorOutput("NullableEnable.capnp.bin", "false", "true", "success", ((string[])(null)));
#line hidden
}
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Variant 10")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Variant 10")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "NullableEnable.capnp.bin")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablegen", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablesupp", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:outcome", "errors")]
public virtual void ValidGeneratorOutput_Variant10()
{
#line 50
this.ValidGeneratorOutput("NullableEnable.capnp.bin", "false", "false", "errors", ((string[])(null)));
#line hidden
}
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Variant 11")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Variant 11")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "NullableDisable2.capnp.bin")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablegen", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablesupp", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:outcome", "errors")]
public virtual void ValidGeneratorOutput_Variant11()
{
#line 50
this.ValidGeneratorOutput("NullableDisable2.capnp.bin", "false", "false", "errors", ((string[])(null)));
#line hidden
}
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Variant 12")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Variant 12")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "NullableDisable2.capnp.bin")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablegen", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablesupp", "true")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:outcome", "success")]
public virtual void ValidGeneratorOutput_Variant12()
{
#line 50
this.ValidGeneratorOutput("NullableDisable2.capnp.bin", "false", "true", "success", ((string[])(null)));
#line hidden
}
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Variant 13")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Variant 13")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "NullableEnable2.capnp.bin")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablegen", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablesupp", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:outcome", "errors")]
public virtual void ValidGeneratorOutput_Variant13()
{
#line 50
this.ValidGeneratorOutput("NullableEnable2.capnp.bin", "false", "false", "errors", ((string[])(null)));
#line hidden
}
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Variant 14")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Variant 14")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "NullableEnable2.capnp.bin")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablegen", "false")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:nullablesupp", "true")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:outcome", "success")]
public virtual void ValidGeneratorOutput_Variant14()
{
#line 50
this.ValidGeneratorOutput("NullableEnable2.capnp.bin", "false", "true", "success", ((string[])(null)));
#line hidden #line hidden
} }
} }

View File

@ -22,7 +22,7 @@ namespace CapnpC.CSharp.Generator.Tests
[TestMethod] [TestMethod]
public void Test01NestedClash() public void Test01NestedClash()
{ {
var (model, codegen) = LoadAndGenerate("UnitTest1.capnp.bin"); var (model, codegen, _) = LoadAndGenerate("UnitTest1.capnp.bin");
var structFoo = GetTypeDef(0x93db6ba5509bac24, model); var structFoo = GetTypeDef(0x93db6ba5509bac24, model);
var names = codegen.GetNames(); var names = codegen.GetNames();
var fieldName = names.GetCodeIdentifier(structFoo.Fields[0]).ToString(); var fieldName = names.GetCodeIdentifier(structFoo.Fields[0]).ToString();
@ -51,7 +51,7 @@ namespace CapnpC.CSharp.Generator.Tests
[TestMethod] [TestMethod]
public void Test10ImportedNamespaces() public void Test10ImportedNamespaces()
{ {
var (model, codegen) = LoadAndGenerate("UnitTest10.capnp.bin"); var (model, codegen, _) = LoadAndGenerate("UnitTest10.capnp.bin");
var outerTypeDef = GetGeneratedFile("UnitTest10.capnp", model).NestedTypes.First(); var outerTypeDef = GetGeneratedFile("UnitTest10.capnp", model).NestedTypes.First();
var outerType = Model.Types.FromDefinition(outerTypeDef); var outerType = Model.Types.FromDefinition(outerTypeDef);
var innerType = outerTypeDef.Fields[0].Type; var innerType = outerTypeDef.Fields[0].Type;
@ -81,6 +81,60 @@ namespace CapnpC.CSharp.Generator.Tests
LoadAndGenerate("UnitTest12.capnp.bin"); LoadAndGenerate("UnitTest12.capnp.bin");
} }
[TestMethod]
public void Test13CSharpNamespace()
{
var (model, _, _) = LoadAndGenerate("UnitTest13.capnp.bin");
var outerTypeDef = GetGeneratedFile("UnitTest13.capnp", model).NestedTypes.First();
string[] outerNamespace = { "Foo", "Bar", "Baz" };
CollectionAssert.AreEqual(outerNamespace, (outerTypeDef.DeclaringElement as Model.GenFile).Namespace);
}
[TestMethod]
public void Test14CSharpNamespacePrecedesCxxNamespace()
{
var (model, _, _) = LoadAndGenerate("UnitTest14.capnp.bin");
var outerTypeDef = GetGeneratedFile("UnitTest14.capnp", model).NestedTypes.First();
string[] outerNamespace = { "Foo", "Bar", "Baz" };
CollectionAssert.AreEqual(outerNamespace, (outerTypeDef.DeclaringElement as Model.GenFile).Namespace);
}
[TestMethod]
public void Test15CSharpMemberNames()
{
var (_, _, code) = LoadAndGenerate("UnitTest15.capnp.bin");
try
{
Assert.IsTrue(code.Contains("CsStruct", StringComparison.Ordinal), "Generated code must contain C# struct name");
Assert.IsFalse(code.Contains("SomeStruct", StringComparison.Ordinal), "Generated code must not contain original struct name");
Assert.IsTrue(code.Contains("CsField", StringComparison.Ordinal), "Generated code must contain C# field name");
Assert.IsFalse(code.Contains("someField", StringComparison.OrdinalIgnoreCase), "Generated code must not contain original field name");
Assert.IsTrue(code.Contains("CsUnion", StringComparison.Ordinal), "Generated code must contain C# union name");
Assert.IsFalse(code.Contains("someUnion", StringComparison.OrdinalIgnoreCase), "Generated code must not contain original union name");
Assert.IsTrue(code.Contains("CsGroup", StringComparison.Ordinal), "Generated code must contain C# group name");
Assert.IsFalse(code.Contains("someGroup", StringComparison.OrdinalIgnoreCase), "Generated code must not contain original group name");
Assert.IsTrue(code.Contains("CsEnum", StringComparison.Ordinal), "Generated code must contain C# enum name");
Assert.IsFalse(code.Contains("SomeEnum", StringComparison.Ordinal), "Generated code must not contain original enum name");
Assert.IsTrue(code.Contains("CsEnumerant", StringComparison.Ordinal), "Generated code must contain C# enumerant name");
Assert.IsFalse(code.Contains("someEnumerant", StringComparison.OrdinalIgnoreCase), "Generated code must not contain original enumerant name");
Assert.IsTrue(code.Contains("CsField", StringComparison.Ordinal), "Generated code must contain C# field name");
Assert.IsFalse(code.Contains("someField", StringComparison.OrdinalIgnoreCase), "Generated code must not contain original field name");
Assert.IsTrue(code.Contains("CsInterface", StringComparison.Ordinal), "Generated code must contain C# interface name");
Assert.IsFalse(code.Contains("SomeInterface", StringComparison.Ordinal), "Generated code must not contain original interface name");
Assert.IsTrue(code.Contains("CsMethod", StringComparison.Ordinal), "Generated code must contain C# method name");
Assert.IsFalse(code.Contains("someMethod", StringComparison.OrdinalIgnoreCase), "Generated code must not contain original method name");
Assert.IsTrue(code.Contains("CsField", StringComparison.Ordinal), "Generated code must contain C# field name");
Assert.IsFalse(code.Contains("someField", StringComparison.OrdinalIgnoreCase), "Generated code must not contain original field name");
Assert.IsTrue(code.Contains("CsResult", StringComparison.Ordinal), "Generated code must contain C# method parameter name");
Assert.IsFalse(code.Contains("someResult", StringComparison.OrdinalIgnoreCase), "Generated code must not contain original method parameter name");
}
catch (AssertFailedException)
{
Console.WriteLine(code);
throw;
}
}
[TestMethod] [TestMethod]
public void Test20AnnotationAndConst() public void Test20AnnotationAndConst()
{ {
@ -93,15 +147,20 @@ namespace CapnpC.CSharp.Generator.Tests
LoadAndGenerate("schema-with-offsets.capnp.bin"); LoadAndGenerate("schema-with-offsets.capnp.bin");
} }
static (Model.SchemaModel, CodeGen.CodeGenerator) LoadAndGenerate(string inputName) static (Model.SchemaModel, CodeGen.CodeGenerator, string) LoadAndGenerate(string inputName)
{ {
var model = Load(inputName); var model = Load(inputName);
var codegen = new CodeGen.CodeGenerator(model, new CodeGen.GeneratorOptions()); var codegen = new CodeGen.CodeGenerator(model, new CodeGen.GeneratorOptions());
var code = model.FilesToGenerate.Select(f => codegen.Transform(f)).ToArray(); var code = model.FilesToGenerate.Select(f => codegen.Transform(f)).ToArray();
Assert.IsTrue(Util.InlineAssemblyCompiler.TryCompileCapnp(code), "Compilation was not successful"); Assert.AreEqual(
Util.InlineAssemblyCompiler.CompileSummary.Success,
Util.InlineAssemblyCompiler.TryCompileCapnp(
Microsoft.CodeAnalysis.NullableContextOptions.Disable,
code),
"Compilation was not successful with no warnings");
return (model, codegen); return (model, codegen, code[0]);
} }
static Model.GenFile GetGeneratedFile(string name, Model.SchemaModel model) static Model.GenFile GetGeneratedFile(string name, Model.SchemaModel model)

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,5 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@ -15,6 +16,8 @@ namespace CapnpC.CSharp.Generator.Tests
string _inputSchemaFileName; string _inputSchemaFileName;
string _inputSchema; string _inputSchema;
string _referenceOutputContent; string _referenceOutputContent;
bool _nullableGenEnable;
bool _nullableSupportEnable;
GenerationResult _result; GenerationResult _result;
@ -56,6 +59,7 @@ namespace CapnpC.CSharp.Generator.Tests
public void GivenIHaveABinaryCodeGeneratorRequest(string binaryRequestFileName) public void GivenIHaveABinaryCodeGeneratorRequest(string binaryRequestFileName)
{ {
_inputStream = LoadResource(binaryRequestFileName); _inputStream = LoadResource(binaryRequestFileName);
_nullableGenEnable = false; // Assume false by default, may be enabled later
} }
[Given(@"my reference output is ""(.*)""")] [Given(@"my reference output is ""(.*)""")]
@ -71,9 +75,14 @@ namespace CapnpC.CSharp.Generator.Tests
[When(@"I invoke capnpc-csharp")] [When(@"I invoke capnpc-csharp")]
public void WhenIInvokeCapnpc_Csharp() public void WhenIInvokeCapnpc_Csharp()
{ {
Console.WriteLine($"Generate nullable reference types? {_nullableGenEnable}");
using (_inputStream) using (_inputStream)
{ {
_result = CapnpCompilation.GenerateFromStream(_inputStream); _result = CapnpCompilation.GenerateFromStream(_inputStream, new CodeGen.GeneratorOptions()
{
NullableEnableDefault = _nullableGenEnable
});
} }
} }
@ -113,13 +122,6 @@ namespace CapnpC.CSharp.Generator.Tests
Assert.IsNotNull(_result.Exception, "Expected an exception"); Assert.IsNotNull(_result.Exception, "Expected an exception");
} }
[Then(@"the invocation must succeed and the generated code must compile")]
public void ThenTheInvocationMustSucceedAndTheGeneratedCodeMustCompile()
{
Assert.IsTrue(_result.IsSuccess, "Tool invocation was not successful");
Assert.IsTrue(Util.InlineAssemblyCompiler.TryCompileCapnp(_result.GeneratedFiles[0].GeneratedContent), "Compilation was not successful");
}
[Given(@"capnp\.exe is installed on my system")] [Given(@"capnp\.exe is installed on my system")]
public void GivenCapnp_ExeIsInstalledOnMySystem() public void GivenCapnp_ExeIsInstalledOnMySystem()
{ {
@ -185,5 +187,69 @@ namespace CapnpC.CSharp.Generator.Tests
{ {
Assert.IsTrue(_result.Messages.Count >= 2); Assert.IsTrue(_result.Messages.Count >= 2);
} }
[Given(@"I enable generation of nullable reference types according to (.*)")]
public void GivenIEnableGenerationOfNullableReferenceTypesAccordingTo(bool enable)
{
_nullableGenEnable = enable;
}
[Given(@"I enable the compiler support of nullable reference types according to (.*)")]
public void GivenIEnableTheCompilerSupportOfNullableReferenceTypesAccordingTo(bool enable)
{
_nullableSupportEnable = enable;
}
[Then(@"the invocation must succeed and attempting to compile the generated code gives (.*)")]
public void ThenTheInvocationMustSucceedAndAttemptingToCompileTheGeneratedCodeGives(string result)
{
Console.WriteLine($"Compiler supports nullable reference types? {_nullableSupportEnable}");
Assert.IsTrue(_result.IsSuccess, "Tool invocation was not successful");
var summary = Util.InlineAssemblyCompiler.TryCompileCapnp(
_nullableSupportEnable ? NullableContextOptions.Enable : NullableContextOptions.Disable,
_result.GeneratedFiles[0].GeneratedContent);
try
{
switch (result)
{
case "success":
Assert.AreEqual(
Util.InlineAssemblyCompiler.CompileSummary.Success,
summary,
"Compilation was expected to succeed");
break;
case "warnings":
Assert.AreEqual(
Util.InlineAssemblyCompiler.CompileSummary.SuccessWithWarnings,
summary,
"Compilation was expected to produce warnings");
break;
case "errors":
Assert.AreEqual(
Util.InlineAssemblyCompiler.CompileSummary.Error,
summary,
"Compilation was expected to fail");
break;
default:
Assert.Fail("Test case bug: unknown outcome specified");
break;
}
}
catch (AssertFailedException)
{
string generated = _result.GeneratedFiles.Single().GeneratedContent;
string path = Path.ChangeExtension(Path.GetTempFileName(), ".capnp.cs");
File.WriteAllText(path, generated);
Console.WriteLine($"Generated code was saved to {path}");
throw;
}
}
} }
} }

View File

@ -0,0 +1,9 @@
@0xbbfd48ae4b99d016;
using CSharp = import "/csharp.capnp";
$CSharp.nullableEnable(false);
struct SomeStruct {
strings @0 : List(Text);
}

View File

@ -0,0 +1,10 @@
@0xbbfd48ae4b99d016;
using CSharp = import "/csharp.capnp";
$CSharp.nullableEnable(false);
$CSharp.emitNullableDirective(true);
struct SomeStruct {
strings @0 : List(Text);
}

View File

@ -0,0 +1,9 @@
@0xbbfd48ae4b99d016;
using CSharp = import "/csharp.capnp";
$CSharp.nullableEnable(true);
struct SomeStruct {
strings @0 : List(Text);
}

View File

@ -0,0 +1,10 @@
@0xbbfd48ae4b99d016;
using CSharp = import "/csharp.capnp";
$CSharp.nullableEnable(true);
$CSharp.emitNullableDirective(true);
struct SomeStruct {
strings @0 : List(Text);
}

View File

@ -0,0 +1,8 @@
@0xbbfd48ae4b99d013;
using CSharp = import "/csharp.capnp";
$CSharp.namespace("Foo.Bar.Baz");
struct SomeStruct {
}

View File

@ -0,0 +1,10 @@
@0xbbfd48ae4b99d014;
using Cxx = import "/capnp/c++.capnp";
using CSharp = import "/csharp.capnp";
$CSharp.namespace("Foo.Bar.Baz");
$Cxx.namespace("X::Y::Z");
struct SomeStruct {
}

View File

@ -0,0 +1,25 @@
@0xbbfd48ae4b99d013;
using CSharp = import "/csharp.capnp";
struct SomeStruct $CSharp.name("CsStruct") {
someField @0 : Int32 $CSharp.name("CsField");
someUnion : union $CSharp.name("CsUnion") {
u0 @1 : Int32;
u1 @2 : Int32;
}
someGroup : group $CSharp.name("CsGroup") {
g0 @3 : Int32;
g1 @4 : Int32;
}
}
enum SomeEnum $CSharp.name("CsEnum") {
someEnumerant @0 $CSharp.name("CsEnumerant");
}
interface SomeInterface $CSharp.name("CsInterface") {
someMethod @0 () -> (someResult :Bool $CSharp.name("CsResult") ) $CSharp.name("CsMethod");
}

View File

@ -1,25 +1,27 @@
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text;
namespace CapnpC.CSharp.Generator.Tests.Util namespace CapnpC.CSharp.Generator.Tests.Util
{ {
class InlineAssemblyCompiler class InlineAssemblyCompiler
{ {
public static bool TryCompileCapnp(string code) public enum CompileSummary
{ {
return TryCompileCapnp(new[] {code}); Success,
SuccessWithWarnings,
Error
} }
public static bool TryCompileCapnp(string[] code) public static CompileSummary TryCompileCapnp(NullableContextOptions nullableContextOptions, params string[] code)
{ {
var options = new CSharpCompilationOptions( var options = new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary, OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Debug); optimizationLevel: OptimizationLevel.Debug,
nullableContextOptions: nullableContextOptions);
string assemblyRoot = Path.GetDirectoryName(typeof(object).Assembly.Location); string assemblyRoot = Path.GetDirectoryName(typeof(object).Assembly.Location);
@ -29,9 +31,13 @@ namespace CapnpC.CSharp.Generator.Tests.Util
"Capnp.Net.Runtime", "Capnp.Net.Runtime",
"bin", "bin",
"Debug", "Debug",
"netcoreapp2.1", "netcoreapp3.0",
"Capnp.Net.Runtime.dll")); "Capnp.Net.Runtime.dll"));
var parseOptions = CSharpParseOptions.Default;
if (nullableContextOptions == NullableContextOptions.Disable)
parseOptions = parseOptions.WithLanguageVersion(LanguageVersion.CSharp7_1);
var compilation = CSharpCompilation.Create( var compilation = CSharpCompilation.Create(
"CompilationTestAssembly", "CompilationTestAssembly",
options: options, options: options,
@ -42,7 +48,7 @@ namespace CapnpC.CSharp.Generator.Tests.Util
MetadataReference.CreateFromFile(Path.Combine(assemblyRoot, "System.Runtime.dll")), MetadataReference.CreateFromFile(Path.Combine(assemblyRoot, "System.Runtime.dll")),
MetadataReference.CreateFromFile(Path.Combine(assemblyRoot, "System.Private.CoreLib.dll")), MetadataReference.CreateFromFile(Path.Combine(assemblyRoot, "System.Private.CoreLib.dll")),
MetadataReference.CreateFromFile(capnpRuntimePath) }, MetadataReference.CreateFromFile(capnpRuntimePath) },
syntaxTrees: Array.ConvertAll(code, new Converter<string, SyntaxTree>(c => CSharpSyntaxTree.ParseText(c)))); syntaxTrees: Array.ConvertAll(code, new Converter<string, SyntaxTree>(c => CSharpSyntaxTree.ParseText(c, parseOptions))));
using (var stream = new MemoryStream()) using (var stream = new MemoryStream())
{ {
@ -61,7 +67,17 @@ namespace CapnpC.CSharp.Generator.Tests.Util
} }
} }
return emitResult.Success; if (emitResult.Success)
{
if (emitResult.Diagnostics.Any(diag => diag.Severity == DiagnosticSeverity.Warning))
return CompileSummary.SuccessWithWarnings;
else
return CompileSummary.Success;
}
else
{
return CompileSummary.Error;
}
} }
} }
} }

View File

@ -27,10 +27,11 @@ namespace CapnpC.CSharp.Generator
/// <summary> /// <summary>
/// Generates C# code from given input stream /// Generates C# code from given input stream
/// </summary> /// </summary>
/// <param name="input">input stream containing the binary code generation request, which the frontend capnpc emits</param> /// <param name="input">Input stream containing the binary code generation request, which the frontend capnpc emits</param>
/// <param name="options">Configuration options for code generator. If null, default options will be used.</param>
/// <returns>generation result</returns> /// <returns>generation result</returns>
/// <exception cref="ArgumentNullException">if <paramref name="input"/> is null</exception> /// <exception cref="ArgumentNullException">if <paramref name="input"/> is null</exception>
public static GenerationResult GenerateFromStream(Stream input) public static GenerationResult GenerateFromStream(Stream input, CodeGen.GeneratorOptions options)
{ {
if (input == null) if (input == null)
throw new ArgumentNullException(nameof(input)); throw new ArgumentNullException(nameof(input));
@ -41,7 +42,7 @@ namespace CapnpC.CSharp.Generator
var dec = DeserializerState.CreateRoot(segments); var dec = DeserializerState.CreateRoot(segments);
var reader = Schema.CodeGeneratorRequest.Reader.Create(dec); var reader = Schema.CodeGeneratorRequest.Reader.Create(dec);
var model = Model.SchemaModel.Create(reader); var model = Model.SchemaModel.Create(reader);
var codeGen = new CodeGen.CodeGenerator(model, new CodeGen.GeneratorOptions()); var codeGen = new CodeGen.CodeGenerator(model, options ?? new CodeGen.GeneratorOptions());
return new GenerationResult(codeGen.Generate()); return new GenerationResult(codeGen.Generate());
} }
catch (Exception exception) catch (Exception exception)
@ -50,6 +51,14 @@ namespace CapnpC.CSharp.Generator
} }
} }
/// <summary>
/// Generates C# code from given input stream
/// </summary>
/// <param name="input">input stream containing the binary code generation request, which the frontend capnpc emits</param>
/// <returns>generation result</returns>
/// <exception cref="ArgumentNullException">if <paramref name="input"/> is null</exception>
public static GenerationResult GenerateFromStream(Stream input) => GenerateFromStream(input, null);
/// <summary> /// <summary>
/// Invokes "capnp.exe -o-" with given additional arguments and redirects the output to the C# generator backend. /// Invokes "capnp.exe -o-" with given additional arguments and redirects the output to the C# generator backend.
/// </summary> /// </summary>

View File

@ -14,6 +14,7 @@
internal class CodeGenerator internal class CodeGenerator
{ {
readonly SchemaModel _model; readonly SchemaModel _model;
readonly GeneratorOptions _options;
readonly GenNames _names; readonly GenNames _names;
readonly CommonSnippetGen _commonGen; readonly CommonSnippetGen _commonGen;
readonly DomainClassSnippetGen _domClassGen; readonly DomainClassSnippetGen _domClassGen;
@ -24,6 +25,7 @@
public CodeGenerator(SchemaModel model, GeneratorOptions options) public CodeGenerator(SchemaModel model, GeneratorOptions options)
{ {
_model = model; _model = model;
_options = options;
_names = new GenNames(options); _names = new GenNames(options);
_commonGen = new CommonSnippetGen(_names); _commonGen = new CommonSnippetGen(_names);
_domClassGen = new DomainClassSnippetGen(_names); _domClassGen = new DomainClassSnippetGen(_names);
@ -61,7 +63,7 @@
{ {
var topDecl = ClassDeclaration(_names.MakeTypeName(def).Identifier) var topDecl = ClassDeclaration(_names.MakeTypeName(def).Identifier)
.AddModifiers(Public) .AddModifiers(Public)
.AddBaseListTypes(SimpleBaseType(Type<Capnp.ICapnpSerializable>())); .AddBaseListTypes(SimpleBaseType(_names.Type<Capnp.ICapnpSerializable>(Nullability.NonNullable)));
if (def.GenericParameters.Count > 0) if (def.GenericParameters.Count > 0)
{ {
@ -184,10 +186,26 @@
internal string Transform(GenFile file) internal string Transform(GenFile file)
{ {
_names.NullableEnable = file.NullableEnable ?? _options.NullableEnableDefault;
NameSyntax topNamespace = GenNames.NamespaceName(file.Namespace) ?? _names.TopNamespace; NameSyntax topNamespace = GenNames.NamespaceName(file.Namespace) ?? _names.TopNamespace;
var ns = NamespaceDeclaration(topNamespace); var ns = NamespaceDeclaration(topNamespace);
if (file.EmitNullableDirective)
{
ns = ns.WithLeadingTrivia(
Trivia(
NullableDirectiveTrivia(
Token(_names.NullableEnable ? SyntaxKind.EnableKeyword : SyntaxKind.DisableKeyword),
true)))
.WithTrailingTrivia(
Trivia(
NullableDirectiveTrivia(
Token(SyntaxKind.RestoreKeyword),
true)));
}
foreach (var def in file.NestedTypes) foreach (var def in file.NestedTypes)
{ {
ns = ns.AddMembers(Transform(def).ToArray()); ns = ns.AddMembers(Transform(def).ToArray());
@ -204,7 +222,15 @@
UsingDirective(ParseName("Capnp")), UsingDirective(ParseName("Capnp")),
UsingDirective(ParseName("Capnp.Rpc")), UsingDirective(ParseName("Capnp.Rpc")),
UsingDirective(ParseName("System")), UsingDirective(ParseName("System")),
UsingDirective(ParseName("System.Collections.Generic")), UsingDirective(ParseName("System.Collections.Generic")));
if (_names.NullableEnable)
{
cu = cu.AddUsings(
UsingDirective(ParseName("System.Diagnostics.CodeAnalysis")));
}
cu = cu.AddUsings(
UsingDirective(ParseName("System.Threading")), UsingDirective(ParseName("System.Threading")),
UsingDirective(ParseName("System.Threading.Tasks"))); UsingDirective(ParseName("System.Threading.Tasks")));

View File

@ -22,7 +22,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
{ {
var whichEnum = EnumDeclaration(_names.UnionDiscriminatorEnum.ToString()) var whichEnum = EnumDeclaration(_names.UnionDiscriminatorEnum.ToString())
.AddModifiers(Public) .AddModifiers(Public)
.AddBaseListTypes(SimpleBaseType(Type<ushort>())); .AddBaseListTypes(SimpleBaseType(_names.Type<ushort>(Nullability.NonNullable)));
var discFields = def.Fields.Where(f => f.DiscValue.HasValue); var discFields = def.Fields.Where(f => f.DiscValue.HasValue);
@ -49,14 +49,14 @@ namespace CapnpC.CSharp.Generator.CodeGen
public EnumDeclarationSyntax MakeEnum(TypeDefinition def) public EnumDeclarationSyntax MakeEnum(TypeDefinition def)
{ {
var decl = EnumDeclaration(def.Name) var decl = EnumDeclaration(_names.GetCodeIdentifier(def))
.WithAttributeLists(MakeTypeIdAttributeLists(def.Id)) .WithAttributeLists(MakeTypeIdAttributeLists(def.Id))
.AddModifiers(Public) .AddModifiers(Public)
.AddBaseListTypes(SimpleBaseType(Type<ushort>())); .AddBaseListTypes(SimpleBaseType(_names.Type<ushort>(Nullability.NonNullable)));
foreach (var enumerant in def.Enumerants.OrderBy(e => e.CodeOrder)) foreach (var enumerant in def.Enumerants.OrderBy(e => e.CodeOrder))
{ {
var mdecl = EnumMemberDeclaration(enumerant.Literal); var mdecl = EnumMemberDeclaration(enumerant.CsLiteral ?? enumerant.Literal);
if (enumerant.Ordinal.HasValue) if (enumerant.Ordinal.HasValue)
{ {

View File

@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static CapnpC.CSharp.Generator.CodeGen.SyntaxHelpers; using static CapnpC.CSharp.Generator.CodeGen.SyntaxHelpers;
using Microsoft.CodeAnalysis;
namespace CapnpC.CSharp.Generator.CodeGen namespace CapnpC.CSharp.Generator.CodeGen
{ {
@ -20,7 +21,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
MemberDeclarationSyntax MakeUnionField(Field field) MemberDeclarationSyntax MakeUnionField(Field field)
{ {
var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.DomainClassNullable); var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.DomainClass, Nullability.NullableRefAndValue);
switch (field.Type.Tag) switch (field.Type.Tag)
{ {
@ -75,7 +76,8 @@ namespace CapnpC.CSharp.Generator.CodeGen
return null; return null;
} }
var prop = PropertyDeclaration(_names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.DomainClass), var prop = PropertyDeclaration(
_names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.DomainClass, Nullability.NullableRef),
_names.GetCodeIdentifier(field).Identifier) _names.GetCodeIdentifier(field).Identifier)
.AddModifiers(Public).AddAccessorListAccessors( .AddModifiers(Public).AddAccessorListAccessors(
AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
@ -111,7 +113,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
MemberDeclarationSyntax MakeUnionContentField() MemberDeclarationSyntax MakeUnionContentField()
{ {
return FieldDeclaration( return FieldDeclaration(
VariableDeclaration(SyntaxHelpers.Type<object>()) VariableDeclaration(_names.Type<object>(Nullability.NullableRef))
.WithVariables( .WithVariables(
SingletonSeparatedList<VariableDeclaratorSyntax>( SingletonSeparatedList<VariableDeclaratorSyntax>(
VariableDeclarator(_names.UnionContentField.Identifier)))) VariableDeclarator(_names.UnionContentField.Identifier))))
@ -163,7 +165,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
case TypeTag.Enum: case TypeTag.Enum:
return MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, return MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
_names.MakeTypeSyntax(value.Type, scope, TypeUsage.NotRelevant), _names.MakeTypeSyntax(value.Type, scope, TypeUsage.NotRelevant, Nullability.NonNullable),
IdentifierName(value.GetEnumerant().Literal)); IdentifierName(value.GetEnumerant().Literal));
case TypeTag.F32: case TypeTag.F32:
@ -262,7 +264,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
value.Decode(); value.Decode();
return ObjectCreationExpression( return ObjectCreationExpression(
_names.MakeTypeSyntax(value.Type, scope, TypeUsage.DomainClass)) _names.MakeTypeSyntax(value.Type, scope, TypeUsage.DomainClass, Nullability.NonNullable))
.WithArgumentList(ArgumentList()) .WithArgumentList(ArgumentList())
.WithInitializer( .WithInitializer(
InitializerExpression( InitializerExpression(
@ -282,7 +284,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
value.Decode(); value.Decode();
return ArrayCreationExpression(ArrayType( return ArrayCreationExpression(ArrayType(
_names.MakeTypeSyntax(value.Type.ElementType, scope, TypeUsage.DomainClass)) _names.MakeTypeSyntax(value.Type.ElementType, scope, TypeUsage.DomainClass, Nullability.NullableRef))
.WithRankSpecifiers( .WithRankSpecifiers(
SingletonList<ArrayRankSpecifierSyntax>( SingletonList<ArrayRankSpecifierSyntax>(
ArrayRankSpecifier( ArrayRankSpecifier(
@ -344,7 +346,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
case TypeTag.Enum: case TypeTag.Enum:
return CastExpression( return CastExpression(
_names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.NotRelevant), _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.NotRelevant, Nullability.NonNullable),
LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0))); LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0)));
case TypeTag.F32: case TypeTag.F32:
@ -451,13 +453,18 @@ namespace CapnpC.CSharp.Generator.CodeGen
Argument(domain), Argument(domain),
Argument( Argument(
ParenthesizedLambdaExpression( ParenthesizedLambdaExpression(
ParameterList(
SeparatedList<ParameterSyntax>(
new SyntaxNodeOrToken[]
{
Parameter(Identifier(s)),
Token(SyntaxKind.CommaToken),
Parameter(Identifier(v))
})),
MakeComplexSerializeParticle( MakeComplexSerializeParticle(
type.ElementType, type.ElementType,
IdentifierName(s), IdentifierName(s),
IdentifierName(v))) IdentifierName(v)))));
.AddParameterListParameters(
Parameter(Identifier(s)),
Parameter(Identifier(v)))));
default: default:
return InvocationExpression( return InvocationExpression(
@ -488,6 +495,11 @@ namespace CapnpC.CSharp.Generator.CodeGen
} }
} }
ExpressionSyntax ConditionalSuppressNullableWarning(ExpressionSyntax expression, bool suppress)
{
return suppress ? _names.SuppressNullableWarning(expression) : expression;
}
StatementSyntax MakeSerializeMethodFieldAssignment(Field field) StatementSyntax MakeSerializeMethodFieldAssignment(Field field)
{ {
var writerProp = MemberAccessExpression( var writerProp = MemberAccessExpression(
@ -519,7 +531,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
writerProp, writerProp,
MemberAccessExpression( MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.SimpleMemberAccessExpression,
_names.GetCodeIdentifier(field).IdentifierName, _names.SuppressNullableWarning(_names.GetCodeIdentifier(field).IdentifierName),
IdentifierName(nameof(Nullable<int>.Value))))); IdentifierName(nameof(Nullable<int>.Value)))));
} }
else else
@ -538,10 +550,11 @@ namespace CapnpC.CSharp.Generator.CodeGen
InvocationExpression( InvocationExpression(
MemberAccessExpression( MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.SimpleMemberAccessExpression,
MemberAccessExpression( ConditionalSuppressNullableWarning(MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.SimpleMemberAccessExpression,
_names.WriterParameter.IdentifierName, _names.WriterParameter.IdentifierName,
_names.GetCodeIdentifier(field).IdentifierName), _names.GetCodeIdentifier(field).IdentifierName),
field.DiscValue.HasValue),
IdentifierName(nameof(Capnp.DynamicSerializerState.SetObject)))) IdentifierName(nameof(Capnp.DynamicSerializerState.SetObject))))
.AddArgumentListArguments( .AddArgumentListArguments(
Argument(_names.GetCodeIdentifier(field).IdentifierName))); Argument(_names.GetCodeIdentifier(field).IdentifierName)));
@ -568,7 +581,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
return ExpressionStatement( return ExpressionStatement(
MakeComplexSerializeParticle( MakeComplexSerializeParticle(
field.Type, field.Type,
writerProp, field.DiscValue.HasValue ? _names.SuppressNullableWarning(writerProp) : writerProp,
_names.GetCodeIdentifier(field).IdentifierName)); _names.GetCodeIdentifier(field).IdentifierName));
case TypeTag.Void: case TypeTag.Void:
@ -599,10 +612,10 @@ namespace CapnpC.CSharp.Generator.CodeGen
ExpressionSyntax MakeInnerStructListConversion(ExpressionSyntax context, TypeSyntax elementType) ExpressionSyntax MakeInnerStructListConversion(ExpressionSyntax context, TypeSyntax elementType)
{ {
return InvocationExpression( return ConditionalAccessExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
context, context,
InvocationExpression(
MemberBindingExpression(
IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList)))) IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList))))
.AddArgumentListArguments(Argument( .AddArgumentListArguments(Argument(
SimpleLambdaExpression(Parameter(Identifier("_")), SimpleLambdaExpression(Parameter(Identifier("_")),
@ -611,8 +624,8 @@ namespace CapnpC.CSharp.Generator.CodeGen
SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(nameof(Capnp.CapnpSerializable)), IdentifierName(nameof(Capnp.CapnpSerializable)),
GenericName(nameof(Capnp.CapnpSerializable.Create)) GenericName(nameof(Capnp.CapnpSerializable.Create))
.AddTypeArgumentListArguments(elementType))) .AddTypeArgumentListArguments(MakeNonNullableType(elementType))))
.AddArgumentListArguments(Argument(IdentifierName("_")))))); .AddArgumentListArguments(Argument(IdentifierName("_")))))));
} }
ExpressionSyntax MakeStructListConversion(ExpressionSyntax context, TypeSyntax elementType, int rank) ExpressionSyntax MakeStructListConversion(ExpressionSyntax context, TypeSyntax elementType, int rank)
@ -624,28 +637,28 @@ namespace CapnpC.CSharp.Generator.CodeGen
string lambdaVarName = $"_{rank}"; string lambdaVarName = $"_{rank}";
return InvocationExpression( return ConditionalAccessExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
context, context,
InvocationExpression(
MemberBindingExpression(
IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList)))) IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList))))
.AddArgumentListArguments(Argument( .AddArgumentListArguments(Argument(
SimpleLambdaExpression( SimpleLambdaExpression(
Parameter(Identifier(lambdaVarName)), Parameter(Identifier(lambdaVarName)),
MakeStructListConversion(IdentifierName(lambdaVarName), elementType, rank - 1)))); MakeStructListConversion(IdentifierName(lambdaVarName), elementType, rank - 1)))));
} }
ExpressionSyntax MakeAnyListConversion(ExpressionSyntax context) ExpressionSyntax MakeAnyListConversion(ExpressionSyntax context)
{ {
return InvocationExpression( return ConditionalAccessExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
context, context,
InvocationExpression(
MemberBindingExpression(
IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList)))) IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList))))
.AddArgumentListArguments(Argument( .AddArgumentListArguments(Argument(
SimpleLambdaExpression( SimpleLambdaExpression(
Parameter(Identifier("_")), Parameter(Identifier("_")),
CastExpression(Type<object>(), IdentifierName("_"))))); CastExpression(_names.Type<object>(Nullability.NonNullable), IdentifierName("_"))))));
} }
ExpressionSyntax MakeDeserializeMethodRightHandSide(Field field) ExpressionSyntax MakeDeserializeMethodRightHandSide(Field field)
@ -662,10 +675,11 @@ namespace CapnpC.CSharp.Generator.CodeGen
IdentifierName(nameof(Capnp.CapnpSerializable)), IdentifierName(nameof(Capnp.CapnpSerializable)),
GenericName(nameof(Capnp.CapnpSerializable.Create)) GenericName(nameof(Capnp.CapnpSerializable.Create))
.AddTypeArgumentListArguments( .AddTypeArgumentListArguments(
_names.MakeTypeSyntax( MakeNonNullableType(_names.MakeTypeSyntax(
field.Type, field.Type,
field.DeclaringType, field.DeclaringType,
TypeUsage.DomainClass)))) TypeUsage.DomainClass,
Nullability.NonNullable)))))
.AddArgumentListArguments(Argument(MemberAccessExpression( .AddArgumentListArguments(Argument(MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.SimpleMemberAccessExpression,
_names.ReaderParameter.IdentifierName, _names.ReaderParameter.IdentifierName,
@ -684,7 +698,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.SimpleMemberAccessExpression,
_names.ReaderParameter.IdentifierName, _names.ReaderParameter.IdentifierName,
_names.GetCodeIdentifier(field).IdentifierName), _names.GetCodeIdentifier(field).IdentifierName),
_names.MakeTypeSyntax(elementType, field.DeclaringType, TypeUsage.DomainClass), _names.MakeTypeSyntax(elementType, field.DeclaringType, TypeUsage.DomainClass, Nullability.NullableRef),
rank); rank);
case TypeTag.ListPointer: case TypeTag.ListPointer:
@ -716,18 +730,6 @@ namespace CapnpC.CSharp.Generator.CodeGen
if (unionField.Type.Tag != TypeTag.Void) if (unionField.Type.Tag != TypeTag.Void)
{ {
ExpressionSyntax right = _names.GetCodeIdentifier(unionField).IdentifierName;
var syntax = _names.MakeTypeSyntax(unionField.Type, unionField.DeclaringType, TypeUsage.DomainClassNullable);
if (syntax is NullableTypeSyntax)
{
right = MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
right,
IdentifierName(nameof(Nullable<int>.Value)));
}
section = section.AddStatements(MakeSerializeMethodFieldAssignment(unionField)); section = section.AddStatements(MakeSerializeMethodFieldAssignment(unionField));
} }
@ -849,7 +851,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
ExplicitInterfaceSpecifier(IdentifierName(nameof(Capnp.ICapnpSerializable)))) ExplicitInterfaceSpecifier(IdentifierName(nameof(Capnp.ICapnpSerializable))))
.AddParameterListParameters( .AddParameterListParameters(
Parameter(_names.AnonymousParameter.Identifier) Parameter(_names.AnonymousParameter.Identifier)
.WithType(Type<Capnp.SerializerState>())) .WithType(_names.Type<Capnp.SerializerState>(Nullability.NonNullable)))
.AddBodyStatements( .AddBodyStatements(
ExpressionStatement( ExpressionStatement(
InvocationExpression(_names.SerializeMethod.IdentifierName) InvocationExpression(_names.SerializeMethod.IdentifierName)
@ -933,7 +935,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
ExplicitInterfaceSpecifier(IdentifierName(nameof(Capnp.ICapnpSerializable)))) ExplicitInterfaceSpecifier(IdentifierName(nameof(Capnp.ICapnpSerializable))))
.AddParameterListParameters( .AddParameterListParameters(
Parameter(_names.AnonymousParameter.Identifier) Parameter(_names.AnonymousParameter.Identifier)
.WithType(Type<Capnp.DeserializerState>())) .WithType(_names.Type<Capnp.DeserializerState>(Nullability.NonNullable)))
.AddBodyStatements(stmts.ToArray()); .AddBodyStatements(stmts.ToArray());
} }
@ -944,7 +946,10 @@ namespace CapnpC.CSharp.Generator.CodeGen
if (def.UnionInfo != null) if (def.UnionInfo != null)
{ {
yield return MakeUnionDiscriminatorField(); yield return MakeUnionDiscriminatorField();
if (def.Fields.Any(f => f.DiscValue.HasValue && f.Type.Tag != TypeTag.Void))
{
yield return MakeUnionContentField(); yield return MakeUnionContentField();
}
yield return MakeUnionDiscriminatorProperty(def); yield return MakeUnionDiscriminatorProperty(def);
} }

View File

@ -22,11 +22,17 @@ namespace CapnpC.CSharp.Generator.CodeGen
{ {
NotRelevant, NotRelevant,
DomainClass, DomainClass,
DomainClassNullable,
Reader, Reader,
Writer Writer
} }
enum Nullability
{
NonNullable,
NullableRefAndValue,
NullableRef
}
class GenNames class GenNames
{ {
readonly Dictionary<Field, Name> _fieldNameMap = new Dictionary<Field, Name>(); readonly Dictionary<Field, Name> _fieldNameMap = new Dictionary<Field, Name>();
@ -66,6 +72,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
public string PipeliningExtensionsClassFormat { get; } public string PipeliningExtensionsClassFormat { get; }
public string ProxyClassFormat { get; } public string ProxyClassFormat { get; }
public string SkeletonClassFormat { get; } public string SkeletonClassFormat { get; }
public bool NullableEnable { get; set; }
public GenNames(GeneratorOptions options) public GenNames(GeneratorOptions options)
{ {
TopNamespace = new Name(options.TopNamespaceName).IdentifierName; TopNamespace = new Name(options.TopNamespaceName).IdentifierName;
@ -106,12 +113,6 @@ namespace CapnpC.CSharp.Generator.CodeGen
} }
public Name MakeTypeName(TypeDefinition def, NameUsage usage = NameUsage.Default) public Name MakeTypeName(TypeDefinition def, NameUsage usage = NameUsage.Default)
{
if (def.Tag == TypeTag.Group)
{
return new Name(SyntaxHelpers.MakeAllLower(def.Name));
}
else
{ {
string name; string name;
@ -124,7 +125,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
switch (def.SpecialName) switch (def.SpecialName)
{ {
case SpecialName.NothingSpecial: case SpecialName.NothingSpecial:
name = def.Name; name = GetCodeIdentifier(def);
break; break;
case SpecialName.MethodParamsStruct: case SpecialName.MethodParamsStruct:
@ -141,19 +142,19 @@ namespace CapnpC.CSharp.Generator.CodeGen
break; break;
case NameUsage.Namespace: case NameUsage.Namespace:
name = def.Name; name = GetCodeIdentifier(def);
break; break;
case NameUsage.Interface: case NameUsage.Interface:
name = "I" + def.Name; name = "I" + GetCodeIdentifier(def);
break; break;
case NameUsage.Proxy: case NameUsage.Proxy:
name = string.Format(ProxyClassFormat, def.Name); name = string.Format(ProxyClassFormat, GetCodeIdentifier(def));
break; break;
case NameUsage.Skeleton: case NameUsage.Skeleton:
name = string.Format(SkeletonClassFormat, def.Name); name = string.Format(SkeletonClassFormat, GetCodeIdentifier(def));
break; break;
default: default:
@ -162,7 +163,6 @@ namespace CapnpC.CSharp.Generator.CodeGen
return new Name(name); return new Name(name);
} }
}
public SimpleNameSyntax MakeGenericTypeName(TypeDefinition def, NameUsage usage = NameUsage.Default) public SimpleNameSyntax MakeGenericTypeName(TypeDefinition def, NameUsage usage = NameUsage.Default)
{ {
@ -184,7 +184,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
TypeSyntax ResolveGenericParameter(GenericParameter p, Model.Type boundType, TypeDefinition def) TypeSyntax ResolveGenericParameter(GenericParameter p, Model.Type boundType, TypeDefinition def)
{ {
var type = boundType.ResolveGenericParameter(p); var type = boundType.ResolveGenericParameter(p);
return MakeTypeSyntax(type, def, TypeUsage.DomainClass); return MakeTypeSyntax(type, def, TypeUsage.DomainClass, Nullability.NonNullable);
} }
public SimpleNameSyntax MakeGenericTypeName(TypeDefinition def, Model.Type boundType, NameUsage usage = NameUsage.Default) public SimpleNameSyntax MakeGenericTypeName(TypeDefinition def, Model.Type boundType, NameUsage usage = NameUsage.Default)
@ -224,10 +224,10 @@ namespace CapnpC.CSharp.Generator.CodeGen
NameSyntax ident = null; NameSyntax ident = null;
if (@namespace != null) if (@namespace != null)
{ {
ident = IdentifierName(SyntaxHelpers.MakeCamel(@namespace[0])); ident = IdentifierName(SyntaxHelpers.MakeUpperCamel(@namespace[0]));
foreach (string name in @namespace.Skip(1)) foreach (string name in @namespace.Skip(1))
{ {
var temp = IdentifierName(SyntaxHelpers.MakeCamel(name)); var temp = IdentifierName(SyntaxHelpers.MakeUpperCamel(name));
ident = QualifiedName(ident, temp); ident = QualifiedName(ident, temp);
} }
} }
@ -288,82 +288,82 @@ namespace CapnpC.CSharp.Generator.CodeGen
case TypeTag.AnyPointer: case TypeTag.AnyPointer:
case TypeTag.StructPointer: case TypeTag.StructPointer:
case TypeTag.ListPointer: case TypeTag.ListPointer:
return SyntaxHelpers.Type<Capnp.ListOfPointersSerializer<Capnp.DynamicSerializerState>>(); return Type<Capnp.ListOfPointersSerializer<Capnp.DynamicSerializerState>>(Nullability.NonNullable);
case TypeTag.CapabilityPointer: case TypeTag.CapabilityPointer:
return SyntaxHelpers.Type<Capnp.ListOfCapsSerializer<Capnp.Rpc.BareProxy>>(); return Type<Capnp.ListOfCapsSerializer<Capnp.Rpc.BareProxy>>(Nullability.NonNullable);
case TypeTag.Data: case TypeTag.Data:
return SyntaxHelpers.Type<Capnp.ListOfPointersSerializer< return Type<Capnp.ListOfPointersSerializer<
Capnp.ListOfPrimitivesSerializer<byte>>>(); Capnp.ListOfPrimitivesSerializer<byte>>>(Nullability.NonNullable);
case TypeTag.Enum: case TypeTag.Enum:
return GenericName("ListOfPrimitivesSerializer") return GenericName("ListOfPrimitivesSerializer")
.AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer)); .AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer, Nullability.NonNullable));
case TypeTag.Group: case TypeTag.Group:
case TypeTag.Struct: case TypeTag.Struct:
return GenericName("ListOfStructsSerializer") return GenericName("ListOfStructsSerializer")
.AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer)); .AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer, Nullability.NonNullable));
case TypeTag.Interface: case TypeTag.Interface:
return GenericName("ListOfCapsSerializer") return GenericName("ListOfCapsSerializer")
.AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer)); .AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer, Nullability.NonNullable));
case TypeTag.List: case TypeTag.List:
return GenericName("ListOfPointersSerializer") return GenericName("ListOfPointersSerializer")
.AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer)); .AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer, Nullability.NonNullable));
case TypeTag.Text: case TypeTag.Text:
return SyntaxHelpers.Type<Capnp.ListOfTextSerializer>(); return Type<Capnp.ListOfTextSerializer>(Nullability.NonNullable);
case TypeTag.Void: case TypeTag.Void:
return SyntaxHelpers.Type<Capnp.ListOfEmptySerializer>(); return Type<Capnp.ListOfEmptySerializer>(Nullability.NonNullable);
case TypeTag.Bool: case TypeTag.Bool:
return SyntaxHelpers.Type<Capnp.ListOfBitsSerializer>(); return Type<Capnp.ListOfBitsSerializer>(Nullability.NonNullable);
case TypeTag.F32: case TypeTag.F32:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<float>>(); return Type<Capnp.ListOfPrimitivesSerializer<float>>(Nullability.NonNullable);
case TypeTag.F64: case TypeTag.F64:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<double>>(); return Type<Capnp.ListOfPrimitivesSerializer<double>>(Nullability.NonNullable);
case TypeTag.S8: case TypeTag.S8:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<sbyte>>(); return Type<Capnp.ListOfPrimitivesSerializer<sbyte>>(Nullability.NonNullable);
case TypeTag.U8: case TypeTag.U8:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<byte>>(); return Type<Capnp.ListOfPrimitivesSerializer<byte>>(Nullability.NonNullable);
case TypeTag.S16: case TypeTag.S16:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<short>>(); return Type<Capnp.ListOfPrimitivesSerializer<short>>(Nullability.NonNullable);
case TypeTag.U16: case TypeTag.U16:
case TypeTag.AnyEnum: case TypeTag.AnyEnum:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<ushort>>(); return Type<Capnp.ListOfPrimitivesSerializer<ushort>>(Nullability.NonNullable);
case TypeTag.S32: case TypeTag.S32:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<int>>(); return Type<Capnp.ListOfPrimitivesSerializer<int>>(Nullability.NonNullable);
case TypeTag.U32: case TypeTag.U32:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<uint>>(); return Type<Capnp.ListOfPrimitivesSerializer<uint>>(Nullability.NonNullable);
case TypeTag.S64: case TypeTag.S64:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<long>>(); return Type<Capnp.ListOfPrimitivesSerializer<long>>(Nullability.NonNullable);
case TypeTag.U64: case TypeTag.U64:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<ulong>>(); return Type<Capnp.ListOfPrimitivesSerializer<ulong>>(Nullability.NonNullable);
default: default:
throw new NotImplementedException("Unexpected type tag, don't know how to deal with this"); throw new NotImplementedException("Unexpected type tag, don't know how to deal with this");
} }
} }
TypeSyntax MaybeNullableValueType(TypeSyntax typeSyntax, TypeUsage usage) TypeSyntax MaybeNullableValueType(TypeSyntax typeSyntax, Nullability nullability)
{ {
switch (usage) switch (nullability)
{ {
case TypeUsage.DomainClassNullable: case Nullability.NullableRefAndValue:
return NullableType(typeSyntax); return NullableType(typeSyntax);
default: default:
@ -371,71 +371,80 @@ namespace CapnpC.CSharp.Generator.CodeGen
} }
} }
public TypeSyntax MakeTypeSyntax(Model.Type type, TypeDefinition scope, TypeUsage usage) TypeSyntax MaybeNullableRefType(TypeSyntax typeSyntax, Nullability nullability)
{
if (!NullableEnable)
return typeSyntax;
switch (nullability)
{
case Nullability.NullableRef:
case Nullability.NullableRefAndValue:
return NullableType(typeSyntax);
default:
return typeSyntax;
}
}
public TypeSyntax MakeTypeSyntax(Model.Type type, TypeDefinition scope, TypeUsage usage, Nullability nullability)
{ {
switch (type.Tag) switch (type.Tag)
{ {
case TypeTag.AnyEnum: case TypeTag.AnyEnum:
return MaybeNullableValueType(SyntaxHelpers.Type<ushort>(), usage); return Type<ushort>(nullability);
case TypeTag.CapabilityPointer: case TypeTag.CapabilityPointer:
if (type.Parameter != null) if (type.Parameter != null)
{ return MaybeNullableRefType(GetQName(type, scope), nullability);
return GetQName(type, scope);
}
else else
{ return Type<Capnp.Rpc.BareProxy>(nullability);
return SyntaxHelpers.Type<Capnp.Rpc.BareProxy>();
}
case TypeTag.AnyPointer: case TypeTag.AnyPointer:
case TypeTag.StructPointer: case TypeTag.StructPointer:
switch (usage) switch (usage)
{ {
case TypeUsage.Reader: case TypeUsage.Reader:
return SyntaxHelpers.Type<Capnp.DeserializerState>(); return Type<Capnp.DeserializerState>(Nullability.NonNullable);
case TypeUsage.Writer: case TypeUsage.Writer:
return SyntaxHelpers.Type<Capnp.DynamicSerializerState>(); return Type<Capnp.DynamicSerializerState>(Nullability.NullableRef);
case TypeUsage.DomainClass when type.Parameter == null:
return Type<object>(nullability);
case TypeUsage.DomainClass when nullability == Nullability.NonNullable:
return GetQName(type, scope);
case TypeUsage.DomainClass: case TypeUsage.DomainClass:
case TypeUsage.DomainClassNullable: return MakeNullableRefType(GetQName(type, scope));
if (type.Parameter != null)
{
return GetQName(type, scope);
}
else
{
return SyntaxHelpers.Type<object>();
}
default: default:
throw new NotImplementedException(); throw new NotImplementedException();
} }
case TypeTag.Bool: case TypeTag.Bool:
return MaybeNullableValueType(SyntaxHelpers.Type<bool>(), usage); return Type<bool>(nullability);
case TypeTag.Data: case TypeTag.Data:
switch (usage) switch (usage)
{ {
case TypeUsage.Reader: case TypeUsage.Reader:
case TypeUsage.DomainClass: case TypeUsage.DomainClass:
case TypeUsage.DomainClassNullable: return Type<IReadOnlyList<byte>>(nullability);
return SyntaxHelpers.Type<IReadOnlyList<byte>>();
case TypeUsage.Writer: case TypeUsage.Writer:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<byte>>(); return Type<Capnp.ListOfPrimitivesSerializer<byte>>(nullability);
default: default:
throw new NotImplementedException(); throw new NotImplementedException();
} }
case TypeTag.Enum: case TypeTag.Enum:
return MaybeNullableValueType(GetQName(type, scope), usage); return MaybeNullableValueType(GetQName(type, scope), nullability);
case TypeTag.Interface: case TypeTag.Interface:
return GetQName(type, scope); return MaybeNullableRefType(GetQName(type, scope), nullability);
case TypeTag.Struct: case TypeTag.Struct:
case TypeTag.Group: case TypeTag.Group:
@ -447,37 +456,38 @@ namespace CapnpC.CSharp.Generator.CodeGen
case TypeUsage.Reader: case TypeUsage.Reader:
return QualifiedName(GetQName(type, scope), ReaderStruct.IdentifierName); return QualifiedName(GetQName(type, scope), ReaderStruct.IdentifierName);
case TypeUsage.DomainClass: case TypeUsage.DomainClass when nullability == Nullability.NonNullable:
case TypeUsage.DomainClassNullable:
return GetQName(type, scope); return GetQName(type, scope);
case TypeUsage.DomainClass:
return MakeNullableRefType(GetQName(type, scope));
default: default:
throw new NotImplementedException(); throw new NotImplementedException();
} }
case TypeTag.F32: case TypeTag.F32:
return MaybeNullableValueType(SyntaxHelpers.Type<float>(), usage); return Type<float>(nullability);
case TypeTag.F64: case TypeTag.F64:
return MaybeNullableValueType(SyntaxHelpers.Type<double>(), usage); return Type<double>(nullability);
case TypeTag.List when type.ElementType.Tag == TypeTag.Void && usage != TypeUsage.Writer: case TypeTag.List when type.ElementType.Tag == TypeTag.Void && usage != TypeUsage.Writer:
return MaybeNullableValueType(SyntaxHelpers.Type<int>(), usage); return Type<int>(nullability);
case TypeTag.List: case TypeTag.List:
switch (usage) switch (usage)
{ {
case TypeUsage.Writer: case TypeUsage.Writer:
return MakeListSerializerSyntax(type.ElementType, scope); return MaybeNullableRefType(MakeListSerializerSyntax(type.ElementType, scope), nullability);
case TypeUsage.Reader: case TypeUsage.Reader:
return GenericName(Identifier("IReadOnlyList")) return MaybeNullableRefType(GenericName(Identifier("IReadOnlyList"))
.AddTypeArgumentListArguments(MakeTypeSyntax(type.ElementType, scope, TypeUsage.Reader)); .AddTypeArgumentListArguments(MakeTypeSyntax(type.ElementType, scope, TypeUsage.Reader, Nullability.NonNullable)), nullability);
case TypeUsage.DomainClass: case TypeUsage.DomainClass:
case TypeUsage.DomainClassNullable: return MaybeNullableRefType(GenericName(Identifier("IReadOnlyList"))
return GenericName(Identifier("IReadOnlyList")) .AddTypeArgumentListArguments(MakeTypeSyntax(type.ElementType, scope, TypeUsage.DomainClass, Nullability.NullableRef)), nullability);
.AddTypeArgumentListArguments(MakeTypeSyntax(type.ElementType, scope, TypeUsage.DomainClass));
default: default:
throw new NotImplementedException(); throw new NotImplementedException();
@ -487,45 +497,49 @@ namespace CapnpC.CSharp.Generator.CodeGen
switch (usage) switch (usage)
{ {
case TypeUsage.Writer: case TypeUsage.Writer:
return SyntaxHelpers.Type<Capnp.SerializerState>(); return Type<Capnp.SerializerState>(Nullability.NonNullable);
case TypeUsage.Reader: case TypeUsage.Reader:
return SyntaxHelpers.Type<IReadOnlyList<Capnp.DeserializerState>>(); return Type<IReadOnlyList<Capnp.DeserializerState>>(Nullability.NonNullable);
case TypeUsage.DomainClass when nullability == Nullability.NonNullable:
return GenericName(Identifier("IReadOnlyList"))
.AddTypeArgumentListArguments(Type<object>(Nullability.NullableRef));
case TypeUsage.DomainClass: case TypeUsage.DomainClass:
case TypeUsage.DomainClassNullable: return MakeNullableRefType(GenericName(Identifier("IReadOnlyList"))
return SyntaxHelpers.Type<IReadOnlyList<object>>(); .AddTypeArgumentListArguments(Type<object>(Nullability.NullableRef)));
default: default:
throw new NotImplementedException(); throw new NotImplementedException();
} }
case TypeTag.S16: case TypeTag.S16:
return MaybeNullableValueType(SyntaxHelpers.Type<short>(), usage); return Type<short>(nullability);
case TypeTag.S32: case TypeTag.S32:
return MaybeNullableValueType(SyntaxHelpers.Type<int>(), usage); return Type<int>(nullability);
case TypeTag.S64: case TypeTag.S64:
return MaybeNullableValueType(SyntaxHelpers.Type<long>(), usage); return Type<long>(nullability);
case TypeTag.S8: case TypeTag.S8:
return MaybeNullableValueType(SyntaxHelpers.Type<sbyte>(), usage); return Type<sbyte>(nullability);
case TypeTag.Text: case TypeTag.Text:
return SyntaxHelpers.Type<string>(); return Type<string>(nullability);
case TypeTag.U16: case TypeTag.U16:
return MaybeNullableValueType(SyntaxHelpers.Type<ushort>(), usage); return Type<ushort>(nullability);
case TypeTag.U32: case TypeTag.U32:
return MaybeNullableValueType(SyntaxHelpers.Type<uint>(), usage); return Type<uint>(nullability);
case TypeTag.U64: case TypeTag.U64:
return MaybeNullableValueType(SyntaxHelpers.Type<ulong>(), usage); return Type<ulong>(nullability);
case TypeTag.U8: case TypeTag.U8:
return MaybeNullableValueType(SyntaxHelpers.Type<byte>(), usage); return Type<byte>(nullability);
case TypeTag.Void: case TypeTag.Void:
return PredefinedType(Token(SyntaxKind.VoidKeyword)); return PredefinedType(Token(SyntaxKind.VoidKeyword));
@ -537,19 +551,22 @@ namespace CapnpC.CSharp.Generator.CodeGen
public string MakeParamsStructName(Method method) public string MakeParamsStructName(Method method)
{ {
return string.Format(ParamsStructFormat, method.Name); return string.Format(ParamsStructFormat, GetCodeIdentifier(method));
} }
public string MakeResultStructName(Method method) public string MakeResultStructName(Method method)
{ {
return string.Format(ResultStructFormat, method.Name); return string.Format(ResultStructFormat, GetCodeIdentifier(method));
} }
public Name GetCodeIdentifier(Method method) public Name GetCodeIdentifier(Method method)
{ {
return new Name(SyntaxHelpers.MakeCamel(method.Name)); return new Name(method.CsName ?? IdentifierRenamer.ToNonKeyword(SyntaxHelpers.MakeUpperCamel(method.Name)));
} }
string GetCodeIdentifierUpperCamel(Field field) => field.CsName ?? SyntaxHelpers.MakeUpperCamel(field.Name);
string GetCodeIdentifierLowerCamel(Field field) => field.CsName ?? IdentifierRenamer.ToNonKeyword(SyntaxHelpers.MakeLowerCamel(field.Name));
public Name GetCodeIdentifier(Field field) public Name GetCodeIdentifier(Field field)
{ {
if (_fieldNameMap.TryGetValue(field, out var name)) if (_fieldNameMap.TryGetValue(field, out var name))
@ -563,7 +580,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
{ {
// Method parameters are internally represented with the same class "Field". // Method parameters are internally represented with the same class "Field".
// They do not have a declaring type. Anyway, they don't suffer from the field-name-equals-nested-type-name problem. // They do not have a declaring type. Anyway, they don't suffer from the field-name-equals-nested-type-name problem.
return new Name(SyntaxHelpers.MakeCamel(field.Name)); return new Name(GetCodeIdentifierLowerCamel(field));
} }
var typeNames = new HashSet<Name>(def.NestedTypes.Select(t => MakeTypeName(t))); var typeNames = new HashSet<Name>(def.NestedTypes.Select(t => MakeTypeName(t)));
@ -571,7 +588,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
foreach (var member in def.Fields) foreach (var member in def.Fields)
{ {
var memberName = new Name(SyntaxHelpers.MakeCamel(member.Name)); var memberName = new Name(GetCodeIdentifierUpperCamel(member));
while (typeNames.Contains(memberName)) while (typeNames.Contains(memberName))
{ {
@ -584,6 +601,16 @@ namespace CapnpC.CSharp.Generator.CodeGen
return _fieldNameMap[field]; return _fieldNameMap[field];
} }
public string GetCodeIdentifier(TypeDefinition def)
{
string id = def.CsName ?? def.Name;
if (def.Tag == TypeTag.Group) // special treatment for groups: Need to disambiguate between
{ // the field name (use original name) and its type (make it start with a lower-case letter)
id = IdentifierRenamer.ToNonKeyword(SyntaxHelpers.MakeLowerCamel(id));
}
return id;
}
public Name GetGenericTypeParameter(string name) public Name GetGenericTypeParameter(string name)
{ {
return new Name(string.Format(GenericTypeParameterFormat, name)); return new Name(string.Format(GenericTypeParameterFormat, name));
@ -637,5 +664,28 @@ namespace CapnpC.CSharp.Generator.CodeGen
method.Name, method.Name,
MakePipeliningSupportExtensionMethodName(path))); MakePipeliningSupportExtensionMethodName(path)));
} }
public TypeSyntax MakeNullableRefType(TypeSyntax type)
{
return NullableEnable ?
NullableType(type) :
type;
}
public TypeSyntax Type<T>(Nullability nullability)
{
return (NullableEnable && !typeof(T).IsValueType && nullability != Nullability.NonNullable) ||
( typeof(T).IsValueType && nullability == Nullability.NullableRefAndValue) ?
NullableType(SyntaxHelpers.NonNullableType<T>()) :
SyntaxHelpers.NonNullableType<T>();
}
public ExpressionSyntax SuppressNullableWarning(ExpressionSyntax expression)
{
return NullableEnable ?
PostfixUnaryExpression(SyntaxKind.SuppressNullableWarningExpression, expression) :
expression;
}
} }
} }

View File

@ -1,6 +1,6 @@
namespace CapnpC.CSharp.Generator.CodeGen namespace CapnpC.CSharp.Generator.CodeGen
{ {
class GeneratorOptions public class GeneratorOptions
{ {
public string TopNamespaceName { get; set; } = "CapnpGen"; public string TopNamespaceName { get; set; } = "CapnpGen";
public string ReaderStructName { get; set; } = "READER"; public string ReaderStructName { get; set; } = "READER";
@ -37,5 +37,6 @@
public string TaskParameterName { get; set; } = "task"; public string TaskParameterName { get; set; } = "task";
public string EagerMethodName { get; set; } = "Eager"; public string EagerMethodName { get; set; } = "Eager";
public string TypeIdFieldName { get; set; } = "typeId"; public string TypeIdFieldName { get; set; } = "typeId";
public bool NullableEnableDefault { get; set; } = false;
} }
} }

View File

@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading; using System.Threading;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis;
namespace CapnpC.CSharp.Generator.CodeGen namespace CapnpC.CSharp.Generator.CodeGen
{ {
@ -27,15 +28,19 @@ namespace CapnpC.CSharp.Generator.CodeGen
case 0: case 0:
return IdentifierName(nameof(Task)); return IdentifierName(nameof(Task));
case 1 when method.Results[0].Type.Tag == TypeTag.Struct:
return GenericName(nameof(Task)).AddTypeArgumentListArguments(
_names.MakeTypeSyntax(method.Results[0].Type, method.DeclaringInterface, TypeUsage.DomainClass, Nullability.NonNullable));
case 1: case 1:
return GenericName(nameof(Task)).AddTypeArgumentListArguments( return GenericName(nameof(Task)).AddTypeArgumentListArguments(
_names.MakeTypeSyntax(method.Results[0].Type, method.DeclaringInterface, TypeUsage.DomainClass)); _names.MakeTypeSyntax(method.Results[0].Type, method.DeclaringInterface, TypeUsage.DomainClass, Nullability.NullableRef));
default: default:
return GenericName(nameof(Task)).AddTypeArgumentListArguments( return GenericName(nameof(Task)).AddTypeArgumentListArguments(
TupleType(SeparatedList( TupleType(SeparatedList(
method.Results.Select( method.Results.Select(
f => TupleElement(_names.MakeTypeSyntax(f.Type, method.DeclaringInterface, TypeUsage.DomainClass)))))); f => TupleElement(_names.MakeTypeSyntax(f.Type, method.DeclaringInterface, TypeUsage.DomainClass, Nullability.NullableRef))))));
} }
} }
@ -50,14 +55,14 @@ namespace CapnpC.CSharp.Generator.CodeGen
if (arg0.Name == null) if (arg0.Name == null)
{ {
list.Add(Parameter(_names.AnonymousParameter.Identifier) list.Add(Parameter(_names.AnonymousParameter.Identifier)
.WithType(_names.MakeTypeSyntax(arg0.Type, method.DeclaringInterface, TypeUsage.DomainClass))); .WithType(_names.MakeTypeSyntax(arg0.Type, method.DeclaringInterface, TypeUsage.DomainClass, Nullability.NullableRef)));
} }
else else
{ {
foreach (var arg in method.Params) foreach (var arg in method.Params)
{ {
list.Add(Parameter(Identifier(IdentifierRenamer.ToNonKeyword(arg.Name))) list.Add(Parameter(_names.GetCodeIdentifier(arg).Identifier)
.WithType(_names.MakeTypeSyntax(arg.Type, method.DeclaringInterface, TypeUsage.DomainClass))); .WithType(_names.MakeTypeSyntax(arg.Type, method.DeclaringInterface, TypeUsage.DomainClass, Nullability.NullableRef)));
} }
} }
} }
@ -124,7 +129,8 @@ namespace CapnpC.CSharp.Generator.CodeGen
ifaceDecl = ifaceDecl.AddBaseListTypes( ifaceDecl = ifaceDecl.AddBaseListTypes(
SimpleBaseType(_names.MakeTypeSyntax( SimpleBaseType(_names.MakeTypeSyntax(
superClass, type, superClass, type,
TypeUsage.NotRelevant))); TypeUsage.DomainClass,
Nullability.NonNullable)));
} }
} }
@ -182,12 +188,15 @@ namespace CapnpC.CSharp.Generator.CodeGen
IEnumerable<ExpressionSyntax> MakeProxyCallInitializerAssignments(Method method) IEnumerable<ExpressionSyntax> MakeProxyCallInitializerAssignments(Method method)
{ {
foreach (var methodParam in method.Params) for (int i = 0; i < method.Params.Count; i++)
{ {
var methodParam = method.Params[i];
var field = method.ParamsStruct.Fields[i];
yield return AssignmentExpression( yield return AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression, SyntaxKind.SimpleAssignmentExpression,
_names.GetCodeIdentifier(methodParam).IdentifierName, _names.GetCodeIdentifier(field).IdentifierName,
IdentifierName(IdentifierRenamer.ToNonKeyword(methodParam.Name))); _names.GetCodeIdentifier(methodParam).IdentifierName);
} }
} }
@ -226,17 +235,24 @@ namespace CapnpC.CSharp.Generator.CodeGen
StatementSyntax MakeProxyCreateResult(Method method) StatementSyntax MakeProxyCreateResult(Method method)
{ {
var resultType = method.ResultStruct; var resultType = method.ResultStruct;
var domainType = _names.MakeTypeSyntax(resultType, method.DeclaringInterface, TypeUsage.DomainClass); var domainType = _names.MakeTypeSyntax(resultType, method.DeclaringInterface, TypeUsage.DomainClass, Nullability.NonNullable);
var createDomain = InvocationExpression( ExpressionSyntax createDomain = InvocationExpression(
MemberAccessExpression( MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(nameof(Capnp.CapnpSerializable)), IdentifierName(nameof(Capnp.CapnpSerializable)),
GenericName(nameof(Capnp.CapnpSerializable.Create)) GenericName(nameof(Capnp.CapnpSerializable.Create))
.AddTypeArgumentListArguments(domainType))) .AddTypeArgumentListArguments(MakeNonNullableType(domainType))))
.AddArgumentListArguments( .AddArgumentListArguments(
Argument(_names.DeserializerLocal.IdentifierName)); Argument(_names.DeserializerLocal.IdentifierName));
if (_names.NullableEnable)
{
createDomain = PostfixUnaryExpression(
SyntaxKind.SuppressNullableWarningExpression,
createDomain);
}
return LocalDeclarationStatement( return LocalDeclarationStatement(
VariableDeclaration( VariableDeclaration(
IdentifierName("var")) IdentifierName("var"))
@ -271,7 +287,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
var classDecl = ClassDeclaration(_names.MakeTypeName(type, NameUsage.Proxy).Identifier) var classDecl = ClassDeclaration(_names.MakeTypeName(type, NameUsage.Proxy).Identifier)
.AddModifiers(Public) .AddModifiers(Public)
.AddBaseListTypes( .AddBaseListTypes(
SimpleBaseType(Type<Capnp.Rpc.Proxy>()), SimpleBaseType(_names.Type<Capnp.Rpc.Proxy>(Nullability.NonNullable)),
SimpleBaseType(_names.MakeGenericTypeName(type, NameUsage.Interface))); SimpleBaseType(_names.MakeGenericTypeName(type, NameUsage.Interface)));
if (type.GenericParameters.Count > 0) if (type.GenericParameters.Count > 0)
@ -311,7 +327,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
_names.MakeTypeSyntax( _names.MakeTypeSyntax(
method.ParamsStruct, method.ParamsStruct,
method.ParamsStruct.Definition, method.ParamsStruct.Definition,
TypeUsage.Writer)))))))))))); TypeUsage.Writer, Nullability.NonNullable))))))))))));
if (method.ParamsStruct.Definition.SpecialName == SpecialName.MethodParamsStruct) if (method.ParamsStruct.Definition.SpecialName == SpecialName.MethodParamsStruct)
{ {
@ -328,7 +344,8 @@ namespace CapnpC.CSharp.Generator.CodeGen
_names.MakeTypeSyntax( _names.MakeTypeSyntax(
method.ParamsStruct, method.ParamsStruct,
method.ParamsStruct.Definition, method.ParamsStruct.Definition,
TypeUsage.DomainClass)) TypeUsage.DomainClass,
Nullability.NonNullable))
.WithArgumentList( .WithArgumentList(
ArgumentList()) ArgumentList())
.WithInitializer( .WithInitializer(
@ -340,13 +357,12 @@ namespace CapnpC.CSharp.Generator.CodeGen
} }
bodyStmts.Add(ExpressionStatement( bodyStmts.Add(ExpressionStatement(
InvocationExpression( ConditionalAccessExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
_names.AnonymousParameter.IdentifierName, _names.AnonymousParameter.IdentifierName,
_names.SerializeMethod.IdentifierName)) InvocationExpression(
MemberBindingExpression(_names.SerializeMethod.IdentifierName))
.AddArgumentListArguments( .AddArgumentListArguments(
Argument(_names.ParamsLocal.IdentifierName)))); Argument(_names.ParamsLocal.IdentifierName)))));
var call = InvocationExpression(IdentifierName(nameof(Capnp.Rpc.BareProxy.Call))) var call = InvocationExpression(IdentifierName(nameof(Capnp.Rpc.BareProxy.Call)))
.AddArgumentListArguments( .AddArgumentListArguments(
@ -361,7 +377,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.SimpleMemberAccessExpression,
_names.ParamsLocal.IdentifierName, _names.ParamsLocal.IdentifierName,
GenericName(nameof(Capnp.SerializerState.Rewrap)) GenericName(nameof(Capnp.SerializerState.Rewrap))
.AddTypeArgumentListArguments(Type<Capnp.DynamicSerializerState>()))) .AddTypeArgumentListArguments(_names.Type<Capnp.DynamicSerializerState>(Nullability.NonNullable))))
.AddArgumentListArguments()), .AddArgumentListArguments()),
Argument( Argument(
LiteralExpression(SyntaxKind.FalseLiteralExpression)), LiteralExpression(SyntaxKind.FalseLiteralExpression)),
@ -446,7 +462,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
GenericName(_names.GetCodeIdentifier(method).ToString()) GenericName(_names.GetCodeIdentifier(method).ToString())
.AddTypeArgumentListArguments( .AddTypeArgumentListArguments(
Enumerable.Repeat( Enumerable.Repeat(
Type<Capnp.AnyPointer>(), _names.Type<Capnp.AnyPointer>(Nullability.NonNullable),
method.GenericParameters.Count).ToArray())); method.GenericParameters.Count).ToArray()));
} }
else else
@ -502,7 +518,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
_names.MakeTypeSyntax( _names.MakeTypeSyntax(
method.ResultStruct, method.ResultStruct,
method.ResultStruct.Definition, method.ResultStruct.Definition,
TypeUsage.Writer))))))))))); TypeUsage.Writer, Nullability.NonNullable)))))))))));
} }
@ -523,7 +539,8 @@ namespace CapnpC.CSharp.Generator.CodeGen
_names.MakeTypeSyntax( _names.MakeTypeSyntax(
method.ResultStruct, method.ResultStruct,
method.ResultStruct.Definition, method.ResultStruct.Definition,
TypeUsage.DomainClass)) TypeUsage.DomainClass,
Nullability.NonNullable))
.WithInitializer( .WithInitializer(
InitializerExpression(SyntaxKind.ObjectInitializerExpression) InitializerExpression(SyntaxKind.ObjectInitializerExpression)
.AddExpressions( .AddExpressions(
@ -575,17 +592,24 @@ namespace CapnpC.CSharp.Generator.CodeGen
if (method.Params.Count > 0) if (method.Params.Count > 0)
{ {
var paramsType = method.ParamsStruct; var paramsType = method.ParamsStruct;
var domainType = _names.MakeTypeSyntax(paramsType, method.ParamsStruct.Definition, TypeUsage.DomainClass); var domainType = _names.MakeTypeSyntax(paramsType, method.ParamsStruct.Definition, TypeUsage.DomainClass, Nullability.NonNullable);
var createDomain = InvocationExpression( ExpressionSyntax createDomain = InvocationExpression(
MemberAccessExpression( MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(nameof(Capnp.CapnpSerializable)), IdentifierName(nameof(Capnp.CapnpSerializable)),
GenericName(nameof(Capnp.CapnpSerializable.Create)) GenericName(nameof(Capnp.CapnpSerializable.Create))
.AddTypeArgumentListArguments(domainType))) .AddTypeArgumentListArguments(MakeNonNullableType(domainType))))
.AddArgumentListArguments( .AddArgumentListArguments(
Argument(_names.DeserializerLocal.IdentifierName)); Argument(_names.DeserializerLocal.IdentifierName));
if (_names.NullableEnable)
{
createDomain = PostfixUnaryExpression(
SyntaxKind.SuppressNullableWarningExpression,
createDomain);
}
if (method.ParamsStruct.Definition.SpecialName == SpecialName.MethodParamsStruct) if (method.ParamsStruct.Definition.SpecialName == SpecialName.MethodParamsStruct)
{ {
yield return LocalDeclarationStatement( yield return LocalDeclarationStatement(
@ -630,10 +654,19 @@ namespace CapnpC.CSharp.Generator.CodeGen
} }
else else
{ {
// CodeAnalysis.CSharp 3.2.1 has a bug which prevents us from using AddParameterListParameters. :-(
var paramList = new List<SyntaxNodeOrToken>();
foreach (var arg in method.Results)
{
if (paramList.Count > 0)
paramList.Add(Token(SyntaxKind.CommaToken));
paramList.Add(Parameter(Identifier(arg.Name)));
}
lambdaArg = ParenthesizedLambdaExpression( lambdaArg = ParenthesizedLambdaExpression(
MakeMaybeTailCallLambdaBody(method)) ParameterList(
.AddParameterListParameters( SeparatedList<ParameterSyntax>(paramList)),
method.Results.Select(arg => Parameter(Identifier(arg.Name))).ToArray()); MakeMaybeTailCallLambdaBody(method));
} }
} }
else else
@ -662,13 +695,13 @@ namespace CapnpC.CSharp.Generator.CodeGen
foreach (var method in def.Methods) foreach (var method in def.Methods)
{ {
var methodDecl = MethodDeclaration( var methodDecl = MethodDeclaration(
Type<Task<Capnp.Rpc.AnswerOrCounterquestion>>(), _names.Type<Task<Capnp.Rpc.AnswerOrCounterquestion>>(Nullability.NonNullable),
_names.GetCodeIdentifier(method).Identifier) _names.GetCodeIdentifier(method).Identifier)
.AddParameterListParameters( .AddParameterListParameters(
Parameter(_names.DeserializerLocal.Identifier) Parameter(_names.DeserializerLocal.Identifier)
.WithType(Type<Capnp.DeserializerState>()), .WithType(_names.Type<Capnp.DeserializerState>(Nullability.NonNullable)),
Parameter(_names.CancellationTokenParameter.Identifier) Parameter(_names.CancellationTokenParameter.Identifier)
.WithType(Type<CancellationToken>())) .WithType(_names.Type<CancellationToken>(Nullability.NonNullable)))
.AddBodyStatements( .AddBodyStatements(
MakeSkeletonMethodBody(method).ToArray()); MakeSkeletonMethodBody(method).ToArray());
@ -709,7 +742,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
.AddArgumentListArguments( .AddArgumentListArguments(
MakeSkeletonSetMethodTableArguments(type).ToArray()))), MakeSkeletonSetMethodTableArguments(type).ToArray()))),
// InterfaceId // InterfaceId
PropertyDeclaration(Type<ulong>(), nameof(Capnp.Rpc.Skeleton<object>.InterfaceId)) PropertyDeclaration(_names.Type<ulong>(Nullability.NonNullable), nameof(Capnp.Rpc.Skeleton<object>.InterfaceId))
.AddModifiers(Public, Override) .AddModifiers(Public, Override)
.WithExpressionBody( .WithExpressionBody(
ArrowExpressionClause( ArrowExpressionClause(
@ -790,7 +823,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
var accessPath = _names.MakeMemberAccessPathFieldName(method, path); var accessPath = _names.MakeMemberAccessPathFieldName(method, path);
var methodName = _names.MakePipeliningSupportExtensionMethodName(path); var methodName = _names.MakePipeliningSupportExtensionMethodName(path);
var capType = path[path.Count - 1].Type; var capType = path[path.Count - 1].Type;
var capTypeSyntax = _names.MakeTypeSyntax(capType, null, TypeUsage.DomainClass); var capTypeSyntax = _names.MakeTypeSyntax(capType, null, TypeUsage.DomainClass, Nullability.NonNullable);
if (!_existingExtensionMethods.Add((capTypeSyntax.ToString(), methodName.ToString()))) if (!_existingExtensionMethods.Add((capTypeSyntax.ToString(), methodName.ToString())))
{ {
@ -815,9 +848,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
.AddModifiers(Static, Readonly); .AddModifiers(Static, Readonly);
var methodDecl = MethodDeclaration( var methodDecl = MethodDeclaration(capTypeSyntax, methodName.Identifier)
capTypeSyntax,
methodName.Identifier)
.AddModifiers(Public, Static) .AddModifiers(Public, Static)
.AddParameterListParameters( .AddParameterListParameters(
Parameter( Parameter(

View File

@ -57,7 +57,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
ParameterList( ParameterList(
SingletonSeparatedList<ParameterSyntax>( SingletonSeparatedList<ParameterSyntax>(
Parameter(_names.ReaderContextField.Identifier) Parameter(_names.ReaderContextField.Identifier)
.WithType(Type<Capnp.DeserializerState>())))) .WithType(_names.Type<Capnp.DeserializerState>(Nullability.NonNullable)))))
.WithExpressionBody( .WithExpressionBody(
ArrowExpressionClause( ArrowExpressionClause(
ObjectCreationExpression(_names.ReaderStruct.IdentifierName) ObjectCreationExpression(_names.ReaderStruct.IdentifierName)
@ -78,7 +78,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
SingletonSeparatedList( SingletonSeparatedList(
Parameter(_names.ContextParameter.Identifier) Parameter(_names.ContextParameter.Identifier)
.WithType( .WithType(
Type<Capnp.DeserializerState>())))) _names.Type<Capnp.DeserializerState>(Nullability.NonNullable)))))
.WithExpressionBody( .WithExpressionBody(
ArrowExpressionClause( ArrowExpressionClause(
ObjectCreationExpression(_names.ReaderStruct.IdentifierName) ObjectCreationExpression(_names.ReaderStruct.IdentifierName)
@ -90,7 +90,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
{ {
yield return FieldDeclaration( yield return FieldDeclaration(
VariableDeclaration( VariableDeclaration(
Type<Capnp.DeserializerState>()) _names.Type<Capnp.DeserializerState>(Nullability.NonNullable))
.AddVariables(_names.ReaderContextField.VariableDeclarator)) .AddVariables(_names.ReaderContextField.VariableDeclarator))
.AddModifiers(Readonly); .AddModifiers(Readonly);
@ -100,7 +100,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
ParameterList( ParameterList(
SingletonSeparatedList( SingletonSeparatedList(
Parameter(_names.ContextParameter.Identifier) Parameter(_names.ContextParameter.Identifier)
.WithType(Type<Capnp.DeserializerState>())))) .WithType(_names.Type<Capnp.DeserializerState>(Nullability.NonNullable)))))
.WithBody( .WithBody(
Block( Block(
SingletonList<StatementSyntax>( SingletonList<StatementSyntax>(
@ -122,7 +122,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
{ {
yield return FieldDeclaration( yield return FieldDeclaration(
VariableDeclaration( VariableDeclaration(
Type<Capnp.DeserializerState>()) _names.Type<Capnp.DeserializerState>(Nullability.NonNullable))
.AddVariables(_names.ReaderContextField.VariableDeclarator)) .AddVariables(_names.ReaderContextField.VariableDeclarator))
.AddModifiers(Readonly); .AddModifiers(Readonly);
@ -131,7 +131,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
.WithParameterList( .WithParameterList(
ParameterList( ParameterList(
SingletonSeparatedList(Parameter(_names.GroupReaderContextArg.Identifier) SingletonSeparatedList(Parameter(_names.GroupReaderContextArg.Identifier)
.WithType(Type<Capnp.DeserializerState>())))) .WithType(_names.Type<Capnp.DeserializerState>(Nullability.NonNullable)))))
.WithBody( .WithBody(
Block( Block(
SingletonList<StatementSyntax>( SingletonList<StatementSyntax>(
@ -173,57 +173,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); .WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
} }
static Func<ExpressionSyntax, ExpressionSyntax> MakeCastFunc(TypeSyntax type) => static Func<ExpressionSyntax, ExpressionSyntax> MakeCastFunc(TypeSyntax type) => x => CastExpression(type, x);
x => CastExpression(type, x);
static Func<ExpressionSyntax, ExpressionSyntax> MakeListCastFunc(string castName, Field field)
{
// Insight: List may have complex default values (e.g. [true, false, false, true] as a
// valid default value for a list of bools. This does not yet fit the author's mindset.
//if (field.DefaultValueIsExplicit)
//{
// return x => InvocationExpression(
// MemberAccessExpression(
// SyntaxKind.SimpleMemberAccessExpression,
// x,
// IdentifierName(castName))
// )
// .AddArgumentListArguments(
// Argument(ValueOf(field.DefaultValue)));
//}
//else
{
return x => InvocationExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
x,
IdentifierName(castName))
);
}
}
static Func<ExpressionSyntax, ExpressionSyntax> MakeListCastFuncWithCons(
string castName, ExpressionSyntax cons)
{
return x => InvocationExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
x,
IdentifierName(castName))
).AddArgumentListArguments(Argument(cons));
}
static Func<ExpressionSyntax, ExpressionSyntax> MakeGenericListCastFunc(string castName, TypeSyntax genericArgument)
{
return x => InvocationExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
x,
GenericName(castName).AddTypeArgumentListArguments(genericArgument))
);
}
PropertyDeclarationSyntax MakeReadProperty(TypeSyntax type, string name, SimpleNameSyntax readName, PropertyDeclarationSyntax MakeReadProperty(TypeSyntax type, string name, SimpleNameSyntax readName,
object indexOrBitOffset, ExpressionSyntax secondArg, object indexOrBitOffset, ExpressionSyntax secondArg,
@ -261,13 +211,19 @@ namespace CapnpC.CSharp.Generator.CodeGen
PropertyDeclarationSyntax MakeReadPrimitiveProperty<T>(Field field, string readName) PropertyDeclarationSyntax MakeReadPrimitiveProperty<T>(Field field, string readName)
{ {
return MakeReadProperty(Type<T>(), _names.GetCodeIdentifier(field).ToString(), readName, field.BitOffset.Value, return MakeReadProperty(
ValueOf(field.DefaultValue.ScalarValue), null, field.DiscValue.HasValue); _names.Type<T>(Nullability.NonNullable),
_names.GetCodeIdentifier(field).ToString(),
readName,
field.BitOffset.Value,
ValueOf(field.DefaultValue.ScalarValue),
null,
field.DiscValue.HasValue);
} }
PropertyDeclarationSyntax MakeReadEnumProperty(Field field) PropertyDeclarationSyntax MakeReadEnumProperty(Field field)
{ {
var typeSyntax = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader); var typeSyntax = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader, Nullability.NonNullable);
return MakeReadProperty(typeSyntax, return MakeReadProperty(typeSyntax,
_names.GetCodeIdentifier(field).ToString(), _names.GetCodeIdentifier(field).ToString(),
nameof(Capnp.SerializerExtensions.ReadDataUShort), field.BitOffset.Value, nameof(Capnp.SerializerExtensions.ReadDataUShort), field.BitOffset.Value,
@ -278,9 +234,16 @@ namespace CapnpC.CSharp.Generator.CodeGen
PropertyDeclarationSyntax MakeReadTextProperty(Field field) PropertyDeclarationSyntax MakeReadTextProperty(Field field)
{ {
return MakeReadProperty(Type<string>(), _names.GetCodeIdentifier(field).ToString(), bool cantBeNull = !field.DiscValue.HasValue && field.DefaultValue.ScalarValue != null;
nameof(Capnp.DeserializerState.ReadText), (int)field.Offset,
ValueOf(field.DefaultValue.ScalarValue), null, field.DiscValue.HasValue); return MakeReadProperty(
_names.Type<string>(cantBeNull ? Nullability.NonNullable : Nullability.NullableRef),
_names.GetCodeIdentifier(field).ToString(),
nameof(Capnp.DeserializerState.ReadText),
(int)field.Offset,
ValueOf(field.DefaultValue.ScalarValue),
null,
field.DiscValue.HasValue);
} }
MemberAccessExpressionSyntax MakeReaderCreator(TypeSyntax qtype) MemberAccessExpressionSyntax MakeReaderCreator(TypeSyntax qtype)
@ -293,7 +256,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
PropertyDeclarationSyntax MakeReadStructProperty(Field field) PropertyDeclarationSyntax MakeReadStructProperty(Field field)
{ {
var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader); var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader, Nullability.NonNullable);
var creator = MakeReaderCreator(qtype); var creator = MakeReaderCreator(qtype);
return MakeReadProperty(qtype, _names.GetCodeIdentifier(field).ToString(), return MakeReadProperty(qtype, _names.GetCodeIdentifier(field).ToString(),
@ -316,18 +279,16 @@ namespace CapnpC.CSharp.Generator.CodeGen
return MakeReaderProperty(type, _names.GetCodeIdentifier(field).ToString(), right, field.DiscValue.HasValue); return MakeReaderProperty(type, _names.GetCodeIdentifier(field).ToString(), right, field.DiscValue.HasValue);
} }
void MakeReadListPropertyImpl(Model.Type elementType, TypeDefinition scope, ExpressionSyntax context, int depth, ExpressionSyntax MakeReadListPropertyImpl(Model.Type elementType, TypeDefinition scope, ExpressionSyntax context, int depth)
out TypeSyntax listType, out ExpressionSyntax impl)
{ {
var elementTypeSyntax = _names.MakeTypeSyntax(elementType, scope, TypeUsage.Reader); var elementTypeSyntax = _names.MakeTypeSyntax(elementType, scope, TypeUsage.Reader, Nullability.NonNullable);
listType = GenericName("IReadOnlyList").AddTypeArgumentListArguments(elementTypeSyntax);
if (elementType.Tag == TypeTag.Interface || if (elementType.Tag == TypeTag.Interface ||
elementType.Tag == TypeTag.CapabilityPointer) elementType.Tag == TypeTag.CapabilityPointer)
{ {
if (depth == 0) if (depth == 0)
{ {
impl = InvocationExpression(MemberAccessExpression( return InvocationExpression(MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.SimpleMemberAccessExpression,
_names.ReaderContextField.IdentifierName, _names.ReaderContextField.IdentifierName,
GenericName(nameof(Capnp.DeserializerState.ReadCapList)) GenericName(nameof(Capnp.DeserializerState.ReadCapList))
@ -336,15 +297,13 @@ namespace CapnpC.CSharp.Generator.CodeGen
} }
else else
{ {
impl = InvocationExpression( return InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
context, context,
GenericName(nameof(Capnp.DeserializerState.RequireCapList)) GenericName(nameof(Capnp.DeserializerState.RequireCapList))
.AddTypeArgumentListArguments(elementTypeSyntax) .AddTypeArgumentListArguments(elementTypeSyntax)
)); ));
} }
return;
} }
if (depth == 0) if (depth == 0)
@ -374,56 +333,44 @@ namespace CapnpC.CSharp.Generator.CodeGen
case TypeTag.List: case TypeTag.List:
{ {
MakeReadListPropertyImpl( var innerImpl = MakeReadListPropertyImpl(
elementType.ElementType, elementType.ElementType,
scope, scope,
lambdaArg, lambdaArg,
depth + 1, depth + 1);
out var innerListType,
out var innerImpl);
listType = GenericName("IReadOnlyList").AddTypeArgumentListArguments(innerListType); return InvocationExpression(
impl = InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
context, context,
IdentifierName(nameof(Capnp.ListDeserializer.Cast)))) IdentifierName(nameof(Capnp.ListDeserializer.Cast))))
.AddArgumentListArguments( .AddArgumentListArguments(
Argument(SimpleLambdaExpression(lambdaParam, innerImpl))); Argument(SimpleLambdaExpression(lambdaParam, innerImpl)));
return;
} }
case TypeTag.ListPointer: case TypeTag.ListPointer:
{ {
listType = Type<IReadOnlyList<object>>();
context = InvocationExpression(MemberAccessExpression( context = InvocationExpression(MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.SimpleMemberAccessExpression,
context, context,
IdentifierName(nameof(Capnp.DeserializerState.RequireList)) IdentifierName(nameof(Capnp.DeserializerState.RequireList))
)).AddArgumentListArguments(Argument(lambdaArg)); )).AddArgumentListArguments(Argument(lambdaArg));
impl = InvocationExpression( return InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
context, context,
IdentifierName(nameof(Capnp.ReadOnlyListExtensions.LazyListSelect)))) IdentifierName(nameof(Capnp.ReadOnlyListExtensions.LazyListSelect))))
.AddArgumentListArguments( .AddArgumentListArguments(
Argument(SimpleLambdaExpression(lambdaParam, context))); Argument(SimpleLambdaExpression(lambdaParam, context)));
return;
} }
case TypeTag.Struct: case TypeTag.Struct:
{ {
impl = InvocationExpression( return InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
context, context,
IdentifierName(nameof(Capnp.ListDeserializer.Cast)))) IdentifierName(nameof(Capnp.ListDeserializer.Cast))))
.AddArgumentListArguments( .AddArgumentListArguments(
Argument(MakeReaderCreator(elementTypeSyntax))); Argument(MakeReaderCreator(elementTypeSyntax)));
return;
} }
case TypeTag.Enum: case TypeTag.Enum:
@ -432,32 +379,26 @@ namespace CapnpC.CSharp.Generator.CodeGen
lambdaParam, lambdaParam,
CastExpression(elementTypeSyntax, lambdaArg)); CastExpression(elementTypeSyntax, lambdaArg));
impl = InvocationExpression( return InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
context, context,
IdentifierName(nameof(Capnp.ListDeserializer.CastEnums)))) IdentifierName(nameof(Capnp.ListDeserializer.CastEnums))))
.AddArgumentListArguments( .AddArgumentListArguments(
Argument(cons)); Argument(cons));
return;
} }
case TypeTag.AnyPointer: case TypeTag.AnyPointer:
case TypeTag.StructPointer: case TypeTag.StructPointer:
{ {
listType = Type<IReadOnlyList<Capnp.DeserializerState>>(); return context;
impl = context;
return;
} }
case TypeTag.Void: case TypeTag.Void:
{ {
listType = Type<int>(); return MemberAccessExpression(
impl = MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.SimpleMemberAccessExpression,
context, context,
IdentifierName(nameof(Capnp.ListDeserializer.Count))); IdentifierName(nameof(Capnp.ListDeserializer.Count)));
return;
} }
case TypeTag.Data: case TypeTag.Data:
@ -517,7 +458,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
throw new NotImplementedException("Unexpected type tag, don't know how to deal with this"); throw new NotImplementedException("Unexpected type tag, don't know how to deal with this");
} }
impl = InvocationExpression(MemberAccessExpression( return InvocationExpression(MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.SimpleMemberAccessExpression,
context, context,
IdentifierName(castFuncName))); IdentifierName(castFuncName)));
@ -527,13 +468,18 @@ namespace CapnpC.CSharp.Generator.CodeGen
{ {
var elementType = field.Type.ElementType; var elementType = field.Type.ElementType;
var context = ValueOf((int)field.Offset); var context = ValueOf((int)field.Offset);
MakeReadListPropertyImpl(elementType, field.DeclaringType, context, 0, out var listType, out var impl); var impl = MakeReadListPropertyImpl(elementType, field.DeclaringType, context, 0);
var listType = _names.MakeTypeSyntax(
field.Type,
field.DeclaringType,
TypeUsage.Reader,
field.DiscValue.HasValue ? Nullability.NullableRef : Nullability.NonNullable);
return MakeReaderProperty(listType, _names.GetCodeIdentifier(field).ToString(), impl, field.DiscValue.HasValue); return MakeReaderProperty(listType, _names.GetCodeIdentifier(field).ToString(), impl, field.DiscValue.HasValue);
} }
PropertyDeclarationSyntax MakeReadAnyListProperty(Field field) PropertyDeclarationSyntax MakeReadAnyListProperty(Field field)
{ {
var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader); var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader, Nullability.NonNullable);
return MakeReadProperty(type, _names.GetCodeIdentifier(field).ToString(), return MakeReadProperty(type, _names.GetCodeIdentifier(field).ToString(),
nameof(Capnp.DeserializerState.ReadList), nameof(Capnp.DeserializerState.ReadList),
@ -542,18 +488,19 @@ namespace CapnpC.CSharp.Generator.CodeGen
PropertyDeclarationSyntax MakeReadCapProperty(Field field) PropertyDeclarationSyntax MakeReadCapProperty(Field field)
{ {
var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader); var nullableType = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader, Nullability.NullableRef);
var nonNullableType = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader, Nullability.NonNullable);
var readName = GenericName(nameof(Capnp.DeserializerState.ReadCap)) var readName = GenericName(nameof(Capnp.DeserializerState.ReadCap))
.AddTypeArgumentListArguments(type); .AddTypeArgumentListArguments(nonNullableType);
return MakeReadProperty(type, _names.GetCodeIdentifier(field).ToString(), return MakeReadProperty(nullableType, _names.GetCodeIdentifier(field).ToString(),
readName, readName,
(int)field.Offset, null, null, field.DiscValue.HasValue); (int)field.Offset, null, null, field.DiscValue.HasValue);
} }
PropertyDeclarationSyntax MakeReadAnyCapProperty(Field field) PropertyDeclarationSyntax MakeReadAnyCapProperty(Field field)
{ {
var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader); var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader, Nullability.NonNullable);
return MakeReadProperty(type, _names.GetCodeIdentifier(field).ToString(), return MakeReadProperty(type, _names.GetCodeIdentifier(field).ToString(),
nameof(Capnp.DeserializerState.ReadCap), nameof(Capnp.DeserializerState.ReadCap),
@ -573,7 +520,8 @@ namespace CapnpC.CSharp.Generator.CodeGen
IdentifierName(nameof(Capnp.ListDeserializer.CastByte)))); IdentifierName(nameof(Capnp.ListDeserializer.CastByte))));
return MakeReaderProperty( return MakeReaderProperty(
_names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader), _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader,
field.DiscValue.HasValue ? Nullability.NullableRefAndValue : Nullability.NonNullable),
_names.GetCodeIdentifier(field).ToString(), impl, field.DiscValue.HasValue); _names.GetCodeIdentifier(field).ToString(), impl, field.DiscValue.HasValue);
} }

View File

@ -9,8 +9,8 @@ namespace CapnpC.CSharp.Generator.CodeGen
{ {
static class SyntaxHelpers static class SyntaxHelpers
{ {
public static string MakeCamel(string name) => $"{char.ToUpperInvariant(name[0])}{name.Substring(1)}"; public static string MakeUpperCamel(string name) => $"{char.ToUpperInvariant(name[0])}{name.Substring(1)}";
public static string MakeAllLower(string name) => $"@{name}"; public static string MakeLowerCamel(string name) => $"{char.ToLowerInvariant(name[0])}{name.Substring(1)}";
public static readonly SyntaxToken Async = Token(SyntaxKind.AsyncKeyword); public static readonly SyntaxToken Async = Token(SyntaxKind.AsyncKeyword);
public static readonly SyntaxToken Public = Token(SyntaxKind.PublicKeyword); public static readonly SyntaxToken Public = Token(SyntaxKind.PublicKeyword);
@ -21,7 +21,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
public static readonly SyntaxToken Partial = Token(SyntaxKind.PartialKeyword); public static readonly SyntaxToken Partial = Token(SyntaxKind.PartialKeyword);
public static readonly SyntaxToken This = Token(SyntaxKind.ThisKeyword); public static readonly SyntaxToken This = Token(SyntaxKind.ThisKeyword);
public static TypeSyntax Type(Type type) public static TypeSyntax NonNullableType(Type type)
{ {
switch (0) switch (0)
{ {
@ -66,14 +66,14 @@ namespace CapnpC.CSharp.Generator.CodeGen
case 0 when type.IsGenericType: case 0 when type.IsGenericType:
return GenericName(type.Name.Substring(0, type.Name.IndexOf('`'))) return GenericName(type.Name.Substring(0, type.Name.IndexOf('`')))
.AddTypeArgumentListArguments(type.GetGenericArguments().Select(Type).ToArray()); .AddTypeArgumentListArguments(type.GetGenericArguments().Select(NonNullableType).ToArray());
default: default:
return ParseTypeName(type.Name); return ParseTypeName(type.Name);
} }
} }
public static TypeSyntax Type<T>() => Type(typeof(T)); public static TypeSyntax NonNullableType<T>() => NonNullableType(typeof(T));
public static ExpressionSyntax ValueOf(object value) public static ExpressionSyntax ValueOf(object value)
{ {
@ -83,16 +83,16 @@ namespace CapnpC.CSharp.Generator.CodeGen
return LiteralExpression(x ? SyntaxKind.TrueLiteralExpression : SyntaxKind.FalseLiteralExpression); return LiteralExpression(x ? SyntaxKind.TrueLiteralExpression : SyntaxKind.FalseLiteralExpression);
case sbyte x: case sbyte x:
return CastExpression(Type<sbyte>(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x))); return CastExpression(NonNullableType<sbyte>(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)));
case byte x: case byte x:
return CastExpression(Type<byte>(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x))); return CastExpression(NonNullableType<byte>(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)));
case short x: case short x:
return CastExpression(Type<short>(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x))); return CastExpression(NonNullableType<short>(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)));
case ushort x: case ushort x:
return CastExpression(Type<ushort>(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x))); return CastExpression(NonNullableType<ushort>(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)));
case int x: case int x:
return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)); return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x));
@ -122,5 +122,14 @@ namespace CapnpC.CSharp.Generator.CodeGen
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
public static TypeSyntax MakeNonNullableType(TypeSyntax type)
{
if (type is NullableTypeSyntax nts)
{
type = nts.ElementType;
}
return type;
}
} }
} }

View File

@ -115,12 +115,21 @@ namespace CapnpC.CSharp.Generator.CodeGen
.AddArgumentListArguments( .AddArgumentListArguments(
Argument(ValueOf(index))); Argument(ValueOf(index)));
ExpressionSyntax MakeLinkSyntax(object index) => ExpressionSyntax MakeLinkSyntax(object index, bool suppressNullableWarning)
InvocationExpression( {
ExpressionSyntax value = IdentifierName("value");
if (suppressNullableWarning)
{
value = _names.SuppressNullableWarning(value);
}
return InvocationExpression(
IdentifierName(SerializerStateWorder.LinkName)) IdentifierName(SerializerStateWorder.LinkName))
.AddArgumentListArguments( .AddArgumentListArguments(
Argument(ValueOf(index)), Argument(ValueOf(index)),
Argument(IdentifierName("value"))); Argument(value));
}
ExpressionSyntax MakeLinkObjectSyntax(object index) => ExpressionSyntax MakeLinkObjectSyntax(object index) =>
InvocationExpression( InvocationExpression(
@ -129,22 +138,46 @@ namespace CapnpC.CSharp.Generator.CodeGen
Argument(ValueOf(index)), Argument(ValueOf(index)),
Argument(IdentifierName("value"))); Argument(IdentifierName("value")));
PropertyDeclarationSyntax MakeWriterRefTypeProperty(
TypeSyntax type,
string name,
ExpressionSyntax getter,
ExpressionSyntax setter,
bool cast,
bool cond)
{
if (cond)
{
type = _names.MakeNullableRefType(type);
}
var prop = MakeWriterProperty(type, name, getter, setter, cast, cond);
if (cond && _names.NullableEnable)
{
prop = prop.AddAttributeLists(
AttributeList(
SingletonSeparatedList(
Attribute(
IdentifierName("DisallowNull")))));
}
return prop;
}
PropertyDeclarationSyntax MakePointerProperty(TypeSyntax type, string name, object index, bool cast, bool cond) PropertyDeclarationSyntax MakePointerProperty(TypeSyntax type, string name, object index, bool cast, bool cond)
{ {
ExpressionSyntax getter = MakePointerSyntax(type, index); ExpressionSyntax getter = MakePointerSyntax(type, index);
ExpressionSyntax setter = MakeLinkSyntax(index); ExpressionSyntax setter = MakeLinkSyntax(index, cond);
return MakeWriterProperty(type, name, getter, setter, cast, cond); return MakeWriterRefTypeProperty(type, name, getter, setter, cast, cond);
} }
PropertyDeclarationSyntax MakePointerAsStructProperty( PropertyDeclarationSyntax MakePointerAsStructProperty(TypeSyntax type, string name, object index, bool cast, bool cond)
TypeSyntax type, string name, object index,
bool cast, bool cond)
{ {
ExpressionSyntax getter = MakeTypedPointerSyntax(index, type); ExpressionSyntax getter = MakeTypedPointerSyntax(index, type);
ExpressionSyntax setter = MakeLinkSyntax(index); ExpressionSyntax setter = MakeLinkSyntax(index, cond);
return MakeWriterProperty(type, name, getter, setter, cast, cond); return MakeWriterRefTypeProperty(type, name, getter, setter, cast, cond);
} }
PropertyDeclarationSyntax MakeProperty( PropertyDeclarationSyntax MakeProperty(
@ -195,7 +228,10 @@ namespace CapnpC.CSharp.Generator.CodeGen
PropertyDeclarationSyntax MakePrimitiveProperty<T>(Field field, string readName) PropertyDeclarationSyntax MakePrimitiveProperty<T>(Field field, string readName)
{ {
return MakeProperty(Type<T>(), null, _names.GetCodeIdentifier(field).ToString(), return MakeProperty(
_names.Type<T>(Nullability.NonNullable),
null,
_names.GetCodeIdentifier(field).ToString(),
readName, readName,
nameof(Capnp.SerializerExtensions.WriteData), nameof(Capnp.SerializerExtensions.WriteData),
field.BitOffset.Value, field.BitOffset.Value,
@ -207,7 +243,9 @@ namespace CapnpC.CSharp.Generator.CodeGen
PropertyDeclarationSyntax MakeEnumProperty(Field field, string readName) PropertyDeclarationSyntax MakeEnumProperty(Field field, string readName)
{ {
return MakeProperty(_names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.NotRelevant), Type<ushort>(), return MakeProperty(
_names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.NotRelevant, Nullability.NonNullable),
_names.Type<ushort>(Nullability.NonNullable),
_names.GetCodeIdentifier(field).ToString(), _names.GetCodeIdentifier(field).ToString(),
readName, readName,
nameof(Capnp.SerializerExtensions.WriteData), nameof(Capnp.SerializerExtensions.WriteData),
@ -220,7 +258,9 @@ namespace CapnpC.CSharp.Generator.CodeGen
PropertyDeclarationSyntax MakeTextProperty(Field field) PropertyDeclarationSyntax MakeTextProperty(Field field)
{ {
return MakeProperty(Type<string>(), null, return MakeProperty(
_names.Type<string>(Nullability.NullableRef),
null,
_names.GetCodeIdentifier(field).ToString(), _names.GetCodeIdentifier(field).ToString(),
nameof(Capnp.SerializerState.ReadText), nameof(Capnp.SerializerState.ReadText),
nameof(Capnp.SerializerState.WriteText), nameof(Capnp.SerializerState.WriteText),
@ -233,7 +273,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
PropertyDeclarationSyntax MakeStructProperty(Field field) PropertyDeclarationSyntax MakeStructProperty(Field field)
{ {
var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer); var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer, Nullability.NonNullable);
return MakePointerAsStructProperty(qtype, _names.GetCodeIdentifier(field).ToString(), return MakePointerAsStructProperty(qtype, _names.GetCodeIdentifier(field).ToString(),
(int)field.Offset, false, field.DiscValue.HasValue); (int)field.Offset, false, field.DiscValue.HasValue);
@ -241,7 +281,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
PropertyDeclarationSyntax MakeGroupProperty(Field field) PropertyDeclarationSyntax MakeGroupProperty(Field field)
{ {
var type = QualifiedName( TypeSyntax type = QualifiedName(
_names.MakeTypeName(field.Type.Definition).IdentifierName, _names.MakeTypeName(field.Type.Definition).IdentifierName,
_names.WriterStruct.IdentifierName); _names.WriterStruct.IdentifierName);
@ -249,12 +289,17 @@ namespace CapnpC.CSharp.Generator.CodeGen
GenericName(nameof(Capnp.SerializerState.Rewrap)) GenericName(nameof(Capnp.SerializerState.Rewrap))
.AddTypeArgumentListArguments(type)); .AddTypeArgumentListArguments(type));
if (field.DiscValue.HasValue)
{
type = _names.MakeNullableRefType(type);
}
return MakeWriterProperty(type, _names.GetCodeIdentifier(field).ToString(), getter, null, false, field.DiscValue.HasValue); return MakeWriterProperty(type, _names.GetCodeIdentifier(field).ToString(), getter, null, false, field.DiscValue.HasValue);
} }
PropertyDeclarationSyntax MakeListProperty(Field field) PropertyDeclarationSyntax MakeListProperty(Field field)
{ {
var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer); var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer, Nullability.NonNullable);
return MakePointerProperty(qtype, _names.GetCodeIdentifier(field).ToString(), return MakePointerProperty(qtype, _names.GetCodeIdentifier(field).ToString(),
(int)field.Offset, false, field.DiscValue.HasValue); (int)field.Offset, false, field.DiscValue.HasValue);
@ -269,20 +314,21 @@ namespace CapnpC.CSharp.Generator.CodeGen
PropertyDeclarationSyntax MakeCapProperty(Field field) PropertyDeclarationSyntax MakeCapProperty(Field field)
{ {
var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer); var nonNullableType = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer, Nullability.NonNullable);
var nullableType = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer, Nullability.NullableRef);
int index = (int)field.Offset; int index = (int)field.Offset;
string name = _names.GetCodeIdentifier(field).ToString(); string name = _names.GetCodeIdentifier(field).ToString();
ExpressionSyntax getter = MakeReadCapSyntax(type, index); ExpressionSyntax getter = MakeReadCapSyntax(nonNullableType, index);
ExpressionSyntax setter = MakeLinkObjectSyntax(index); ExpressionSyntax setter = MakeLinkObjectSyntax(index);
return MakeWriterProperty(type, name, getter, setter, false, field.DiscValue.HasValue); return MakeWriterProperty(nullableType, name, getter, setter, false, field.DiscValue.HasValue);
} }
PropertyDeclarationSyntax MakeWriterUnionSelector(TypeDefinition def) PropertyDeclarationSyntax MakeWriterUnionSelector(TypeDefinition def)
{ {
return MakeProperty( return MakeProperty(
_names.UnionDiscriminatorEnum.IdentifierName, _names.UnionDiscriminatorEnum.IdentifierName,
Type<ushort>(), _names.Type<ushort>(Nullability.NonNullable),
_names.UnionDiscriminatorProp.ToString(), _names.UnionDiscriminatorProp.ToString(),
nameof(Capnp.SerializerExtensions.ReadDataUShort), nameof(Capnp.SerializerExtensions.ReadDataUShort),
nameof(Capnp.SerializerExtensions.WriteData), nameof(Capnp.SerializerExtensions.WriteData),

View File

@ -8,6 +8,7 @@
get => _literal; get => _literal;
set => _literal = IdentifierRenamer.ToNonKeyword(value); set => _literal = IdentifierRenamer.ToNonKeyword(value);
} }
public string CsLiteral { get; set; }
public ushort? Ordinal { get; set; } public ushort? Ordinal { get; set; }
public int CodeOrder { get; set; } public int CodeOrder { get; set; }
} }

View File

@ -5,6 +5,7 @@
public TypeDefinition DeclaringType { get; set; } public TypeDefinition DeclaringType { get; set; }
public Field Parent { get; set; } public Field Parent { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string CsName { get; set; }
public Type Type { get; set; } public Type Type { get; set; }
public Value DefaultValue { get; set; } public Value DefaultValue { get; set; }
public bool DefaultValueIsExplicit { get; set; } public bool DefaultValueIsExplicit { get; set; }

View File

@ -11,6 +11,8 @@ namespace CapnpC.CSharp.Generator.Model
public string Name { get; set; } public string Name { get; set; }
public string[] Namespace { get; set; } public string[] Namespace { get; set; }
public bool? NullableEnable { get; set; }
public bool EmitNullableDirective { get; set; }
public IEnumerable<TypeDefinition> NestedTypes { get => this.GetNestedTypes(); } public IEnumerable<TypeDefinition> NestedTypes { get => this.GetNestedTypes(); }
public ICollection<IDefinition> NestedDefinitions { get; } = new List<IDefinition>(); public ICollection<IDefinition> NestedDefinitions { get; } = new List<IDefinition>();

View File

@ -8,6 +8,7 @@ namespace CapnpC.CSharp.Generator.Model
public TypeDefinition DeclaringInterface { get; set; } public TypeDefinition DeclaringInterface { get; set; }
public int Id { get; set; } public int Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string CsName { get; set; }
public List<Field> Params { get; } = new List<Field>(); public List<Field> Params { get; } = new List<Field>();
public List<Field> Results { get; } = new List<Field>(); public List<Field> Results { get; } = new List<Field>();
public Type ParamsStruct { get; set; } public Type ParamsStruct { get; set; }
@ -21,6 +22,7 @@ namespace CapnpC.CSharp.Generator.Model
DeclaringInterface = DeclaringInterface, DeclaringInterface = DeclaringInterface,
Id = Id, Id = Id,
Name = Name, Name = Name,
CsName = CsName,
ParamsStruct = ParamsStruct, ParamsStruct = ParamsStruct,
ResultStruct = ResultStruct ResultStruct = ResultStruct
}; };

View File

@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using static CapnpC.CSharp.Generator.Model.SupportedAnnotations;
namespace CapnpC.CSharp.Generator.Model namespace CapnpC.CSharp.Generator.Model
{ {
@ -92,6 +92,8 @@ namespace CapnpC.CSharp.Generator.Model
state.parent = null; state.parent = null;
file.Namespace = GetNamespaceAnnotation(node); file.Namespace = GetNamespaceAnnotation(node);
file.Name = name; file.Name = name;
file.NullableEnable = GetNullableEnable(node);
file.EmitNullableDirective = GetEmitNullableDirective(node) ?? false;
return ProcessNodePass1(id, name, state) as GenFile; return ProcessNodePass1(id, name, state) as GenFile;
} }
@ -145,6 +147,7 @@ namespace CapnpC.CSharp.Generator.Model
Trace.Assert(state.parent != null, $"The {node.GetTypeTag().ToString()} node {node.StrId()} was expected to have a parent."); Trace.Assert(state.parent != null, $"The {node.GetTypeTag().ToString()} node {node.StrId()} was expected to have a parent.");
var typeDef = _typeDefMgr.CreateTypeDef(id, node.GetTypeTag(), state.parent); var typeDef = _typeDefMgr.CreateTypeDef(id, node.GetTypeTag(), state.parent);
typeDef.Name = name; typeDef.Name = name;
typeDef.CsName = GetCsName(node);
state.parent = typeDef; state.parent = typeDef;
def = typeDef; def = typeDef;
} }
@ -175,18 +178,6 @@ namespace CapnpC.CSharp.Generator.Model
return def; return def;
} }
string[] GetNamespaceAnnotation(Schema.Node.Reader fileNode)
{
foreach (var annotation in fileNode.Annotations)
{
if (annotation.Id == 0xb9c6f99ebf805f2c) // Cxx namespace
{
return annotation.Value.Text.Split(new string[1] { "::" }, default);
}
}
return null;
}
// 2nd pass: Generate types based on definitions // 2nd pass: Generate types based on definitions
struct Pass2State struct Pass2State
@ -492,6 +483,7 @@ namespace CapnpC.CSharp.Generator.Model
{ {
DeclaringType = declaringType, DeclaringType = declaringType,
Name = fieldReader.Name, Name = fieldReader.Name,
CsName = GetCsName(fieldReader),
CodeOrder = fieldReader.CodeOrder CodeOrder = fieldReader.CodeOrder
}; };
@ -505,6 +497,8 @@ namespace CapnpC.CSharp.Generator.Model
case 0 when fieldReader.IsGroup: case 0 when fieldReader.IsGroup:
var def = ProcessTypeDef(fieldReader.Group_TypeId, state, TypeTag.Group); var def = ProcessTypeDef(fieldReader.Group_TypeId, state, TypeTag.Group);
field.Type = Types.FromDefinition(def); field.Type = Types.FromDefinition(def);
def.CsName = field.CsName; // Type definitions for unions are artificially generated.
// Transfer the C# name of the using field.
break; break;
case 0 when fieldReader.IsSlot: case 0 when fieldReader.IsSlot:
@ -549,7 +543,8 @@ namespace CapnpC.CSharp.Generator.Model
{ {
DeclaringInterface = def, DeclaringInterface = def,
Id = def.Methods.Count, Id = def.Methods.Count,
Name = methodReader.Name Name = methodReader.Name,
CsName = GetCsName(methodReader)
}; };
foreach (var implicitParameterReader in methodReader.ImplicitParameters) foreach (var implicitParameterReader in methodReader.ImplicitParameters)
{ {
@ -664,6 +659,7 @@ namespace CapnpC.CSharp.Generator.Model
{ {
TypeDefinition = def, TypeDefinition = def,
Literal = fieldReader.Name, Literal = fieldReader.Name,
CsLiteral = GetCsName(fieldReader),
CodeOrder = fieldReader.CodeOrder CodeOrder = fieldReader.CodeOrder
}; };
@ -745,9 +741,6 @@ namespace CapnpC.CSharp.Generator.Model
public static class SchemaExtensions public static class SchemaExtensions
{ {
public static string GetName(this Schema.Node.Reader node)
=> node.DisplayName.Substring((int)node.DisplayNamePrefixLength);
public static string StrId(this Schema.Node.Reader node) public static string StrId(this Schema.Node.Reader node)
=> $"0x{node.Id:X}"; => $"0x{node.Id:X}";

View File

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CapnpC.CSharp.Generator.Model
{
static class SupportedAnnotations
{
static class AnnotationIds
{
public static class Cxx
{
public const ulong Namespace = 0xb9c6f99ebf805f2c;
}
public static class Cs
{
public const ulong Namespace = 0xeb0d831668c6eda0;
public const ulong NullableEnable = 0xeb0d831668c6eda1;
public const ulong Name = 0xeb0d831668c6eda2;
public const ulong EmitNullableDirective = 0xeb0d831668c6eda3;
}
}
public static string[] GetNamespaceAnnotation(Schema.Node.Reader fileNode)
{
foreach (var annotation in fileNode.Annotations)
{
if (annotation.Id == AnnotationIds.Cs.Namespace)
{
return annotation.Value.Text.Split(new string[1] { "." }, default);
}
if (annotation.Id == AnnotationIds.Cxx.Namespace)
{
return annotation.Value.Text.Split(new string[1] { "::" }, default);
}
}
return null;
}
public static string GetCsName(Schema.Field.Reader node)
{
foreach (var annotation in node.Annotations)
{
if (annotation.Id == AnnotationIds.Cs.Name)
{
return annotation.Value.Text;
}
}
return null;
}
public static string GetCsName(Schema.Node.Reader node)
{
foreach (var annotation in node.Annotations)
{
if (annotation.Id == AnnotationIds.Cs.Name)
{
return annotation.Value.Text;
}
}
return null;
}
public static string GetCsName(Schema.Method.Reader node)
{
foreach (var annotation in node.Annotations)
{
if (annotation.Id == AnnotationIds.Cs.Name)
{
return annotation.Value.Text;
}
}
return null;
}
public static bool? GetNullableEnable(Schema.Node.Reader node)
{
foreach (var annotation in node.Annotations)
{
if (annotation.Id == AnnotationIds.Cs.NullableEnable && annotation.Value.IsBool)
{
return annotation.Value.Bool;
}
}
return null;
}
public static bool? GetEmitNullableDirective(Schema.Node.Reader node)
{
foreach (var annotation in node.Annotations)
{
if (annotation.Id == AnnotationIds.Cs.EmitNullableDirective && annotation.Value.IsBool)
{
return annotation.Value.Bool;
}
}
return null;
}
}
}

View File

@ -37,6 +37,7 @@ namespace CapnpC.CSharp.Generator.Model
public Method UsingMethod { get; set; } public Method UsingMethod { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string CsName { get; set; }
public SpecialName SpecialName { get; set; } public SpecialName SpecialName { get; set; }
public DiscriminationInfo UnionInfo { get; set; } public DiscriminationInfo UnionInfo { get; set; }
public List<Field> Fields { get; } = new List<Field>(); public List<Field> Fields { get; } = new List<Field>();

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>

View File

@ -2,8 +2,9 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
<Version>1.2-local</Version> <LangVersion>8.0</LangVersion>
<Nullable>Enable</Nullable>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<PackageReferenceVersion Condition="'$(PackageReferenceVersion)'==''">$(Version)*</PackageReferenceVersion> <PackageReferenceVersion Condition="'$(PackageReferenceVersion)'==''">$(Version)*</PackageReferenceVersion>
</PropertyGroup> </PropertyGroup>

View File

@ -9,13 +9,24 @@ namespace MsBuildGenerationTest
// Instantiate some generated classes ensures that they are really present. // Instantiate some generated classes ensures that they are really present.
// Note that this code is not supposed to test runtime behavior, we have plenty of other test cases for that purpose. // Note that this code is not supposed to test runtime behavior, we have plenty of other test cases for that purpose.
void use(object y)
{
}
var vatId = new Capnp.Rpc.Twoparty.VatId(); var vatId = new Capnp.Rpc.Twoparty.VatId();
use(vatId);
var msg = new Capnp.Rpc.Message(); var msg = new Capnp.Rpc.Message();
use(msg);
var node = new Capnp.Schema.Node(); var node = new Capnp.Schema.Node();
use(node);
var x = Capnproto_test.Capnp.Test.TestEnum.garply; var x = Capnproto_test.Capnp.Test.TestEnum.garply;
use(x);
var imp = new CapnpGen.TestImport(); var imp = new CapnpGen.TestImport();
use(imp);
var imp2 = new CapnpGen.TestImport2(); var imp2 = new CapnpGen.TestImport2();
use(imp2);
var book = new CapnpGen.AddressBook(); var book = new CapnpGen.AddressBook();
use(book);
} }
} }
} }

View File

@ -110,7 +110,7 @@
# it for free. # it for free.
using Cxx = import "/capnp/c++.capnp"; using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("capnp::rpc"); $Cxx.namespace("test::rpc");
# ======================================================================================== # ========================================================================================
# The Four Tables # The Four Tables

View File

@ -55,14 +55,14 @@ test_script:
- cmd: | - cmd: |
nbgv get-version -v NuGetPackageVersion >> version.txt nbgv get-version -v NuGetPackageVersion >> version.txt
set /P VERSION=< version.txt set /P VERSION=< version.txt
vstest.console /logger:Appveyor /inIsolation CapnpC.CSharp.Generator.Tests\bin\Release\netcoreapp2.1\CapnpC.CSharp.Generator.Tests.dll vstest.console /logger:Appveyor /inIsolation CapnpC.CSharp.Generator.Tests\bin\Release\netcoreapp3.0\CapnpC.CSharp.Generator.Tests.dll
choco install capnproto --source="https://chocolatey.org/api/v2" --force -y choco install capnproto --source="https://chocolatey.org/api/v2" --force -y
cd %APPVEYOR_BUILD_FOLDER%\capnpc-csharp cd %APPVEYOR_BUILD_FOLDER%\capnpc-csharp
dotnet tool install --global --add-source ./nupkg capnpc-csharp --version %VERSION% dotnet tool install --global --add-source ./nupkg capnpc-csharp --version %VERSION%
cd %APPVEYOR_BUILD_FOLDER%\install-test cd %APPVEYOR_BUILD_FOLDER%\install-test
compile-test compile-test
cd %APPVEYOR_BUILD_FOLDER% cd %APPVEYOR_BUILD_FOLDER%
vstest.console /logger:Appveyor /inIsolation CapnpC.CSharp.Generator.Tests\bin\Release\netcoreapp2.1\CapnpC.CSharp.Generator.Tests.dll vstest.console /logger:Appveyor /inIsolation CapnpC.CSharp.Generator.Tests\bin\Release\netcoreapp3.0\CapnpC.CSharp.Generator.Tests.dll
dotnet tool uninstall --global capnpc-csharp dotnet tool uninstall --global capnpc-csharp
cd %APPVEYOR_BUILD_FOLDER%\install-test cd %APPVEYOR_BUILD_FOLDER%\install-test
notinstalled-test notinstalled-test
@ -73,7 +73,7 @@ test_script:
choco uninstall capnpc-csharp-win-x86 -y choco uninstall capnpc-csharp-win-x86 -y
notinstalled-test notinstalled-test
cd %APPVEYOR_BUILD_FOLDER% cd %APPVEYOR_BUILD_FOLDER%
vstest.console /logger:Appveyor /inIsolation CapnpC.CSharp.MsBuild.Generation.Tests\bin\Release\netcoreapp2.1\CapnpC.CSharp.MsBuild.Generation.Tests.dll vstest.console /logger:Appveyor /inIsolation CapnpC.CSharp.MsBuild.Generation.Tests\bin\Release\netcoreapp3.0\CapnpC.CSharp.MsBuild.Generation.Tests.dll
msbuild -t:restore ./MsBuildGenerationTest/MsBuildGenerationTest.csproj /p:Configuration="Debug" /p:PackageReferenceVersion="%VERSION%" msbuild -t:restore ./MsBuildGenerationTest/MsBuildGenerationTest.csproj /p:Configuration="Debug" /p:PackageReferenceVersion="%VERSION%"
msbuild ./MsBuildGenerationTest/MsBuildGenerationTest.sln /p:Configuration="Debug" /p:PackageReferenceVersion="%VERSION%" msbuild ./MsBuildGenerationTest/MsBuildGenerationTest.sln /p:Configuration="Debug" /p:PackageReferenceVersion="%VERSION%"
vstest.console /logger:Appveyor /inIsolation Capnp.Net.Runtime.Tests\bin\Debug\net471\Capnp.Net.Runtime.Tests.Std20.dll vstest.console /logger:Appveyor /inIsolation Capnp.Net.Runtime.Tests\bin\Debug\net471\Capnp.Net.Runtime.Tests.Std20.dll

14
include/csharp.capnp Normal file
View File

@ -0,0 +1,14 @@
@0xeb0d831668c6edab;
$namespace("Capnp.Annotations");
annotation namespace @0xeb0d831668c6eda0 (file) : Text;
# C# namespace for code generation
annotation nullableEnable @0xeb0d831668c6eda1 (file) : Bool;
# Whether to generate C# nullable reference types
annotation emitNullableDirective @0xeb0d831668c6eda3 (file) : Bool;
# Whether to surround the generated with with #nullable enable/disable ... #nullable restore
annotation name @0xeb0d831668c6eda2 (field, enumerant, struct, enum, interface, method, param, group, union) : Text;
# C# member name for code generation