This commit is contained in:
Christian Köllner 2019-10-19 13:52:16 +02:00
parent 4637b4c4cf
commit 44d5f7a08c
22 changed files with 551 additions and 17 deletions

View File

@ -14,6 +14,7 @@
<Compile Include="..\Capnp.Net.Runtime.Tests\DynamicSerializerStateTests.cs" Link="DynamicSerializerStateTests.cs" />
<Compile Include="..\Capnp.Net.Runtime.Tests\FramePumpTests.cs" Link="FramePumpTests.cs" />
<Compile Include="..\Capnp.Net.Runtime.Tests\General.cs" Link="General.cs" />
<Compile Include="..\Capnp.Net.Runtime.Tests\Issue19.cs" Link="Issue19.cs" />
<Compile Include="..\Capnp.Net.Runtime.Tests\JobUtil.cs" Link="JobUtil.cs" />
<Compile Include="..\Capnp.Net.Runtime.Tests\MessageBuilderTests.cs" Link="MessageBuilderTests.cs" />
<Compile Include="..\Capnp.Net.Runtime.Tests\ProvidedCapabilityMock.cs" Link="ProvidedCapabilityMock.cs" />

View File

@ -0,0 +1,332 @@
using Capnp;
using Capnp.Rpc;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace CapnpGen
{
[TypeId(0x8f85f6df684d47b5UL), Proxy(typeof(GenericAProxy<>)), Skeleton(typeof(GenericASkeleton<>))]
public interface IGenericA<TT> : IDisposable where TT : class
{
Task MethodA(TT param1, CancellationToken cancellationToken_ = default);
}
public class GenericAProxy<TT> : Proxy, IGenericA<TT> where TT : class
{
public async Task MethodA(TT param1, CancellationToken cancellationToken_ = default)
{
var in_ = SerializerState.CreateForRpc<CapnpGen.GenericA<TT>.Params_methodA.WRITER>();
var arg_ = new CapnpGen.GenericA<TT>.Params_methodA()
{Param1 = param1};
arg_.serialize(in_);
var d_ = await Call(10341943558714247093UL, 0, in_.Rewrap<DynamicSerializerState>(), false, cancellationToken_).WhenReturned;
var r_ = CapnpSerializable.Create<CapnpGen.GenericA<TT>.Result_methodA>(d_);
return;
}
}
public class GenericASkeleton<TT> : Skeleton<IGenericA<TT>> where TT : class
{
public GenericASkeleton()
{
SetMethodTable(MethodA);
}
public override ulong InterfaceId => 10341943558714247093UL;
async Task<AnswerOrCounterquestion> MethodA(DeserializerState d_, CancellationToken cancellationToken_)
{
var in_ = CapnpSerializable.Create<CapnpGen.GenericA<TT>.Params_methodA>(d_);
await Impl.MethodA(in_.Param1, cancellationToken_);
var s_ = SerializerState.CreateForRpc<CapnpGen.GenericA<TT>.Result_methodA.WRITER>();
return s_;
}
}
public static class GenericA<TT>
where TT : class
{
[TypeId(0xe67c3d73f913c24fUL)]
public class Params_methodA : ICapnpSerializable
{
public const UInt64 typeId = 0xe67c3d73f913c24fUL;
void ICapnpSerializable.Deserialize(DeserializerState arg_)
{
var reader = READER.create(arg_);
Param1 = CapnpSerializable.Create<TT>(reader.Param1);
applyDefaults();
}
public void serialize(WRITER writer)
{
writer.Param1.SetObject(Param1);
}
void ICapnpSerializable.Serialize(SerializerState arg_)
{
serialize(arg_.Rewrap<WRITER>());
}
public void applyDefaults()
{
}
public TT Param1
{
get;
set;
}
public struct READER
{
readonly DeserializerState ctx;
public READER(DeserializerState ctx)
{
this.ctx = ctx;
}
public static READER create(DeserializerState ctx) => new READER(ctx);
public static implicit operator DeserializerState(READER reader) => reader.ctx;
public static implicit operator READER(DeserializerState ctx) => new READER(ctx);
public DeserializerState Param1 => ctx.StructReadPointer(0);
}
public class WRITER : SerializerState
{
public WRITER()
{
this.SetStruct(0, 1);
}
public DynamicSerializerState Param1
{
get => BuildPointer<DynamicSerializerState>(0);
set => Link(0, value);
}
}
}
[TypeId(0xd6791784c41c75f3UL)]
public class Result_methodA : ICapnpSerializable
{
public const UInt64 typeId = 0xd6791784c41c75f3UL;
void ICapnpSerializable.Deserialize(DeserializerState arg_)
{
var reader = READER.create(arg_);
applyDefaults();
}
public void serialize(WRITER writer)
{
}
void ICapnpSerializable.Serialize(SerializerState arg_)
{
serialize(arg_.Rewrap<WRITER>());
}
public void applyDefaults()
{
}
public struct READER
{
readonly DeserializerState ctx;
public READER(DeserializerState ctx)
{
this.ctx = ctx;
}
public static READER create(DeserializerState ctx) => new READER(ctx);
public static implicit operator DeserializerState(READER reader) => reader.ctx;
public static implicit operator READER(DeserializerState ctx) => new READER(ctx);
}
public class WRITER : SerializerState
{
public WRITER()
{
this.SetStruct(0, 0);
}
}
}
}
[TypeId(0xe3cd928b8338eac9UL), Proxy(typeof(B2Proxy)), Skeleton(typeof(B2Skeleton))]
public interface IB2 : CapnpGen.IGenericA<string>
{
Task<string> MethodB(long param1, CancellationToken cancellationToken_ = default);
}
public class B2Proxy : Proxy, IB2
{
public async Task<string> MethodB(long param1, CancellationToken cancellationToken_ = default)
{
var in_ = SerializerState.CreateForRpc<CapnpGen.B2.Params_methodB.WRITER>();
var arg_ = new CapnpGen.B2.Params_methodB()
{Param1 = param1};
arg_.serialize(in_);
var d_ = await Call(16414937344734980809UL, 0, in_.Rewrap<DynamicSerializerState>(), false, cancellationToken_).WhenReturned;
var r_ = CapnpSerializable.Create<CapnpGen.B2.Result_methodB>(d_);
return (r_.Res);
}
public async Task MethodA(string param1, CancellationToken cancellationToken_ = default)
{
var in_ = SerializerState.CreateForRpc<CapnpGen.GenericA<string>.Params_methodA.WRITER>();
var arg_ = new CapnpGen.GenericA<string>.Params_methodA()
{Param1 = param1};
arg_.serialize(in_);
var d_ = await Call(10341943558714247093UL, 0, in_.Rewrap<DynamicSerializerState>(), false, cancellationToken_).WhenReturned;
var r_ = CapnpSerializable.Create<CapnpGen.GenericA<string>.Result_methodA>(d_);
return;
}
}
public class B2Skeleton : Skeleton<IB2>
{
public B2Skeleton()
{
SetMethodTable(MethodB);
}
public override ulong InterfaceId => 16414937344734980809UL;
Task<AnswerOrCounterquestion> MethodB(DeserializerState d_, CancellationToken cancellationToken_)
{
var in_ = CapnpSerializable.Create<CapnpGen.B2.Params_methodB>(d_);
return Impatient.MaybeTailCall(Impl.MethodB(in_.Param1, cancellationToken_), res =>
{
var s_ = SerializerState.CreateForRpc<CapnpGen.B2.Result_methodB.WRITER>();
var r_ = new CapnpGen.B2.Result_methodB{Res = res};
r_.serialize(s_);
return s_;
}
);
}
}
public static class B2
{
[TypeId(0xdf7b0019c911a776UL)]
public class Params_methodB : ICapnpSerializable
{
public const UInt64 typeId = 0xdf7b0019c911a776UL;
void ICapnpSerializable.Deserialize(DeserializerState arg_)
{
var reader = READER.create(arg_);
Param1 = reader.Param1;
applyDefaults();
}
public void serialize(WRITER writer)
{
writer.Param1 = Param1;
}
void ICapnpSerializable.Serialize(SerializerState arg_)
{
serialize(arg_.Rewrap<WRITER>());
}
public void applyDefaults()
{
}
public long Param1
{
get;
set;
}
public struct READER
{
readonly DeserializerState ctx;
public READER(DeserializerState ctx)
{
this.ctx = ctx;
}
public static READER create(DeserializerState ctx) => new READER(ctx);
public static implicit operator DeserializerState(READER reader) => reader.ctx;
public static implicit operator READER(DeserializerState ctx) => new READER(ctx);
public long Param1 => ctx.ReadDataLong(0UL, 0L);
}
public class WRITER : SerializerState
{
public WRITER()
{
this.SetStruct(1, 0);
}
public long Param1
{
get => this.ReadDataLong(0UL, 0L);
set => this.WriteData(0UL, value, 0L);
}
}
}
[TypeId(0xea5fbbd3b20b4347UL)]
public class Result_methodB : ICapnpSerializable
{
public const UInt64 typeId = 0xea5fbbd3b20b4347UL;
void ICapnpSerializable.Deserialize(DeserializerState arg_)
{
var reader = READER.create(arg_);
Res = reader.Res;
applyDefaults();
}
public void serialize(WRITER writer)
{
writer.Res = Res;
}
void ICapnpSerializable.Serialize(SerializerState arg_)
{
serialize(arg_.Rewrap<WRITER>());
}
public void applyDefaults()
{
}
public string Res
{
get;
set;
}
public struct READER
{
readonly DeserializerState ctx;
public READER(DeserializerState ctx)
{
this.ctx = ctx;
}
public static READER create(DeserializerState ctx) => new READER(ctx);
public static implicit operator DeserializerState(READER reader) => reader.ctx;
public static implicit operator READER(DeserializerState ctx) => new READER(ctx);
public string Res => ctx.ReadText(0, "");
}
public class WRITER : SerializerState
{
public WRITER()
{
this.SetStruct(0, 1);
}
public string Res
{
get => this.ReadText(0, "");
set => this.WriteText(0, value, "");
}
}
}
}
}

View File

@ -120,5 +120,28 @@ namespace Capnp.Net.Runtime.Tests
}
}
}
[TestMethod]
public void InheritFromGenericInterface()
{
using (var server = SetupServer())
{
var counters = new Counters();
server.Main = new B2Impl();
using (var client = SetupClient())
{
client.WhenConnected.Wait();
using (var main = client.GetMain<CapnpGen.IB2>())
{
Assert.IsTrue(main.MethodA("42").Wait(MediumNonDbgTimeout));
var b = main.MethodB(123);
Assert.IsTrue(b.Wait(MediumNonDbgTimeout));
Assert.AreEqual("42", b.Result);
}
}
}
}
}
}

View File

@ -753,4 +753,27 @@ namespace Capnp.Net.Runtime.Tests.GenImpls
}
}
#endregion TestHandle
#region B2
class B2Impl : CapnpGen.IB2
{
string _s;
public void Dispose()
{
}
public Task MethodA(string param1, CancellationToken cancellationToken_ = default)
{
_s = param1;
return Task.CompletedTask;
}
public Task<string> MethodB(long param1, CancellationToken cancellationToken_ = default)
{
return Task.FromResult(_s);
}
}
#endregion B2
}

View File

@ -18,7 +18,7 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryType>Git</RepositoryType>
<PackageTags>capnp "Cap'n Proto" RPC serialization cerealization</PackageTags>
<Version>1.1-local$([System.DateTime]::UtcNow.ToString(yyMMddHHmm))</Version>
<Version>1.2-local$([System.DateTime]::UtcNow.ToString(yyMMddHHmm))</Version>
<Configurations>Debug;Release</Configurations>
</PropertyGroup>

View File

@ -119,21 +119,20 @@ namespace Capnp.Rpc
try
{
var attrs = (from iface in _.GetInterfaces()
from attr in iface.GetCustomAttributes(typeof(SkeletonAttribute), true)
select (SkeletonAttribute)attr).ToArray();
let generics = iface.GetGenericArguments()
from attr in iface.GetCustomAttributes(typeof(SkeletonAttribute), false)
select (Attr: (SkeletonAttribute)attr, Generics: generics)).ToArray();
if (attrs.Length == 0)
throw new InvalidCapabilityInterfaceException("No 'Skeleton' attribute defined, don't know how to create the skeleton");
Type[] genericArguments = type.GetGenericArguments();
if (attrs.Length == 1)
{
return CreateMonoSkeletonFactory(attrs[0], genericArguments);
return CreateMonoSkeletonFactory(attrs[0].Attr, attrs[0].Generics);
}
else
{
var monoFactories = attrs.Select(a => CreateMonoSkeletonFactory(a, genericArguments)).ToArray();
var monoFactories = attrs.Select(a => CreateMonoSkeletonFactory(a.Attr, a.Generics)).ToArray();
return new PolySkeletonFactory(monoFactories);
}
}
@ -174,7 +173,7 @@ namespace Capnp.Rpc
try
{
var attrs = type
.GetCustomAttributes(typeof(ProxyAttribute), true)
.GetCustomAttributes(typeof(ProxyAttribute), false)
.Cast<ProxyAttribute>()
.ToArray();

View File

@ -10,7 +10,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.3.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.3.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="Mono.Cecil" Version="0.11.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
<PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
<PackageReference Include="SpecFlow" Version="3.0.225" />
@ -24,6 +27,7 @@
<None Remove="Embedded Resources\Empty.capnp" />
<None Remove="Embedded Resources\Empty1.capnp" />
<None Remove="Embedded Resources\invalid.capnp" />
<None Remove="Embedded Resources\Issue19.capnp.bin" />
<None Remove="Embedded Resources\null.bin" />
<None Remove="Embedded Resources\test.capnp" />
<None Remove="Embedded Resources\test.capnp.bin" />
@ -37,6 +41,7 @@
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Embedded Resources\Issue19.capnp.bin" />
<EmbeddedResource Include="Embedded Resources\Empty.capnp" />
<EmbeddedResource Include="Embedded Resources\Empty1.capnp" />
<EmbeddedResource Include="Embedded Resources\invalid.capnp" />

View File

@ -46,3 +46,12 @@ Scenario: Multiple errors
Then the invocation must fail
And the reason must be bad input
And the error output must contain multiple messages
Scenario Outline: Valid generator output
Given I have a binary code generator request <bin>
When I invoke capnpc-csharp
Then the invocation must succeed and the generated code must compile
Examples:
| bin |
| Issue19.capnp.bin |

View File

@ -241,6 +241,34 @@ this.ScenarioInitialize(scenarioInfo);
#line hidden
this.ScenarioCleanup();
}
public virtual void ValidGeneratorOutput(string bin, string[] exampleTags)
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Valid generator output", null, exampleTags);
#line 50
this.ScenarioInitialize(scenarioInfo);
this.ScenarioStart();
#line 51
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
this.ScenarioCleanup();
}
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Valid generator output: Issue19.capnp.bin")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("VariantName", "Issue19.capnp.bin")]
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("Parameter:bin", "Issue19.capnp.bin")]
public virtual void ValidGeneratorOutput_Issue19_Capnp_Bin()
{
#line 50
this.ValidGeneratorOutput("Issue19.capnp.bin", ((string[])(null)));
#line hidden
}
}
}
#pragma warning restore

View File

@ -102,6 +102,13 @@ namespace CapnpC.CSharp.Generator.Tests
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")]
public void GivenCapnp_ExeIsInstalledOnMySystem()
{

View File

@ -0,0 +1,49 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
namespace CapnpC.CSharp.Generator.Tests.Util
{
class InlineAssemblyCompiler
{
public static bool TryCompileCapnp(string code)
{
var options = new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Debug);
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
string assemblyRoot = Path.GetDirectoryName(typeof(object).Assembly.Location);
string capnpRuntimePath = Path.GetFullPath(Path.Combine(
Assembly.GetExecutingAssembly().Location,
@"..\..\..\..\..\Capnp.Net.Runtime\bin\Debug\netcoreapp2.1\Capnp.Net.Runtime.dll"));
var capnpRuntimeMetadataRef = MetadataReference.CreateFromFile(capnpRuntimePath);
var compilation = CSharpCompilation.Create(
"CompilationTestAssembly",
options: options,
references: new MetadataReference[] {
MetadataReference.CreateFromFile(Path.Combine(assemblyRoot, "mscorlib.dll")),
MetadataReference.CreateFromFile(Path.Combine(assemblyRoot, "System.dll")),
MetadataReference.CreateFromFile(Path.Combine(assemblyRoot, "System.Core.dll")),
MetadataReference.CreateFromFile(Path.Combine(assemblyRoot, "System.Runtime.dll")),
MetadataReference.CreateFromFile(Path.Combine(assemblyRoot, "System.Private.CoreLib.dll")),
capnpRuntimeMetadataRef },
syntaxTrees: new SyntaxTree[] { syntaxTree });
using (var stream = new MemoryStream())
{
var emitResult = compilation.Emit(stream);
return emitResult.Success;
}
}
}
}

View File

@ -283,7 +283,7 @@ namespace CapnpC.CSharp.Generator.CodeGen
var allMethods =
from c in Types.FromDefinition(type).AllImplementedClasses
from m in c.Definition.Methods
from m in c.Methods
select m;
foreach (var method in allMethods)

View File

@ -5,7 +5,6 @@ namespace CapnpC.CSharp.Generator.Model
abstract class AbstractType
{
public TypeTag Tag { get; set; }
protected List<Field> Fields { get; } = new List<Field>();
public uint? FixedBitWidth
{

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
namespace CapnpC.CSharp.Generator.Model
{
@ -12,5 +13,21 @@ namespace CapnpC.CSharp.Generator.Model
public Type ParamsStruct { get; set; }
public Type ResultStruct { get; set; }
public List<string> GenericParameters { get; } = new List<string>();
public Method Clone()
{
var method = new Method()
{
DeclaringInterface = DeclaringInterface,
Id = Id,
Name = Name,
ParamsStruct = ParamsStruct,
ResultStruct = ResultStruct
};
method.Params.AddRange(Params.Select((p => p.Clone())));
method.Results.AddRange(Results.Select(r => r.Clone()));
method.GenericParameters.AddRange(GenericParameters);
return method;
}
}
}

View File

@ -648,8 +648,9 @@ namespace CapnpC.CSharp.Generator.Model
{
foreach (var superClassReader in ifaceReader.Interface_Superclasses)
{
var superClass = ProcessTypeDef(superClassReader.Id, state, TypeTag.Interface);
def.Superclasses.Add(Types.FromDefinition(superClass));
var superClass = Types.FromDefinition(ProcessTypeDef(superClassReader.Id, state, TypeTag.Interface));
ProcessBrand(superClassReader.Brand, superClass, state);
def.Superclasses.Add(superClass);
}
return ProcessInterfaceOrStructTail(def, ifaceReader, state);

View File

@ -83,7 +83,7 @@ namespace CapnpC.CSharp.Generator.Model
}
else
{
return Types.AnyPointer;
return type;
}
}
@ -117,7 +117,25 @@ namespace CapnpC.CSharp.Generator.Model
return result;
}
public new IReadOnlyList<Field> Fields => Definition.Fields.LazyListSelect(SubstituteGenerics);
Method SubstituteGenerics(Method method)
{
var result = method.Clone();
result.ParamsStruct = SubstituteGenerics(result.ParamsStruct);
result.ResultStruct = SubstituteGenerics(result.ResultStruct);
foreach (var field in result.Params)
{
field.Type = SubstituteGenerics(field.Type);
}
foreach (var field in result.Results)
{
field.Type = SubstituteGenerics(field.Type);
}
return result;
}
public IReadOnlyList<Field> Fields => Definition.Fields.LazyListSelect(SubstituteGenerics);
public IReadOnlyList<Method> Methods => Definition.Methods.LazyListSelect(SubstituteGenerics);
public Type DeclaringType
{

View File

@ -39,7 +39,7 @@ namespace CapnpC.CSharp.Generator.Model
public string Name { get; set; }
public SpecialName SpecialName { get; set; }
public DiscriminationInfo UnionInfo { get; set; }
public new List<Field> Fields => base.Fields;
public List<Field> Fields { get; } = new List<Field>();
public List<Enumerant> Enumerants { get; } = new List<Enumerant>();
public ICollection<IDefinition> NestedDefinitions { get; } = new List<IDefinition>();
public IEnumerable<TypeDefinition> NestedTypes { get => this.GetNestedTypes(); }

View File

@ -0,0 +1,9 @@
@0xe169e9301753ca94;
interface GenericA(T) {
methodA @0 (param1 :T) -> ();
}
interface B2 extends (GenericA(Text)) {
methodB @0 (param1 :Int64) -> (res :Text);
}

View File

@ -8,6 +8,14 @@
<PackageReferenceVersion Condition="'$(PackageReferenceVersion)'==''">$(Version)*</PackageReferenceVersion>
</PropertyGroup>
<ItemGroup>
<CapnpCsharpObsoleteCodeBehindFiles Remove="capnp\bergm.capnp.cs" />
</ItemGroup>
<ItemGroup>
<None Remove="capnp\bergm.capnp.bin" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Capnp.Net.Runtime" Version="$(PackageReferenceVersion)" />
<PackageReference Include="CapnpC.CSharp.MsBuild.Generation" Version="$(PackageReferenceVersion)" />
@ -55,4 +63,10 @@
</CapnpFiles>
</ItemGroup>
<ItemGroup>
<Compile Update="issue19.capnp.cs">
<DependentUpon>Issue19.capnp</DependentUpon>
</Compile>
</ItemGroup>
</Project>

View File

@ -1,4 +1,4 @@
version: '1.1.{build}'
version: '1.2.{build}'
image: Visual Studio 2019
# branches:
# only:

View File

@ -13,7 +13,7 @@
<PackageProjectUrl>https://github.com/c80k/capnproto-dotnetcore</PackageProjectUrl>
<RepositoryType>Git</RepositoryType>
<PackageTags>capnp capnpc RPC serialization cerealization</PackageTags>
<Version>1.1.0</Version>
<Version>1.2.0</Version>
<Configurations>Debug;Release</Configurations>
</PropertyGroup>