mirror of
https://github.com/FabInfra/capnproto-dotnetcore_Runtime.git
synced 2025-03-12 14:51:41 +01:00
WIP
This commit is contained in:
parent
107d10e3f4
commit
2f21dc217a
@ -11,11 +11,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capnp.Net.Runtime.Tests.Std
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capnp.Net.Runtime.Tests.Core21", "Capnp.Net.Runtime.Tests.Core21\Capnp.Net.Runtime.Tests.Core21.csproj", "{58E8FFC8-D207-4B0F-842A-58ED9D3D9EEF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "capnpc-csharp.tests", "capnpc-csharp.tests\capnpc-csharp.tests.csproj", "{B77AC567-E232-4072-85C3-8689566BF3D4}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CapnpC.CSharp.Generator.Tests", "CapnpC.CSharp.Generator.Tests\CapnpC.CSharp.Generator.Tests.csproj", "{B77AC567-E232-4072-85C3-8689566BF3D4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capnpc.Csharp.MsBuild.Generation", "Capnpc.Csharp.MsBuild.Generation\Capnpc.Csharp.MsBuild.Generation.csproj", "{1EFC1F20-C7BB-4F44-8BF9-DBB123AACCF4}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CapnpC.CSharp.MsBuild.Generation", "CapnpC.CSharp.MsBuild.Generation\CapnpC.CSharp.MsBuild.Generation.csproj", "{1EFC1F20-C7BB-4F44-8BF9-DBB123AACCF4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CapnpC.CSharp.Generator", "CapnpC.CSharp.Generator\CapnpC.CSharp.Generator.csproj", "{C3A3BB49-356E-4762-A190-76D877BE18F7}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CapnpC.CSharp.Generator", "CapnpC.CSharp.Generator\CapnpC.CSharp.Generator.csproj", "{C3A3BB49-356E-4762-A190-76D877BE18F7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CapnpC.CSharp.MsBuild.Generation.Tests", "CapnpC.CSharp.MsBuild.Generation.Tests\CapnpC.CSharp.MsBuild.Generation.Tests.csproj", "{EF05AD68-DE31-448E-B88D-4144F928ED5D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -51,6 +53,10 @@ Global
|
||||
{C3A3BB49-356E-4762-A190-76D877BE18F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C3A3BB49-356E-4762-A190-76D877BE18F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C3A3BB49-356E-4762-A190-76D877BE18F7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EF05AD68-DE31-448E-B88D-4144F928ED5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EF05AD68-DE31-448E-B88D-4144F928ED5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EF05AD68-DE31-448E-B88D-4144F928ED5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EF05AD68-DE31-448E-B88D-4144F928ED5D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
<RootNamespace>capnpc_csharp.Tests</RootNamespace>
|
||||
<RootNamespace>CapnpC.CSharp.Generator.Tests</RootNamespace>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
@ -16,17 +16,18 @@
|
||||
<PackageReference Include="SpecFlow.Tools.MsBuild.Generation" Version="3.0.225" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\capnpc-csharp\capnpc-csharp.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="*.cs" />
|
||||
<Compile Remove="Embedded Resources\test.cs" />
|
||||
<None Remove="Embedded Resources\Empty.capnp" />
|
||||
<None Remove="Embedded Resources\Empty1.capnp" />
|
||||
<None Remove="Embedded Resources\invalid.capnp" />
|
||||
<None Remove="Embedded Resources\null.bin" />
|
||||
<None Remove="Embedded Resources\test.capnp" />
|
||||
<None Remove="Embedded Resources\test.capnp.bin" />
|
||||
<Compile Include="UnitTests.cs" />
|
||||
<None Remove="Embedded Resources\UnitTest1.capnp" />
|
||||
<Compile Include="CapnpMessageUnitTests.cs" />
|
||||
<Compile Include="CodeGeneratorUnitTests.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -34,9 +35,13 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Embedded Resources\Empty.capnp" />
|
||||
<EmbeddedResource Include="Embedded Resources\Empty1.capnp" />
|
||||
<EmbeddedResource Include="Embedded Resources\invalid.capnp" />
|
||||
<EmbeddedResource Include="Embedded Resources\null.bin" />
|
||||
<EmbeddedResource Include="Embedded Resources\test.capnp.bin" />
|
||||
<EmbeddedResource Include="Embedded Resources\test.cs" />
|
||||
<EmbeddedResource Include="Embedded Resources\UnitTest1.capnp" />
|
||||
<EmbeddedResource Include="Embedded Resources\UnitTest1.capnp.bin" />
|
||||
<EmbeddedResource Include="Embedded Resources\UnitTest2.capnp.bin" />
|
||||
<EmbeddedResource Include="Embedded Resources\UnitTest3.capnp.bin" />
|
||||
@ -47,6 +52,10 @@
|
||||
<EmbeddedResource Include="Embedded Resources\schema-with-offsets.capnp.bin" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CapnpC.CSharp.Generator\CapnpC.CSharp.Generator.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<SpecFlowFeatureFiles Update="CodeGenerator.feature">
|
||||
<Generator>SpecFlowSingleFileGenerator</Generator>
|
107
CapnpC.CSharp.Generator.Tests/CapnpMessageUnitTests.cs
Normal file
107
CapnpC.CSharp.Generator.Tests/CapnpMessageUnitTests.cs
Normal file
@ -0,0 +1,107 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace CapnpC.CSharp.Generator.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class CapnpMessageUnitTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void ParseError()
|
||||
{
|
||||
var msg = new CapnpMessage(@"f:\code\invalid.capnp:5:1: error: Parse error.");
|
||||
Assert.AreEqual(@"f:\code\invalid.capnp:5:1: error: Parse error.", msg.FullMessage);
|
||||
Assert.IsTrue(msg.IsParseSuccess);
|
||||
Assert.AreEqual(@"f:\code\invalid.capnp", msg.FileName);
|
||||
Assert.AreEqual(5, msg.Line);
|
||||
Assert.AreEqual(1, msg.Column);
|
||||
Assert.AreEqual(0, msg.EndColumn);
|
||||
Assert.AreEqual("error", msg.Category);
|
||||
Assert.AreEqual("Parse error.", msg.MessageText);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ColumnSpan()
|
||||
{
|
||||
var msg = new CapnpMessage(@"f:\code\invalid.capnp:10:7-8: error: Duplicate ordinal number.");
|
||||
Assert.IsTrue(msg.IsParseSuccess);
|
||||
Assert.AreEqual(@"f:\code\invalid.capnp", msg.FileName);
|
||||
Assert.AreEqual(10, msg.Line);
|
||||
Assert.AreEqual(7, msg.Column);
|
||||
Assert.AreEqual(8, msg.EndColumn);
|
||||
Assert.AreEqual("error", msg.Category);
|
||||
Assert.AreEqual("Duplicate ordinal number.", msg.MessageText);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NoSuchFile()
|
||||
{
|
||||
var msg = new CapnpMessage(@"C:\ProgramData\chocolatey\lib\capnproto\tools\capnproto-tools-win32-0.7.0\capnp.exe compile: doesnotexist.capnp: no such file");
|
||||
Assert.IsFalse(msg.IsParseSuccess);
|
||||
Assert.AreEqual(@"C:\ProgramData\chocolatey\lib\capnproto\tools\capnproto-tools-win32-0.7.0\capnp.exe compile: doesnotexist.capnp: no such file", msg.FullMessage);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NoId()
|
||||
{
|
||||
var msg = new CapnpMessage(@"empty.capnp:1:1: error: File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;");
|
||||
Assert.IsTrue(msg.IsParseSuccess);
|
||||
Assert.AreEqual("empty.capnp", msg.FileName);
|
||||
Assert.AreEqual(1, msg.Line);
|
||||
Assert.AreEqual(1, msg.Column);
|
||||
Assert.AreEqual("error", msg.Category);
|
||||
Assert.AreEqual("File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;", msg.MessageText);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AnnoyingNTFSAlternateDataStream1()
|
||||
{
|
||||
var msg = new CapnpMessage(@"3:2:1:1: error: File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;");
|
||||
Assert.IsTrue(msg.IsParseSuccess);
|
||||
Assert.AreEqual("3:2", msg.FileName);
|
||||
Assert.AreEqual(1, msg.Line);
|
||||
Assert.AreEqual(1, msg.Column);
|
||||
Assert.AreEqual("error", msg.Category);
|
||||
Assert.AreEqual("File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;", msg.MessageText);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AnnoyingNTFSAlternateDataStream2()
|
||||
{
|
||||
var msg = new CapnpMessage(@"c:\3:2:1:1: error: File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;");
|
||||
Assert.IsTrue(msg.IsParseSuccess);
|
||||
Assert.AreEqual(@"c:\3:2", msg.FileName);
|
||||
Assert.AreEqual(1, msg.Line);
|
||||
Assert.AreEqual(1, msg.Column);
|
||||
Assert.AreEqual("error", msg.Category);
|
||||
Assert.AreEqual("File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;", msg.MessageText);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AnnoyingNTFSAlternateDataStream3()
|
||||
{
|
||||
var msg = new CapnpMessage(@"\\?\c:\3:2:1:1: error: File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;");
|
||||
Assert.IsTrue(msg.IsParseSuccess);
|
||||
Assert.AreEqual(@"\\?\c:\3:2", msg.FileName);
|
||||
Assert.AreEqual(1, msg.Line);
|
||||
Assert.AreEqual(1, msg.Column);
|
||||
Assert.AreEqual("error", msg.Category);
|
||||
Assert.AreEqual("File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;", msg.MessageText);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AnnoyingNTFSAlternateDataStream4()
|
||||
{
|
||||
var msg = new CapnpMessage(@"1:2-3:10:7-8: error: Duplicate ordinal number.");
|
||||
Assert.IsTrue(msg.IsParseSuccess);
|
||||
Assert.AreEqual(@"1:2-3", msg.FileName);
|
||||
Assert.AreEqual(10, msg.Line);
|
||||
Assert.AreEqual(7, msg.Column);
|
||||
Assert.AreEqual(8, msg.EndColumn);
|
||||
Assert.AreEqual("error", msg.Category);
|
||||
Assert.AreEqual("Duplicate ordinal number.", msg.MessageText);
|
||||
}
|
||||
}
|
||||
}
|
48
CapnpC.CSharp.Generator.Tests/CodeGenerator.feature
Normal file
48
CapnpC.CSharp.Generator.Tests/CodeGenerator.feature
Normal file
@ -0,0 +1,48 @@
|
||||
Feature: CodeGenerator
|
||||
In order to ensure that the generator backend produces valid output
|
||||
As a contributor
|
||||
I want to get notified when there is any deviation from reference output
|
||||
|
||||
Scenario: Comparing backend output with reference
|
||||
Given I have a binary code generator request "test.capnp.bin"
|
||||
And my reference output is "test.cs"
|
||||
When I invoke capnpc-csharp
|
||||
Then the generated output must match the reference
|
||||
|
||||
Scenario Outline: Invalid binary code generator requests
|
||||
Given I have a binary code generator request <bin>
|
||||
When I invoke capnpc-csharp
|
||||
Then the invocation must fail
|
||||
|
||||
Examples:
|
||||
| bin |
|
||||
| null.bin |
|
||||
| test.cs |
|
||||
|
||||
Scenario: Combining frontend and backend
|
||||
Given capnp.exe is installed on my system
|
||||
And I have a schema "UnitTest1.capnp"
|
||||
When I try to generate code from that schema
|
||||
Then code generation must succeed
|
||||
|
||||
Scenario: Missing frontend
|
||||
Given capnp.exe is not installed on my system
|
||||
And I have a schema "UnitTest1.capnp"
|
||||
When I try to generate code from that schema
|
||||
Then the invocation must fail
|
||||
|
||||
Scenario: Schema without ID
|
||||
Given capnp.exe is installed on my system
|
||||
And I have a schema "Empty1.capnp"
|
||||
When I try to generate code from that schema
|
||||
Then the invocation must fail
|
||||
And the reason must be bad input
|
||||
And the error output must contain "File does not declare an ID"
|
||||
|
||||
Scenario: Multiple errors
|
||||
Given capnp.exe is installed on my system
|
||||
And I have a schema "invalid.capnp"
|
||||
When I try to generate code from that schema
|
||||
Then the invocation must fail
|
||||
And the reason must be bad input
|
||||
And the error output must contain multiple messages
|
@ -10,7 +10,7 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
#region Designer generated code
|
||||
#pragma warning disable
|
||||
namespace capnpc_csharp.Tests
|
||||
namespace CapnpC.CSharp.Generator.Tests
|
||||
{
|
||||
using TechTalk.SpecFlow;
|
||||
|
||||
@ -63,7 +63,7 @@ namespace capnpc_csharp.Tests
|
||||
if (((testRunner.FeatureContext != null)
|
||||
&& (testRunner.FeatureContext.FeatureInfo.Title != "CodeGenerator")))
|
||||
{
|
||||
global::capnpc_csharp.Tests.CodeGeneratorFeature.FeatureSetup(null);
|
||||
global::CapnpC.CSharp.Generator.Tests.CodeGeneratorFeature.FeatureSetup(null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,6 +149,98 @@ this.InvalidBinaryCodeGeneratorRequests("null.bin", ((string[])(null)));
|
||||
this.InvalidBinaryCodeGeneratorRequests("test.cs", ((string[])(null)));
|
||||
#line hidden
|
||||
}
|
||||
|
||||
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
|
||||
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Combining frontend and backend")]
|
||||
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
|
||||
public virtual void CombiningFrontendAndBackend()
|
||||
{
|
||||
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Combining frontend and backend", null, ((string[])(null)));
|
||||
#line 22
|
||||
this.ScenarioInitialize(scenarioInfo);
|
||||
this.ScenarioStart();
|
||||
#line 23
|
||||
testRunner.Given("capnp.exe is installed on my system", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
|
||||
#line 24
|
||||
testRunner.And("I have a schema \"UnitTest1.capnp\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
|
||||
#line 25
|
||||
testRunner.When("I try to generate code from that schema", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
|
||||
#line 26
|
||||
testRunner.Then("code generation must succeed", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
|
||||
#line hidden
|
||||
this.ScenarioCleanup();
|
||||
}
|
||||
|
||||
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
|
||||
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Missing frontend")]
|
||||
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
|
||||
public virtual void MissingFrontend()
|
||||
{
|
||||
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Missing frontend", null, ((string[])(null)));
|
||||
#line 28
|
||||
this.ScenarioInitialize(scenarioInfo);
|
||||
this.ScenarioStart();
|
||||
#line 29
|
||||
testRunner.Given("capnp.exe is not installed on my system", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
|
||||
#line 30
|
||||
testRunner.And("I have a schema \"UnitTest1.capnp\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
|
||||
#line 31
|
||||
testRunner.When("I try to generate code from that schema", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
|
||||
#line 32
|
||||
testRunner.Then("the invocation must fail", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
|
||||
#line hidden
|
||||
this.ScenarioCleanup();
|
||||
}
|
||||
|
||||
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
|
||||
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Schema without ID")]
|
||||
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
|
||||
public virtual void SchemaWithoutID()
|
||||
{
|
||||
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Schema without ID", null, ((string[])(null)));
|
||||
#line 34
|
||||
this.ScenarioInitialize(scenarioInfo);
|
||||
this.ScenarioStart();
|
||||
#line 35
|
||||
testRunner.Given("capnp.exe is installed on my system", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
|
||||
#line 36
|
||||
testRunner.And("I have a schema \"Empty1.capnp\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
|
||||
#line 37
|
||||
testRunner.When("I try to generate code from that schema", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
|
||||
#line 38
|
||||
testRunner.Then("the invocation must fail", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
|
||||
#line 39
|
||||
testRunner.And("the reason must be bad input", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
|
||||
#line 40
|
||||
testRunner.And("the error output must contain \"File does not declare an ID\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
|
||||
#line hidden
|
||||
this.ScenarioCleanup();
|
||||
}
|
||||
|
||||
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
|
||||
[Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Multiple errors")]
|
||||
[Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
|
||||
public virtual void MultipleErrors()
|
||||
{
|
||||
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Multiple errors", null, ((string[])(null)));
|
||||
#line 42
|
||||
this.ScenarioInitialize(scenarioInfo);
|
||||
this.ScenarioStart();
|
||||
#line 43
|
||||
testRunner.Given("capnp.exe is installed on my system", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
|
||||
#line 44
|
||||
testRunner.And("I have a schema \"invalid.capnp\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
|
||||
#line 45
|
||||
testRunner.When("I try to generate code from that schema", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
|
||||
#line 46
|
||||
testRunner.Then("the invocation must fail", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
|
||||
#line 47
|
||||
testRunner.And("the reason must be bad input", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
|
||||
#line 48
|
||||
testRunner.And("the error output must contain multiple messages", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
|
||||
#line hidden
|
||||
this.ScenarioCleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore
|
@ -1,13 +1,13 @@
|
||||
using Capnp;
|
||||
using Model = CapnpC.Model;
|
||||
using Generator = CapnpC.Generator;
|
||||
using CodeGeneratorRequest = CapnpC.Schema.CodeGeneratorRequest;
|
||||
using Model = CapnpC.CSharp.Generator.Model;
|
||||
using CodeGen = CapnpC.CSharp.Generator.CodeGen;
|
||||
using CodeGeneratorRequest = CapnpC.CSharp.Generator.Schema.CodeGeneratorRequest;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace capnpc_csharp.Tests
|
||||
namespace CapnpC.CSharp.Generator.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class CodeGeneratorUnitTests
|
||||
@ -98,13 +98,13 @@ namespace capnpc_csharp.Tests
|
||||
struct Run
|
||||
{
|
||||
public Model.SchemaModel Model;
|
||||
public Generator.CodeGenerator CodeGen;
|
||||
public CodeGen.CodeGenerator CodeGen;
|
||||
public Model.GenFile FirstFile;
|
||||
public string Code;
|
||||
}
|
||||
|
||||
static Generator.CodeGenerator NewGeneratorFor(Model.SchemaModel model)
|
||||
=> new Generator.CodeGenerator(model, new Generator.GeneratorOptions());
|
||||
static CodeGen.CodeGenerator NewGeneratorFor(Model.SchemaModel model)
|
||||
=> new CodeGen.CodeGenerator(model, new CodeGen.GeneratorOptions());
|
||||
|
||||
Run LoadAndGenerate(string inputName, int? testNum = null)
|
||||
{
|
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,11 @@
|
||||
@0xa5ac546b7bf6fbbc
|
||||
|
||||
enum Enumerant {
|
||||
byte @0;
|
||||
bit @1;
|
||||
}
|
||||
|
||||
struct Foo {
|
||||
foo @0: UInt8;
|
||||
bar @0: UInt8;
|
||||
}
|
149
CapnpC.CSharp.Generator.Tests/FeatureSteps/CodeGeneratorSteps.cs
Normal file
149
CapnpC.CSharp.Generator.Tests/FeatureSteps/CodeGeneratorSteps.cs
Normal file
@ -0,0 +1,149 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using TechTalk.SpecFlow;
|
||||
|
||||
namespace CapnpC.CSharp.Generator.Tests
|
||||
{
|
||||
[Binding]
|
||||
public class CodeGeneratorSteps
|
||||
{
|
||||
Stream _inputStream;
|
||||
string _inputSchemaFileName;
|
||||
string _inputSchema;
|
||||
string _referenceOutputContent;
|
||||
|
||||
GenerationResult _result;
|
||||
|
||||
public static Stream LoadResource(string name)
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
string[] names = assembly.GetManifestResourceNames();
|
||||
string urn = Array.Find(names, n => n.EndsWith(name, StringComparison.OrdinalIgnoreCase));
|
||||
Assert.IsNotNull(urn, $"Test specification error: {name} does not exist");
|
||||
return assembly.GetManifestResourceStream(urn);
|
||||
}
|
||||
|
||||
internal static bool IsCapnpExeInstalled()
|
||||
{
|
||||
using (var process = Process.Start("where", "capnp.exe"))
|
||||
{
|
||||
if (process == null)
|
||||
Assert.Fail("Unable to start 'where'");
|
||||
|
||||
process.WaitForExit();
|
||||
|
||||
return process.ExitCode == 0;
|
||||
}
|
||||
}
|
||||
|
||||
[Given(@"I have a binary code generator request ""(.*)""")]
|
||||
[Given(@"I have a binary code generator request (.*)")]
|
||||
public void GivenIHaveABinaryCodeGeneratorRequest(string binaryRequestFileName)
|
||||
{
|
||||
_inputStream = LoadResource(binaryRequestFileName);
|
||||
}
|
||||
|
||||
[Given(@"my reference output is ""(.*)""")]
|
||||
public void GivenMyReferenceOutputIs(string expectedOutputFileName)
|
||||
{
|
||||
using (var stream = LoadResource(expectedOutputFileName))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
_referenceOutputContent = reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
[When(@"I invoke capnpc-csharp")]
|
||||
public void WhenIInvokeCapnpc_Csharp()
|
||||
{
|
||||
using (_inputStream)
|
||||
{
|
||||
_result = CapnpCompilation.GenerateFromStream(_inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
[Then(@"the generated output must match the reference")]
|
||||
public void ThenTheGeneratedOutputMustMatchTheReference()
|
||||
{
|
||||
Assert.IsTrue(_result.IsSuccess, $"Tool invocation failed: {_result.Exception?.Message}");
|
||||
Assert.AreEqual(_referenceOutputContent, _result.GeneratedFiles.Single().GeneratedContent);
|
||||
}
|
||||
|
||||
[Then(@"the invocation must fail")]
|
||||
public void ThenTheInvocationMustFail()
|
||||
{
|
||||
Assert.IsFalse(_result.IsSuccess, "Tool invocation was supposed to fail, but it didn't");
|
||||
Assert.IsNotNull(_result.Exception, "Expected an exception");
|
||||
}
|
||||
|
||||
[Given(@"capnp\.exe is installed on my system")]
|
||||
public void GivenCapnp_ExeIsInstalledOnMySystem()
|
||||
{
|
||||
if (!IsCapnpExeInstalled())
|
||||
{
|
||||
Assert.Inconclusive("capnp.exe not found. Precondition of this test is not met.");
|
||||
}
|
||||
}
|
||||
|
||||
[Given(@"I have a schema ""(.*)""")]
|
||||
public void GivenIHaveASchema(string capnpFileName)
|
||||
{
|
||||
_inputSchemaFileName = capnpFileName;
|
||||
|
||||
using (var stream = LoadResource(capnpFileName))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
_inputSchema = reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
[When(@"I try to generate code from that schema")]
|
||||
public void WhenIWantToGenerateCodeFromThatSchema()
|
||||
{
|
||||
string path = Path.Combine(Path.GetTempPath(), _inputSchemaFileName);
|
||||
File.WriteAllText(path, _inputSchema);
|
||||
_result = CapnpCompilation.InvokeCapnpAndGenerate(new string[] { path });
|
||||
}
|
||||
|
||||
[Then(@"code generation must succeed")]
|
||||
public void ThenCodeGenerationMustSucceed()
|
||||
{
|
||||
Assert.IsNotNull(_result, "expected generation result");
|
||||
Assert.IsTrue(_result.IsSuccess, $"Tool invocation failed: {_result.Exception?.Message}");
|
||||
Assert.IsTrue(_result.GeneratedFiles.Count == 1, "Expected exactly one file");
|
||||
Assert.IsTrue(_result.GeneratedFiles[0].IsSuccess, $"Code generation failed: {_result.GeneratedFiles[0].Exception?.Message}");
|
||||
Assert.IsFalse(string.IsNullOrEmpty(_result.GeneratedFiles[0].GeneratedContent), "Expected non-empty generated content");
|
||||
}
|
||||
|
||||
[Given(@"capnp\.exe is not installed on my system")]
|
||||
public void GivenCapnp_ExeIsNotInstalledOnMySystem()
|
||||
{
|
||||
if (IsCapnpExeInstalled())
|
||||
{
|
||||
Assert.Inconclusive("capnp.exe found. Precondition of this test is not met.");
|
||||
}
|
||||
}
|
||||
|
||||
[Then(@"the reason must be bad input")]
|
||||
public void ThenTheReasonMustBeBadInput()
|
||||
{
|
||||
Assert.IsTrue(_result.ErrorCategory == CapnpProcessFailure.BadInput);
|
||||
}
|
||||
|
||||
[Then(@"the error output must contain ""(.*)""")]
|
||||
public void ThenTheErrorOutputMustContain(string p0)
|
||||
{
|
||||
Assert.IsTrue(_result.Messages.Any(m => m.FullMessage.Contains(p0)));
|
||||
}
|
||||
|
||||
[Then(@"the error output must contain multiple messages")]
|
||||
public void ThenTheErrorOutputMustContainMultipleMessages()
|
||||
{
|
||||
Assert.IsTrue(_result.Messages.Count >= 2);
|
||||
}
|
||||
}
|
||||
}
|
4
CapnpC.CSharp.Generator.Tests/capnpc-csharp.tests.csproj
Normal file
4
CapnpC.CSharp.Generator.Tests/capnpc-csharp.tests.csproj
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<TargetFrameworks>netstandard2.0;netcoreapp2.1</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,7 +1,12 @@
|
||||
using Capnp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
[assembly: InternalsVisibleTo("CapnpC.CSharp.Generator.Tests")]
|
||||
|
||||
namespace CapnpC.CSharp.Generator
|
||||
{
|
||||
@ -23,13 +28,7 @@ namespace CapnpC.CSharp.Generator
|
||||
|
||||
try
|
||||
{
|
||||
WireFrame segments;
|
||||
|
||||
using (input)
|
||||
{
|
||||
segments = Framing.ReadSegments(input);
|
||||
}
|
||||
|
||||
var segments = Framing.ReadSegments(input);
|
||||
var dec = DeserializerState.CreateRoot(segments);
|
||||
var reader = Schema.CodeGeneratorRequest.Reader.Create(dec);
|
||||
var model = Model.SchemaModel.Create(reader);
|
||||
@ -42,8 +41,66 @@ namespace CapnpC.CSharp.Generator
|
||||
}
|
||||
}
|
||||
|
||||
public static GenerationResult InvokeCapnpcAndGenerate()
|
||||
/// <summary>
|
||||
/// Invokes "capnp.exe -o-" with given additional arguments and redirects the output to the C# generator backend.
|
||||
/// </summary>
|
||||
/// <param name="arguments">additional command line arguments</param>
|
||||
/// <returns>generation result</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="arguments"/>is null</exception>
|
||||
public static GenerationResult InvokeCapnpAndGenerate(IEnumerable<string> arguments)
|
||||
{
|
||||
if (arguments == null)
|
||||
throw new ArgumentNullException(nameof(arguments));
|
||||
|
||||
using (var compiler = new Process())
|
||||
{
|
||||
var argList = new List<string>();
|
||||
argList.Add("compile");
|
||||
argList.Add($"-o-");
|
||||
argList.AddRange(arguments);
|
||||
|
||||
compiler.StartInfo.FileName = "capnp.exe";
|
||||
compiler.StartInfo.Arguments = string.Join(" ", argList);
|
||||
compiler.StartInfo.UseShellExecute = false;
|
||||
compiler.StartInfo.RedirectStandardOutput = true;
|
||||
compiler.StartInfo.RedirectStandardError = true;
|
||||
|
||||
try
|
||||
{
|
||||
compiler.Start();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
return new GenerationResult(exception)
|
||||
{
|
||||
ErrorCategory = CapnpProcessFailure.NotFound
|
||||
};
|
||||
}
|
||||
|
||||
var result = GenerateFromStream(compiler.StandardOutput.BaseStream);
|
||||
|
||||
var messageList = new List<CapnpMessage>();
|
||||
|
||||
while (!compiler.StandardError.EndOfStream)
|
||||
{
|
||||
messageList.Add(new CapnpMessage(compiler.StandardError.ReadLine()));
|
||||
}
|
||||
|
||||
result.Messages = messageList;
|
||||
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
compiler.WaitForExit();
|
||||
int exitCode = compiler.ExitCode;
|
||||
|
||||
if (exitCode == 0)
|
||||
result.ErrorCategory = CapnpProcessFailure.BadOutput;
|
||||
else
|
||||
result.ErrorCategory = CapnpProcessFailure.BadInput;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
116
CapnpC.CSharp.Generator/CapnpMessage.cs
Normal file
116
CapnpC.CSharp.Generator/CapnpMessage.cs
Normal file
@ -0,0 +1,116 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
[assembly: InternalsVisibleTo("CapnpC.CSharp.Generator.Tests")]
|
||||
|
||||
namespace CapnpC.CSharp.Generator
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a capnp.exe output message
|
||||
/// </summary>
|
||||
public class CapnpMessage
|
||||
{
|
||||
// capnp outputs look like this:
|
||||
// empty.capnp:1:1: error: File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;
|
||||
// f:\code\invalid.capnp:9:7-8: error: Ordinal @0 originally used here.
|
||||
// Parsing them is harder than it seems because the colon may be part of the file name (as in the example above).
|
||||
// And it becomes even worse! NTFS has a rarely used feature called "alternate data streams", identified by a colon:
|
||||
// f:\code\somefile:stream.capnp:9:7-8: error: Ordinal @0 originally used here.
|
||||
// What about a name which looks like a line number? (Hint: the 10 denotes the alternate data stream)
|
||||
// f:\code\somefile:10:9:7-8: error: Ordinal @0 originally used here.
|
||||
// Watching for the *last* colon as message separator does not work either, either. See first example.
|
||||
// Strategy: Watch out for the *last* occurence of pattern :[line]:[column]
|
||||
|
||||
static readonly Regex LineColumnRegex = new Regex(@":(?<Line>\d+):(?<Column>\d+)(-(?<EndColumn>\d+))?:", RegexOptions.Compiled | RegexOptions.RightToLeft);
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an instance from given message
|
||||
/// </summary>
|
||||
/// <param name="fullMessage">output message (one line)</param>
|
||||
public CapnpMessage(string fullMessage)
|
||||
{
|
||||
FullMessage = fullMessage;
|
||||
|
||||
var match = LineColumnRegex.Match(fullMessage);
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
IsParseSuccess = true;
|
||||
FileName = fullMessage.Substring(0, match.Index);
|
||||
var lineMatch = match.Groups["Line"];
|
||||
if (lineMatch.Success)
|
||||
{
|
||||
int.TryParse(lineMatch.Value, out int value);
|
||||
Line = value;
|
||||
}
|
||||
var columnMatch = match.Groups["Column"];
|
||||
if (columnMatch.Success)
|
||||
{
|
||||
int.TryParse(columnMatch.Value, out int value);
|
||||
Column = value;
|
||||
}
|
||||
var endColumnMatch = match.Groups["EndColumn"];
|
||||
if (endColumnMatch.Success)
|
||||
{
|
||||
int.TryParse(endColumnMatch.Value, out int value);
|
||||
EndColumn = value;
|
||||
}
|
||||
|
||||
int restIndex = match.Index + match.Length;
|
||||
int bodyIndex = fullMessage.IndexOf(':', restIndex);
|
||||
|
||||
if (bodyIndex >= 0)
|
||||
{
|
||||
Category = fullMessage.Substring(restIndex, bodyIndex - restIndex).Trim();
|
||||
MessageText = fullMessage.Substring(bodyIndex + 1).Trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Never observed "in the wild", just in case...
|
||||
Category = string.Empty;
|
||||
MessageText = fullMessage.Substring(restIndex).Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The original message
|
||||
/// </summary>
|
||||
public string FullMessage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the message could be decompsed into [filename]:[line]:[column]: [category]: [text]
|
||||
/// </summary>
|
||||
public bool IsParseSuccess { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Parsed file name (null iff not IsParseSuccess)
|
||||
/// </summary>
|
||||
public string FileName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Parsed line (0 if not IsParseSuccess)
|
||||
/// </summary>
|
||||
public int Line { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Parsed column (0 if not IsParseSuccess)
|
||||
/// </summary>
|
||||
public int Column { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Parsed end column (0 if there is none)
|
||||
/// </summary>
|
||||
public int EndColumn { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Parsed category (e.g. "error", null iff not IsParseSuccess)
|
||||
/// </summary>
|
||||
public string Category { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Parsed message body text (0 if not IsParseSuccess)
|
||||
/// </summary>
|
||||
public string MessageText { get; }
|
||||
}
|
||||
}
|
23
CapnpC.CSharp.Generator/CapnpProcessFailure.cs
Normal file
23
CapnpC.CSharp.Generator/CapnpProcessFailure.cs
Normal file
@ -0,0 +1,23 @@
|
||||
namespace CapnpC.CSharp.Generator
|
||||
{
|
||||
/// <summary>
|
||||
/// Why did invocation of capnpc.exe fail?
|
||||
/// </summary>
|
||||
public enum CapnpProcessFailure
|
||||
{
|
||||
/// <summary>
|
||||
/// Because capnpc.exe was not found. It is probably not installed.
|
||||
/// </summary>
|
||||
NotFound,
|
||||
|
||||
/// <summary>
|
||||
/// Because it exited with an error. Probably invalid .capnp file input.
|
||||
/// </summary>
|
||||
BadInput,
|
||||
|
||||
/// <summary>
|
||||
/// Because it produced an apparently bad code generation request.
|
||||
/// </summary>
|
||||
BadOutput
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
using static SyntaxHelpers;
|
||||
|
||||
class CodeGenerator
|
||||
internal class CodeGenerator
|
||||
{
|
||||
readonly SchemaModel _model;
|
||||
readonly GenNames _names;
|
||||
|
@ -40,5 +40,15 @@ namespace CapnpC.CSharp.Generator
|
||||
/// true iff generation was successful
|
||||
/// </summary>
|
||||
public bool IsSuccess => GeneratedFiles != null;
|
||||
|
||||
/// <summary>
|
||||
/// Messages read from standard error. Valid for both failure and success (capnp might spit out some warnings).
|
||||
/// </summary>
|
||||
public IReadOnlyList<CapnpMessage> Messages { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Error classification (if any error)
|
||||
/// </summary>
|
||||
public CapnpProcessFailure ErrorCategory { get; internal set; }
|
||||
}
|
||||
}
|
||||
|
42
CapnpC.CSharp.MsBuild.Generation.Tests/BuildEngineMock.cs
Normal file
42
CapnpC.CSharp.MsBuild.Generation.Tests/BuildEngineMock.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Microsoft.Build.Framework;
|
||||
|
||||
namespace CapnpC.CSharp.MsBuild.Generation.Tests
|
||||
{
|
||||
class BuildEngineMock : IBuildEngine
|
||||
{
|
||||
public bool ContinueOnError => true;
|
||||
|
||||
public int LineNumberOfTaskNode => 0;
|
||||
|
||||
public int ColumnNumberOfTaskNode => 0;
|
||||
|
||||
public string ProjectFileOfTaskNode => null;
|
||||
|
||||
public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void LogCustomEvent(CustomBuildEventArgs e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
}
|
||||
|
||||
public void LogErrorEvent(BuildErrorEventArgs e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
}
|
||||
|
||||
public void LogMessageEvent(BuildMessageEventArgs e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
}
|
||||
|
||||
public void LogWarningEvent(BuildWarningEventArgs e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="15.8.166" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CapnpC.CSharp.Generator.Tests\CapnpC.CSharp.Generator.Tests.csproj" />
|
||||
<ProjectReference Include="..\CapnpC.CSharp.MsBuild.Generation\CapnpC.CSharp.MsBuild.Generation.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -0,0 +1,51 @@
|
||||
using CapnpC.CSharp.Generator.Tests;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System.IO;
|
||||
|
||||
namespace CapnpC.CSharp.MsBuild.Generation.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class GenerateCapnpFileCodeBehindTaskTest
|
||||
{
|
||||
string LoadResourceContent(string name)
|
||||
{
|
||||
using (var stream = CodeGeneratorSteps.LoadResource("UnitTest1.capnp"))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ExecutionWithoutParameters()
|
||||
{
|
||||
var task = new GenerateCapnpFileCodeBehindTask();
|
||||
task.BuildEngine = new BuildEngineMock();
|
||||
task.Execute();
|
||||
// Should not crash. Should Execute() return true or false if there is no input?
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SimpleGeneration()
|
||||
{
|
||||
string capnpFile = "UnitTask1.capnp";
|
||||
string content = LoadResourceContent(capnpFile);
|
||||
string tmpPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
Directory.CreateDirectory(tmpPath);
|
||||
string capnpPath = Path.Combine(tmpPath, capnpFile);
|
||||
File.WriteAllText(capnpPath, content);
|
||||
|
||||
var task = new GenerateCapnpFileCodeBehindTask();
|
||||
task.BuildEngine = new BuildEngineMock();
|
||||
task.ProjectPath = Path.Combine(tmpPath, "doesnotneedtoexist.csproj");
|
||||
task.CapnpFiles = new ITaskItem[1] { new TaskItemMock() { ItemSpec = capnpPath } };
|
||||
Assert.IsTrue(task.Execute());
|
||||
Assert.IsNotNull(task.GeneratedFiles);
|
||||
Assert.AreEqual(1, task.GeneratedFiles.Length);
|
||||
string csPath = Path.Combine(tmpPath, task.GeneratedFiles[0].ItemSpec);
|
||||
Assert.AreEqual(capnpPath + ".cs", csPath);
|
||||
Assert.IsTrue(File.Exists(csPath));
|
||||
}
|
||||
}
|
||||
}
|
37
CapnpC.CSharp.MsBuild.Generation.Tests/TaskItemMock.cs
Normal file
37
CapnpC.CSharp.MsBuild.Generation.Tests/TaskItemMock.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System.Collections;
|
||||
using Microsoft.Build.Framework;
|
||||
|
||||
namespace CapnpC.CSharp.MsBuild.Generation.Tests
|
||||
{
|
||||
|
||||
class TaskItemMock : ITaskItem
|
||||
{
|
||||
public string ItemSpec { get; set; }
|
||||
|
||||
public ICollection MetadataNames => null;
|
||||
|
||||
public int MetadataCount => 0;
|
||||
|
||||
public IDictionary CloneCustomMetadata()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void CopyMetadataTo(ITaskItem destinationItem)
|
||||
{
|
||||
}
|
||||
|
||||
public string GetMetadata(string metadataName)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void RemoveMetadata(string metadataName)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetMetadata(string metadataName, string metadataValue)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
51
CapnpC.CSharp.MsBuild.Generation/CsFileGeneratorResult.cs
Normal file
51
CapnpC.CSharp.MsBuild.Generation/CsFileGeneratorResult.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using CapnpC.CSharp.Generator;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace CapnpC.CSharp.MsBuild.Generation
|
||||
{
|
||||
public class CsFileGeneratorResult
|
||||
{
|
||||
public CsFileGeneratorResult(FileGenerationResult generatorResult, string fileName, IReadOnlyList<CapnpMessage> messages)
|
||||
{
|
||||
if (generatorResult == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(generatorResult));
|
||||
}
|
||||
|
||||
Filename = fileName ?? throw new ArgumentNullException(nameof(fileName));
|
||||
|
||||
Error = generatorResult.Exception?.Message;
|
||||
GeneratedCode = generatorResult.GeneratedContent;
|
||||
Messages = messages;
|
||||
}
|
||||
|
||||
public CsFileGeneratorResult(string error)
|
||||
{
|
||||
Error = error;
|
||||
}
|
||||
|
||||
public CsFileGeneratorResult(string error, IReadOnlyList<CapnpMessage> messages)
|
||||
{
|
||||
Error = error;
|
||||
Messages = messages;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The error, if any.
|
||||
/// </summary>
|
||||
public string Error { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The generated code.
|
||||
/// </summary>
|
||||
public string GeneratedCode { get; }
|
||||
|
||||
public IReadOnlyList<CapnpMessage> Messages { get; }
|
||||
|
||||
public bool Success => Error == null;
|
||||
|
||||
public string Filename { get; }
|
||||
}
|
||||
}
|
@ -1,41 +1,85 @@
|
||||
using System;
|
||||
using CapnpC.CSharp.Generator;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Capnpc.Csharp.MsBuild.Generation
|
||||
namespace CapnpC.CSharp.MsBuild.Generation
|
||||
{
|
||||
public class CapnpCodeBehindGenerator : IDisposable
|
||||
{
|
||||
//private SpecFlowProject _specFlowProject;
|
||||
//private ITestGenerator _testGenerator;
|
||||
|
||||
public void InitializeProject(string projectPath)
|
||||
{
|
||||
//_specFlowProject = MsBuildProjectReader.LoadSpecFlowProjectFromMsBuild(Path.GetFullPath(projectPath), rootNamespace);
|
||||
|
||||
//var projectSettings = _specFlowProject.ProjectSettings;
|
||||
|
||||
//var testGeneratorFactory = new TestGeneratorFactory();
|
||||
|
||||
//_testGenerator = testGeneratorFactory.CreateGenerator(projectSettings, generatorPlugins);
|
||||
}
|
||||
|
||||
|
||||
public TestFileGeneratorResult GenerateCodeBehindFile(string capnpFile)
|
||||
public CsFileGeneratorResult GenerateCodeBehindFile(string capnpFile)
|
||||
{
|
||||
//var featureFileInput = new FeatureFileInput(featureFile);
|
||||
//var generatedFeatureFileName = Path.GetFileName(_testGenerator.GetTestFullPath(featureFileInput));
|
||||
// Works around a weird capnp.exe behavior: When the input file is empty, it will spit out an exception dump
|
||||
// instead of a parse error. But the parse error is nice because it contains a generated ID. We want the parse error!
|
||||
// Workaround: Generate a temporary file that contains a single line break (such that it is not empty...)
|
||||
try
|
||||
{
|
||||
if (File.Exists(capnpFile) && new FileInfo(capnpFile).Length == 0)
|
||||
{
|
||||
string tempFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".capnp");
|
||||
|
||||
//var testGeneratorResult = _testGenerator.GenerateTestFile(featureFileInput, new GenerationSettings());
|
||||
File.WriteAllText(tempFile, Environment.NewLine);
|
||||
try
|
||||
{
|
||||
return GenerateCodeBehindFile(tempFile);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return new TestFileGeneratorResult(
|
||||
new TestGeneratorResult() { GeneratedTestCode = "//dummy" },
|
||||
capnpFile + ".cs");
|
||||
var result = CapnpCompilation.InvokeCapnpAndGenerate(new string[] { capnpFile });
|
||||
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
if (result.GeneratedFiles.Count == 1)
|
||||
{
|
||||
return new CsFileGeneratorResult(
|
||||
result.GeneratedFiles[0],
|
||||
capnpFile + ".cs",
|
||||
result.Messages);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new CsFileGeneratorResult(
|
||||
"Code generation produced more than one file. This is not supported.",
|
||||
result.Messages);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (result.ErrorCategory)
|
||||
{
|
||||
case CapnpProcessFailure.NotFound:
|
||||
return new CsFileGeneratorResult("Unable to find capnp.exe - please install capnproto on your system first.");
|
||||
|
||||
case CapnpProcessFailure.BadInput:
|
||||
return new CsFileGeneratorResult("Invalid schema", result.Messages);
|
||||
|
||||
case CapnpProcessFailure.BadOutput:
|
||||
return new CsFileGeneratorResult(
|
||||
"Internal error: capnp.exe produced a binary code generation request which was not understood by the backend",
|
||||
result.Messages);
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("Invalid error category");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
//_testGenerator?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -5,16 +5,13 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Build.Utilities;
|
||||
|
||||
namespace Capnpc.Csharp.MsBuild.Generation
|
||||
namespace CapnpC.CSharp.MsBuild.Generation
|
||||
{
|
||||
public class CapnpFileCodeBehindGenerator : ICapnpcCsharpGenerator
|
||||
{
|
||||
private readonly FilePathGenerator _filePathGenerator;
|
||||
|
||||
public CapnpFileCodeBehindGenerator(TaskLoggingHelper log)
|
||||
{
|
||||
Log = log ?? throw new ArgumentNullException(nameof(log));
|
||||
_filePathGenerator = new FilePathGenerator();
|
||||
}
|
||||
|
||||
public TaskLoggingHelper Log { get; }
|
||||
@ -39,33 +36,44 @@ namespace Capnpc.Csharp.MsBuild.Generation
|
||||
|
||||
foreach (var capnpFile in capnpFiles)
|
||||
{
|
||||
var capnpFileItemSpec = capnpFile;
|
||||
var generatorResult = capnpCodeBehindGenerator.GenerateCodeBehindFile(capnpFileItemSpec);
|
||||
var generatorResult = capnpCodeBehindGenerator.GenerateCodeBehindFile(capnpFile);
|
||||
|
||||
if (!generatorResult.Success)
|
||||
{
|
||||
foreach (var error in generatorResult.Errors)
|
||||
if (!string.IsNullOrEmpty(generatorResult.Error))
|
||||
{
|
||||
//Log.LogError(
|
||||
// null,
|
||||
// null,
|
||||
// null,
|
||||
// featureFile,
|
||||
// error.Line,
|
||||
// error.LinePosition,
|
||||
// 0,
|
||||
// 0,
|
||||
// error.Message);
|
||||
Log.LogError("{0}", generatorResult.Error);
|
||||
}
|
||||
|
||||
if (generatorResult.Messages != null)
|
||||
{
|
||||
foreach (var message in generatorResult.Messages)
|
||||
{
|
||||
if (message.IsParseSuccess)
|
||||
{
|
||||
Log.LogError(
|
||||
subcategory: null,
|
||||
errorCode: null,
|
||||
helpKeyword: null,
|
||||
file: capnpFile,
|
||||
lineNumber: message.Line,
|
||||
columnNumber: message.Column,
|
||||
endLineNumber: message.Line,
|
||||
endColumnNumber: message.EndColumn == 0 ? message.Column : message.EndColumn,
|
||||
"{0}",
|
||||
message.MessageText);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.LogError("{0}", message.FullMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var targetFilePath = _filePathGenerator.GenerateFilePath(
|
||||
projectFolder,
|
||||
capnpFile,
|
||||
generatorResult.Filename);
|
||||
|
||||
var resultedFile = codeBehindWriter.WriteCodeBehindFile(targetFilePath, capnpFile, generatorResult);
|
||||
var resultedFile = codeBehindWriter.WriteCodeBehindFile(generatorResult.Filename, generatorResult);
|
||||
|
||||
yield return FileSystemHelper.GetRelativePath(resultedFile, projectFolder);
|
||||
}
|
||||
|
@ -2,9 +2,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net471;netcoreapp2.1</TargetFrameworks>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<NuspecFile>$(MSBuildThisFileDirectory)Capnpc.Csharp.MsBuild.Generation.nuspec</NuspecFile>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
|
||||
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<NoPackageAnalysis>true</NoPackageAnalysis>
|
||||
@ -12,6 +9,13 @@
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||
<FileVersion>1.0.0.0</FileVersion>
|
||||
<Version>1.0.0</Version>
|
||||
|
||||
<NuspecFile>$(MSBuildThisFileDirectory)CapnpC.CSharp.MsBuild.Generation.nuspec</NuspecFile>
|
||||
<NuspecProperties>version=$(Version)</NuspecProperties>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -54,6 +58,12 @@
|
||||
<Folder Include="FrameworkDependent\FullFramework\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CapnpC.CSharp.Generator\CapnpC.CSharp.Generator.csproj">
|
||||
<Private>true</Private>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Build">
|
||||
<HintPath>Microsoft.Build</HintPath>
|
||||
@ -68,11 +78,28 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="buildMultiTargeting\CapnpC.CSharp.MsBuild.Generation.props">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="build\CapnpC.CSharp.MsBuild.Generation.props">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="build\CapnpC.CSharp.MsBuild.Generation.targets">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="build\CapnpC.CSharp.MsBuild.Generation.tasks">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="build\CPS\Buildsystem\CpsExtension.DesignTime.targets">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="build\CPS\Buildsystem\Rules\CapnpFileType.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="build\CPS\Buildsystem\Rules\ProjectItemsSchema.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
<?xml version="1.0"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Capnpc.Csharp.MsBuild.Generation</id>
|
||||
<version>1.0.0</version>
|
||||
<title>Capnpc.Csharp.MsBuild.Generation</title>
|
||||
<id>CapnpC.CSharp.MsBuild.Generation</id>
|
||||
<version>$version$</version>
|
||||
<title>CapnpC.CSharp.MsBuild.Generation</title>
|
||||
<authors>Christian Köllner and contributors</authors>
|
||||
<owners>Christian Köllner</owners>
|
||||
<description>Package to enable the .capnp -> .cs file generation during build time</description>
|
||||
@ -15,6 +15,7 @@
|
||||
<tags>capnproto csharp msbuild</tags>
|
||||
<copyright>Christian Köllner and contributors</copyright>
|
||||
<dependencies>
|
||||
<dependency id="Capnp.Net.Runtime" version="1.0" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
|
@ -2,7 +2,7 @@
|
||||
using System.IO;
|
||||
using Microsoft.Build.Utilities;
|
||||
|
||||
namespace Capnpc.Csharp.MsBuild.Generation
|
||||
namespace CapnpC.CSharp.MsBuild.Generation
|
||||
{
|
||||
public class CodeBehindWriter
|
||||
{
|
||||
@ -13,14 +13,8 @@ namespace Capnpc.Csharp.MsBuild.Generation
|
||||
|
||||
public TaskLoggingHelper Log { get; }
|
||||
|
||||
public string WriteCodeBehindFile(string outputPath, string capnpFile, TestFileGeneratorResult testFileGeneratorResult)
|
||||
public string WriteCodeBehindFile(string outputPath, CsFileGeneratorResult testFileGeneratorResult)
|
||||
{
|
||||
//if (string.IsNullOrEmpty(testFileGeneratorResult.Filename))
|
||||
//{
|
||||
// Log?.LogWithNameTag(Log.LogError, $"{featureFile} has no generated filename");
|
||||
// return null;
|
||||
//}
|
||||
|
||||
string directoryPath = Path.GetDirectoryName(outputPath) ?? throw new InvalidOperationException();
|
||||
Log?.LogWithNameTag(Log.LogMessage, directoryPath);
|
||||
|
||||
@ -35,11 +29,6 @@ namespace Capnpc.Csharp.MsBuild.Generation
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Directory.Exists(directoryPath))
|
||||
{
|
||||
Directory.CreateDirectory(directoryPath);
|
||||
}
|
||||
|
||||
File.WriteAllText(outputPath, testFileGeneratorResult.GeneratedCode);
|
||||
}
|
||||
|
||||
|
@ -1,31 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Capnpc.Csharp.MsBuild.Generation
|
||||
{
|
||||
public class FilePathGenerator
|
||||
{
|
||||
public string GenerateFilePath(string projectFolder, string capnpFileName, string generatedCodeBehindFileName)
|
||||
{
|
||||
if (projectFolder is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectFolder));
|
||||
}
|
||||
|
||||
if (capnpFileName is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(capnpFileName));
|
||||
}
|
||||
|
||||
if (generatedCodeBehindFileName is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(generatedCodeBehindFileName));
|
||||
}
|
||||
|
||||
string featureFileFullPath = Path.GetFullPath(Path.Combine(projectFolder, capnpFileName));
|
||||
string featureFileDirPath = Path.GetDirectoryName(featureFileFullPath);
|
||||
|
||||
return Path.Combine(featureFileDirPath, generatedCodeBehindFileName);
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Capnpc.Csharp.MsBuild.Generation
|
||||
namespace CapnpC.CSharp.MsBuild.Generation
|
||||
{
|
||||
public static class FileSystemHelper
|
||||
{
|
||||
|
@ -7,7 +7,7 @@ using System.Resources;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
|
||||
namespace Capnpc.Csharp.MsBuild.Generation
|
||||
namespace CapnpC.CSharp.MsBuild.Generation
|
||||
{
|
||||
public class GenerateCapnpFileCodeBehindTask : Task
|
||||
{
|
||||
|
@ -5,7 +5,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Build.Utilities;
|
||||
|
||||
namespace Capnpc.Csharp.MsBuild.Generation
|
||||
namespace CapnpC.CSharp.MsBuild.Generation
|
||||
{
|
||||
public static class LogExtensions
|
||||
{
|
||||
@ -15,7 +15,7 @@ namespace Capnpc.Csharp.MsBuild.Generation
|
||||
string message,
|
||||
params object[] messageArgs)
|
||||
{
|
||||
string fullMessage = $"[SpecFlow] {message}";
|
||||
string fullMessage = $"[Cap'n Proto] {message}";
|
||||
loggingMethod?.Invoke(fullMessage, messageArgs);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Capnpc.Csharp.MsBuild.Generation
|
||||
namespace CapnpC.CSharp.MsBuild.Generation
|
||||
{
|
||||
public interface ICapnpcCsharpGenerator
|
||||
{
|
||||
|
@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Capnpc.Csharp.MsBuild.Generation
|
||||
{
|
||||
public class TestFileGeneratorResult
|
||||
{
|
||||
public TestFileGeneratorResult(TestGeneratorResult generatorResult, string fileName)
|
||||
{
|
||||
if (generatorResult == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(generatorResult));
|
||||
}
|
||||
|
||||
Filename = fileName ?? throw new ArgumentNullException(nameof(fileName));
|
||||
|
||||
Errors = generatorResult.Errors;
|
||||
IsUpToDate = generatorResult.IsUpToDate;
|
||||
GeneratedCode = generatorResult.GeneratedTestCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The errors, if any.
|
||||
/// </summary>
|
||||
public IEnumerable<TestGenerationError> Errors { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The generated file was up-to-date.
|
||||
/// </summary>
|
||||
public bool IsUpToDate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The generated code.
|
||||
/// </summary>
|
||||
public string GeneratedCode { get; }
|
||||
|
||||
public bool Success => Errors == null || !Errors.Any();
|
||||
|
||||
public string Filename { get; }
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
namespace Capnpc.Csharp.MsBuild.Generation
|
||||
{
|
||||
public class TestGenerationError
|
||||
{
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Capnpc.Csharp.MsBuild.Generation
|
||||
{
|
||||
public class TestGeneratorResult
|
||||
{
|
||||
public IEnumerable<TestGenerationError> Errors { get; internal set; }
|
||||
public bool IsUpToDate { get; internal set; }
|
||||
public string GeneratedTestCode { get; internal set; }
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@
|
||||
<StringProperty Name="Link" Visible="false" />
|
||||
<StringProperty Name="Generator" Visible="true" DisplayName="Custom Tool"/>
|
||||
|
||||
<StringProperty Name="WorkingDirectory" DisplayName="Working Directory" ReadOnly="false" Category="Misc">
|
||||
<!--<StringProperty Name="WorkingDirectory" DisplayName="Working Directory" ReadOnly="false" Category="Misc">
|
||||
<StringProperty.DataSource>
|
||||
<DataSource Persistence="ProjectFile" ItemType="WorkingDirectory" PersistedName="WorkingDirectory" />
|
||||
</StringProperty.DataSource>
|
||||
@ -39,5 +39,5 @@
|
||||
<StringProperty.DataSource>
|
||||
<DataSource Persistence="ProjectFile" ItemType="AdditionalOptions" PersistedName="AdditionalOptions" />
|
||||
</StringProperty.DataSource>
|
||||
</StringProperty>
|
||||
</StringProperty>-->
|
||||
</Rule>
|
@ -54,7 +54,7 @@
|
||||
- after deletion of a feature file
|
||||
- after pulling latest changes from version control with above changes
|
||||
-->
|
||||
<SpecFlowObsoleteCodeBehindFiles Include="**\*.capnp.cs" Exclude="@(CapnpFiles->'%(CodeBehindFile)')" />
|
||||
<CapnpCsharpObsoleteCodeBehindFiles Include="**\*.capnp.cs" Exclude="@(CapnpFiles->'%(CodeBehindFile)')" />
|
||||
|
||||
<!-- Support for Visual Studio Incremental Build
|
||||
https://github.com/techtalk/SpecFlow/issues/1319
|
||||
@ -69,9 +69,9 @@
|
||||
<PropertyGroup>
|
||||
<_CapnpcCsharp_TaskFolder Condition=" '$(MSBuildRuntimeType)' == 'Core' And '$(_CapnpcCsharp_TaskFolder)' == ''">netcoreapp2.1</_CapnpcCsharp_TaskFolder>
|
||||
<_CapnpcCsharp_TaskFolder Condition=" '$(MSBuildRuntimeType)' != 'Core' And '$(_CapnpcCsharp_TaskFolder)' == ''">net471</_CapnpcCsharp_TaskFolder>
|
||||
<_CapnpcCsharp_TaskAssembly Condition=" '$(_CapnpcCsharp_TaskAssembly)' == '' ">..\tasks\$(_CapnpcCsharp_TaskFolder)\Capnpc.Csharp.MsBuild.Generation.dll</_CapnpcCsharp_TaskAssembly>
|
||||
<_CapnpcCsharp_TaskAssembly Condition=" '$(_CapnpcCsharp_TaskAssembly)' == '' ">..\tasks\$(_CapnpcCsharp_TaskFolder)\CapnpC.CSharp.MsBuild.Generation.dll</_CapnpcCsharp_TaskAssembly>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="Capnpc.Csharp.MsBuild.Generation.tasks"/>
|
||||
<Import Project="CapnpC.CSharp.MsBuild.Generation.tasks"/>
|
||||
|
||||
</Project>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
|
||||
<Import Project="Capnpc.Csharp.MsBuild.Generation.props" Condition="'$(_CapnpcCsharpPropsImported)'==''"/>
|
||||
<Import Project="CapnpC.CSharp.MsBuild.Generation.props" Condition="'$(_CapnpcCsharpPropsImported)'==''"/>
|
||||
|
||||
<PropertyGroup Condition="'$(BuildServerMode)' == ''">
|
||||
<BuildServerMode Condition="'$(BuildingInsideVisualStudio)'=='true'">false</BuildServerMode>
|
||||
@ -48,9 +48,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<Target Name="WarnForFeatureCodeBehindFilesWithoutCorrespondingFeatureFile" AfterTargets="CoreCompile"
|
||||
<Target Name="WarnForCapnpCsharpCodeBehindFilesWithoutCorrespondingCapnpFile" AfterTargets="CoreCompile"
|
||||
Condition="'$(CapnpcCsharp_EnableWarnForFeatureCodeBehindFilesWithoutCorrespondingCapnpFile)' == 'true'">
|
||||
<Warning Text="For codebehind file '@(SpecFlowObsoleteCodeBehindFiles)', no capnp file was found." File="@(SpecFlowObsoleteCodeBehindFiles)" Condition="'@(SpecFlowObsoleteCodeBehindFiles)' != ''" />
|
||||
<Warning Text="For codebehind file '@(CapnpCsharpObsoleteCodeBehindFiles)', no capnp file was found." File="@(CapnpCsharpObsoleteCodeBehindFiles)" Condition="'@(CapnpCsharpObsoleteCodeBehindFiles)' != ''" />
|
||||
</Target>
|
||||
|
||||
|
||||
@ -100,7 +100,7 @@
|
||||
Condition="'%(Compile.Identity)' == '@(CapnpFiles->'%(CodeBehindFile)')'" />
|
||||
|
||||
<!-- remove files which got obsolete, typically after rename operation, or getting changes from source control -->
|
||||
<Compile Remove="@(SpecFlowObsoleteCodeBehindFiles)" />
|
||||
<Compile Remove="@(CapnpCsharpObsoleteCodeBehindFiles)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
@ -128,6 +128,6 @@
|
||||
- after deletion of a capnp file
|
||||
- after pulling latest changes from version control with above changes
|
||||
-->
|
||||
<Delete Files="@(SpecFlowObsoleteCodeBehindFiles)" ContinueOnError="true" />
|
||||
<Delete Files="@(CapnpCsharpObsoleteCodeBehindFiles)" ContinueOnError="true" />
|
||||
</Target>
|
||||
</Project>
|
@ -1,3 +1,3 @@
|
||||
<Project>
|
||||
<UsingTask TaskName="Capnpc.Csharp.MsBuild.Generation.GenerateCapnpFileCodeBehindTask" AssemblyFile="$(_CapnpcCsharp_TaskAssembly)" />
|
||||
<UsingTask TaskName="CapnpC.CSharp.MsBuild.Generation.GenerateCapnpFileCodeBehindTask" AssemblyFile="$(_CapnpcCsharp_TaskAssembly)" />
|
||||
</Project>
|
@ -1,5 +1,5 @@
|
||||
<Project TreatAsLocalProperty="TaskFolder;TaskAssembly">
|
||||
|
||||
<Import Project="..\build\Capnpc.Csharp.MsBuild.Generation.props"/>
|
||||
<Import Project="..\build\CapnpC.CSharp.MsBuild.Generation.props"/>
|
||||
|
||||
</Project>
|
@ -53,7 +53,7 @@ Solution/project structure is as follows:
|
||||
* Capnp.Net.Runtime is the runtime implementation, a .NET assembly.
|
||||
* capnpc-csharp is the generator backend for C# language.
|
||||
* Capnp.Net.Runtime.Tests is an MS Unit Testing assembly, containing - you guessed it - the test suite.
|
||||
* capnpc-csharp.tests contains the generator backend test suite.
|
||||
* CapnpC.CSharp.Generator.Tests contains the generator backend test suite.
|
||||
- CapnpCompatTest.sln compiles to a native x86 executable which depends on the original Cap'n Proto C++ implementation. It is (partially) required by the test suite for interoperability testing.
|
||||
|
||||
## Features
|
||||
|
@ -29,7 +29,7 @@ before_build:
|
||||
- cmd: dotnet restore ./Capnp.Net.Runtime.Tests/Capnp.Net.Runtime.Tests.Std20.csproj --verbosity m
|
||||
- cmd: dotnet restore ./Capnp.Net.Runtime.Tests.Core21/Capnp.Net.Runtime.Tests.Core21.csproj --verbosity m
|
||||
- cmd: dotnet restore ./capnpc-csharp/capnpc-csharp.csproj --verbosity m
|
||||
- cmd: dotnet restore ./capnpc-csharp.tests/capnpc-csharp.tests.csproj --verbosity m
|
||||
- cmd: dotnet restore ./CapnpC.CSharp.Generator.Tests/CapnpC.CSharp.Generator.Tests.csproj --verbosity m
|
||||
build_script:
|
||||
- cmd: msbuild ./Capnp.Net.sln /p:Configuration="Debug"
|
||||
- cmd: msbuild ./Capnp.Net.sln /p:Configuration="Release"
|
||||
@ -50,11 +50,12 @@ artifacts:
|
||||
type: NuGetPackage
|
||||
clone_depth: 1
|
||||
test_script:
|
||||
- cmd: vstest.console /logger:Appveyor /inIsolation capnpc-csharp.tests\bin\Release\netcoreapp2.2\capnpc-csharp.tests.dll
|
||||
- cmd: vstest.console /logger:Appveyor /inIsolation CapnpC.CSharp.Generator.Tests\bin\Release\netcoreapp2.2\CapnpC.CSharp.Generator.Tests.dll
|
||||
- cmd: cd %APPVEYOR_BUILD_FOLDER%\chocolatey\install
|
||||
- cmd: choco install capnpc-csharp --source=".;https://chocolatey.org/api/v2" --force -y
|
||||
- cmd: cd %APPVEYOR_BUILD_FOLDER%\install-test
|
||||
- cmd: compile-test
|
||||
- cmd: vstest.console /logger:Appveyor /inIsolation CapnpC.CSharp.Generator.Tests\bin\Release\netcoreapp2.2\CapnpC.CSharp.Generator.Tests.dll
|
||||
- cmd: choco uninstall capnpc-csharp -y
|
||||
- cmd: notinstalled-test
|
||||
- cmd: cd %APPVEYOR_BUILD_FOLDER%\chocolatey\install
|
||||
|
@ -1,20 +0,0 @@
|
||||
Feature: CodeGenerator
|
||||
In order to ensure that the generator backend produces valid output
|
||||
As a contributor
|
||||
I want to get notified when there is any deviation from reference output
|
||||
|
||||
Scenario: Comparing backend output with reference
|
||||
Given I have a binary code generator request "test.capnp.bin"
|
||||
And my reference output is "test.cs"
|
||||
When I invoke capnpc-csharp
|
||||
Then the generated output must match the reference
|
||||
|
||||
Scenario Outline: Invalid binary code generator requests
|
||||
Given I have a binary code generator request <bin>
|
||||
When I invoke capnpc-csharp
|
||||
Then the invocation must fail
|
||||
|
||||
Examples:
|
||||
| bin |
|
||||
| null.bin |
|
||||
| test.cs |
|
@ -1,83 +0,0 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using TechTalk.SpecFlow;
|
||||
|
||||
namespace capnpc_csharp.Tests
|
||||
{
|
||||
[Binding]
|
||||
public class CodeGeneratorSteps
|
||||
{
|
||||
Stream _inputStream;
|
||||
string _referenceOutputContent;
|
||||
string _exceptedOutputFileName;
|
||||
string _actualGeneratedContent;
|
||||
bool _success;
|
||||
Exception _generateException;
|
||||
|
||||
internal static Stream LoadResource(string name)
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
string[] names = assembly.GetManifestResourceNames();
|
||||
string urn = Array.Find(names, n => n.EndsWith(name, StringComparison.OrdinalIgnoreCase));
|
||||
Assert.IsNotNull(urn, $"Test specification error: {name} does not exist");
|
||||
return assembly.GetManifestResourceStream(urn);
|
||||
}
|
||||
|
||||
[Given(@"I have a binary code generator request ""(.*)""")]
|
||||
[Given(@"I have a binary code generator request (.*)")]
|
||||
public void GivenIHaveABinaryCodeGeneratorRequest(string binaryRequestFileName)
|
||||
{
|
||||
_inputStream = LoadResource(binaryRequestFileName);
|
||||
}
|
||||
|
||||
[Given(@"my reference output is ""(.*)""")]
|
||||
public void GivenMyReferenceOutputIs(string expectedOutputFileName)
|
||||
{
|
||||
_exceptedOutputFileName = expectedOutputFileName;
|
||||
using (var stream = LoadResource(expectedOutputFileName))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
_referenceOutputContent = reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
[When(@"I invoke capnpc-csharp")]
|
||||
public void WhenIInvokeCapnpc_Csharp()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (_inputStream)
|
||||
{
|
||||
string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
Directory.CreateDirectory(tempDir);
|
||||
Environment.CurrentDirectory = tempDir;
|
||||
|
||||
CapnpC.Program.GenerateFromStream(_inputStream);
|
||||
|
||||
string outPath = Path.Combine(tempDir, _exceptedOutputFileName);
|
||||
_actualGeneratedContent = File.ReadAllText(outPath);
|
||||
_success = true;
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_generateException = exception;
|
||||
}
|
||||
}
|
||||
|
||||
[Then(@"the generated output must match the reference")]
|
||||
public void ThenTheGeneratedOutputMustMatchTheReference()
|
||||
{
|
||||
Assert.IsTrue(_success, $"Code generation failed: {_generateException?.Message}");
|
||||
Assert.AreEqual(_referenceOutputContent, _actualGeneratedContent);
|
||||
}
|
||||
|
||||
[Then(@"the invocation must fail")]
|
||||
public void ThenTheInvocationMustFail()
|
||||
{
|
||||
Assert.IsFalse(_success, "Code generation was supposed to fail, but it didn't");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,182 +0,0 @@
|
||||
namespace CapnpC.Generator
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using CapnpC.Model;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
using static SyntaxHelpers;
|
||||
|
||||
class CodeGenerator
|
||||
{
|
||||
readonly SchemaModel _model;
|
||||
readonly GenNames _names;
|
||||
readonly CommonSnippetGen _commonGen;
|
||||
readonly DomainClassSnippetGen _domClassGen;
|
||||
readonly ReaderSnippetGen _readerGen;
|
||||
readonly WriterSnippetGen _writerGen;
|
||||
readonly InterfaceSnippetGen _interfaceGen;
|
||||
|
||||
public CodeGenerator(SchemaModel model, GeneratorOptions options)
|
||||
{
|
||||
_model = model;
|
||||
_names = new GenNames(options);
|
||||
_commonGen = new CommonSnippetGen(_names);
|
||||
_domClassGen = new DomainClassSnippetGen(_names);
|
||||
_readerGen = new ReaderSnippetGen(_names);
|
||||
_writerGen = new WriterSnippetGen(_names);
|
||||
_interfaceGen = new InterfaceSnippetGen(_names);
|
||||
}
|
||||
|
||||
internal GenNames GetNames() => _names;
|
||||
|
||||
IEnumerable<MemberDeclarationSyntax> TransformEnum(TypeDefinition def)
|
||||
{
|
||||
yield return _commonGen.MakeEnum(def);
|
||||
}
|
||||
|
||||
IEnumerable<TypeParameterSyntax> MakeTypeParameters(TypeDefinition def)
|
||||
{
|
||||
foreach (string name in def.GenericParameters)
|
||||
{
|
||||
yield return TypeParameter(_names.GetGenericTypeParameter(name).Identifier);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<TypeParameterConstraintClauseSyntax> MakeTypeParameterConstraints(TypeDefinition def)
|
||||
{
|
||||
foreach (string name in def.GenericParameters)
|
||||
{
|
||||
yield return TypeParameterConstraintClause(
|
||||
_names.GetGenericTypeParameter(name).IdentifierName)
|
||||
.AddConstraints(ClassOrStructConstraint(SyntaxKind.ClassConstraint));
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<MemberDeclarationSyntax> TransformStruct(TypeDefinition def)
|
||||
{
|
||||
var topDecl = ClassDeclaration(_names.MakeTypeName(def).Identifier)
|
||||
.AddModifiers(Public)
|
||||
.AddBaseListTypes(SimpleBaseType(Type<Capnp.ICapnpSerializable>()));
|
||||
|
||||
if (def.GenericParameters.Count > 0)
|
||||
{
|
||||
topDecl = topDecl
|
||||
.AddTypeParameterListParameters(MakeTypeParameters(def).ToArray())
|
||||
.AddConstraintClauses(MakeTypeParameterConstraints(def).ToArray());
|
||||
}
|
||||
|
||||
if (def.UnionInfo != null)
|
||||
{
|
||||
topDecl = topDecl.AddMembers(_commonGen.MakeUnionSelectorEnum(def));
|
||||
}
|
||||
|
||||
topDecl = topDecl.AddMembers(_domClassGen.MakeDomainClassMembers(def));
|
||||
topDecl = topDecl.AddMembers(_readerGen.MakeReaderStruct(def));
|
||||
topDecl = topDecl.AddMembers(_writerGen.MakeWriterStruct(def));
|
||||
|
||||
foreach (var nestedGroup in def.NestedGroups)
|
||||
{
|
||||
topDecl = topDecl.AddMembers(Transform(nestedGroup).ToArray());
|
||||
}
|
||||
|
||||
foreach (var nestedDef in def.NestedTypes)
|
||||
{
|
||||
topDecl = topDecl.AddMembers(Transform(nestedDef).ToArray());
|
||||
}
|
||||
|
||||
yield return topDecl;
|
||||
}
|
||||
|
||||
IEnumerable<MemberDeclarationSyntax> TransformInterface(TypeDefinition def)
|
||||
{
|
||||
yield return _interfaceGen.MakeInterface(def);
|
||||
yield return _interfaceGen.MakeProxy(def);
|
||||
yield return _interfaceGen.MakeSkeleton(def);
|
||||
|
||||
if (_interfaceGen.RequiresPipeliningSupport(def))
|
||||
{
|
||||
yield return _interfaceGen.MakePipeliningSupport(def);
|
||||
}
|
||||
|
||||
if (def.NestedTypes.Any())
|
||||
{
|
||||
var ns = ClassDeclaration(
|
||||
_names.MakeTypeName(def, NameUsage.Namespace).ToString())
|
||||
.AddModifiers(Public, Static);
|
||||
|
||||
if (def.GenericParameters.Count > 0)
|
||||
{
|
||||
ns = ns
|
||||
.AddTypeParameterListParameters(MakeTypeParameters(def).ToArray())
|
||||
.AddConstraintClauses(MakeTypeParameterConstraints(def).ToArray());
|
||||
}
|
||||
|
||||
foreach (var nestedDef in def.NestedTypes)
|
||||
{
|
||||
ns = ns.AddMembers(Transform(nestedDef).ToArray());
|
||||
}
|
||||
|
||||
yield return ns;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<MemberDeclarationSyntax> Transform(TypeDefinition def)
|
||||
{
|
||||
switch (def.Tag)
|
||||
{
|
||||
case TypeTag.Enum:
|
||||
return TransformEnum(def);
|
||||
|
||||
case TypeTag.Group:
|
||||
case TypeTag.Struct:
|
||||
return TransformStruct(def);
|
||||
|
||||
case TypeTag.Interface:
|
||||
return TransformInterface(def);
|
||||
|
||||
default:
|
||||
throw new NotSupportedException($"Cannot declare type of kind {def.Tag} here");
|
||||
}
|
||||
}
|
||||
|
||||
internal string Transform(GenFile file)
|
||||
{
|
||||
NameSyntax topNamespace = GenNames.NamespaceName(file.Namespace) ?? _names.TopNamespace;
|
||||
|
||||
var ns = NamespaceDeclaration(topNamespace);
|
||||
|
||||
foreach (var def in file.NestedTypes)
|
||||
{
|
||||
ns = ns.AddMembers(Transform(def).ToArray());
|
||||
}
|
||||
|
||||
var cu = CompilationUnit().AddUsings(
|
||||
UsingDirective(ParseName("Capnp")),
|
||||
UsingDirective(ParseName("Capnp.Rpc")),
|
||||
UsingDirective(ParseName("System")),
|
||||
UsingDirective(ParseName("System.Collections.Generic")),
|
||||
UsingDirective(ParseName("System.Threading")),
|
||||
UsingDirective(ParseName("System.Threading.Tasks")));
|
||||
|
||||
cu = cu.AddMembers(ns);
|
||||
|
||||
return cu.NormalizeWhitespace().ToFullString();
|
||||
}
|
||||
|
||||
public void Generate()
|
||||
{
|
||||
foreach (var file in _model.FilesToGenerate)
|
||||
{
|
||||
string content = Transform(file);
|
||||
string path = Path.ChangeExtension(file.Name, ".cs");
|
||||
File.WriteAllText(path, content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using CapnpC.Model;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
using static CapnpC.Generator.SyntaxHelpers;
|
||||
|
||||
namespace CapnpC.Generator
|
||||
{
|
||||
class CommonSnippetGen
|
||||
{
|
||||
readonly GenNames _names;
|
||||
|
||||
public CommonSnippetGen(GenNames names)
|
||||
{
|
||||
_names = names;
|
||||
}
|
||||
|
||||
public EnumDeclarationSyntax MakeUnionSelectorEnum(TypeDefinition def)
|
||||
{
|
||||
var whichEnum = EnumDeclaration(_names.UnionDiscriminatorEnum.ToString())
|
||||
.AddModifiers(Public)
|
||||
.AddBaseListTypes(SimpleBaseType(Type<ushort>()));
|
||||
|
||||
var discFields = def.Fields.Where(f => f.DiscValue.HasValue);
|
||||
|
||||
foreach (var discField in discFields)
|
||||
{
|
||||
whichEnum = whichEnum.AddMembers(
|
||||
EnumMemberDeclaration(_names.GetCodeIdentifier(discField).Identifier)
|
||||
.WithEqualsValue(
|
||||
EqualsValueClause(LiteralExpression(
|
||||
SyntaxKind.NumericLiteralExpression,
|
||||
Literal(discField.DiscValue.Value)))));
|
||||
}
|
||||
|
||||
var ndecl = EnumMemberDeclaration(_names.UnionDiscriminatorUndefined.ToString()).WithEqualsValue(
|
||||
EqualsValueClause(
|
||||
LiteralExpression(
|
||||
SyntaxKind.NumericLiteralExpression,
|
||||
Literal(Schema.Field.Reader.NoDiscriminant))));
|
||||
|
||||
whichEnum = whichEnum.AddMembers(ndecl);
|
||||
|
||||
return whichEnum;
|
||||
}
|
||||
|
||||
public EnumDeclarationSyntax MakeEnum(TypeDefinition def)
|
||||
{
|
||||
var decl = EnumDeclaration(def.Name)
|
||||
.AddModifiers(Public)
|
||||
.AddBaseListTypes(SimpleBaseType(Type<ushort>()));
|
||||
|
||||
foreach (var enumerant in def.Enumerants.OrderBy(e => e.CodeOrder))
|
||||
{
|
||||
var mdecl = EnumMemberDeclaration(enumerant.Literal);
|
||||
|
||||
if (enumerant.Ordinal.HasValue)
|
||||
{
|
||||
mdecl = mdecl.WithEqualsValue(
|
||||
EqualsValueClause(
|
||||
LiteralExpression(
|
||||
SyntaxKind.NumericLiteralExpression,
|
||||
Literal(enumerant.Ordinal.Value))));
|
||||
}
|
||||
|
||||
decl = decl.AddMembers(mdecl);
|
||||
}
|
||||
|
||||
return decl;
|
||||
}
|
||||
|
||||
public static IEnumerable<SyntaxNodeOrToken> MakeCommaSeparatedList(IEnumerable<ExpressionSyntax> expressions)
|
||||
{
|
||||
bool first = true;
|
||||
|
||||
foreach (var expr in expressions)
|
||||
{
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
yield return Token(SyntaxKind.CommaToken);
|
||||
|
||||
yield return expr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,972 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using CapnpC.Model;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
using static CapnpC.Generator.SyntaxHelpers;
|
||||
|
||||
namespace CapnpC.Generator
|
||||
{
|
||||
class DomainClassSnippetGen
|
||||
{
|
||||
readonly GenNames _names;
|
||||
|
||||
public DomainClassSnippetGen(GenNames names)
|
||||
{
|
||||
_names = names;
|
||||
}
|
||||
|
||||
MemberDeclarationSyntax MakeUnionField(Field field)
|
||||
{
|
||||
var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.DomainClassNullable);
|
||||
|
||||
switch (field.Type.Tag)
|
||||
{
|
||||
case TypeTag.Void:
|
||||
return null;
|
||||
|
||||
default:
|
||||
return PropertyDeclaration(type,
|
||||
_names.GetCodeIdentifier(field).Identifier)
|
||||
.AddModifiers(Public).AddAccessorListAccessors(
|
||||
AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
|
||||
.WithExpressionBody(
|
||||
ArrowExpressionClause(
|
||||
ConditionalExpression(
|
||||
BinaryExpression(
|
||||
SyntaxKind.EqualsExpression,
|
||||
_names.UnionDiscriminatorField.IdentifierName,
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.UnionDiscriminatorEnum.IdentifierName,
|
||||
_names.GetCodeIdentifier(field).IdentifierName)),
|
||||
CastExpression(type,
|
||||
_names.UnionContentField.IdentifierName),
|
||||
LiteralExpression(
|
||||
SyntaxKind.NullLiteralExpression))))
|
||||
.WithSemicolonToken(
|
||||
Token(SyntaxKind.SemicolonToken)),
|
||||
AccessorDeclaration(
|
||||
SyntaxKind.SetAccessorDeclaration)
|
||||
.WithBody(
|
||||
Block(
|
||||
ExpressionStatement(
|
||||
AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
_names.UnionDiscriminatorField.IdentifierName,
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.UnionDiscriminatorEnum.IdentifierName,
|
||||
_names.GetCodeIdentifier(field).IdentifierName))),
|
||||
ExpressionStatement(
|
||||
AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
_names.UnionContentField.IdentifierName,
|
||||
IdentifierName("value"))))));
|
||||
}
|
||||
}
|
||||
|
||||
MemberDeclarationSyntax MakeStructField(Field field)
|
||||
{
|
||||
if (field.Type.Tag == TypeTag.Void)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var prop = PropertyDeclaration(_names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.DomainClass),
|
||||
_names.GetCodeIdentifier(field).Identifier)
|
||||
.AddModifiers(Public).AddAccessorListAccessors(
|
||||
AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
|
||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
|
||||
AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
|
||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)));
|
||||
|
||||
if (field.DefaultValueIsExplicit && field.Type.IsValueType)
|
||||
{
|
||||
prop = prop.WithInitializer(
|
||||
EqualsValueClause(MakeDefaultValue(field)))
|
||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
MemberDeclarationSyntax MakeUnionDiscriminatorField()
|
||||
{
|
||||
return FieldDeclaration(
|
||||
VariableDeclaration(_names.UnionDiscriminatorEnum.IdentifierName)
|
||||
.AddVariables(
|
||||
VariableDeclarator(_names.UnionDiscriminatorField.Identifier)
|
||||
.WithInitializer(
|
||||
EqualsValueClause(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.UnionDiscriminatorEnum.IdentifierName,
|
||||
_names.UnionDiscriminatorUndefined.IdentifierName)))))
|
||||
.AddModifiers(Private);
|
||||
}
|
||||
|
||||
MemberDeclarationSyntax MakeUnionContentField()
|
||||
{
|
||||
return FieldDeclaration(
|
||||
VariableDeclaration(SyntaxHelpers.Type<object>())
|
||||
.WithVariables(
|
||||
SingletonSeparatedList<VariableDeclaratorSyntax>(
|
||||
VariableDeclarator(_names.UnionContentField.Identifier))))
|
||||
.AddModifiers(Private);
|
||||
}
|
||||
|
||||
IEnumerable<ExpressionSyntax> MakeInitializerAssignments(Value structValue, TypeDefinition scope)
|
||||
{
|
||||
foreach (var fieldValue in structValue.Fields)
|
||||
{
|
||||
var valueExpr = MakeValue(fieldValue.Item2, scope);
|
||||
if (valueExpr == null)
|
||||
continue;
|
||||
|
||||
yield return AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
_names.GetCodeIdentifier(fieldValue.Item1).IdentifierName,
|
||||
valueExpr);
|
||||
}
|
||||
}
|
||||
|
||||
ExpressionSyntax MakeValue(Value value, TypeDefinition scope)
|
||||
{
|
||||
switch (value.Type.Tag)
|
||||
{
|
||||
case TypeTag.AnyEnum:
|
||||
return LiteralExpression(
|
||||
SyntaxKind.NumericLiteralExpression, Literal((ushort)value.ScalarValue));
|
||||
|
||||
case TypeTag.Bool:
|
||||
|
||||
if ((bool)value.ScalarValue)
|
||||
return LiteralExpression(SyntaxKind.TrueLiteralExpression);
|
||||
else
|
||||
return LiteralExpression(SyntaxKind.FalseLiteralExpression);
|
||||
|
||||
case TypeTag.Data:
|
||||
return ArrayCreationExpression(ArrayType(
|
||||
PredefinedType(Token(SyntaxKind.ByteKeyword)))
|
||||
.WithRankSpecifiers(
|
||||
SingletonList<ArrayRankSpecifierSyntax>(
|
||||
ArrayRankSpecifier(
|
||||
SingletonSeparatedList<ExpressionSyntax>(
|
||||
OmittedArraySizeExpression())))))
|
||||
.WithInitializer(
|
||||
InitializerExpression(
|
||||
SyntaxKind.ArrayInitializerExpression)
|
||||
.AddExpressions(value.Items.Select(v => MakeValue(v, scope)).ToArray()));
|
||||
|
||||
case TypeTag.Enum:
|
||||
return MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.MakeTypeSyntax(value.Type, scope, TypeUsage.NotRelevant),
|
||||
IdentifierName(value.GetEnumerant().Literal));
|
||||
|
||||
case TypeTag.F32:
|
||||
switch ((float)value.ScalarValue)
|
||||
{
|
||||
case float.NaN:
|
||||
return MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName("float"),
|
||||
IdentifierName(nameof(float.NaN)));
|
||||
|
||||
case float.NegativeInfinity:
|
||||
return MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName("float"),
|
||||
IdentifierName(nameof(float.NegativeInfinity)));
|
||||
|
||||
case float.PositiveInfinity:
|
||||
return MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName("float"),
|
||||
IdentifierName(nameof(float.PositiveInfinity)));
|
||||
|
||||
default:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression,
|
||||
Literal((float)value.ScalarValue));
|
||||
}
|
||||
|
||||
case TypeTag.F64:
|
||||
switch ((double)value.ScalarValue)
|
||||
{
|
||||
case double.NaN:
|
||||
return MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName("double"),
|
||||
IdentifierName(nameof(double.NaN)));
|
||||
|
||||
case double.NegativeInfinity:
|
||||
return MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName("double"),
|
||||
IdentifierName(nameof(double.NegativeInfinity)));
|
||||
|
||||
case double.PositiveInfinity:
|
||||
return MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName("double"),
|
||||
IdentifierName(nameof(double.PositiveInfinity)));
|
||||
|
||||
default:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression,
|
||||
Literal((double)value.ScalarValue));
|
||||
}
|
||||
|
||||
case TypeTag.S8:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression,
|
||||
Literal((sbyte)value.ScalarValue));
|
||||
|
||||
case TypeTag.S16:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression,
|
||||
Literal((short)value.ScalarValue));
|
||||
|
||||
case TypeTag.S32:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression,
|
||||
Literal((int)value.ScalarValue));
|
||||
|
||||
case TypeTag.S64:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression,
|
||||
Literal((long)value.ScalarValue));
|
||||
|
||||
case TypeTag.U8:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression,
|
||||
Literal((byte)value.ScalarValue));
|
||||
|
||||
case TypeTag.U16:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression,
|
||||
Literal((ushort)value.ScalarValue));
|
||||
|
||||
case TypeTag.U32:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression,
|
||||
Literal((uint)value.ScalarValue));
|
||||
|
||||
case TypeTag.U64:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression,
|
||||
Literal((ulong)value.ScalarValue));
|
||||
|
||||
case TypeTag.Text:
|
||||
value.Decode();
|
||||
return value.ScalarValue == null ?
|
||||
LiteralExpression(SyntaxKind.NullLiteralExpression) :
|
||||
LiteralExpression(SyntaxKind.StringLiteralExpression,
|
||||
Literal((string)value.ScalarValue));
|
||||
|
||||
case TypeTag.Group:
|
||||
case TypeTag.Struct:
|
||||
value.Decode();
|
||||
|
||||
return ObjectCreationExpression(
|
||||
_names.MakeTypeSyntax(value.Type, scope, TypeUsage.DomainClass))
|
||||
.WithArgumentList(ArgumentList())
|
||||
.WithInitializer(
|
||||
InitializerExpression(
|
||||
SyntaxKind.ObjectInitializerExpression)
|
||||
.AddExpressions(MakeInitializerAssignments(value, scope).ToArray()));
|
||||
|
||||
case TypeTag.ListPointer:
|
||||
// TBD
|
||||
return LiteralExpression(SyntaxKind.NullLiteralExpression);
|
||||
|
||||
case TypeTag.List when value.Type.ElementType.Tag == TypeTag.Void:
|
||||
value.Decode();
|
||||
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal((int)value.VoidListCount));
|
||||
|
||||
case TypeTag.List:
|
||||
value.Decode();
|
||||
|
||||
return ArrayCreationExpression(ArrayType(
|
||||
_names.MakeTypeSyntax(value.Type.ElementType, scope, TypeUsage.DomainClass))
|
||||
.WithRankSpecifiers(
|
||||
SingletonList<ArrayRankSpecifierSyntax>(
|
||||
ArrayRankSpecifier(
|
||||
SingletonSeparatedList<ExpressionSyntax>(
|
||||
OmittedArraySizeExpression())))))
|
||||
.WithInitializer(
|
||||
InitializerExpression(
|
||||
SyntaxKind.ArrayInitializerExpression)
|
||||
.AddExpressions(value.Items.Select(v => MakeValue(v, scope)).ToArray()));
|
||||
|
||||
case TypeTag.AnyPointer:
|
||||
case TypeTag.CapabilityPointer:
|
||||
// TBD
|
||||
return null;
|
||||
|
||||
case TypeTag.Interface:
|
||||
return null;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
ExpressionSyntax MakeDefaultValue(Field field)
|
||||
{
|
||||
if (field.DefaultValueIsExplicit)
|
||||
{
|
||||
return MakeValue(field.DefaultValue, field.DeclaringType);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (field.Type.Tag)
|
||||
{
|
||||
case TypeTag.AnyEnum:
|
||||
case TypeTag.S16:
|
||||
case TypeTag.S32:
|
||||
case TypeTag.S64:
|
||||
case TypeTag.S8:
|
||||
case TypeTag.U16:
|
||||
case TypeTag.U32:
|
||||
case TypeTag.U64:
|
||||
case TypeTag.U8:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0));
|
||||
|
||||
case TypeTag.AnyPointer:
|
||||
case TypeTag.CapabilityPointer:
|
||||
case TypeTag.Data:
|
||||
case TypeTag.Group:
|
||||
case TypeTag.Interface:
|
||||
case TypeTag.List:
|
||||
case TypeTag.ListPointer:
|
||||
case TypeTag.Struct:
|
||||
case TypeTag.StructPointer:
|
||||
case TypeTag.Text:
|
||||
return LiteralExpression(SyntaxKind.NullLiteralExpression);
|
||||
|
||||
case TypeTag.Bool:
|
||||
return LiteralExpression(SyntaxKind.FalseLiteralExpression);
|
||||
|
||||
case TypeTag.Enum:
|
||||
return MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.NotRelevant),
|
||||
_names.UnionDiscriminatorUndefined.IdentifierName);
|
||||
|
||||
case TypeTag.F32:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0.0f));
|
||||
|
||||
case TypeTag.F64:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0.0));
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<SwitchSectionSyntax> MakeUnionDiscriminatorSetter(TypeDefinition def)
|
||||
{
|
||||
var unionFields = def.Fields.Where(f => f.DiscValue.HasValue);
|
||||
|
||||
foreach (var unionField in unionFields)
|
||||
{
|
||||
var section = SwitchSection()
|
||||
.WithLabels(
|
||||
SingletonList<SwitchLabelSyntax>(
|
||||
CaseSwitchLabel(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.UnionDiscriminatorEnum.IdentifierName,
|
||||
_names.GetCodeIdentifier(unionField).IdentifierName))));
|
||||
|
||||
if (unionField.Type.Tag != TypeTag.Void)
|
||||
{
|
||||
section = section.AddStatements(
|
||||
ExpressionStatement(
|
||||
AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
_names.UnionContentField.IdentifierName,
|
||||
MakeDefaultValue(unionField))));
|
||||
}
|
||||
|
||||
section = section.AddStatements(BreakStatement());
|
||||
|
||||
yield return section;
|
||||
}
|
||||
}
|
||||
|
||||
MemberDeclarationSyntax MakeUnionDiscriminatorProperty(TypeDefinition def)
|
||||
{
|
||||
return PropertyDeclaration(_names.UnionDiscriminatorEnum.IdentifierName,
|
||||
_names.UnionDiscriminatorProp.Identifier)
|
||||
.AddModifiers(Public).AddAccessorListAccessors(
|
||||
AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
|
||||
.WithExpressionBody(
|
||||
ArrowExpressionClause(_names.UnionDiscriminatorField.IdentifierName))
|
||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
|
||||
AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
|
||||
.WithBody(
|
||||
Block(
|
||||
IfStatement(
|
||||
BinaryExpression(
|
||||
SyntaxKind.EqualsExpression,
|
||||
IdentifierName("value"),
|
||||
_names.UnionDiscriminatorField.IdentifierName),
|
||||
ReturnStatement()),
|
||||
ExpressionStatement(
|
||||
AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
_names.UnionDiscriminatorField.IdentifierName,
|
||||
IdentifierName("value"))),
|
||||
SwitchStatement(IdentifierName("value"))
|
||||
.WithOpenParenToken(
|
||||
Token(SyntaxKind.OpenParenToken))
|
||||
.WithCloseParenToken(
|
||||
Token(SyntaxKind.CloseParenToken))
|
||||
.AddSections(MakeUnionDiscriminatorSetter(def).ToArray()))));
|
||||
}
|
||||
|
||||
MemberDeclarationSyntax MakeField(Field field)
|
||||
{
|
||||
if (field.DiscValue.HasValue)
|
||||
return MakeUnionField(field);
|
||||
else
|
||||
return MakeStructField(field);
|
||||
}
|
||||
|
||||
ExpressionSyntax MakeListSerializeParticle(Model.Type type, ExpressionSyntax writer, ExpressionSyntax domain)
|
||||
{
|
||||
string s = $"_s{type.GetRank().Item1}";
|
||||
string v = $"_v{type.GetRank().Item1}";
|
||||
|
||||
switch (type.ElementType?.Tag)
|
||||
{
|
||||
case TypeTag.List:
|
||||
case TypeTag.ListPointer:
|
||||
case TypeTag.Struct:
|
||||
case TypeTag.Group:
|
||||
case TypeTag.StructPointer:
|
||||
case TypeTag.Data:
|
||||
|
||||
return InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
writer,
|
||||
IdentifierName(nameof(Capnp.ListOfPrimitivesSerializer<int>.Init))))
|
||||
.AddArgumentListArguments(
|
||||
Argument(domain),
|
||||
Argument(
|
||||
ParenthesizedLambdaExpression(
|
||||
MakeComplexSerializeParticle(
|
||||
type.ElementType,
|
||||
IdentifierName(s),
|
||||
IdentifierName(v)))
|
||||
.AddParameterListParameters(
|
||||
Parameter(Identifier(s)),
|
||||
Parameter(Identifier(v)))));
|
||||
|
||||
default:
|
||||
return InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
writer,
|
||||
IdentifierName(nameof(Capnp.ListOfPrimitivesSerializer<int>.Init))))
|
||||
.AddArgumentListArguments(Argument(domain));
|
||||
}
|
||||
}
|
||||
|
||||
ExpressionSyntax MakeComplexSerializeParticle(Model.Type type, ExpressionSyntax writer, ExpressionSyntax domain)
|
||||
{
|
||||
switch (type.Tag)
|
||||
{
|
||||
case TypeTag.Data:
|
||||
case TypeTag.List:
|
||||
return MakeListSerializeParticle(type, writer, domain);
|
||||
|
||||
case TypeTag.Struct:
|
||||
case TypeTag.Group:
|
||||
return ConditionalAccessExpression(domain,
|
||||
InvocationExpression(MemberBindingExpression(_names.SerializeMethod.IdentifierName))
|
||||
.AddArgumentListArguments(Argument(writer)));
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
StatementSyntax MakeSerializeMethodFieldAssignment(Field field)
|
||||
{
|
||||
var writerProp = MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.WriterParameter.IdentifierName,
|
||||
_names.GetCodeIdentifier(field).IdentifierName);
|
||||
|
||||
switch (field.Type.Tag)
|
||||
{
|
||||
case TypeTag.Bool:
|
||||
case TypeTag.Enum:
|
||||
case TypeTag.F32:
|
||||
case TypeTag.F64:
|
||||
case TypeTag.S16:
|
||||
case TypeTag.S32:
|
||||
case TypeTag.S64:
|
||||
case TypeTag.S8:
|
||||
case TypeTag.U16:
|
||||
case TypeTag.U32:
|
||||
case TypeTag.U64:
|
||||
case TypeTag.U8:
|
||||
case TypeTag.AnyEnum:
|
||||
case TypeTag.List when field.Type.Tag == TypeTag.Void:
|
||||
if (field.DiscValue.HasValue)
|
||||
{
|
||||
return ExpressionStatement(
|
||||
AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
writerProp,
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.GetCodeIdentifier(field).IdentifierName,
|
||||
IdentifierName(nameof(Nullable<int>.Value)))));
|
||||
}
|
||||
else
|
||||
{
|
||||
return ExpressionStatement(
|
||||
AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
writerProp,
|
||||
_names.GetCodeIdentifier(field).IdentifierName));
|
||||
}
|
||||
|
||||
case TypeTag.AnyPointer:
|
||||
case TypeTag.ListPointer:
|
||||
case TypeTag.StructPointer:
|
||||
return ExpressionStatement(
|
||||
InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.WriterParameter.IdentifierName,
|
||||
_names.GetCodeIdentifier(field).IdentifierName),
|
||||
IdentifierName(nameof(Capnp.DynamicSerializerState.SetObject))))
|
||||
.AddArgumentListArguments(
|
||||
Argument(_names.GetCodeIdentifier(field).IdentifierName)));
|
||||
|
||||
case TypeTag.CapabilityPointer:
|
||||
case TypeTag.Interface:
|
||||
return ExpressionStatement(
|
||||
AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
writerProp,
|
||||
_names.GetCodeIdentifier(field).IdentifierName));
|
||||
|
||||
case TypeTag.Text:
|
||||
return ExpressionStatement(
|
||||
AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
writerProp,
|
||||
_names.GetCodeIdentifier(field).IdentifierName));
|
||||
|
||||
case TypeTag.Data:
|
||||
case TypeTag.List:
|
||||
case TypeTag.Struct:
|
||||
case TypeTag.Group:
|
||||
return ExpressionStatement(
|
||||
MakeComplexSerializeParticle(
|
||||
field.Type,
|
||||
writerProp,
|
||||
_names.GetCodeIdentifier(field).IdentifierName));
|
||||
|
||||
case TypeTag.Void:
|
||||
return null;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
StatementSyntax MakeApplyDefaultsMethodFieldAssignment(Field field)
|
||||
{
|
||||
var lhs = _names.GetCodeIdentifier(field).IdentifierName;
|
||||
var rhs = MakeDefaultValue(field);
|
||||
|
||||
if (rhs == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ExpressionStatement(
|
||||
AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
lhs,
|
||||
BinaryExpression(SyntaxKind.CoalesceExpression,
|
||||
lhs, rhs)));
|
||||
}
|
||||
|
||||
ExpressionSyntax MakeInnerStructListConversion(ExpressionSyntax context, TypeSyntax elementType)
|
||||
{
|
||||
return InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
context,
|
||||
IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList))))
|
||||
.AddArgumentListArguments(Argument(
|
||||
SimpleLambdaExpression(Parameter(Identifier("_")),
|
||||
InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(nameof(Capnp.CapnpSerializable)),
|
||||
GenericName(nameof(Capnp.CapnpSerializable.Create))
|
||||
.AddTypeArgumentListArguments(elementType)))
|
||||
.AddArgumentListArguments(Argument(IdentifierName("_"))))));
|
||||
}
|
||||
|
||||
ExpressionSyntax MakeStructListConversion(ExpressionSyntax context, TypeSyntax elementType, int rank)
|
||||
{
|
||||
if (rank == 1)
|
||||
{
|
||||
return MakeInnerStructListConversion(context, elementType);
|
||||
}
|
||||
|
||||
string lambdaVarName = $"_{rank}";
|
||||
|
||||
return InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
context,
|
||||
IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList))))
|
||||
.AddArgumentListArguments(Argument(
|
||||
SimpleLambdaExpression(
|
||||
Parameter(Identifier(lambdaVarName)),
|
||||
MakeStructListConversion(IdentifierName(lambdaVarName), elementType, rank - 1))));
|
||||
}
|
||||
|
||||
ExpressionSyntax MakeAnyListConversion(ExpressionSyntax context)
|
||||
{
|
||||
return InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
context,
|
||||
IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList))))
|
||||
.AddArgumentListArguments(Argument(
|
||||
SimpleLambdaExpression(
|
||||
Parameter(Identifier("_")),
|
||||
CastExpression(Type<object>(), IdentifierName("_")))));
|
||||
}
|
||||
|
||||
ExpressionSyntax MakeDeserializeMethodRightHandSide(Field field)
|
||||
{
|
||||
switch (field.Type.Tag)
|
||||
{
|
||||
case TypeTag.Struct:
|
||||
case TypeTag.Group:
|
||||
case TypeTag.StructPointer:
|
||||
case TypeTag.AnyPointer:
|
||||
return InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(nameof(Capnp.CapnpSerializable)),
|
||||
GenericName(nameof(Capnp.CapnpSerializable.Create))
|
||||
.AddTypeArgumentListArguments(
|
||||
_names.MakeTypeSyntax(
|
||||
field.Type,
|
||||
field.DeclaringType,
|
||||
TypeUsage.DomainClass))))
|
||||
.AddArgumentListArguments(Argument(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.ReaderParameter.IdentifierName,
|
||||
_names.GetCodeIdentifier(field).IdentifierName)));
|
||||
|
||||
case TypeTag.Void:
|
||||
return null;
|
||||
|
||||
case TypeTag.List:
|
||||
(var rank, var elementType) = field.Type.GetRank();
|
||||
if (elementType.Tag != TypeTag.Struct)
|
||||
break;
|
||||
|
||||
return MakeStructListConversion(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.ReaderParameter.IdentifierName,
|
||||
_names.GetCodeIdentifier(field).IdentifierName),
|
||||
_names.MakeTypeSyntax(elementType, field.DeclaringType, TypeUsage.DomainClass),
|
||||
rank);
|
||||
|
||||
case TypeTag.ListPointer:
|
||||
return MakeAnyListConversion(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.ReaderParameter.IdentifierName,
|
||||
_names.GetCodeIdentifier(field).IdentifierName));
|
||||
}
|
||||
|
||||
return MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.ReaderParameter.IdentifierName,
|
||||
_names.GetCodeIdentifier(field).IdentifierName);
|
||||
}
|
||||
|
||||
IEnumerable<SwitchSectionSyntax> MakeSerializeMethodSwitchSections(TypeDefinition def)
|
||||
{
|
||||
var unionFields = def.Fields.Where(f => f.DiscValue.HasValue);
|
||||
|
||||
foreach (var unionField in unionFields)
|
||||
{
|
||||
var section = SwitchSection()
|
||||
.AddLabels(
|
||||
CaseSwitchLabel(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.UnionDiscriminatorEnum.IdentifierName,
|
||||
_names.GetCodeIdentifier(unionField).IdentifierName)));
|
||||
|
||||
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(BreakStatement());
|
||||
|
||||
yield return section;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<SwitchSectionSyntax> MakeDeserializeMethodSwitchSections(TypeDefinition def)
|
||||
{
|
||||
var unionFields = def.Fields.Where(f => f.DiscValue.HasValue);
|
||||
|
||||
foreach (var unionField in unionFields)
|
||||
{
|
||||
var section = SwitchSection()
|
||||
.AddLabels(
|
||||
CaseSwitchLabel(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.UnionDiscriminatorEnum.IdentifierName,
|
||||
_names.GetCodeIdentifier(unionField).IdentifierName)));
|
||||
|
||||
switch (unionField.Type.Tag)
|
||||
{
|
||||
case TypeTag.Void:
|
||||
section = section.AddStatements(
|
||||
ExpressionStatement(AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
_names.UnionDiscriminatorProp.IdentifierName,
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.ReaderParameter.IdentifierName,
|
||||
_names.UnionDiscriminatorProp.IdentifierName))));
|
||||
break;
|
||||
|
||||
default:
|
||||
section = section.AddStatements(
|
||||
ExpressionStatement(AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
_names.GetCodeIdentifier(unionField).IdentifierName,
|
||||
MakeDeserializeMethodRightHandSide(unionField))));
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
section = section.AddStatements(BreakStatement());
|
||||
|
||||
yield return section;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<StatementSyntax> MakeSerializeStatements(TypeDefinition def)
|
||||
{
|
||||
if (def.UnionInfo != null)
|
||||
{
|
||||
yield return ExpressionStatement(AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.WriterParameter.IdentifierName,
|
||||
_names.UnionDiscriminatorProp.IdentifierName),
|
||||
_names.UnionDiscriminatorProp.IdentifierName));
|
||||
|
||||
yield return SwitchStatement(_names.UnionDiscriminatorProp.IdentifierName)
|
||||
.WithOpenParenToken(Token(SyntaxKind.OpenParenToken))
|
||||
.WithCloseParenToken(Token(SyntaxKind.CloseParenToken))
|
||||
.AddSections(MakeSerializeMethodSwitchSections(def).ToArray());
|
||||
}
|
||||
|
||||
var nondiscFields = def.Fields.Where(f => !f.DiscValue.HasValue && f.Type.Tag != TypeTag.Void);
|
||||
|
||||
foreach (var field in nondiscFields)
|
||||
{
|
||||
var asmt = MakeSerializeMethodFieldAssignment(field);
|
||||
|
||||
if (asmt != null)
|
||||
{
|
||||
yield return asmt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<StatementSyntax> MakeApplyDefaultsStatements(TypeDefinition def)
|
||||
{
|
||||
var relevantFields = def.Fields.Where(
|
||||
f => !f.DiscValue.HasValue &&
|
||||
f.Type.Tag != TypeTag.Void &&
|
||||
f.DefaultValueIsExplicit &&
|
||||
!f.Type.IsValueType);
|
||||
|
||||
foreach (var field in relevantFields)
|
||||
{
|
||||
var asmt = MakeApplyDefaultsMethodFieldAssignment(field);
|
||||
|
||||
if (asmt != null)
|
||||
{
|
||||
yield return asmt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MemberDeclarationSyntax MakeSerializeMethod(TypeDefinition def)
|
||||
{
|
||||
return MethodDeclaration(PredefinedType(
|
||||
Token(SyntaxKind.VoidKeyword)),
|
||||
_names.SerializeMethod.Identifier)
|
||||
.AddModifiers(Public)
|
||||
.AddParameterListParameters(
|
||||
Parameter(_names.WriterParameter.Identifier)
|
||||
.WithType(_names.WriterStruct.IdentifierName))
|
||||
.AddBodyStatements(MakeSerializeStatements(def).ToArray());
|
||||
}
|
||||
|
||||
MemberDeclarationSyntax MakeSerializeInterfaceMethod()
|
||||
{
|
||||
return MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)),
|
||||
Identifier(nameof(Capnp.ICapnpSerializable.Serialize)))
|
||||
.WithExplicitInterfaceSpecifier(
|
||||
ExplicitInterfaceSpecifier(IdentifierName(nameof(Capnp.ICapnpSerializable))))
|
||||
.AddParameterListParameters(
|
||||
Parameter(_names.AnonymousParameter.Identifier)
|
||||
.WithType(Type<Capnp.SerializerState>()))
|
||||
.AddBodyStatements(
|
||||
ExpressionStatement(
|
||||
InvocationExpression(_names.SerializeMethod.IdentifierName)
|
||||
.AddArgumentListArguments(
|
||||
Argument(
|
||||
InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.AnonymousParameter.IdentifierName,
|
||||
GenericName(Identifier(nameof(Capnp.SerializerState.Rewrap)))
|
||||
.AddTypeArgumentListArguments(_names.WriterStruct.IdentifierName)))))));
|
||||
}
|
||||
|
||||
MemberDeclarationSyntax MakeApplyDefaultsMethod(TypeDefinition def)
|
||||
{
|
||||
return MethodDeclaration(PredefinedType(
|
||||
Token(SyntaxKind.VoidKeyword)),
|
||||
_names.ApplyDefaultsMethod.Identifier)
|
||||
.AddModifiers(Public)
|
||||
.AddBodyStatements(MakeApplyDefaultsStatements(def).ToArray());
|
||||
}
|
||||
|
||||
IEnumerable<StatementSyntax> MakeDeserializeStatements(TypeDefinition def)
|
||||
{
|
||||
var relevantFields = def.Fields.Where(
|
||||
f => !f.DiscValue.HasValue &&
|
||||
f.Type.Tag != TypeTag.Void);
|
||||
|
||||
foreach (var field in relevantFields)
|
||||
{
|
||||
var rhs = MakeDeserializeMethodRightHandSide(field);
|
||||
|
||||
if (rhs != null)
|
||||
{
|
||||
yield return ExpressionStatement(AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
_names.GetCodeIdentifier(field).IdentifierName,
|
||||
rhs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MemberDeclarationSyntax MakeDeserializeMethod(TypeDefinition def)
|
||||
{
|
||||
var stmts = new List<StatementSyntax>();
|
||||
|
||||
stmts.Add(LocalDeclarationStatement(
|
||||
VariableDeclaration(IdentifierName("var"))
|
||||
.AddVariables(
|
||||
VariableDeclarator(_names.ReaderParameter.Identifier)
|
||||
.WithInitializer(
|
||||
EqualsValueClause(
|
||||
InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.ReaderStruct.IdentifierName,
|
||||
_names.ReaderCreateMethod.IdentifierName))
|
||||
.AddArgumentListArguments(
|
||||
Argument(_names.AnonymousParameter.IdentifierName)))))));
|
||||
|
||||
|
||||
if (def.UnionInfo != null)
|
||||
{
|
||||
stmts.Add(SwitchStatement(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.ReaderParameter.IdentifierName,
|
||||
_names.UnionDiscriminatorProp.IdentifierName))
|
||||
.WithOpenParenToken(Token(SyntaxKind.OpenParenToken))
|
||||
.WithCloseParenToken(Token(SyntaxKind.CloseParenToken))
|
||||
.AddSections(MakeDeserializeMethodSwitchSections(def).ToArray()));
|
||||
}
|
||||
|
||||
stmts.AddRange(MakeDeserializeStatements(def));
|
||||
stmts.Add(ExpressionStatement(InvocationExpression(
|
||||
_names.ApplyDefaultsMethod.IdentifierName)));
|
||||
|
||||
return MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)),
|
||||
Identifier(nameof(Capnp.ICapnpSerializable.Deserialize)))
|
||||
.WithExplicitInterfaceSpecifier(
|
||||
ExplicitInterfaceSpecifier(IdentifierName(nameof(Capnp.ICapnpSerializable))))
|
||||
.AddParameterListParameters(
|
||||
Parameter(_names.AnonymousParameter.Identifier)
|
||||
.WithType(Type<Capnp.DeserializerState>()))
|
||||
.AddBodyStatements(stmts.ToArray());
|
||||
}
|
||||
|
||||
IEnumerable<MemberDeclarationSyntax> EnumerateDomainClassMembers(TypeDefinition def)
|
||||
{
|
||||
yield return MakeDeserializeMethod(def);
|
||||
|
||||
if (def.UnionInfo != null)
|
||||
{
|
||||
yield return MakeUnionDiscriminatorField();
|
||||
yield return MakeUnionContentField();
|
||||
yield return MakeUnionDiscriminatorProperty(def);
|
||||
}
|
||||
|
||||
yield return MakeSerializeMethod(def);
|
||||
yield return MakeSerializeInterfaceMethod();
|
||||
yield return MakeApplyDefaultsMethod(def);
|
||||
|
||||
foreach (var field in def.Fields)
|
||||
{
|
||||
var decl = MakeField(field);
|
||||
|
||||
if (decl != null)
|
||||
yield return decl;
|
||||
}
|
||||
}
|
||||
|
||||
public MemberDeclarationSyntax[] MakeDomainClassMembers(TypeDefinition def)
|
||||
{
|
||||
return EnumerateDomainClassMembers(def).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,601 +0,0 @@
|
||||
using CapnpC.Model;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
|
||||
namespace CapnpC.Generator
|
||||
{
|
||||
enum NameUsage
|
||||
{
|
||||
Default,
|
||||
Interface,
|
||||
Proxy,
|
||||
Skeleton,
|
||||
Namespace
|
||||
}
|
||||
|
||||
enum TypeUsage
|
||||
{
|
||||
NotRelevant,
|
||||
DomainClass,
|
||||
DomainClassNullable,
|
||||
Reader,
|
||||
Writer
|
||||
}
|
||||
|
||||
class GenNames
|
||||
{
|
||||
readonly Dictionary<Field, Name> _fieldNameMap = new Dictionary<Field, Name>();
|
||||
|
||||
public NameSyntax TopNamespace { get; set; }
|
||||
public Name ReaderStruct { get; }
|
||||
public Name ReaderParameter { get; }
|
||||
public Name WriterParameter { get; }
|
||||
public Name WriterStruct { get; }
|
||||
public Name ReaderCreateMethod { get; }
|
||||
public Name ReaderContextField { get; }
|
||||
public Name ContextParameter { get; }
|
||||
public Name GroupReaderContextArg { get; }
|
||||
public Name GroupWriterContextArg { get; }
|
||||
public Name UnionDiscriminatorEnum { get; }
|
||||
public Name UnionDiscriminatorProp { get; }
|
||||
public Name UnionDiscriminatorUndefined { get; }
|
||||
public Name UnionDiscriminatorField { get; }
|
||||
public Name UnionContentField { get; }
|
||||
public Name AnonymousParameter { get; }
|
||||
public Name CancellationTokenParameter { get; }
|
||||
public Name ParamsLocal { get; }
|
||||
public Name DeserializerLocal { get; }
|
||||
public Name SerializerLocal { get; }
|
||||
public Name ResultLocal { get; }
|
||||
public Name SerializeMethod { get; }
|
||||
public Name ApplyDefaultsMethod { get; }
|
||||
public Name InstLocalName { get; }
|
||||
public string ParamsStructFormat { get; }
|
||||
public string ResultStructFormat { get; }
|
||||
public string PropertyNamedLikeTypeRenameFormat { get; }
|
||||
public string GenericTypeParameterFormat { get; }
|
||||
public Name PipeliningExtensionsClassName { get; }
|
||||
public string MemberAccessPathNameFormat { get; }
|
||||
public Name TaskParameter { get; }
|
||||
public Name EagerMethod { get; }
|
||||
|
||||
public GenNames(GeneratorOptions options)
|
||||
{
|
||||
TopNamespace = new Name(options.TopNamespaceName).IdentifierName;
|
||||
ReaderStruct = new Name(options.ReaderStructName);
|
||||
WriterStruct = new Name(options.WriterStructName);
|
||||
ReaderParameter = new Name(options.ReaderParameterName);
|
||||
WriterParameter = new Name(options.WriterParameterName);
|
||||
ReaderCreateMethod = new Name(options.ReaderCreateMethodName);
|
||||
ReaderContextField = new Name(options.ReaderContextFieldName);
|
||||
ContextParameter = new Name(options.ContextParameterName);
|
||||
GroupReaderContextArg = new Name(options.GroupReaderContextArgName);
|
||||
GroupWriterContextArg = new Name(options.GroupWriterContextArgName);
|
||||
UnionDiscriminatorEnum = new Name(options.UnionDisciminatorEnumName);
|
||||
UnionDiscriminatorProp = new Name(options.UnionDiscriminatorPropName);
|
||||
UnionDiscriminatorUndefined = new Name(options.UnionDisciminatorUndefinedName);
|
||||
UnionDiscriminatorField = new Name(options.UnionDiscriminatorFieldName);
|
||||
UnionContentField = new Name(options.UnionContentFieldName);
|
||||
SerializeMethod = new Name(options.SerializeMethodName);
|
||||
ApplyDefaultsMethod = new Name(options.ApplyDefaultsMethodName);
|
||||
AnonymousParameter = new Name(options.AnonymousParameterName);
|
||||
CancellationTokenParameter = new Name(options.CancellationTokenParameterName);
|
||||
ParamsLocal = new Name(options.ParamsLocalName);
|
||||
DeserializerLocal = new Name(options.DeserializerLocalName);
|
||||
SerializerLocal = new Name(options.SerializerLocalName);
|
||||
ResultLocal = new Name(options.ResultLocalName);
|
||||
InstLocalName = new Name(options.InstLocalName);
|
||||
ParamsStructFormat = options.ParamsStructFormat;
|
||||
ResultStructFormat = options.ResultStructFormat;
|
||||
PropertyNamedLikeTypeRenameFormat = options.PropertyNamedLikeTypeRenameFormat;
|
||||
GenericTypeParameterFormat = options.GenericTypeParameterFormat;
|
||||
PipeliningExtensionsClassName = new Name(options.PipeliningExtensionsClassName);
|
||||
MemberAccessPathNameFormat = options.MemberAccessPathNameFormat;
|
||||
TaskParameter = new Name(options.TaskParameterName);
|
||||
EagerMethod = new Name(options.EagerMethodName);
|
||||
}
|
||||
|
||||
public Name MakeTypeName(TypeDefinition def, NameUsage usage = NameUsage.Default)
|
||||
{
|
||||
if (def.Tag == TypeTag.Group)
|
||||
{
|
||||
return new Name(SyntaxHelpers.MakeAllLower(def.Name));
|
||||
}
|
||||
else
|
||||
{
|
||||
string name;
|
||||
|
||||
switch (usage)
|
||||
{
|
||||
case NameUsage.Default:
|
||||
if (def.Tag == TypeTag.Interface)
|
||||
goto case NameUsage.Interface;
|
||||
|
||||
switch (def.SpecialName)
|
||||
{
|
||||
case SpecialName.NothingSpecial:
|
||||
name = def.Name;
|
||||
break;
|
||||
|
||||
case SpecialName.MethodParamsStruct:
|
||||
name = MakeParamsStructName(def.UsingMethod);
|
||||
break;
|
||||
|
||||
case SpecialName.MethodResultStruct:
|
||||
name = MakeResultStructName(def.UsingMethod);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
break;
|
||||
|
||||
case NameUsage.Namespace:
|
||||
name = def.Name;
|
||||
break;
|
||||
|
||||
case NameUsage.Interface:
|
||||
name = "I" + def.Name;
|
||||
break;
|
||||
|
||||
case NameUsage.Proxy:
|
||||
name = def.Name + "Proxy";
|
||||
break;
|
||||
|
||||
case NameUsage.Skeleton:
|
||||
name = def.Name + "Skeleton";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
return new Name(name);
|
||||
}
|
||||
}
|
||||
|
||||
public SimpleNameSyntax MakeGenericTypeName(TypeDefinition def, NameUsage usage = NameUsage.Default)
|
||||
{
|
||||
var name = MakeTypeName(def, usage);
|
||||
|
||||
if (def.GenericParameters.Count > 0)
|
||||
{
|
||||
return GenericName(name.Identifier)
|
||||
.AddTypeArgumentListArguments(def
|
||||
.GenericParameters
|
||||
.Select(p => GetGenericTypeParameter(p).IdentifierName).ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
return name.IdentifierName;
|
||||
}
|
||||
}
|
||||
|
||||
TypeSyntax ResolveGenericParameter(GenericParameter p, Model.Type boundType, TypeDefinition def)
|
||||
{
|
||||
var type = boundType.ResolveGenericParameter(p);
|
||||
return MakeTypeSyntax(type, def, TypeUsage.DomainClass);
|
||||
}
|
||||
|
||||
public SimpleNameSyntax MakeGenericTypeName(TypeDefinition def, Model.Type boundType, NameUsage usage = NameUsage.Default)
|
||||
{
|
||||
var name = MakeTypeName(def, usage);
|
||||
|
||||
if (def.GenericParameters.Count > 0)
|
||||
{
|
||||
return GenericName(name.Identifier)
|
||||
.AddTypeArgumentListArguments(def
|
||||
.GetLocalTypeParameters()
|
||||
.Select(p => ResolveGenericParameter(p, boundType, def)).ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
return name.IdentifierName;
|
||||
}
|
||||
}
|
||||
|
||||
public SimpleNameSyntax MakeGenericTypeNameForAttribute(TypeDefinition def, NameUsage usage)
|
||||
{
|
||||
var name = MakeTypeName(def, usage);
|
||||
|
||||
if (def.GenericParameters.Count > 0)
|
||||
{
|
||||
return GenericName(name.Identifier).AddTypeArgumentListArguments();
|
||||
}
|
||||
else
|
||||
{
|
||||
return name.IdentifierName;
|
||||
}
|
||||
}
|
||||
|
||||
public static NameSyntax NamespaceName(string[] @namespace)
|
||||
{
|
||||
NameSyntax ident = null;
|
||||
if (@namespace != null)
|
||||
{
|
||||
ident = IdentifierName(SyntaxHelpers.MakeCamel(@namespace[0]));
|
||||
foreach (string name in @namespace.Skip(1))
|
||||
{
|
||||
var temp = IdentifierName(SyntaxHelpers.MakeCamel(name));
|
||||
ident = QualifiedName(ident, temp);
|
||||
}
|
||||
}
|
||||
return ident;
|
||||
}
|
||||
|
||||
NameSyntax GetNamespaceFor(TypeDefinition def) => NamespaceName(def?.File?.Namespace);
|
||||
|
||||
internal NameSyntax GetQName(Model.Type type, TypeDefinition scope)
|
||||
{
|
||||
// FIXME: With the help of the 'scope' parameter we will be able to generate abbreviated
|
||||
// qualified names. Unfortunately the commented approach is too naive. It will fail if
|
||||
// there are multiple objects with identical name up the hierarchy. We will need a more
|
||||
// sophisticated algorithm.
|
||||
|
||||
var scopeSet = new HashSet<TypeDefinition>();
|
||||
//while (scope != null)
|
||||
//{
|
||||
// scopeSet.Add(scope);
|
||||
// scope = scope.DeclaringElement as TypeDefinition;
|
||||
//}
|
||||
|
||||
if (type.Definition != null)
|
||||
{
|
||||
var stack = new Stack<SimpleNameSyntax>();
|
||||
|
||||
var def = type.Definition;
|
||||
stack.Push(MakeGenericTypeName(def, type, NameUsage.Default));
|
||||
|
||||
while (def.DeclaringElement is TypeDefinition pdef && !scopeSet.Contains(pdef))
|
||||
{
|
||||
stack.Push(MakeGenericTypeName(pdef, type, NameUsage.Namespace));
|
||||
def = pdef;
|
||||
}
|
||||
|
||||
var qtype =
|
||||
GetNamespaceFor(type.Definition)
|
||||
?? GetNamespaceFor(scope)
|
||||
?? TopNamespace;
|
||||
|
||||
foreach (var name in stack)
|
||||
{
|
||||
qtype = QualifiedName(qtype, name);
|
||||
}
|
||||
|
||||
return qtype;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetGenericTypeParameter(type.Parameter.Name).IdentifierName;
|
||||
}
|
||||
}
|
||||
|
||||
public TypeSyntax MakeListSerializerSyntax(Model.Type elementType, TypeDefinition scope)
|
||||
{
|
||||
switch (elementType.Tag)
|
||||
{
|
||||
case TypeTag.AnyPointer:
|
||||
case TypeTag.StructPointer:
|
||||
case TypeTag.ListPointer:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfPointersSerializer<Capnp.DynamicSerializerState>>();
|
||||
|
||||
case TypeTag.CapabilityPointer:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfCapsSerializer<Capnp.Rpc.BareProxy>>();
|
||||
|
||||
case TypeTag.Data:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfPointersSerializer<
|
||||
Capnp.ListOfPrimitivesSerializer<byte>>>();
|
||||
|
||||
case TypeTag.Enum:
|
||||
return GenericName("ListOfPrimitivesSerializer")
|
||||
.AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer));
|
||||
|
||||
case TypeTag.Group:
|
||||
case TypeTag.Struct:
|
||||
return GenericName("ListOfStructsSerializer")
|
||||
.AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer));
|
||||
|
||||
case TypeTag.Interface:
|
||||
return GenericName("ListOfCapsSerializer")
|
||||
.AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer));
|
||||
|
||||
case TypeTag.List:
|
||||
return GenericName("ListOfPointersSerializer")
|
||||
.AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer));
|
||||
|
||||
case TypeTag.Text:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfTextSerializer>();
|
||||
|
||||
case TypeTag.Void:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfEmptySerializer>();
|
||||
|
||||
case TypeTag.Bool:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfBitsSerializer>();
|
||||
|
||||
case TypeTag.F32:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<float>>();
|
||||
|
||||
case TypeTag.F64:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<double>>();
|
||||
|
||||
case TypeTag.S8:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<sbyte>>();
|
||||
|
||||
case TypeTag.U8:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<byte>>();
|
||||
|
||||
case TypeTag.S16:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<short>>();
|
||||
|
||||
case TypeTag.U16:
|
||||
case TypeTag.AnyEnum:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<ushort>>();
|
||||
|
||||
case TypeTag.S32:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<int>>();
|
||||
|
||||
case TypeTag.U32:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<uint>>();
|
||||
|
||||
case TypeTag.S64:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<long>>();
|
||||
|
||||
case TypeTag.U64:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<ulong>>();
|
||||
|
||||
default:
|
||||
throw new NotImplementedException("Unexpected type tag, don't know how to deal with this");
|
||||
}
|
||||
}
|
||||
|
||||
TypeSyntax MaybeNullableValueType(TypeSyntax typeSyntax, TypeUsage usage)
|
||||
{
|
||||
switch (usage)
|
||||
{
|
||||
case TypeUsage.DomainClassNullable:
|
||||
return NullableType(typeSyntax);
|
||||
|
||||
default:
|
||||
return typeSyntax;
|
||||
}
|
||||
}
|
||||
|
||||
public TypeSyntax MakeTypeSyntax(Model.Type type, TypeDefinition scope, TypeUsage usage)
|
||||
{
|
||||
switch (type.Tag)
|
||||
{
|
||||
case TypeTag.AnyEnum:
|
||||
return MaybeNullableValueType(SyntaxHelpers.Type<ushort>(), usage);
|
||||
|
||||
case TypeTag.CapabilityPointer:
|
||||
if (type.Parameter != null)
|
||||
{
|
||||
return GetQName(type, scope);
|
||||
}
|
||||
else
|
||||
{
|
||||
return SyntaxHelpers.Type<Capnp.Rpc.BareProxy>();
|
||||
}
|
||||
|
||||
case TypeTag.AnyPointer:
|
||||
case TypeTag.StructPointer:
|
||||
switch (usage)
|
||||
{
|
||||
case TypeUsage.Reader:
|
||||
return SyntaxHelpers.Type<Capnp.DeserializerState>();
|
||||
|
||||
case TypeUsage.Writer:
|
||||
return SyntaxHelpers.Type<Capnp.DynamicSerializerState>();
|
||||
|
||||
case TypeUsage.DomainClass:
|
||||
case TypeUsage.DomainClassNullable:
|
||||
if (type.Parameter != null)
|
||||
{
|
||||
return GetQName(type, scope);
|
||||
}
|
||||
else
|
||||
{
|
||||
return SyntaxHelpers.Type<Capnp.AnyPointer>();
|
||||
}
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
case TypeTag.Bool:
|
||||
return MaybeNullableValueType(SyntaxHelpers.Type<bool>(), usage);
|
||||
|
||||
case TypeTag.Data:
|
||||
switch (usage)
|
||||
{
|
||||
case TypeUsage.Reader:
|
||||
case TypeUsage.DomainClass:
|
||||
case TypeUsage.DomainClassNullable:
|
||||
return SyntaxHelpers.Type<IReadOnlyList<byte>>();
|
||||
|
||||
case TypeUsage.Writer:
|
||||
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<byte>>();
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
case TypeTag.Enum:
|
||||
return MaybeNullableValueType(GetQName(type, scope), usage);
|
||||
|
||||
case TypeTag.Interface:
|
||||
return GetQName(type, scope);
|
||||
|
||||
case TypeTag.Struct:
|
||||
case TypeTag.Group:
|
||||
switch (usage)
|
||||
{
|
||||
case TypeUsage.Writer:
|
||||
return QualifiedName(GetQName(type, scope), WriterStruct.IdentifierName);
|
||||
|
||||
case TypeUsage.Reader:
|
||||
return QualifiedName(GetQName(type, scope), ReaderStruct.IdentifierName);
|
||||
|
||||
case TypeUsage.DomainClass:
|
||||
case TypeUsage.DomainClassNullable:
|
||||
return GetQName(type, scope);
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
case TypeTag.F32:
|
||||
return MaybeNullableValueType(SyntaxHelpers.Type<float>(), usage);
|
||||
|
||||
case TypeTag.F64:
|
||||
return MaybeNullableValueType(SyntaxHelpers.Type<double>(), usage);
|
||||
|
||||
case TypeTag.List when type.ElementType.Tag == TypeTag.Void && usage != TypeUsage.Writer:
|
||||
return MaybeNullableValueType(SyntaxHelpers.Type<int>(), usage);
|
||||
|
||||
case TypeTag.List:
|
||||
switch (usage)
|
||||
{
|
||||
case TypeUsage.Writer:
|
||||
return MakeListSerializerSyntax(type.ElementType, scope);
|
||||
|
||||
case TypeUsage.Reader:
|
||||
return GenericName(Identifier("IReadOnlyList"))
|
||||
.AddTypeArgumentListArguments(MakeTypeSyntax(type.ElementType, scope, TypeUsage.Reader));
|
||||
|
||||
case TypeUsage.DomainClass:
|
||||
case TypeUsage.DomainClassNullable:
|
||||
return GenericName(Identifier("IReadOnlyList"))
|
||||
.AddTypeArgumentListArguments(MakeTypeSyntax(type.ElementType, scope, TypeUsage.DomainClass));
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
case TypeTag.ListPointer:
|
||||
switch (usage)
|
||||
{
|
||||
case TypeUsage.Writer:
|
||||
return SyntaxHelpers.Type<Capnp.SerializerState>();
|
||||
|
||||
case TypeUsage.Reader:
|
||||
return SyntaxHelpers.Type<IReadOnlyList<Capnp.DeserializerState>>();
|
||||
|
||||
case TypeUsage.DomainClass:
|
||||
case TypeUsage.DomainClassNullable:
|
||||
return SyntaxHelpers.Type<IReadOnlyList<object>>();
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
case TypeTag.S16:
|
||||
return MaybeNullableValueType(SyntaxHelpers.Type<short>(), usage);
|
||||
|
||||
case TypeTag.S32:
|
||||
return MaybeNullableValueType(SyntaxHelpers.Type<int>(), usage);
|
||||
|
||||
case TypeTag.S64:
|
||||
return MaybeNullableValueType(SyntaxHelpers.Type<long>(), usage);
|
||||
|
||||
case TypeTag.S8:
|
||||
return MaybeNullableValueType(SyntaxHelpers.Type<sbyte>(), usage);
|
||||
|
||||
case TypeTag.Text:
|
||||
return SyntaxHelpers.Type<string>();
|
||||
|
||||
case TypeTag.U16:
|
||||
return MaybeNullableValueType(SyntaxHelpers.Type<ushort>(), usage);
|
||||
|
||||
case TypeTag.U32:
|
||||
return MaybeNullableValueType(SyntaxHelpers.Type<uint>(), usage);
|
||||
|
||||
case TypeTag.U64:
|
||||
return MaybeNullableValueType(SyntaxHelpers.Type<ulong>(), usage);
|
||||
|
||||
case TypeTag.U8:
|
||||
return MaybeNullableValueType(SyntaxHelpers.Type<byte>(), usage);
|
||||
|
||||
case TypeTag.Void:
|
||||
return PredefinedType(Token(SyntaxKind.VoidKeyword));
|
||||
|
||||
default:
|
||||
throw new NotImplementedException("Unexpected type tag, don't know how to deal with this");
|
||||
}
|
||||
}
|
||||
|
||||
public string MakeParamsStructName(Method method)
|
||||
{
|
||||
return string.Format(ParamsStructFormat, method.Name);
|
||||
}
|
||||
|
||||
public string MakeResultStructName(Method method)
|
||||
{
|
||||
return string.Format(ResultStructFormat, method.Name);
|
||||
}
|
||||
|
||||
public Name GetCodeIdentifier(Method method)
|
||||
{
|
||||
return new Name(SyntaxHelpers.MakeCamel(method.Name));
|
||||
}
|
||||
|
||||
public Name GetCodeIdentifier(Field field)
|
||||
{
|
||||
if (_fieldNameMap.TryGetValue(field, out var name))
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
var def = field.DeclaringType;
|
||||
|
||||
if (def == null)
|
||||
{
|
||||
// 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.
|
||||
return new Name(SyntaxHelpers.MakeCamel(field.Name));
|
||||
}
|
||||
|
||||
var typeNames = new HashSet<Name>(def.NestedTypes.Select(t => MakeTypeName(t)));
|
||||
typeNames.Add(MakeTypeName(def));
|
||||
|
||||
foreach (var member in def.Fields)
|
||||
{
|
||||
var memberName = new Name(SyntaxHelpers.MakeCamel(member.Name));
|
||||
|
||||
while (typeNames.Contains(memberName))
|
||||
{
|
||||
memberName = new Name(string.Format(PropertyNamedLikeTypeRenameFormat, memberName.ToString()));
|
||||
}
|
||||
|
||||
_fieldNameMap.Add(member, memberName);
|
||||
}
|
||||
|
||||
return _fieldNameMap[field];
|
||||
}
|
||||
|
||||
public Name GetGenericTypeParameter(string name)
|
||||
{
|
||||
return new Name(string.Format(GenericTypeParameterFormat, name));
|
||||
}
|
||||
|
||||
public Name MakePipeliningSupportExtensionMethodName(IReadOnlyList<Field> path)
|
||||
{
|
||||
if (path.Count == 1 && path[0].Offset == 0)
|
||||
return EagerMethod;
|
||||
else
|
||||
return new Name(string.Join("_", path.Select(f => GetCodeIdentifier(f).ToString())));
|
||||
}
|
||||
|
||||
public Name MakeMemberAccessPathFieldName(Method method, IReadOnlyList<Field> path)
|
||||
{
|
||||
return new Name(string.Format(MemberAccessPathNameFormat,
|
||||
method.Name,
|
||||
MakePipeliningSupportExtensionMethodName(path)));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace CapnpC.Generator
|
||||
{
|
||||
class GeneratorOptions
|
||||
{
|
||||
public string TopNamespaceName { get; set; } = "CapnpGen";
|
||||
public string ReaderStructName { get; set; } = "READER";
|
||||
public string WriterStructName { get; set; } = "WRITER";
|
||||
public string ReaderParameterName { get; set; } = "reader";
|
||||
public string WriterParameterName { get; set; } = "writer";
|
||||
public string ReaderCreateMethodName { get; set; } = "create";
|
||||
public string ReaderContextFieldName { get; set; } = "ctx";
|
||||
public string ContextParameterName { get; set; } = "ctx";
|
||||
public string GroupReaderContextArgName { get; set; } = "ctx";
|
||||
public string GroupWriterContextArgName { get; set; } = "ctx";
|
||||
public string UnionDisciminatorEnumName { get; set; } = "WHICH";
|
||||
public string UnionDiscriminatorPropName { get; set; } = "which";
|
||||
public string UnionDiscriminatorFieldName { get; set; } = "_which";
|
||||
public string UnionDisciminatorUndefinedName { get; set; } = "undefined";
|
||||
public string UnionContentFieldName { get; set; } = "_content";
|
||||
public string SerializeMethodName { get; set; } = "serialize";
|
||||
public string ApplyDefaultsMethodName { get; set; } = "applyDefaults";
|
||||
public string AnonymousParameterName { get; set; } = "arg_";
|
||||
public string CancellationTokenParameterName { get; set; } = "cancellationToken_";
|
||||
public string ParamsLocalName { get; set; } = "in_";
|
||||
public string DeserializerLocalName { get; set; } = "d_";
|
||||
public string SerializerLocalName { get; set; } = "s_";
|
||||
public string ResultLocalName { get; set; } = "r_";
|
||||
public string ParamsStructFormat { get; set; } = "Params_{0}";
|
||||
public string ResultStructFormat { get; set; } = "Result_{0}";
|
||||
public string PropertyNamedLikeTypeRenameFormat { get; set; } = "The{0}";
|
||||
public string InstLocalName { get; set; } = "inst";
|
||||
public string GenericTypeParameterFormat { get; set; } = "T{0}";
|
||||
public string PipeliningExtensionsClassName { get; set; } = "PipeliningSupportExtensions";
|
||||
public string MemberAccessPathNameFormat { get; set; } = "Path_{0}_{1}";
|
||||
public string TaskParameterName { get; set; } = "task";
|
||||
public string EagerMethodName { get; set; } = "Eager";
|
||||
}
|
||||
}
|
@ -1,844 +0,0 @@
|
||||
using CapnpC.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
using static CapnpC.Generator.SyntaxHelpers;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
|
||||
namespace CapnpC.Generator
|
||||
{
|
||||
class InterfaceSnippetGen
|
||||
{
|
||||
readonly GenNames _names;
|
||||
|
||||
public InterfaceSnippetGen(GenNames names)
|
||||
{
|
||||
_names = names;
|
||||
}
|
||||
|
||||
TypeSyntax TransformReturnType(Method method)
|
||||
{
|
||||
switch (method.Results.Count)
|
||||
{
|
||||
case 0:
|
||||
return IdentifierName(nameof(Task));
|
||||
|
||||
case 1:
|
||||
return GenericName(nameof(Task)).AddTypeArgumentListArguments(
|
||||
_names.MakeTypeSyntax(method.Results[0].Type, method.DeclaringInterface, TypeUsage.DomainClass));
|
||||
|
||||
default:
|
||||
return GenericName(nameof(Task)).AddTypeArgumentListArguments(
|
||||
TupleType(SeparatedList(
|
||||
method.Results.Select(
|
||||
f => TupleElement(_names.MakeTypeSyntax(f.Type, method.DeclaringInterface, TypeUsage.DomainClass))))));
|
||||
}
|
||||
}
|
||||
|
||||
ParameterSyntax[] TransformParameters(Method method)
|
||||
{
|
||||
var list = new List<ParameterSyntax>();
|
||||
|
||||
if (method.Params.Count > 0)
|
||||
{
|
||||
var arg0 = method.Params[0];
|
||||
|
||||
if (arg0.Name == null)
|
||||
{
|
||||
list.Add(Parameter(_names.AnonymousParameter.Identifier)
|
||||
.WithType(_names.MakeTypeSyntax(arg0.Type, method.DeclaringInterface, TypeUsage.DomainClass)));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var arg in method.Params)
|
||||
{
|
||||
list.Add(Parameter(Identifier(arg.Name))
|
||||
.WithType(_names.MakeTypeSyntax(arg.Type, method.DeclaringInterface, TypeUsage.DomainClass)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list.Add(Parameter(_names.CancellationTokenParameter.Identifier)
|
||||
.WithType(IdentifierName(nameof(CancellationToken)))
|
||||
.WithDefault(EqualsValueClause(LiteralExpression(
|
||||
SyntaxKind.DefaultLiteralExpression,
|
||||
Token(SyntaxKind.DefaultKeyword)))));
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
IEnumerable<TypeParameterSyntax> MakeTypeParameters(TypeDefinition def)
|
||||
{
|
||||
foreach (string name in def.GenericParameters)
|
||||
{
|
||||
yield return TypeParameter(_names.GetGenericTypeParameter(name).Identifier);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<TypeParameterConstraintClauseSyntax> MakeTypeParameterConstraints(TypeDefinition def)
|
||||
{
|
||||
foreach (string name in def.GenericParameters)
|
||||
{
|
||||
yield return TypeParameterConstraintClause(
|
||||
_names.GetGenericTypeParameter(name).IdentifierName)
|
||||
.AddConstraints(ClassOrStructConstraint(SyntaxKind.ClassConstraint));
|
||||
}
|
||||
}
|
||||
|
||||
public MemberDeclarationSyntax MakeInterface(TypeDefinition type)
|
||||
{
|
||||
var ifaceDecl = InterfaceDeclaration(_names.MakeTypeName(type, NameUsage.Interface).Identifier)
|
||||
.AddModifiers(Public)
|
||||
.AddAttributeLists(
|
||||
AttributeList()
|
||||
.AddAttributes(
|
||||
Attribute(IdentifierName("Proxy"))
|
||||
.AddArgumentListArguments(
|
||||
AttributeArgument(
|
||||
TypeOfExpression(_names.MakeGenericTypeNameForAttribute(type, NameUsage.Proxy)))),
|
||||
Attribute(IdentifierName("Skeleton"))
|
||||
.AddArgumentListArguments(
|
||||
AttributeArgument(
|
||||
TypeOfExpression(_names.MakeGenericTypeNameForAttribute(type, NameUsage.Skeleton))))));
|
||||
|
||||
if (type.GenericParameters.Count > 0)
|
||||
{
|
||||
ifaceDecl = ifaceDecl.AddTypeParameterListParameters(MakeTypeParameters(type).ToArray());
|
||||
}
|
||||
|
||||
if (type.Superclasses.Count == 0)
|
||||
{
|
||||
ifaceDecl = ifaceDecl.AddBaseListTypes(SimpleBaseType(IdentifierName(nameof(IDisposable))));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var superClass in type.Superclasses)
|
||||
{
|
||||
ifaceDecl = ifaceDecl.AddBaseListTypes(
|
||||
SimpleBaseType(_names.MakeTypeSyntax(
|
||||
superClass, type,
|
||||
TypeUsage.NotRelevant)));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var method in type.Methods)
|
||||
{
|
||||
var methodDecl = MethodDeclaration(
|
||||
TransformReturnType(method),
|
||||
_names.GetCodeIdentifier(method).Identifier)
|
||||
.AddParameterListParameters(TransformParameters(method))
|
||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
|
||||
|
||||
if (method.GenericParameters.Count > 0)
|
||||
{
|
||||
methodDecl = methodDecl
|
||||
.AddTypeParameterListParameters(MakeTypeParameters(method).ToArray())
|
||||
.AddConstraintClauses(MakeTypeParameterConstraints(method).ToArray());
|
||||
|
||||
}
|
||||
|
||||
ifaceDecl = ifaceDecl.AddMembers(methodDecl);
|
||||
}
|
||||
|
||||
return ifaceDecl;
|
||||
}
|
||||
|
||||
bool IsSubjectToPipelining(Model.Type type, HashSet<Model.Type> visited)
|
||||
{
|
||||
if (!visited.Add(type))
|
||||
return false;
|
||||
|
||||
switch (type.Tag)
|
||||
{
|
||||
case TypeTag.AnyPointer:
|
||||
case TypeTag.CapabilityPointer:
|
||||
case TypeTag.Interface:
|
||||
case TypeTag.ListPointer:
|
||||
case TypeTag.StructPointer:
|
||||
return true;
|
||||
|
||||
case TypeTag.List:
|
||||
return IsSubjectToPipelining(type.ElementType, visited);
|
||||
|
||||
case TypeTag.Struct:
|
||||
return type.Fields.Any(f => IsSubjectToPipelining(f.Type, visited));
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsSubjectToPipelining(Method method)
|
||||
{
|
||||
return method.Results.Any(r => IsSubjectToPipelining(r.Type, new HashSet<Model.Type>()));
|
||||
}
|
||||
|
||||
IEnumerable<ExpressionSyntax> MakeProxyCallInitializerAssignments(Method method)
|
||||
{
|
||||
foreach (var methodParam in method.Params)
|
||||
{
|
||||
yield return AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
_names.GetCodeIdentifier(methodParam).IdentifierName,
|
||||
IdentifierName(methodParam.Name));
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<ArgumentSyntax> MakeProxyReturnResultTupleElements(Method method)
|
||||
{
|
||||
foreach (var item in method.ResultStruct.Fields)
|
||||
{
|
||||
yield return Argument(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.ResultLocal.IdentifierName,
|
||||
_names.GetCodeIdentifier(item).IdentifierName));
|
||||
}
|
||||
}
|
||||
|
||||
StatementSyntax MakeProxyReturnResult(Method method)
|
||||
{
|
||||
if (method.ResultStruct.Definition.SpecialName == SpecialName.MethodResultStruct)
|
||||
{
|
||||
if (method.ResultStruct.Fields.Count == 0)
|
||||
{
|
||||
return ReturnStatement();
|
||||
}
|
||||
else
|
||||
{
|
||||
return ReturnStatement(TupleExpression()
|
||||
.AddArguments(MakeProxyReturnResultTupleElements(method).ToArray()));
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return ReturnStatement(_names.ResultLocal.IdentifierName);
|
||||
}
|
||||
}
|
||||
|
||||
StatementSyntax MakeProxyCreateResult(Method method)
|
||||
{
|
||||
var resultType = method.ResultStruct;
|
||||
var domainType = _names.MakeTypeSyntax(resultType, method.DeclaringInterface, TypeUsage.DomainClass);
|
||||
|
||||
var createDomain = InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(nameof(Capnp.CapnpSerializable)),
|
||||
GenericName(nameof(Capnp.CapnpSerializable.Create))
|
||||
.AddTypeArgumentListArguments(domainType)))
|
||||
.AddArgumentListArguments(
|
||||
Argument(_names.DeserializerLocal.IdentifierName));
|
||||
|
||||
return LocalDeclarationStatement(
|
||||
VariableDeclaration(
|
||||
IdentifierName("var"))
|
||||
.WithVariables(
|
||||
SingletonSeparatedList(
|
||||
VariableDeclarator(
|
||||
_names.ResultLocal.Identifier)
|
||||
.WithInitializer(
|
||||
EqualsValueClause(createDomain)))));
|
||||
}
|
||||
|
||||
IEnumerable<TypeParameterSyntax> MakeTypeParameters(Method method)
|
||||
{
|
||||
foreach (string name in method.GenericParameters)
|
||||
{
|
||||
yield return TypeParameter(_names.GetGenericTypeParameter(name).Identifier);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<TypeParameterConstraintClauseSyntax> MakeTypeParameterConstraints(Method method)
|
||||
{
|
||||
foreach (string name in method.GenericParameters)
|
||||
{
|
||||
yield return TypeParameterConstraintClause(
|
||||
_names.GetGenericTypeParameter(name).IdentifierName)
|
||||
.AddConstraints(ClassOrStructConstraint(SyntaxKind.ClassConstraint));
|
||||
}
|
||||
}
|
||||
|
||||
public MemberDeclarationSyntax MakeProxy(TypeDefinition type)
|
||||
{
|
||||
var classDecl = ClassDeclaration(_names.MakeTypeName(type, NameUsage.Proxy).Identifier)
|
||||
.AddModifiers(Public)
|
||||
.AddBaseListTypes(
|
||||
SimpleBaseType(Type<Capnp.Rpc.Proxy>()),
|
||||
SimpleBaseType(_names.MakeGenericTypeName(type, NameUsage.Interface)));
|
||||
|
||||
if (type.GenericParameters.Count > 0)
|
||||
{
|
||||
classDecl = classDecl
|
||||
.AddTypeParameterListParameters(MakeTypeParameters(type).ToArray())
|
||||
.AddConstraintClauses(MakeTypeParameterConstraints(type).ToArray());
|
||||
}
|
||||
|
||||
var allMethods =
|
||||
from c in Types.FromDefinition(type).AllImplementedClasses
|
||||
from m in c.Definition.Methods
|
||||
select m;
|
||||
|
||||
foreach (var method in allMethods)
|
||||
{
|
||||
var bodyStmts = new List<StatementSyntax>();
|
||||
|
||||
bodyStmts.Add(LocalDeclarationStatement(
|
||||
VariableDeclaration(
|
||||
IdentifierName("var"))
|
||||
.WithVariables(
|
||||
SingletonSeparatedList(
|
||||
VariableDeclarator(
|
||||
_names.ParamsLocal.Identifier)
|
||||
.WithInitializer(
|
||||
EqualsValueClause(
|
||||
InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(nameof(Capnp.SerializerState)),
|
||||
GenericName(
|
||||
Identifier(nameof(Capnp.SerializerState.CreateForRpc)))
|
||||
.WithTypeArgumentList(
|
||||
TypeArgumentList(
|
||||
SingletonSeparatedList(
|
||||
_names.MakeTypeSyntax(
|
||||
method.ParamsStruct,
|
||||
method.ParamsStruct.Definition,
|
||||
TypeUsage.Writer))))))))))));
|
||||
|
||||
if (method.ParamsStruct.Definition.SpecialName == SpecialName.MethodParamsStruct)
|
||||
{
|
||||
bodyStmts.Add(LocalDeclarationStatement(
|
||||
VariableDeclaration(
|
||||
IdentifierName("var"))
|
||||
.WithVariables(
|
||||
SingletonSeparatedList<VariableDeclaratorSyntax>(
|
||||
VariableDeclarator(
|
||||
_names.AnonymousParameter.Identifier)
|
||||
.WithInitializer(
|
||||
EqualsValueClause(
|
||||
ObjectCreationExpression(
|
||||
_names.MakeTypeSyntax(
|
||||
method.ParamsStruct,
|
||||
method.ParamsStruct.Definition,
|
||||
TypeUsage.DomainClass))
|
||||
.WithArgumentList(
|
||||
ArgumentList())
|
||||
.WithInitializer(
|
||||
InitializerExpression(
|
||||
SyntaxKind.ObjectInitializerExpression,
|
||||
SeparatedList<ExpressionSyntax>(
|
||||
CommonSnippetGen.MakeCommaSeparatedList(
|
||||
MakeProxyCallInitializerAssignments(method)).ToArray())))))))));
|
||||
}
|
||||
|
||||
bodyStmts.Add(ExpressionStatement(
|
||||
InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.AnonymousParameter.IdentifierName,
|
||||
_names.SerializeMethod.IdentifierName))
|
||||
.AddArgumentListArguments(
|
||||
Argument(_names.ParamsLocal.IdentifierName))));
|
||||
|
||||
var call = InvocationExpression(IdentifierName(nameof(Capnp.Rpc.BareProxy.Call)))
|
||||
.AddArgumentListArguments(
|
||||
Argument(
|
||||
LiteralExpression(SyntaxKind.NumericLiteralExpression,
|
||||
Literal(method.DeclaringInterface.Id))),
|
||||
Argument(
|
||||
LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(method.Id))),
|
||||
Argument(
|
||||
InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.ParamsLocal.IdentifierName,
|
||||
GenericName(nameof(Capnp.SerializerState.Rewrap))
|
||||
.AddTypeArgumentListArguments(Type<Capnp.DynamicSerializerState>())))
|
||||
.AddArgumentListArguments()),
|
||||
Argument(
|
||||
LiteralExpression(SyntaxKind.FalseLiteralExpression)),
|
||||
Argument(
|
||||
_names.CancellationTokenParameter.IdentifierName));
|
||||
|
||||
MethodDeclarationSyntax methodDecl;
|
||||
|
||||
if (IsSubjectToPipelining(method))
|
||||
{
|
||||
methodDecl = MethodDeclaration(
|
||||
TransformReturnType(method),
|
||||
_names.GetCodeIdentifier(method).Identifier)
|
||||
.AddParameterListParameters(TransformParameters(method))
|
||||
.AddModifiers(Public);
|
||||
|
||||
var pipelineAwareCall = InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(nameof(Capnp.Rpc.Impatient)),
|
||||
IdentifierName(nameof(Capnp.Rpc.Impatient.MakePipelineAware))))
|
||||
.AddArgumentListArguments(
|
||||
Argument(call),
|
||||
Argument(SimpleLambdaExpression(
|
||||
Parameter(_names.DeserializerLocal.Identifier),
|
||||
Block(
|
||||
MakeProxyCreateResult(method),
|
||||
MakeProxyReturnResult(method)))));
|
||||
|
||||
bodyStmts.Add(ReturnStatement(pipelineAwareCall));
|
||||
}
|
||||
else
|
||||
{
|
||||
methodDecl = MethodDeclaration(
|
||||
TransformReturnType(method),
|
||||
_names.GetCodeIdentifier(method).Identifier)
|
||||
.AddParameterListParameters(TransformParameters(method))
|
||||
.AddModifiers(Public, Token(SyntaxKind.AsyncKeyword));
|
||||
|
||||
var whenReturned = MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
call,
|
||||
IdentifierName(nameof(Capnp.Rpc.IPromisedAnswer.WhenReturned)));
|
||||
|
||||
var assignAwaited = LocalDeclarationStatement(
|
||||
VariableDeclaration(
|
||||
IdentifierName("var"))
|
||||
.AddVariables(
|
||||
VariableDeclarator(
|
||||
_names.DeserializerLocal.Identifier)
|
||||
.WithInitializer(
|
||||
EqualsValueClause(
|
||||
AwaitExpression(whenReturned)))));
|
||||
|
||||
bodyStmts.Add(assignAwaited);
|
||||
bodyStmts.Add(MakeProxyCreateResult(method));
|
||||
bodyStmts.Add(MakeProxyReturnResult(method));
|
||||
}
|
||||
|
||||
if (method.GenericParameters.Count > 0)
|
||||
{
|
||||
methodDecl = methodDecl
|
||||
.AddTypeParameterListParameters(MakeTypeParameters(method).ToArray())
|
||||
.AddConstraintClauses(MakeTypeParameterConstraints(method).ToArray());
|
||||
}
|
||||
|
||||
methodDecl = methodDecl.AddBodyStatements(bodyStmts.ToArray());
|
||||
|
||||
classDecl = classDecl.AddMembers(methodDecl);
|
||||
}
|
||||
|
||||
return classDecl;
|
||||
}
|
||||
|
||||
IEnumerable<ArgumentSyntax> MakeSkeletonSetMethodTableArguments(TypeDefinition def)
|
||||
{
|
||||
foreach (var method in def.Methods)
|
||||
{
|
||||
if (method.GenericParameters.Count > 0)
|
||||
{
|
||||
yield return Argument(
|
||||
GenericName(_names.GetCodeIdentifier(method).ToString())
|
||||
.AddTypeArgumentListArguments(
|
||||
Enumerable.Repeat(
|
||||
Type<Capnp.AnyPointer>(),
|
||||
method.GenericParameters.Count).ToArray()));
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return Argument(_names.GetCodeIdentifier(method).IdentifierName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<ExpressionSyntax> MakeSkeletonMethodResultStructInitializer(Method method)
|
||||
{
|
||||
foreach (var arg in method.Results)
|
||||
{
|
||||
yield return AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
_names.GetCodeIdentifier(arg).IdentifierName,
|
||||
IdentifierName(arg.Name));
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<ArgumentSyntax> MakeSkeletonMethodCallArgs(Method method)
|
||||
{
|
||||
foreach (var arg in method.ParamsStruct.Fields)
|
||||
{
|
||||
yield return Argument(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.ParamsLocal.IdentifierName,
|
||||
_names.GetCodeIdentifier(arg).IdentifierName));
|
||||
}
|
||||
}
|
||||
|
||||
StatementSyntax MakeSkeletonMethodSerializerLocalDeclaration(Method method)
|
||||
{
|
||||
return LocalDeclarationStatement(
|
||||
VariableDeclaration(
|
||||
IdentifierName("var"))
|
||||
.WithVariables(
|
||||
SingletonSeparatedList(
|
||||
VariableDeclarator(
|
||||
_names.SerializerLocal.Identifier)
|
||||
.WithInitializer(
|
||||
EqualsValueClause(
|
||||
InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(nameof(Capnp.SerializerState)),
|
||||
GenericName(
|
||||
Identifier(nameof(Capnp.SerializerState.CreateForRpc)))
|
||||
.WithTypeArgumentList(
|
||||
TypeArgumentList(
|
||||
SingletonSeparatedList(
|
||||
_names.MakeTypeSyntax(
|
||||
method.ResultStruct,
|
||||
method.ResultStruct.Definition,
|
||||
TypeUsage.Writer)))))))))));
|
||||
|
||||
}
|
||||
|
||||
CSharpSyntaxNode MakeMaybeTailCallLambdaBody(Method method)
|
||||
{
|
||||
var block = Block(
|
||||
MakeSkeletonMethodSerializerLocalDeclaration(method));
|
||||
|
||||
if (method.ResultStruct.Definition.SpecialName == SpecialName.MethodResultStruct)
|
||||
{
|
||||
block = block.AddStatements(
|
||||
LocalDeclarationStatement(
|
||||
VariableDeclaration(
|
||||
IdentifierName("var"))
|
||||
.AddVariables(
|
||||
VariableDeclarator(_names.ResultLocal.Identifier)
|
||||
.WithInitializer(EqualsValueClause(ObjectCreationExpression(
|
||||
_names.MakeTypeSyntax(
|
||||
method.ResultStruct,
|
||||
method.ResultStruct.Definition,
|
||||
TypeUsage.DomainClass))
|
||||
.WithInitializer(
|
||||
InitializerExpression(SyntaxKind.ObjectInitializerExpression)
|
||||
.AddExpressions(
|
||||
MakeSkeletonMethodResultStructInitializer(method).ToArray())))))));
|
||||
}
|
||||
|
||||
if (method.Results.Count > 0)
|
||||
{
|
||||
block = block.AddStatements(
|
||||
ExpressionStatement(
|
||||
InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.ResultLocal.IdentifierName,
|
||||
_names.SerializeMethod.IdentifierName))
|
||||
.AddArgumentListArguments(
|
||||
Argument(_names.SerializerLocal.IdentifierName))));
|
||||
}
|
||||
|
||||
block = block.AddStatements(
|
||||
ReturnStatement(_names.SerializerLocal.IdentifierName));
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
IEnumerable<StatementSyntax> MakeSkeletonMethodBody(Method method)
|
||||
{
|
||||
var call = InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(SkeletonWorder.ImplName),
|
||||
_names.GetCodeIdentifier(method).IdentifierName));
|
||||
|
||||
if (method.Params.Count > 0)
|
||||
{
|
||||
var paramsType = method.ParamsStruct;
|
||||
var domainType = _names.MakeTypeSyntax(paramsType, method.ParamsStruct.Definition, TypeUsage.DomainClass);
|
||||
|
||||
var createDomain = InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(nameof(Capnp.CapnpSerializable)),
|
||||
GenericName(nameof(Capnp.CapnpSerializable.Create))
|
||||
.AddTypeArgumentListArguments(domainType)))
|
||||
.AddArgumentListArguments(
|
||||
Argument(_names.DeserializerLocal.IdentifierName));
|
||||
|
||||
if (method.ParamsStruct.Definition.SpecialName == SpecialName.MethodParamsStruct)
|
||||
{
|
||||
yield return LocalDeclarationStatement(
|
||||
VariableDeclaration(
|
||||
IdentifierName("var"))
|
||||
.AddVariables(
|
||||
VariableDeclarator(_names.ParamsLocal.Identifier)
|
||||
.WithInitializer(EqualsValueClause(createDomain))));
|
||||
|
||||
call = call.AddArgumentListArguments(
|
||||
MakeSkeletonMethodCallArgs(method).ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
call = call.AddArgumentListArguments(
|
||||
Argument(createDomain));
|
||||
}
|
||||
}
|
||||
|
||||
call = call.AddArgumentListArguments(
|
||||
Argument(
|
||||
_names.CancellationTokenParameter.IdentifierName));
|
||||
|
||||
if (method.Results.Count == 0)
|
||||
{
|
||||
var awaitCall = AwaitExpression(call);
|
||||
yield return ExpressionStatement(awaitCall);
|
||||
yield return MakeSkeletonMethodSerializerLocalDeclaration(method);
|
||||
yield return ReturnStatement(_names.SerializerLocal.IdentifierName);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExpressionSyntax lambdaArg;
|
||||
|
||||
if (method.ResultStruct.Definition.SpecialName == SpecialName.MethodResultStruct)
|
||||
{
|
||||
if (method.Results.Count == 1)
|
||||
{
|
||||
lambdaArg = SimpleLambdaExpression(
|
||||
Parameter(Identifier(method.Results.Single().Name)),
|
||||
MakeMaybeTailCallLambdaBody(method));
|
||||
}
|
||||
else
|
||||
{
|
||||
lambdaArg = ParenthesizedLambdaExpression(
|
||||
MakeMaybeTailCallLambdaBody(method))
|
||||
.AddParameterListParameters(
|
||||
method.Results.Select(arg => Parameter(Identifier(arg.Name))).ToArray());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lambdaArg = SimpleLambdaExpression(
|
||||
Parameter(_names.ResultLocal.Identifier),
|
||||
MakeMaybeTailCallLambdaBody(method));
|
||||
|
||||
}
|
||||
|
||||
var maybeTailCall = InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(nameof(Capnp.Rpc.Impatient)),
|
||||
IdentifierName(nameof(Capnp.Rpc.Impatient.MaybeTailCall))))
|
||||
.AddArgumentListArguments(
|
||||
Argument(call),
|
||||
Argument(lambdaArg));
|
||||
|
||||
yield return ReturnStatement(maybeTailCall);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<MemberDeclarationSyntax> MakeSkeletonMethods(TypeDefinition def)
|
||||
{
|
||||
foreach (var method in def.Methods)
|
||||
{
|
||||
var methodDecl = MethodDeclaration(
|
||||
Type<Task<Capnp.Rpc.AnswerOrCounterquestion>>(),
|
||||
_names.GetCodeIdentifier(method).Identifier)
|
||||
.AddParameterListParameters(
|
||||
Parameter(_names.DeserializerLocal.Identifier)
|
||||
.WithType(Type<Capnp.DeserializerState>()),
|
||||
Parameter(_names.CancellationTokenParameter.Identifier)
|
||||
.WithType(Type<CancellationToken>()))
|
||||
.AddBodyStatements(
|
||||
MakeSkeletonMethodBody(method).ToArray());
|
||||
|
||||
if (method.Results.Count == 0)
|
||||
{
|
||||
methodDecl = methodDecl.AddModifiers(Async);
|
||||
}
|
||||
|
||||
if (method.GenericParameters.Count > 0)
|
||||
{
|
||||
methodDecl = methodDecl
|
||||
.AddTypeParameterListParameters(MakeTypeParameters(method).ToArray())
|
||||
.AddConstraintClauses(MakeTypeParameterConstraints(method).ToArray());
|
||||
}
|
||||
|
||||
yield return methodDecl;
|
||||
}
|
||||
}
|
||||
|
||||
public MemberDeclarationSyntax MakeSkeleton(TypeDefinition type)
|
||||
{
|
||||
var name = _names.MakeTypeName(type, NameUsage.Skeleton).Identifier;
|
||||
var classDecl = ClassDeclaration(name)
|
||||
.AddModifiers(Public)
|
||||
.AddBaseListTypes(
|
||||
SimpleBaseType(
|
||||
GenericName(nameof(Capnp.Rpc.Skeleton))
|
||||
.AddTypeArgumentListArguments(
|
||||
_names.MakeGenericTypeName(type, NameUsage.Interface))))
|
||||
.AddMembers(
|
||||
// C'tor
|
||||
ConstructorDeclaration(name)
|
||||
.AddModifiers(Public)
|
||||
.AddBodyStatements(
|
||||
ExpressionStatement(
|
||||
InvocationExpression(
|
||||
IdentifierName(SkeletonWorder.SetMethodTableName))
|
||||
.AddArgumentListArguments(
|
||||
MakeSkeletonSetMethodTableArguments(type).ToArray()))),
|
||||
// InterfaceId
|
||||
PropertyDeclaration(Type<ulong>(), nameof(Capnp.Rpc.Skeleton<object>.InterfaceId))
|
||||
.AddModifiers(Public, Override)
|
||||
.WithExpressionBody(
|
||||
ArrowExpressionClause(
|
||||
ValueOf(type.Id)))
|
||||
.WithSemicolonToken(
|
||||
Token(SyntaxKind.SemicolonToken)));
|
||||
|
||||
if (type.GenericParameters.Count > 0)
|
||||
{
|
||||
classDecl = classDecl
|
||||
.AddTypeParameterListParameters(MakeTypeParameters(type).ToArray())
|
||||
.AddConstraintClauses(MakeTypeParameterConstraints(type).ToArray());
|
||||
}
|
||||
|
||||
classDecl = classDecl.AddMembers(MakeSkeletonMethods(type).ToArray());
|
||||
|
||||
return classDecl;
|
||||
}
|
||||
|
||||
public bool RequiresPipeliningSupport(TypeDefinition type)
|
||||
{
|
||||
return type.Methods.Any(m => ExpandPipeliningPaths(m).Any());
|
||||
}
|
||||
|
||||
IEnumerable<IReadOnlyList<Field>> ExpandPipeliningPaths(Method method)
|
||||
{
|
||||
var stack = new Stack<List<Field>>();
|
||||
foreach (var field in method.ResultStruct.Fields)
|
||||
{
|
||||
stack.Push(new List<Field>() { field });
|
||||
}
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var path = stack.Pop();
|
||||
var last = path[path.Count - 1];
|
||||
|
||||
switch (last.Type.Tag)
|
||||
{
|
||||
case TypeTag.Interface:
|
||||
case TypeTag.CapabilityPointer:
|
||||
yield return path;
|
||||
break;
|
||||
|
||||
case TypeTag.Struct:
|
||||
foreach (var field in last.Type.Fields)
|
||||
{
|
||||
if (path.Contains(field))
|
||||
{
|
||||
// Recursive structs protection
|
||||
continue;
|
||||
}
|
||||
|
||||
var copy = new List<Field>();
|
||||
copy.AddRange(path);
|
||||
copy.Add(field);
|
||||
stack.Push(copy);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readonly HashSet<(string, string)> _existingExtensionMethods = new HashSet<(string, string)>();
|
||||
|
||||
public MemberDeclarationSyntax MakePipeliningSupport(TypeDefinition type)
|
||||
{
|
||||
var classDecl = ClassDeclaration(_names.PipeliningExtensionsClassName.Identifier)
|
||||
.AddModifiers(Public, Static, Partial);
|
||||
|
||||
foreach (var method in type.Methods)
|
||||
{
|
||||
foreach (var path in ExpandPipeliningPaths(method))
|
||||
{
|
||||
var accessPath = _names.MakeMemberAccessPathFieldName(method, path);
|
||||
var methodName = _names.MakePipeliningSupportExtensionMethodName(path);
|
||||
var capType = path[path.Count - 1].Type;
|
||||
var capTypeSyntax = _names.MakeTypeSyntax(capType, null, TypeUsage.DomainClass);
|
||||
|
||||
if (!_existingExtensionMethods.Add((capTypeSyntax.ToString(), methodName.ToString())))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var pathDecl = FieldDeclaration(
|
||||
VariableDeclaration(
|
||||
IdentifierName(nameof(Capnp.Rpc.MemberAccessPath)))
|
||||
.AddVariables(
|
||||
VariableDeclarator(
|
||||
accessPath.Identifier)
|
||||
.WithInitializer(
|
||||
EqualsValueClause(
|
||||
ObjectCreationExpression(
|
||||
IdentifierName(nameof(Capnp.Rpc.MemberAccessPath)))
|
||||
.AddArgumentListArguments(
|
||||
path.Select(
|
||||
f => Argument(
|
||||
LiteralExpression(SyntaxKind.NumericLiteralExpression,
|
||||
Literal(f.Offset)))).ToArray())))))
|
||||
.AddModifiers(Static, Readonly);
|
||||
|
||||
|
||||
var methodDecl = MethodDeclaration(
|
||||
capTypeSyntax,
|
||||
methodName.Identifier)
|
||||
.AddModifiers(Public, Static)
|
||||
.AddParameterListParameters(
|
||||
Parameter(
|
||||
_names.TaskParameter.Identifier)
|
||||
.AddModifiers(This)
|
||||
.WithType(TransformReturnType(method)))
|
||||
.AddBodyStatements(
|
||||
ReturnStatement(
|
||||
CastExpression(
|
||||
capTypeSyntax,
|
||||
InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(nameof(Capnp.Rpc.CapabilityReflection)),
|
||||
GenericName(
|
||||
Identifier(nameof(Capnp.Rpc.CapabilityReflection.CreateProxy)))
|
||||
.AddTypeArgumentListArguments(
|
||||
capTypeSyntax)))
|
||||
.AddArgumentListArguments(
|
||||
Argument(
|
||||
InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(nameof(Capnp.Rpc.Impatient)),
|
||||
IdentifierName(nameof(Capnp.Rpc.Impatient.GetAnswer))))
|
||||
.AddArgumentListArguments(
|
||||
Argument(
|
||||
_names.TaskParameter.IdentifierName)),
|
||||
IdentifierName(nameof(Capnp.Rpc.IPromisedAnswer.Access))))
|
||||
.AddArgumentListArguments(
|
||||
Argument(
|
||||
accessPath.IdentifierName)))))));
|
||||
|
||||
classDecl = classDecl.AddMembers(pathDecl, methodDecl);
|
||||
}
|
||||
}
|
||||
|
||||
return classDecl;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace CapnpC.Generator
|
||||
{
|
||||
class Name
|
||||
{
|
||||
readonly string _name;
|
||||
|
||||
public Name(string name)
|
||||
{
|
||||
_name = name ?? throw new ArgumentNullException(nameof(name));
|
||||
IdentifierName = SyntaxFactory.IdentifierName(_name);
|
||||
Identifier = SyntaxFactory.Identifier(_name);
|
||||
VariableDeclarator = SyntaxFactory.VariableDeclarator(_name);
|
||||
}
|
||||
|
||||
public IdentifierNameSyntax IdentifierName { get; }
|
||||
public SyntaxToken Identifier { get; }
|
||||
public VariableDeclaratorSyntax VariableDeclarator { get; }
|
||||
|
||||
public override string ToString() => _name;
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Name other && _name == other._name;
|
||||
}
|
||||
|
||||
public override int GetHashCode() => _name.GetHashCode();
|
||||
}
|
||||
}
|
@ -1,715 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using CapnpC.Model;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
using static CapnpC.Generator.SyntaxHelpers;
|
||||
|
||||
namespace CapnpC.Generator
|
||||
{
|
||||
class ReaderSnippetGen
|
||||
{
|
||||
readonly GenNames _names;
|
||||
|
||||
public ReaderSnippetGen(GenNames names)
|
||||
{
|
||||
_names = names;
|
||||
}
|
||||
|
||||
MemberDeclarationSyntax MakeReaderImplicitConversionOperator1()
|
||||
{
|
||||
return ConversionOperatorDeclaration(
|
||||
Token(SyntaxKind.ImplicitKeyword),
|
||||
IdentifierName(nameof(Capnp.DeserializerState)))
|
||||
.WithModifiers(
|
||||
TokenList(
|
||||
new[]{
|
||||
Token(SyntaxKind.PublicKeyword),
|
||||
Token(SyntaxKind.StaticKeyword)}))
|
||||
.WithParameterList(
|
||||
ParameterList(
|
||||
SingletonSeparatedList<ParameterSyntax>(
|
||||
Parameter(_names.ReaderParameter.Identifier)
|
||||
.WithType(_names.ReaderStruct.IdentifierName))))
|
||||
.WithExpressionBody(
|
||||
ArrowExpressionClause(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.ReaderParameter.IdentifierName,
|
||||
_names.ReaderContextField.IdentifierName)))
|
||||
.WithSemicolonToken(
|
||||
Token(SyntaxKind.SemicolonToken));
|
||||
}
|
||||
|
||||
MemberDeclarationSyntax MakeReaderImplicitConversionOperator2()
|
||||
{
|
||||
return ConversionOperatorDeclaration(
|
||||
Token(SyntaxKind.ImplicitKeyword),
|
||||
_names.ReaderStruct.IdentifierName)
|
||||
.WithModifiers(
|
||||
TokenList(
|
||||
new[] {
|
||||
Token(SyntaxKind.PublicKeyword),
|
||||
Token(SyntaxKind.StaticKeyword) }))
|
||||
.WithParameterList(
|
||||
ParameterList(
|
||||
SingletonSeparatedList<ParameterSyntax>(
|
||||
Parameter(_names.ReaderContextField.Identifier)
|
||||
.WithType(Type<Capnp.DeserializerState>()))))
|
||||
.WithExpressionBody(
|
||||
ArrowExpressionClause(
|
||||
ObjectCreationExpression(_names.ReaderStruct.IdentifierName)
|
||||
.WithArgumentList(
|
||||
ArgumentList(
|
||||
SingletonSeparatedList<ArgumentSyntax>(
|
||||
Argument(_names.ReaderContextField.IdentifierName))))))
|
||||
.WithSemicolonToken(
|
||||
Token(SyntaxKind.SemicolonToken));
|
||||
}
|
||||
|
||||
MemberDeclarationSyntax MakeReaderCreateMethod()
|
||||
{
|
||||
return MethodDeclaration(_names.ReaderStruct.IdentifierName, _names.ReaderCreateMethod.Identifier)
|
||||
.AddModifiers(Public, Static)
|
||||
.WithParameterList(
|
||||
ParameterList(
|
||||
SingletonSeparatedList(
|
||||
Parameter(_names.ContextParameter.Identifier)
|
||||
.WithType(
|
||||
Type<Capnp.DeserializerState>()))))
|
||||
.WithExpressionBody(
|
||||
ArrowExpressionClause(
|
||||
ObjectCreationExpression(_names.ReaderStruct.IdentifierName)
|
||||
.AddArgumentListArguments(Argument(_names.ContextParameter.IdentifierName))))
|
||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
|
||||
}
|
||||
|
||||
IEnumerable<MemberDeclarationSyntax> MakeReaderStructMembers()
|
||||
{
|
||||
yield return FieldDeclaration(
|
||||
VariableDeclaration(
|
||||
Type<Capnp.DeserializerState>())
|
||||
.AddVariables(_names.ReaderContextField.VariableDeclarator))
|
||||
.AddModifiers(Readonly);
|
||||
|
||||
yield return ConstructorDeclaration(_names.ReaderStruct.Identifier)
|
||||
.AddModifiers(Public)
|
||||
.WithParameterList(
|
||||
ParameterList(
|
||||
SingletonSeparatedList(
|
||||
Parameter(_names.ContextParameter.Identifier)
|
||||
.WithType(Type<Capnp.DeserializerState>()))))
|
||||
.WithBody(
|
||||
Block(
|
||||
SingletonList<StatementSyntax>(
|
||||
ExpressionStatement(
|
||||
AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
ThisExpression(),
|
||||
_names.ReaderContextField.IdentifierName),
|
||||
_names.ContextParameter.IdentifierName)))));
|
||||
|
||||
yield return MakeReaderCreateMethod();
|
||||
yield return MakeReaderImplicitConversionOperator1();
|
||||
yield return MakeReaderImplicitConversionOperator2();
|
||||
}
|
||||
|
||||
IEnumerable<MemberDeclarationSyntax> MakeGroupReaderStructMembers()
|
||||
{
|
||||
yield return FieldDeclaration(
|
||||
VariableDeclaration(
|
||||
Type<Capnp.DeserializerState>())
|
||||
.AddVariables(_names.ReaderContextField.VariableDeclarator))
|
||||
.AddModifiers(Readonly);
|
||||
|
||||
yield return ConstructorDeclaration(_names.ReaderStruct.Identifier)
|
||||
.AddModifiers(Public)
|
||||
.WithParameterList(
|
||||
ParameterList(
|
||||
SingletonSeparatedList(Parameter(_names.GroupReaderContextArg.Identifier)
|
||||
.WithType(Type<Capnp.DeserializerState>()))))
|
||||
.WithBody(
|
||||
Block(
|
||||
SingletonList<StatementSyntax>(
|
||||
ExpressionStatement(
|
||||
AssignmentExpression(
|
||||
SyntaxKind.SimpleAssignmentExpression,
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
ThisExpression(),
|
||||
_names.ReaderContextField.IdentifierName),
|
||||
_names.GroupReaderContextArg.IdentifierName)))));
|
||||
|
||||
yield return MakeReaderCreateMethod();
|
||||
yield return MakeReaderImplicitConversionOperator1();
|
||||
yield return MakeReaderImplicitConversionOperator2();
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeReaderProperty(TypeSyntax type, string name, ExpressionSyntax right, bool cond)
|
||||
{
|
||||
if (cond)
|
||||
{
|
||||
right = ConditionalExpression(
|
||||
BinaryExpression(
|
||||
SyntaxKind.EqualsExpression,
|
||||
_names.UnionDiscriminatorProp.IdentifierName,
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.UnionDiscriminatorEnum.IdentifierName,
|
||||
IdentifierName(name))),
|
||||
right,
|
||||
LiteralExpression(
|
||||
SyntaxKind.DefaultLiteralExpression,
|
||||
Token(SyntaxKind.DefaultKeyword)));
|
||||
}
|
||||
|
||||
return PropertyDeclaration(type, name)
|
||||
.AddModifiers(Public)
|
||||
.WithExpressionBody(ArrowExpressionClause(right))
|
||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
|
||||
}
|
||||
|
||||
static Func<ExpressionSyntax, ExpressionSyntax> MakeCastFunc(TypeSyntax type) =>
|
||||
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,
|
||||
object indexOrBitOffset, ExpressionSyntax secondArg,
|
||||
Func<ExpressionSyntax, ExpressionSyntax> cast, bool cond)
|
||||
{
|
||||
var right = InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.ReaderContextField.IdentifierName,
|
||||
readName))
|
||||
.AddArgumentListArguments(
|
||||
Argument(ValueOf(indexOrBitOffset)));
|
||||
|
||||
if (secondArg != null)
|
||||
{
|
||||
right = right.AddArgumentListArguments(Argument(secondArg));
|
||||
}
|
||||
|
||||
ExpressionSyntax expr = right;
|
||||
|
||||
if (cast != null)
|
||||
{
|
||||
expr = cast(expr);
|
||||
}
|
||||
|
||||
return MakeReaderProperty(type, name, expr, cond);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeReadProperty(TypeSyntax type, string name, string readName,
|
||||
object indexOrBitOffset, ExpressionSyntax secondArg,
|
||||
Func<ExpressionSyntax, ExpressionSyntax> cast, bool cond)
|
||||
{
|
||||
return MakeReadProperty(type, name, IdentifierName(readName), indexOrBitOffset, secondArg, cast, cond);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeReadPrimitiveProperty<T>(Field field, string readName)
|
||||
{
|
||||
return MakeReadProperty(Type<T>(), _names.GetCodeIdentifier(field).ToString(), readName, field.BitOffset.Value,
|
||||
ValueOf(field.DefaultValue.ScalarValue), null, field.DiscValue.HasValue);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeReadEnumProperty(Field field)
|
||||
{
|
||||
var typeSyntax = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader);
|
||||
return MakeReadProperty(typeSyntax,
|
||||
_names.GetCodeIdentifier(field).ToString(),
|
||||
nameof(Capnp.SerializerExtensions.ReadDataUShort), field.BitOffset.Value,
|
||||
ValueOf(field.DefaultValue.ScalarValue),
|
||||
x => CastExpression(typeSyntax, x),
|
||||
field.DiscValue.HasValue);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeReadTextProperty(Field field)
|
||||
{
|
||||
return MakeReadProperty(Type<string>(), _names.GetCodeIdentifier(field).ToString(),
|
||||
nameof(Capnp.DeserializerState.ReadText), (int)field.Offset,
|
||||
ValueOf(field.DefaultValue.ScalarValue), null, field.DiscValue.HasValue);
|
||||
}
|
||||
|
||||
MemberAccessExpressionSyntax MakeReaderCreator(TypeSyntax qtype)
|
||||
{
|
||||
return MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
qtype,
|
||||
_names.ReaderCreateMethod.IdentifierName);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeReadStructProperty(Field field)
|
||||
{
|
||||
var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader);
|
||||
var creator = MakeReaderCreator(qtype);
|
||||
|
||||
return MakeReadProperty(qtype, _names.GetCodeIdentifier(field).ToString(),
|
||||
nameof(Capnp.DeserializerState.ReadStruct), (int)field.Offset,
|
||||
creator, null, field.DiscValue.HasValue);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeReadGroupProperty(Field field)
|
||||
{
|
||||
var type = QualifiedName(
|
||||
_names.MakeTypeName(field.Type.Definition).IdentifierName,
|
||||
_names.ReaderStruct.IdentifierName);
|
||||
|
||||
var right = ObjectCreationExpression(type)
|
||||
.WithArgumentList(
|
||||
ArgumentList(
|
||||
SingletonSeparatedList(
|
||||
Argument(_names.ReaderContextField.IdentifierName))));
|
||||
|
||||
return MakeReaderProperty(type, _names.GetCodeIdentifier(field).ToString(), right, field.DiscValue.HasValue);
|
||||
}
|
||||
|
||||
void MakeReadListPropertyImpl(Model.Type elementType, TypeDefinition scope, ExpressionSyntax context, int depth,
|
||||
out TypeSyntax listType, out ExpressionSyntax impl)
|
||||
{
|
||||
var elementTypeSyntax = _names.MakeTypeSyntax(elementType, scope, TypeUsage.Reader);
|
||||
listType = GenericName("IReadOnlyList").AddTypeArgumentListArguments(elementTypeSyntax);
|
||||
|
||||
if (elementType.Tag == TypeTag.Interface ||
|
||||
elementType.Tag == TypeTag.CapabilityPointer)
|
||||
{
|
||||
if (depth == 0)
|
||||
{
|
||||
impl = InvocationExpression(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.ReaderContextField.IdentifierName,
|
||||
GenericName(nameof(Capnp.DeserializerState.ReadCapList))
|
||||
.AddTypeArgumentListArguments(elementTypeSyntax)
|
||||
)).AddArgumentListArguments(Argument(context));
|
||||
}
|
||||
else
|
||||
{
|
||||
impl = InvocationExpression(
|
||||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||
context,
|
||||
GenericName(nameof(Capnp.DeserializerState.RequireCapList))
|
||||
.AddTypeArgumentListArguments(elementTypeSyntax)
|
||||
));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (depth == 0)
|
||||
{
|
||||
context = InvocationExpression(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.ReaderContextField.IdentifierName,
|
||||
IdentifierName(nameof(Capnp.DeserializerState.ReadList))))
|
||||
.AddArgumentListArguments(Argument(context));
|
||||
}
|
||||
else
|
||||
{
|
||||
context = InvocationExpression(
|
||||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||
context,
|
||||
IdentifierName(nameof(Capnp.DeserializerState.RequireList))
|
||||
));
|
||||
}
|
||||
|
||||
string lambdaParamName = "_" + depth;
|
||||
var lambdaParam = Parameter(Identifier(lambdaParamName));
|
||||
var lambdaArg = IdentifierName(lambdaParamName);
|
||||
string castFuncName;
|
||||
|
||||
switch (elementType.Tag)
|
||||
{
|
||||
case TypeTag.List:
|
||||
{
|
||||
|
||||
MakeReadListPropertyImpl(
|
||||
elementType.ElementType,
|
||||
scope,
|
||||
lambdaArg,
|
||||
depth + 1,
|
||||
out var innerListType,
|
||||
out var innerImpl);
|
||||
|
||||
listType = GenericName("IReadOnlyList").AddTypeArgumentListArguments(innerListType);
|
||||
|
||||
impl = InvocationExpression(
|
||||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||
context,
|
||||
IdentifierName(nameof(Capnp.ListDeserializer.Cast))))
|
||||
.AddArgumentListArguments(
|
||||
Argument(SimpleLambdaExpression(lambdaParam, innerImpl)));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case TypeTag.ListPointer:
|
||||
{
|
||||
listType = Type<IReadOnlyList<object>>();
|
||||
|
||||
context = InvocationExpression(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
context,
|
||||
IdentifierName(nameof(Capnp.DeserializerState.RequireList))
|
||||
)).AddArgumentListArguments(Argument(lambdaArg));
|
||||
|
||||
impl = InvocationExpression(
|
||||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||
context,
|
||||
IdentifierName(nameof(Capnp.ReadOnlyListExtensions.LazyListSelect))))
|
||||
.AddArgumentListArguments(
|
||||
Argument(SimpleLambdaExpression(lambdaParam, context)));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case TypeTag.Struct:
|
||||
{
|
||||
impl = InvocationExpression(
|
||||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||
context,
|
||||
IdentifierName(nameof(Capnp.ListDeserializer.Cast))))
|
||||
.AddArgumentListArguments(
|
||||
Argument(MakeReaderCreator(elementTypeSyntax)));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case TypeTag.Enum:
|
||||
{
|
||||
var cons = SimpleLambdaExpression(
|
||||
lambdaParam,
|
||||
CastExpression(elementTypeSyntax, lambdaArg));
|
||||
|
||||
impl = InvocationExpression(
|
||||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||
context,
|
||||
IdentifierName(nameof(Capnp.ListDeserializer.CastEnums))))
|
||||
.AddArgumentListArguments(
|
||||
Argument(cons));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case TypeTag.AnyPointer:
|
||||
case TypeTag.StructPointer:
|
||||
{
|
||||
listType = Type<IReadOnlyList<Capnp.DeserializerState>>();
|
||||
impl = context;
|
||||
return;
|
||||
}
|
||||
|
||||
case TypeTag.Void:
|
||||
{
|
||||
listType = Type<int>();
|
||||
impl = MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
context,
|
||||
IdentifierName(nameof(Capnp.ListDeserializer.Count)));
|
||||
return;
|
||||
}
|
||||
|
||||
case TypeTag.Data:
|
||||
castFuncName = nameof(Capnp.ListDeserializer.CastData);
|
||||
break;
|
||||
|
||||
case TypeTag.Text:
|
||||
castFuncName = nameof(Capnp.ListDeserializer.CastText2);
|
||||
break;
|
||||
|
||||
case TypeTag.Bool:
|
||||
castFuncName = nameof(Capnp.ListDeserializer.CastBool);
|
||||
break;
|
||||
|
||||
case TypeTag.F32:
|
||||
castFuncName = nameof(Capnp.ListDeserializer.CastFloat);
|
||||
break;
|
||||
|
||||
case TypeTag.F64:
|
||||
castFuncName = nameof(Capnp.ListDeserializer.CastDouble);
|
||||
break;
|
||||
|
||||
case TypeTag.S8:
|
||||
castFuncName = nameof(Capnp.ListDeserializer.CastSByte);
|
||||
break;
|
||||
|
||||
case TypeTag.U8:
|
||||
castFuncName = nameof(Capnp.ListDeserializer.CastByte);
|
||||
break;
|
||||
|
||||
case TypeTag.S16:
|
||||
castFuncName = nameof(Capnp.ListDeserializer.CastShort);
|
||||
break;
|
||||
|
||||
case TypeTag.U16:
|
||||
case TypeTag.AnyEnum:
|
||||
castFuncName = nameof(Capnp.ListDeserializer.CastUShort);
|
||||
break;
|
||||
|
||||
case TypeTag.S32:
|
||||
castFuncName = nameof(Capnp.ListDeserializer.CastInt);
|
||||
break;
|
||||
|
||||
case TypeTag.U32:
|
||||
castFuncName = nameof(Capnp.ListDeserializer.CastUInt);
|
||||
break;
|
||||
|
||||
case TypeTag.S64:
|
||||
castFuncName = nameof(Capnp.ListDeserializer.CastLong);
|
||||
break;
|
||||
|
||||
case TypeTag.U64:
|
||||
castFuncName = nameof(Capnp.ListDeserializer.CastULong);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException("Unexpected type tag, don't know how to deal with this");
|
||||
}
|
||||
|
||||
impl = InvocationExpression(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
context,
|
||||
IdentifierName(castFuncName)));
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeReadListProperty(Field field)
|
||||
{
|
||||
var elementType = field.Type.ElementType;
|
||||
var context = ValueOf((int)field.Offset);
|
||||
MakeReadListPropertyImpl(elementType, field.DeclaringType, context, 0, out var listType, out var impl);
|
||||
return MakeReaderProperty(listType, _names.GetCodeIdentifier(field).ToString(), impl, field.DiscValue.HasValue);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeReadAnyListProperty(Field field)
|
||||
{
|
||||
var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader);
|
||||
|
||||
return MakeReadProperty(type, _names.GetCodeIdentifier(field).ToString(),
|
||||
nameof(Capnp.DeserializerState.ReadList),
|
||||
(int)field.Offset, null, x => CastExpression(type, x), field.DiscValue.HasValue);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeReadCapProperty(Field field)
|
||||
{
|
||||
var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader);
|
||||
var readName = GenericName(nameof(Capnp.DeserializerState.ReadCap))
|
||||
.AddTypeArgumentListArguments(type);
|
||||
|
||||
return MakeReadProperty(type, _names.GetCodeIdentifier(field).ToString(),
|
||||
readName,
|
||||
(int)field.Offset, null, null, field.DiscValue.HasValue);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeReadAnyCapProperty(Field field)
|
||||
{
|
||||
var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader);
|
||||
|
||||
return MakeReadProperty(type, _names.GetCodeIdentifier(field).ToString(),
|
||||
nameof(Capnp.DeserializerState.ReadCap),
|
||||
(int)field.Offset, null, null, field.DiscValue.HasValue);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeReadDataProperty(Field field)
|
||||
{
|
||||
var context = InvocationExpression(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.ReaderContextField.IdentifierName,
|
||||
IdentifierName(nameof(Capnp.DeserializerState.ReadList))))
|
||||
.AddArgumentListArguments(Argument(ValueOf((int)field.Offset)));
|
||||
var impl = InvocationExpression(MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
context,
|
||||
IdentifierName(nameof(Capnp.ListDeserializer.CastByte))));
|
||||
|
||||
return MakeReaderProperty(
|
||||
_names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader),
|
||||
_names.GetCodeIdentifier(field).ToString(), impl, field.DiscValue.HasValue);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeReadAnyPointerProperty(Field field)
|
||||
{
|
||||
var type = IdentifierName(nameof(Capnp.DeserializerState));
|
||||
|
||||
return MakeReadProperty(type, _names.GetCodeIdentifier(field).ToString(),
|
||||
nameof(Capnp.DeserializerState.StructReadPointer),
|
||||
(int)field.Offset, null, null, field.DiscValue.HasValue);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeReaderUnionSelector(TypeDefinition def)
|
||||
{
|
||||
var type = _names.UnionDiscriminatorEnum.IdentifierName;
|
||||
return MakeReadProperty(
|
||||
type,
|
||||
_names.UnionDiscriminatorProp.ToString(),
|
||||
nameof(Capnp.SerializerExtensions.ReadDataUShort),
|
||||
def.UnionInfo.TagOffset,
|
||||
ValueOf(default(ushort)),
|
||||
MakeCastFunc(type), false);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeReaderFieldProperty(Field field)
|
||||
{
|
||||
switch (field.Type.Tag)
|
||||
{
|
||||
case TypeTag.Bool:
|
||||
return MakeReadPrimitiveProperty<bool>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataBool));
|
||||
|
||||
case TypeTag.S8:
|
||||
return MakeReadPrimitiveProperty<sbyte>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataSByte));
|
||||
|
||||
case TypeTag.U8:
|
||||
return MakeReadPrimitiveProperty<byte>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataByte));
|
||||
|
||||
case TypeTag.S16:
|
||||
return MakeReadPrimitiveProperty<short>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataShort));
|
||||
|
||||
case TypeTag.U16:
|
||||
return MakeReadPrimitiveProperty<ushort>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataUShort));
|
||||
|
||||
case TypeTag.S32:
|
||||
return MakeReadPrimitiveProperty<int>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataInt));
|
||||
|
||||
case TypeTag.U32:
|
||||
return MakeReadPrimitiveProperty<uint>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataUInt));
|
||||
|
||||
case TypeTag.S64:
|
||||
return MakeReadPrimitiveProperty<long>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataLong));
|
||||
|
||||
case TypeTag.U64:
|
||||
return MakeReadPrimitiveProperty<ulong>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataULong));
|
||||
|
||||
case TypeTag.F32:
|
||||
return MakeReadPrimitiveProperty<float>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataFloat));
|
||||
|
||||
case TypeTag.F64:
|
||||
return MakeReadPrimitiveProperty<double>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataDouble));
|
||||
|
||||
case TypeTag.Enum:
|
||||
return MakeReadEnumProperty(field);
|
||||
|
||||
case TypeTag.Text:
|
||||
return MakeReadTextProperty(field);
|
||||
|
||||
case TypeTag.Struct:
|
||||
return MakeReadStructProperty(field);
|
||||
|
||||
case TypeTag.Group:
|
||||
return MakeReadGroupProperty(field);
|
||||
|
||||
case TypeTag.List:
|
||||
return MakeReadListProperty(field);
|
||||
|
||||
case TypeTag.Interface:
|
||||
return MakeReadCapProperty(field);
|
||||
|
||||
case TypeTag.CapabilityPointer:
|
||||
return MakeReadAnyCapProperty(field);
|
||||
|
||||
case TypeTag.ListPointer:
|
||||
return MakeReadAnyListProperty(field);
|
||||
|
||||
case TypeTag.AnyPointer:
|
||||
case TypeTag.StructPointer:
|
||||
return MakeReadAnyPointerProperty(field);
|
||||
|
||||
case TypeTag.Data:
|
||||
return MakeReadDataProperty(field);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public StructDeclarationSyntax MakeReaderStruct(TypeDefinition def)
|
||||
{
|
||||
var readerDecl = StructDeclaration(_names.ReaderStruct.ToString()).AddModifiers(Public);
|
||||
|
||||
var members = def.Tag == TypeTag.Group ?
|
||||
MakeGroupReaderStructMembers() :
|
||||
MakeReaderStructMembers();
|
||||
|
||||
readerDecl = readerDecl.AddMembers(members.ToArray());
|
||||
|
||||
if (def.UnionInfo != null)
|
||||
{
|
||||
readerDecl = readerDecl.AddMembers(MakeReaderUnionSelector(def));
|
||||
}
|
||||
|
||||
foreach (var field in def.Fields)
|
||||
{
|
||||
var propDecl = MakeReaderFieldProperty(field);
|
||||
|
||||
if (propDecl != null)
|
||||
{
|
||||
readerDecl = readerDecl.AddMembers(propDecl);
|
||||
}
|
||||
}
|
||||
|
||||
return readerDecl;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
using Capnp;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace CapnpC.Generator
|
||||
{
|
||||
class SerializerStateWorder: SerializerState
|
||||
{
|
||||
public const string LinkName = nameof(SerializerStateWorder.Link);
|
||||
public const string SetStructName = nameof(SerializerStateWorder.SetStruct);
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace CapnpC.Generator
|
||||
{
|
||||
class SkeletonWorder : Capnp.Rpc.Skeleton<object>
|
||||
{
|
||||
public override ulong InterfaceId => throw new NotImplementedException();
|
||||
|
||||
public const string SetMethodTableName = nameof(SkeletonWorder.SetMethodTable);
|
||||
public const string ImplName = nameof(SkeletonWorder.Impl);
|
||||
}
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
|
||||
namespace CapnpC.Generator
|
||||
{
|
||||
static class SyntaxHelpers
|
||||
{
|
||||
public static string MakeCamel(string name) => $"{char.ToUpperInvariant(name[0])}{name.Substring(1)}";
|
||||
public static string MakeAllLower(string name) => $"@{name}";
|
||||
|
||||
public static readonly SyntaxToken Async = Token(SyntaxKind.AsyncKeyword);
|
||||
public static readonly SyntaxToken Public = Token(SyntaxKind.PublicKeyword);
|
||||
public static readonly SyntaxToken Private = Token(SyntaxKind.PrivateKeyword);
|
||||
public static readonly SyntaxToken Readonly = Token(SyntaxKind.ReadOnlyKeyword);
|
||||
public static readonly SyntaxToken Static = Token(SyntaxKind.StaticKeyword);
|
||||
public static readonly SyntaxToken Override = Token(SyntaxKind.OverrideKeyword);
|
||||
public static readonly SyntaxToken Partial = Token(SyntaxKind.PartialKeyword);
|
||||
public static readonly SyntaxToken This = Token(SyntaxKind.ThisKeyword);
|
||||
|
||||
public static TypeSyntax Type(Type type)
|
||||
{
|
||||
switch (0)
|
||||
{
|
||||
case 0 when type == typeof(bool):
|
||||
return PredefinedType(Token(SyntaxKind.BoolKeyword));
|
||||
|
||||
case 0 when type == typeof(sbyte):
|
||||
return PredefinedType(Token(SyntaxKind.SByteKeyword));
|
||||
|
||||
case 0 when type == typeof(byte):
|
||||
return PredefinedType(Token(SyntaxKind.ByteKeyword));
|
||||
|
||||
case 0 when type == typeof(short):
|
||||
return PredefinedType(Token(SyntaxKind.ShortKeyword));
|
||||
|
||||
case 0 when type == typeof(ushort):
|
||||
return PredefinedType(Token(SyntaxKind.UShortKeyword));
|
||||
|
||||
case 0 when type == typeof(int):
|
||||
return PredefinedType(Token(SyntaxKind.IntKeyword));
|
||||
|
||||
case 0 when type == typeof(uint):
|
||||
return PredefinedType(Token(SyntaxKind.UIntKeyword));
|
||||
|
||||
case 0 when type == typeof(long):
|
||||
return PredefinedType(Token(SyntaxKind.LongKeyword));
|
||||
|
||||
case 0 when type == typeof(ulong):
|
||||
return PredefinedType(Token(SyntaxKind.ULongKeyword));
|
||||
|
||||
case 0 when type == typeof(float):
|
||||
return PredefinedType(Token(SyntaxKind.FloatKeyword));
|
||||
|
||||
case 0 when type == typeof(double):
|
||||
return PredefinedType(Token(SyntaxKind.DoubleKeyword));
|
||||
|
||||
case 0 when type == typeof(string):
|
||||
return PredefinedType(Token(SyntaxKind.StringKeyword));
|
||||
|
||||
case 0 when type == typeof(object):
|
||||
return PredefinedType(Token(SyntaxKind.ObjectKeyword));
|
||||
|
||||
case 0 when type.IsGenericType:
|
||||
return GenericName(type.Name.Substring(0, type.Name.IndexOf('`')))
|
||||
.AddTypeArgumentListArguments(type.GetGenericArguments().Select(Type).ToArray());
|
||||
|
||||
default:
|
||||
return ParseTypeName(type.Name);
|
||||
}
|
||||
}
|
||||
|
||||
public static TypeSyntax Type<T>() => Type(typeof(T));
|
||||
|
||||
public static ExpressionSyntax ValueOf(object value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case bool x:
|
||||
return LiteralExpression(x ? SyntaxKind.TrueLiteralExpression : SyntaxKind.FalseLiteralExpression);
|
||||
|
||||
case sbyte x:
|
||||
return CastExpression(Type<sbyte>(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)));
|
||||
|
||||
case byte x:
|
||||
return CastExpression(Type<byte>(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)));
|
||||
|
||||
case short x:
|
||||
return CastExpression(Type<short>(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)));
|
||||
|
||||
case ushort x:
|
||||
return CastExpression(Type<ushort>(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)));
|
||||
|
||||
case int x:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x));
|
||||
|
||||
case uint x:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x));
|
||||
|
||||
case long x:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x));
|
||||
|
||||
case ulong x:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x));
|
||||
|
||||
case float x:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x));
|
||||
|
||||
case double x:
|
||||
return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x));
|
||||
|
||||
case string x:
|
||||
return LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(x));
|
||||
|
||||
case null:
|
||||
return LiteralExpression(SyntaxKind.NullLiteralExpression);
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,406 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using CapnpC.Model;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
using static CapnpC.Generator.SyntaxHelpers;
|
||||
|
||||
namespace CapnpC.Generator
|
||||
{
|
||||
class WriterSnippetGen
|
||||
{
|
||||
readonly GenNames _names;
|
||||
|
||||
public WriterSnippetGen(GenNames names)
|
||||
{
|
||||
_names = names;
|
||||
}
|
||||
|
||||
IEnumerable<MemberDeclarationSyntax> MakeWriterStructMembers(TypeDefinition structType)
|
||||
{
|
||||
yield return ConstructorDeclaration(_names.WriterStruct.Identifier)
|
||||
.AddModifiers(Public)
|
||||
.WithBody(
|
||||
Block(
|
||||
ExpressionStatement(
|
||||
InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
ThisExpression(),
|
||||
IdentifierName(SerializerStateWorder.SetStructName)))
|
||||
.AddArgumentListArguments(
|
||||
Argument(
|
||||
LiteralExpression(
|
||||
SyntaxKind.NumericLiteralExpression,
|
||||
Literal(structType.StructDataWordCount))),
|
||||
Argument(
|
||||
LiteralExpression(
|
||||
SyntaxKind.NumericLiteralExpression,
|
||||
Literal(structType.StructPointerCount)))))));
|
||||
}
|
||||
|
||||
IEnumerable<MemberDeclarationSyntax> MakeGroupWriterStructMembers()
|
||||
{
|
||||
yield return ConstructorDeclaration(_names.WriterStruct.Identifier)
|
||||
.AddModifiers(Public)
|
||||
.WithBody(Block());
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeWriterProperty(
|
||||
TypeSyntax type,
|
||||
string name,
|
||||
ExpressionSyntax getter,
|
||||
ExpressionSyntax setter,
|
||||
bool cast,
|
||||
bool cond)
|
||||
{
|
||||
if (cast)
|
||||
{
|
||||
getter = CastExpression(type, getter);
|
||||
}
|
||||
|
||||
if (cond)
|
||||
{
|
||||
getter = ConditionalExpression(
|
||||
BinaryExpression(
|
||||
SyntaxKind.EqualsExpression,
|
||||
_names.UnionDiscriminatorProp.IdentifierName,
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
_names.UnionDiscriminatorEnum.IdentifierName,
|
||||
IdentifierName(name))),
|
||||
getter,
|
||||
LiteralExpression(
|
||||
SyntaxKind.DefaultLiteralExpression,
|
||||
Token(SyntaxKind.DefaultKeyword)));
|
||||
}
|
||||
|
||||
var accessors = new AccessorDeclarationSyntax[setter != null ? 2 : 1];
|
||||
|
||||
accessors[0] = AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
|
||||
.WithExpressionBody(ArrowExpressionClause(getter))
|
||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
|
||||
|
||||
if (setter != null)
|
||||
{
|
||||
accessors[1] = AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
|
||||
.WithExpressionBody(ArrowExpressionClause(setter))
|
||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
|
||||
}
|
||||
|
||||
return PropertyDeclaration(type, name)
|
||||
.AddModifiers(Public)
|
||||
.AddAccessorListAccessors(accessors);
|
||||
}
|
||||
|
||||
ExpressionSyntax MakePointerSyntax(TypeSyntax type, object index) =>
|
||||
InvocationExpression(
|
||||
GenericName(nameof(Capnp.SerializerState.BuildPointer))
|
||||
.AddTypeArgumentListArguments(type))
|
||||
.AddArgumentListArguments(
|
||||
Argument(ValueOf(index)));
|
||||
|
||||
ExpressionSyntax MakeReadCapSyntax(TypeSyntax type, object index) =>
|
||||
InvocationExpression(
|
||||
GenericName(nameof(Capnp.SerializerState.ReadCap))
|
||||
.AddTypeArgumentListArguments(type))
|
||||
.AddArgumentListArguments(
|
||||
Argument(ValueOf(index)));
|
||||
|
||||
ExpressionSyntax MakeTypedPointerSyntax(object index, TypeSyntax type) =>
|
||||
InvocationExpression(
|
||||
GenericName(nameof(Capnp.SerializerState.BuildPointer))
|
||||
.AddTypeArgumentListArguments(type))
|
||||
.AddArgumentListArguments(
|
||||
Argument(ValueOf(index)));
|
||||
|
||||
ExpressionSyntax MakeLinkSyntax(object index) =>
|
||||
InvocationExpression(
|
||||
IdentifierName(SerializerStateWorder.LinkName))
|
||||
.AddArgumentListArguments(
|
||||
Argument(ValueOf(index)),
|
||||
Argument(IdentifierName("value")));
|
||||
|
||||
ExpressionSyntax MakeLinkObjectSyntax(object index) =>
|
||||
InvocationExpression(
|
||||
IdentifierName(nameof(Capnp.SerializerState.LinkObject)))
|
||||
.AddArgumentListArguments(
|
||||
Argument(ValueOf(index)),
|
||||
Argument(IdentifierName("value")));
|
||||
|
||||
PropertyDeclarationSyntax MakePointerProperty(TypeSyntax type, string name, object index, bool cast, bool cond)
|
||||
{
|
||||
ExpressionSyntax getter = MakePointerSyntax(type, index);
|
||||
ExpressionSyntax setter = MakeLinkSyntax(index);
|
||||
|
||||
return MakeWriterProperty(type, name, getter, setter, cast, cond);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakePointerAsStructProperty(
|
||||
TypeSyntax type, string name, object index,
|
||||
bool cast, bool cond)
|
||||
{
|
||||
ExpressionSyntax getter = MakeTypedPointerSyntax(index, type);
|
||||
ExpressionSyntax setter = MakeLinkSyntax(index);
|
||||
|
||||
return MakeWriterProperty(type, name, getter, setter, cast, cond);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeProperty(
|
||||
TypeSyntax outerType,
|
||||
TypeSyntax innerType,
|
||||
string name,
|
||||
string readName,
|
||||
string writeName,
|
||||
object indexOrBitOffset,
|
||||
ExpressionSyntax secondArg,
|
||||
bool cast,
|
||||
bool cond,
|
||||
bool pasd)
|
||||
{
|
||||
var getter = InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
ThisExpression(),
|
||||
IdentifierName(readName)))
|
||||
.AddArgumentListArguments(
|
||||
Argument(ValueOf(indexOrBitOffset)),
|
||||
Argument(secondArg));
|
||||
|
||||
ExpressionSyntax value = IdentifierName("value");
|
||||
|
||||
if (cast)
|
||||
{
|
||||
value = CastExpression(innerType, value);
|
||||
}
|
||||
|
||||
var setter = InvocationExpression(
|
||||
MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
ThisExpression(),
|
||||
IdentifierName(writeName)))
|
||||
.AddArgumentListArguments(
|
||||
Argument(ValueOf(indexOrBitOffset)),
|
||||
Argument(value),
|
||||
Argument(secondArg));
|
||||
|
||||
if (pasd)
|
||||
{
|
||||
setter.AddArgumentListArguments(Argument(secondArg));
|
||||
}
|
||||
|
||||
return MakeWriterProperty(outerType, name, getter, setter, cast, cond);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakePrimitiveProperty<T>(Field field, string readName)
|
||||
{
|
||||
return MakeProperty(Type<T>(), null, _names.GetCodeIdentifier(field).ToString(),
|
||||
readName,
|
||||
nameof(Capnp.SerializerExtensions.WriteData),
|
||||
field.BitOffset.Value,
|
||||
ValueOf(field.DefaultValue.ScalarValue),
|
||||
false,
|
||||
field.DiscValue.HasValue,
|
||||
true);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeEnumProperty(Field field, string readName)
|
||||
{
|
||||
return MakeProperty(_names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.NotRelevant), Type<ushort>(),
|
||||
_names.GetCodeIdentifier(field).ToString(),
|
||||
readName,
|
||||
nameof(Capnp.SerializerExtensions.WriteData),
|
||||
field.BitOffset.Value,
|
||||
ValueOf(field.DefaultValue.ScalarValue),
|
||||
true,
|
||||
field.DiscValue.HasValue,
|
||||
true);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeTextProperty(Field field)
|
||||
{
|
||||
return MakeProperty(Type<string>(), null,
|
||||
_names.GetCodeIdentifier(field).ToString(),
|
||||
nameof(Capnp.SerializerState.ReadText),
|
||||
nameof(Capnp.SerializerState.WriteText),
|
||||
(int)field.Offset,
|
||||
ValueOf(field.DefaultValue.ScalarValue),
|
||||
false,
|
||||
field.DiscValue.HasValue,
|
||||
false);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeStructProperty(Field field)
|
||||
{
|
||||
var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer);
|
||||
|
||||
return MakePointerAsStructProperty(qtype, _names.GetCodeIdentifier(field).ToString(),
|
||||
(int)field.Offset, false, field.DiscValue.HasValue);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeGroupProperty(Field field)
|
||||
{
|
||||
var type = QualifiedName(
|
||||
_names.MakeTypeName(field.Type.Definition).IdentifierName,
|
||||
_names.WriterStruct.IdentifierName);
|
||||
|
||||
var getter = InvocationExpression(
|
||||
GenericName(nameof(Capnp.SerializerState.Rewrap))
|
||||
.AddTypeArgumentListArguments(type));
|
||||
|
||||
return MakeWriterProperty(type, _names.GetCodeIdentifier(field).ToString(), getter, null, false, field.DiscValue.HasValue);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeListProperty(Field field)
|
||||
{
|
||||
var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer);
|
||||
|
||||
return MakePointerProperty(qtype, _names.GetCodeIdentifier(field).ToString(),
|
||||
(int)field.Offset, false, field.DiscValue.HasValue);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakePointerProperty(Field field)
|
||||
{
|
||||
var type = IdentifierName(nameof(Capnp.DynamicSerializerState));
|
||||
|
||||
return MakePointerProperty(type, _names.GetCodeIdentifier(field).ToString(), (int)field.Offset, false, field.DiscValue.HasValue);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeCapProperty(Field field)
|
||||
{
|
||||
var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer);
|
||||
int index = (int)field.Offset;
|
||||
string name = _names.GetCodeIdentifier(field).ToString();
|
||||
ExpressionSyntax getter = MakeReadCapSyntax(type, index);
|
||||
ExpressionSyntax setter = MakeLinkObjectSyntax(index);
|
||||
|
||||
return MakeWriterProperty(type, name, getter, setter, false, field.DiscValue.HasValue);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeWriterUnionSelector(TypeDefinition def)
|
||||
{
|
||||
return MakeProperty(
|
||||
_names.UnionDiscriminatorEnum.IdentifierName,
|
||||
Type<ushort>(),
|
||||
_names.UnionDiscriminatorProp.ToString(),
|
||||
nameof(Capnp.SerializerExtensions.ReadDataUShort),
|
||||
nameof(Capnp.SerializerExtensions.WriteData),
|
||||
def.UnionInfo.TagOffset,
|
||||
ValueOf(default(ushort)),
|
||||
true, false, true);
|
||||
}
|
||||
|
||||
PropertyDeclarationSyntax MakeWriterFieldProperty(Field field)
|
||||
{
|
||||
switch (field.Type.Tag)
|
||||
{
|
||||
case TypeTag.Bool:
|
||||
return MakePrimitiveProperty<bool>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataBool));
|
||||
|
||||
case TypeTag.S8:
|
||||
return MakePrimitiveProperty<sbyte>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataSByte));
|
||||
|
||||
case TypeTag.U8:
|
||||
return MakePrimitiveProperty<byte>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataByte));
|
||||
|
||||
case TypeTag.S16:
|
||||
return MakePrimitiveProperty<short>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataShort));
|
||||
|
||||
case TypeTag.U16:
|
||||
return MakePrimitiveProperty<ushort>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataUShort));
|
||||
|
||||
case TypeTag.S32:
|
||||
return MakePrimitiveProperty<int>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataInt));
|
||||
|
||||
case TypeTag.U32:
|
||||
return MakePrimitiveProperty<uint>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataUInt));
|
||||
|
||||
case TypeTag.S64:
|
||||
return MakePrimitiveProperty<long>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataLong));
|
||||
|
||||
case TypeTag.U64:
|
||||
return MakePrimitiveProperty<ulong>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataULong));
|
||||
|
||||
case TypeTag.F32:
|
||||
return MakePrimitiveProperty<float>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataFloat));
|
||||
|
||||
case TypeTag.F64:
|
||||
return MakePrimitiveProperty<double>(field,
|
||||
nameof(Capnp.SerializerExtensions.ReadDataDouble));
|
||||
|
||||
case TypeTag.Enum:
|
||||
return MakeEnumProperty(field, nameof(Capnp.SerializerExtensions.ReadDataUShort));
|
||||
|
||||
case TypeTag.Text:
|
||||
return MakeTextProperty(field);
|
||||
|
||||
case TypeTag.Struct:
|
||||
return MakeStructProperty(field);
|
||||
|
||||
case TypeTag.Group:
|
||||
return MakeGroupProperty(field);
|
||||
|
||||
case TypeTag.List:
|
||||
case TypeTag.Data:
|
||||
return MakeListProperty(field);
|
||||
|
||||
case TypeTag.AnyPointer:
|
||||
case TypeTag.StructPointer:
|
||||
case TypeTag.ListPointer:
|
||||
return MakePointerProperty(field);
|
||||
|
||||
case TypeTag.CapabilityPointer:
|
||||
case TypeTag.Interface:
|
||||
return MakeCapProperty(field);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ClassDeclarationSyntax MakeWriterStruct(TypeDefinition def)
|
||||
{
|
||||
var WriterDecl = ClassDeclaration(_names.WriterStruct.ToString())
|
||||
.AddModifiers(Public)
|
||||
.AddBaseListTypes(
|
||||
SimpleBaseType(IdentifierName(nameof(Capnp.SerializerState))));
|
||||
|
||||
var members = def.Tag == TypeTag.Group ?
|
||||
MakeGroupWriterStructMembers() :
|
||||
MakeWriterStructMembers(def);
|
||||
|
||||
WriterDecl = WriterDecl.AddMembers(members.ToArray());
|
||||
|
||||
if (def.UnionInfo != null)
|
||||
{
|
||||
WriterDecl = WriterDecl.AddMembers(MakeWriterUnionSelector(def));
|
||||
}
|
||||
|
||||
foreach (var field in def.Fields)
|
||||
{
|
||||
var propDecl = MakeWriterFieldProperty(field);
|
||||
|
||||
if (propDecl != null)
|
||||
{
|
||||
WriterDecl = WriterDecl.AddMembers(propDecl);
|
||||
}
|
||||
}
|
||||
|
||||
return WriterDecl;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
abstract class AbstractType
|
||||
{
|
||||
public TypeTag Tag { get; set; }
|
||||
protected List<Field> Fields { get; } = new List<Field>();
|
||||
|
||||
public uint? FixedBitWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Tag)
|
||||
{
|
||||
case TypeTag.Bool:
|
||||
return 1;
|
||||
|
||||
case TypeTag.U8:
|
||||
case TypeTag.S8:
|
||||
return 8;
|
||||
|
||||
case TypeTag.U16:
|
||||
case TypeTag.S16:
|
||||
case TypeTag.Enum:
|
||||
case TypeTag.AnyEnum:
|
||||
return 16;
|
||||
|
||||
case TypeTag.U32:
|
||||
case TypeTag.S32:
|
||||
case TypeTag.F32:
|
||||
return 32;
|
||||
|
||||
case TypeTag.U64:
|
||||
case TypeTag.S64:
|
||||
case TypeTag.F64:
|
||||
return 64;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
class Annotation : IDefinition
|
||||
{
|
||||
public ulong Id { get; }
|
||||
public bool IsGenerated { get; }
|
||||
public TypeTag Tag { get => TypeTag.Annotation; }
|
||||
public IHasNestedDefinitions DeclaringElement { get; }
|
||||
|
||||
public Type Type { get; set; }
|
||||
|
||||
public Annotation(ulong id, IHasNestedDefinitions parent)
|
||||
{
|
||||
Trace.Assert(parent != null);
|
||||
Id = id;
|
||||
IsGenerated = (parent as IDefinition).IsGenerated;
|
||||
DeclaringElement = parent;
|
||||
parent.NestedDefinitions.Add(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
class Constant : IDefinition
|
||||
{
|
||||
public ulong Id { get; }
|
||||
public bool IsGenerated { get; }
|
||||
public TypeTag Tag { get => TypeTag.Const; }
|
||||
public IHasNestedDefinitions DeclaringElement { get; }
|
||||
|
||||
public Value Value { get; set; }
|
||||
|
||||
public Constant(ulong id, IHasNestedDefinitions parent)
|
||||
{
|
||||
Trace.Assert(parent != null);
|
||||
Id = id;
|
||||
IsGenerated = (parent as IDefinition).IsGenerated;
|
||||
DeclaringElement = parent;
|
||||
parent.NestedDefinitions.Add(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
class DefinitionManager
|
||||
{
|
||||
readonly Dictionary<ulong, IDefinition> _id2def = new Dictionary<ulong, IDefinition>();
|
||||
|
||||
public GenFile CreateFile(ulong id, bool isGenerated)
|
||||
=> CreateId<GenFile>(id, () => new GenFile(id, isGenerated));
|
||||
public GenFile GetExistingFile(ulong id)
|
||||
=> GetId<GenFile>(id, TypeTag.File);
|
||||
|
||||
public TypeDefinition CreateTypeDef(ulong id, TypeTag tag, IHasNestedDefinitions decl)
|
||||
=> CreateId<TypeDefinition>(id, () => new TypeDefinition(tag, id, decl));
|
||||
public TypeDefinition GetExistingTypeDef(ulong id, TypeTag tag)
|
||||
{
|
||||
var def = GetId<TypeDefinition>(id, tag);
|
||||
if (def.Tag == TypeTag.Unknown) def.Tag = tag;
|
||||
return def;
|
||||
}
|
||||
|
||||
public Annotation CreateAnnotation(ulong id, IHasNestedDefinitions decl)
|
||||
=> CreateId<Annotation>(id, () => new Annotation(id, decl));
|
||||
public Annotation GetExistingAnnotation(ulong id)
|
||||
=> GetId<Annotation>(id, TypeTag.Annotation);
|
||||
|
||||
public Constant CreateConstant(ulong id, IHasNestedDefinitions decl)
|
||||
=> CreateId<Constant>(id, () => new Constant(id, decl));
|
||||
public Constant GetExistingConstant(ulong id)
|
||||
=> GetId<Constant>(id, TypeTag.Const);
|
||||
|
||||
public IDefinition GetExistingDef(ulong id, TypeTag tag)
|
||||
=> GetId<IDefinition>(id, tag);
|
||||
|
||||
public IEnumerable<GenFile> Files
|
||||
{
|
||||
get => _id2def.Values.Where(d => d.Tag == TypeTag.File).Select(f => f as GenFile);
|
||||
}
|
||||
|
||||
T CreateId<T>(ulong id, Func<IDefinition> creator) where T : class, IDefinition
|
||||
{
|
||||
if (_id2def.TryGetValue(id, out var d))
|
||||
{
|
||||
throw new ArgumentException(nameof(id), $"Attempting to redefine {d.Tag.ToString()} {id.StrId()} (as {nameof(T)}).");
|
||||
}
|
||||
var def = creator();
|
||||
_id2def.Add(id, def);
|
||||
return def as T;
|
||||
}
|
||||
|
||||
T GetId<T>(ulong id, TypeTag tag) where T : IDefinition
|
||||
{
|
||||
if (!_id2def.TryGetValue(id, out var anyDef))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException($"Attempting to retrieve nonexistent node {id.StrId()}.");
|
||||
}
|
||||
if (!(anyDef is T def) || (tag != TypeTag.Unknown && def.Tag != tag))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException($"Attempting to retrieve {tag.ToString()} {id.StrId()}, but found {anyDef.Tag.ToString()} instead.");
|
||||
}
|
||||
return def;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
class Enumerant
|
||||
{
|
||||
string _literal;
|
||||
public TypeDefinition TypeDefinition { get; set; }
|
||||
public string Literal {
|
||||
get => _literal;
|
||||
set => _literal = IdentifierRenamer.ToNonKeyword(value);
|
||||
}
|
||||
public ushort? Ordinal { get; set; }
|
||||
public int CodeOrder { get; set; }
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
class Field
|
||||
{
|
||||
public TypeDefinition DeclaringType { get; set; }
|
||||
public Field Parent { get; set; }
|
||||
public string Name { get; set; }
|
||||
public Type Type { get; set; }
|
||||
public Value DefaultValue { get; set; }
|
||||
public bool DefaultValueIsExplicit { get; set; }
|
||||
public ushort? DiscValue { get; set; }
|
||||
public uint Offset { get; set; }
|
||||
public int CodeOrder { get; set; }
|
||||
|
||||
public ulong? BitOffset => (ulong)Offset * Type?.FixedBitWidth;
|
||||
|
||||
public Field Clone()
|
||||
{
|
||||
var field = new Field()
|
||||
{
|
||||
DeclaringType = DeclaringType,
|
||||
Parent = Parent,
|
||||
Name = Name,
|
||||
Type = Type,
|
||||
DefaultValue = DefaultValue,
|
||||
DefaultValueIsExplicit = DefaultValueIsExplicit,
|
||||
DiscValue = DiscValue,
|
||||
Offset = Offset,
|
||||
CodeOrder = CodeOrder,
|
||||
};
|
||||
field.InheritFreeGenericParameters();
|
||||
return field;
|
||||
}
|
||||
|
||||
public void InheritFreeGenericParameters()
|
||||
{
|
||||
Type.InheritFreeParameters(DeclaringType);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Field other &&
|
||||
DeclaringType == other.DeclaringType &&
|
||||
Name == other.Name;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (DeclaringType, Name).GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
class GenFile: IDefinition, IHasNestedDefinitions
|
||||
{
|
||||
public ulong Id { get; }
|
||||
public bool IsGenerated { get; }
|
||||
public TypeTag Tag { get => TypeTag.File; }
|
||||
public IHasNestedDefinitions DeclaringElement { get => null; }
|
||||
|
||||
public string Name { get; set; }
|
||||
public string[] Namespace { get; set; }
|
||||
|
||||
public IEnumerable<TypeDefinition> NestedTypes { get => this.GetNestedTypes(); }
|
||||
public ICollection<IDefinition> NestedDefinitions { get; } = new List<IDefinition>();
|
||||
public ICollection<Constant> Constants { get; } = new List<Constant>();
|
||||
|
||||
public GenFile(ulong id, bool isGenerated)
|
||||
{
|
||||
Id = id;
|
||||
IsGenerated = isGenerated;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
class GenericParameter
|
||||
{
|
||||
public IHasGenericParameters DeclaringEntity { get; set; }
|
||||
public int Index { get; set; }
|
||||
public string Name => DeclaringEntity.GenericParameters[Index];
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
// Instead of equality by Name, we could instead take (DeclaringEntity, Index), but there is a caveat:
|
||||
// Since methods can also own generic parameters, we have different classes of declaring entities involved.
|
||||
// Both the method will define generic parameters, and the Cap'n'p-generated params/result structs.
|
||||
// Therefore we end in two GenericParameter instances, one with the Method as declaring entity, the
|
||||
// other one with the params/result type definition as declaring entity. They are semantically the same,
|
||||
// and the easy way to match them is by Name. Equality by Name is the only working choice, even though
|
||||
// it feels a little less reboust than by matching declaring entity + parameter position.
|
||||
return obj is GenericParameter other && Name == other.Name;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Name.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
static class HasGenericParameters
|
||||
{
|
||||
public static IEnumerable<GenericParameter> GetLocalTypeParameters(this IHasGenericParameters me)
|
||||
{
|
||||
for (int i = 0; i < me.GenericParameters.Count; i++)
|
||||
{
|
||||
yield return new GenericParameter()
|
||||
{
|
||||
DeclaringEntity = me,
|
||||
Index = i
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
interface IDefinition
|
||||
{
|
||||
ulong Id { get; }
|
||||
bool IsGenerated { get; }
|
||||
TypeTag Tag { get; }
|
||||
IHasNestedDefinitions DeclaringElement { get; }
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
interface IHasGenericParameters
|
||||
{
|
||||
List<string> GenericParameters { get; }
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
interface IHasNestedDefinitions
|
||||
{
|
||||
IEnumerable<TypeDefinition> NestedTypes { get; }
|
||||
ICollection<IDefinition> NestedDefinitions { get; }
|
||||
ICollection<Constant> Constants { get; }
|
||||
}
|
||||
|
||||
static partial class Extensions
|
||||
{
|
||||
public static IEnumerable<TypeDefinition> GetNestedTypes(this IHasNestedDefinitions def)
|
||||
=> def.NestedDefinitions.Select(d => d as TypeDefinition).Where(d => d != null);
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
public class IdentifierRenamer
|
||||
{
|
||||
public static bool IsAnyKeyword(string str)
|
||||
{
|
||||
return SyntaxFacts.GetKeywordKind(str) != SyntaxKind.None
|
||||
|| SyntaxFacts.GetContextualKeywordKind(str) != SyntaxKind.None;
|
||||
}
|
||||
public static string ToNonKeyword(string str)
|
||||
{
|
||||
// Capnp schema identifiers should be already valid, but could be a keyword
|
||||
if (IsAnyKeyword(str)) return $"@{str}";
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
class InvalidSchemaException : Exception
|
||||
{
|
||||
public InvalidSchemaException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
class Method: IHasGenericParameters
|
||||
{
|
||||
public TypeDefinition DeclaringInterface { get; set; }
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public List<Field> Params { get; } = new List<Field>();
|
||||
public List<Field> Results { get; } = new List<Field>();
|
||||
public Type ParamsStruct { get; set; }
|
||||
public Type ResultStruct { get; set; }
|
||||
public List<string> GenericParameters { get; } = new List<string>();
|
||||
}
|
||||
}
|
@ -1,783 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
class SchemaModel
|
||||
{
|
||||
readonly Schema.CodeGeneratorRequest.Reader _request;
|
||||
readonly List<GenFile> _generatedFiles = new List<GenFile>();
|
||||
readonly DefinitionManager _typeDefMgr = new DefinitionManager();
|
||||
|
||||
readonly Dictionary<ulong, Schema.Node.Reader> _id2node = new Dictionary<ulong, Schema.Node.Reader>();
|
||||
|
||||
public SchemaModel(Schema.CodeGeneratorRequest.Reader request)
|
||||
{
|
||||
_request = request;
|
||||
}
|
||||
|
||||
public IReadOnlyList<GenFile> FilesToGenerate => _generatedFiles;
|
||||
|
||||
Schema.Node.Reader? IdToNode(ulong id, bool mustExist)
|
||||
{
|
||||
if (_id2node.TryGetValue(id, out var node))
|
||||
return node;
|
||||
if (mustExist)
|
||||
throw new InvalidSchemaException($"Node with ID {id.StrId()} is required by the codegen backend but is missing.");
|
||||
return null;
|
||||
}
|
||||
|
||||
Schema.Node.Reader IdToNode(ulong id)
|
||||
{
|
||||
return (Schema.Node.Reader)IdToNode(id, true);
|
||||
}
|
||||
|
||||
void Build()
|
||||
{
|
||||
if (_request.Nodes == null || _request.Nodes.Count == 0)
|
||||
{
|
||||
throw new InvalidSchemaException("No nodes, nothing to generate");
|
||||
}
|
||||
|
||||
foreach (var node in _request.Nodes)
|
||||
{
|
||||
if (_id2node.TryGetValue(node.Id, out var existingNode))
|
||||
{
|
||||
throw new InvalidSchemaException($"Node {node.StrId()} \"{node.DisplayName}\" has a duplicate ID, prior node was \"{existingNode.DisplayName}\"");
|
||||
}
|
||||
_id2node[node.Id] = node;
|
||||
}
|
||||
|
||||
var requestedFiles = _request.RequestedFiles.ToDictionary(req => req.Id);
|
||||
BuildPass1(requestedFiles);
|
||||
BuildPass2(requestedFiles);
|
||||
}
|
||||
|
||||
// First pass: create type definitions for each node.
|
||||
|
||||
struct Pass1State
|
||||
{
|
||||
public HashSet<ulong> unprocessedNodes;
|
||||
public IHasNestedDefinitions parent;
|
||||
}
|
||||
|
||||
void BuildPass1(Dictionary<ulong, Schema.CodeGeneratorRequest.RequestedFile.Reader> requestedFiles)
|
||||
{
|
||||
Pass1State state = new Pass1State()
|
||||
{
|
||||
unprocessedNodes = new HashSet<ulong>(_id2node.Keys)
|
||||
};
|
||||
foreach (var node in _id2node.Values.Where(n => n.IsFile))
|
||||
{
|
||||
GenFile file;
|
||||
bool isGenerated = requestedFiles.TryGetValue(node.Id, out var req);
|
||||
var filename = isGenerated ? req.Filename : node.DisplayName;
|
||||
file = ProcessFilePass1(node.Id, filename, state, isGenerated);
|
||||
if (isGenerated)
|
||||
_generatedFiles.Add(file);
|
||||
}
|
||||
if (state.unprocessedNodes.Count != 0)
|
||||
{
|
||||
throw new InvalidSchemaException("Unreferenced nodes were present in the schema.");
|
||||
}
|
||||
}
|
||||
|
||||
GenFile ProcessFilePass1(ulong id, string name, Pass1State state, bool isGenerated)
|
||||
{
|
||||
var file = _typeDefMgr.CreateFile(id, isGenerated);
|
||||
var node = IdToNode(id);
|
||||
state.parent = null;
|
||||
file.Namespace = GetNamespaceAnnotation(node);
|
||||
file.Name = name;
|
||||
return ProcessNodePass1(id, name, state) as GenFile;
|
||||
}
|
||||
|
||||
IDefinition ProcessNodePass1(ulong id, string name, Pass1State state)
|
||||
{
|
||||
bool mustExist = state.parent == null || (state.parent as IDefinition).IsGenerated;
|
||||
if (!(IdToNode(id, mustExist) is Schema.Node.Reader node))
|
||||
return null;
|
||||
if (!state.unprocessedNodes.Remove(id))
|
||||
return null;
|
||||
|
||||
IDefinition def = null;
|
||||
bool processNestedNodes = false;
|
||||
bool processFields = false;
|
||||
bool processInterfaceMethods = false;
|
||||
|
||||
switch (node.GetKind())
|
||||
{
|
||||
case NodeKind.Annotation:
|
||||
return _typeDefMgr.CreateAnnotation(id, state.parent);
|
||||
case NodeKind.Const:
|
||||
return _typeDefMgr.CreateConstant(id, state.parent);
|
||||
case NodeKind.File:
|
||||
if (state.parent != null)
|
||||
throw new InvalidSchemaException($"Did not expect a file node {node.StrId()} to be a nested node.");
|
||||
var file = _typeDefMgr.GetExistingFile(id);
|
||||
file.Namespace = GetNamespaceAnnotation(node);
|
||||
file.Name = name;
|
||||
state.parent = file;
|
||||
def = file;
|
||||
processNestedNodes = true;
|
||||
break;
|
||||
case NodeKind.Enum:
|
||||
break;
|
||||
case NodeKind.Interface:
|
||||
processNestedNodes = true;
|
||||
processFields = true;
|
||||
processInterfaceMethods = true;
|
||||
break;
|
||||
case NodeKind.Struct:
|
||||
case NodeKind.Group:
|
||||
processNestedNodes = true;
|
||||
processFields = true;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidSchemaException($"Don't know how to process node {node.StrId()} \"{node.DisplayName}\"");
|
||||
}
|
||||
|
||||
if (def == null)
|
||||
{
|
||||
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);
|
||||
typeDef.Name = name;
|
||||
state.parent = typeDef;
|
||||
def = typeDef;
|
||||
}
|
||||
|
||||
if (processNestedNodes && node.NestedNodes != null)
|
||||
foreach (var nested in node.NestedNodes)
|
||||
{
|
||||
ProcessNodePass1(nested.Id, nested.Name, state);
|
||||
}
|
||||
if (processFields && node.Fields != null)
|
||||
foreach (var field in node.Fields.Where(f => f.IsGroup))
|
||||
{
|
||||
var group = IdToNode(field.Group_TypeId);
|
||||
if (!group.IsStruct || !group.Struct_IsGroup)
|
||||
{
|
||||
throw new InvalidSchemaException($"Expected node with id {group.StrId()} to be a struct definition");
|
||||
}
|
||||
ProcessNodePass1(field.Group_TypeId, field.Name, state);
|
||||
}
|
||||
if (processInterfaceMethods && node.Interface_Methods != null)
|
||||
foreach (var method in node.Interface_Methods)
|
||||
{
|
||||
var pnode = IdToNode(method.ParamStructType);
|
||||
if (pnode.ScopeId == 0) ProcessNodePass1(pnode.Id, null, state); // Anonymous generated type
|
||||
pnode = IdToNode(method.ResultStructType);
|
||||
if (pnode.ScopeId == 0) ProcessNodePass1(pnode.Id, null, state); // Anonymous generated type
|
||||
}
|
||||
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
|
||||
|
||||
struct Pass2State
|
||||
{
|
||||
public Method currentMethod;
|
||||
public HashSet<ulong> processedNodes;
|
||||
}
|
||||
|
||||
void BuildPass2(Dictionary<ulong, Schema.CodeGeneratorRequest.RequestedFile.Reader> requestedFiles)
|
||||
{
|
||||
var state = new Pass2State() { processedNodes = new HashSet<ulong>() };
|
||||
foreach (var file in _typeDefMgr.Files)
|
||||
{
|
||||
var node = IdToNode(file.Id);
|
||||
ProcessNestedNodes(node.NestedNodes, state, file.IsGenerated);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessNestedNodes(IEnumerable<Schema.Node.NestedNode.Reader> nestedNodes, Pass2State state, bool mustExist)
|
||||
{
|
||||
foreach (var nestedNode in nestedNodes)
|
||||
{
|
||||
ProcessNode(nestedNode.Id, state, mustExist);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessBrand(Schema.Brand.Reader brandReader, Type type, Pass2State state)
|
||||
{
|
||||
foreach (var scopeReader in brandReader.Scopes)
|
||||
{
|
||||
var whatToBind = ProcessTypeDef(scopeReader.ScopeId, state);
|
||||
int index = 0;
|
||||
|
||||
switch (0)
|
||||
{
|
||||
case 0 when scopeReader.IsBind:
|
||||
foreach (var bindingReader in scopeReader.Bind)
|
||||
{
|
||||
var typeParameter = new GenericParameter()
|
||||
{
|
||||
DeclaringEntity = whatToBind,
|
||||
Index = index++
|
||||
};
|
||||
|
||||
switch (0)
|
||||
{
|
||||
case 0 when bindingReader.IsType:
|
||||
type.BindGenericParameter(typeParameter, ProcessType(bindingReader.Type, state));
|
||||
break;
|
||||
|
||||
case 0 when bindingReader.IsUnbound:
|
||||
type.BindGenericParameter(typeParameter, Types.FromParameter(typeParameter));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0 when scopeReader.IsInherit:
|
||||
for (index = 0; index < type.DeclaringType.Definition.GenericParameters.Count; index++)
|
||||
{
|
||||
var typeParameter = new GenericParameter()
|
||||
{
|
||||
DeclaringEntity = whatToBind,
|
||||
Index = index
|
||||
};
|
||||
|
||||
type.BindGenericParameter(typeParameter, Types.FromParameter(typeParameter));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Type ProcessType(Schema.Type.Reader typeReader, Pass2State state)
|
||||
{
|
||||
Type result;
|
||||
|
||||
switch (0)
|
||||
{
|
||||
case 0 when typeReader.IsAnyPointer:
|
||||
switch (0)
|
||||
{
|
||||
case 0 when typeReader.AnyPointer_IsParameter:
|
||||
return Types.FromParameter(
|
||||
new GenericParameter()
|
||||
{
|
||||
DeclaringEntity = ProcessTypeDef(typeReader.AnyPointer_Parameter_ScopeId, state),
|
||||
Index = typeReader.AnyPointer_Parameter_ParameterIndex
|
||||
});
|
||||
|
||||
case 0 when typeReader.AnyPointer_IsImplicitMethodParameter:
|
||||
return Types.FromParameter(
|
||||
new GenericParameter()
|
||||
{
|
||||
DeclaringEntity = state.currentMethod ?? throw new InvalidOperationException("current method not set"),
|
||||
Index = typeReader.AnyPointer_ImplicitMethodParameter_ParameterIndex
|
||||
});
|
||||
|
||||
case 0 when typeReader.AnyPointer_IsUnconstrained:
|
||||
|
||||
switch (0)
|
||||
{
|
||||
case 0 when typeReader.AnyPointer_Unconstrained_IsAnyKind:
|
||||
return Types.AnyPointer;
|
||||
|
||||
case 0 when typeReader.AnyPointer_Unconstrained_IsCapability:
|
||||
return Types.CapabilityPointer;
|
||||
|
||||
case 0 when typeReader.AnyPointer_Unconstrained_IsList:
|
||||
return Types.ListPointer;
|
||||
|
||||
case 0 when typeReader.AnyPointer_Unconstrained_IsStruct:
|
||||
return Types.StructPointer;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
case 0 when typeReader.IsBool:
|
||||
return Types.Bool;
|
||||
|
||||
case 0 when typeReader.IsData:
|
||||
return Types.Data;
|
||||
|
||||
case 0 when typeReader.IsFloat64:
|
||||
return Types.F64;
|
||||
|
||||
case 0 when typeReader.IsEnum:
|
||||
return Types.FromDefinition(ProcessTypeDef(typeReader.Enum_TypeId, state, TypeTag.Enum));
|
||||
|
||||
case 0 when typeReader.IsFloat32:
|
||||
return Types.F32;
|
||||
|
||||
case 0 when typeReader.IsInt16:
|
||||
return Types.S16;
|
||||
|
||||
case 0 when typeReader.IsInt32:
|
||||
return Types.S32;
|
||||
|
||||
case 0 when typeReader.IsInt64:
|
||||
return Types.S64;
|
||||
|
||||
case 0 when typeReader.IsInt8:
|
||||
return Types.S8;
|
||||
|
||||
case 0 when typeReader.IsInterface:
|
||||
result = Types.FromDefinition(ProcessTypeDef(typeReader.Interface_TypeId, state, TypeTag.Interface));
|
||||
ProcessBrand(typeReader.Interface_Brand, result, state);
|
||||
return result;
|
||||
|
||||
case 0 when typeReader.IsList:
|
||||
return Types.List(ProcessType(typeReader.List_ElementType, state));
|
||||
|
||||
case 0 when typeReader.IsStruct:
|
||||
result = Types.FromDefinition(ProcessTypeDef(typeReader.Struct_TypeId, state, TypeTag.Struct));
|
||||
ProcessBrand(typeReader.Struct_Brand, result, state);
|
||||
return result;
|
||||
|
||||
case 0 when typeReader.IsText:
|
||||
return Types.Text;
|
||||
|
||||
case 0 when typeReader.IsUInt16:
|
||||
return Types.U16;
|
||||
|
||||
case 0 when typeReader.IsUInt32:
|
||||
return Types.U32;
|
||||
|
||||
case 0 when typeReader.IsUInt64:
|
||||
return Types.U64;
|
||||
|
||||
case 0 when typeReader.IsUInt8:
|
||||
return Types.U8;
|
||||
|
||||
case 0 when typeReader.IsVoid:
|
||||
return Types.Void;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
Value ProcessValue(Schema.Value.Reader valueReader)
|
||||
{
|
||||
var value = new Value();
|
||||
|
||||
switch (0)
|
||||
{
|
||||
case 0 when valueReader.IsAnyPointer:
|
||||
value.ScalarValue = valueReader.AnyPointer;
|
||||
value.Type = Types.AnyPointer;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsBool:
|
||||
value.ScalarValue = valueReader.Bool;
|
||||
value.Type = Types.Bool;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsData:
|
||||
value.Items.AddRange(valueReader.Data.CastByte().Select(Value.Scalar));
|
||||
value.Type = Types.Data;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsEnum:
|
||||
value.ScalarValue = valueReader.Enum;
|
||||
value.Type = Types.AnyEnum;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsFloat32:
|
||||
value.ScalarValue = valueReader.Float32;
|
||||
value.Type = Types.F32;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsFloat64:
|
||||
value.ScalarValue = valueReader.Float64;
|
||||
value.Type = Types.F64;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsInt16:
|
||||
value.ScalarValue = valueReader.Int16;
|
||||
value.Type = Types.S16;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsInt32:
|
||||
value.ScalarValue = valueReader.Int32;
|
||||
value.Type = Types.S32;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsInt64:
|
||||
value.ScalarValue = valueReader.Int64;
|
||||
value.Type = Types.S64;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsInt8:
|
||||
value.ScalarValue = valueReader.Int8;
|
||||
value.Type = Types.S8;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsInterface:
|
||||
value.ScalarValue = null;
|
||||
value.Type = Types.CapabilityPointer;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsList:
|
||||
value.RawValue = valueReader.List;
|
||||
value.Type = Types.ListPointer;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsStruct:
|
||||
value.RawValue = valueReader.Struct;
|
||||
value.Type = Types.StructPointer;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsText:
|
||||
value.ScalarValue = valueReader.Text;
|
||||
value.Type = Types.Text;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsUInt16:
|
||||
value.ScalarValue = valueReader.UInt16;
|
||||
value.Type = Types.U16;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsUInt32:
|
||||
value.ScalarValue = valueReader.UInt32;
|
||||
value.Type = Types.U32;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsUInt64:
|
||||
value.ScalarValue = valueReader.UInt64;
|
||||
value.Type = Types.U64;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsUInt8:
|
||||
value.ScalarValue = valueReader.UInt8;
|
||||
value.Type = Types.U8;
|
||||
break;
|
||||
|
||||
case 0 when valueReader.IsVoid:
|
||||
value.Type = Types.Void;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void ProcessFields(Schema.Node.Reader reader, TypeDefinition declaringType, List<Field> fields, Pass2State state)
|
||||
{
|
||||
if (reader.Fields == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var fieldReader in reader.Fields)
|
||||
{
|
||||
var field = new Field()
|
||||
{
|
||||
DeclaringType = declaringType,
|
||||
Name = fieldReader.Name,
|
||||
CodeOrder = fieldReader.CodeOrder
|
||||
};
|
||||
|
||||
if (fieldReader.DiscriminantValue != Schema.Field.Reader.NoDiscriminant)
|
||||
{
|
||||
field.DiscValue = fieldReader.DiscriminantValue;
|
||||
}
|
||||
|
||||
switch (0)
|
||||
{
|
||||
case 0 when fieldReader.IsGroup:
|
||||
var def = ProcessTypeDef(fieldReader.Group_TypeId, state, TypeTag.Group);
|
||||
field.Type = Types.FromDefinition(def);
|
||||
break;
|
||||
|
||||
case 0 when fieldReader.IsSlot:
|
||||
field.DefaultValue = ProcessValue(fieldReader.Slot_DefaultValue);
|
||||
field.DefaultValueIsExplicit = fieldReader.Slot_HadExplicitDefault;
|
||||
field.Offset = fieldReader.Slot_Offset;
|
||||
field.Type = ProcessType(fieldReader.Slot_Type, state);
|
||||
field.DefaultValue.Type = field.Type;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
field.InheritFreeGenericParameters();
|
||||
|
||||
fields.Add(field);
|
||||
}
|
||||
}
|
||||
|
||||
TypeDefinition ProcessInterfaceOrStructTail(TypeDefinition def, Schema.Node.Reader reader, Pass2State state)
|
||||
{
|
||||
def.IsGeneric = reader.IsGeneric;
|
||||
|
||||
if (def.IsGeneric)
|
||||
{
|
||||
foreach (var paramReader in reader.Parameters)
|
||||
{
|
||||
def.GenericParameters.Add(paramReader.Name);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessNestedNodes(reader.NestedNodes, state, def.File.IsGenerated);
|
||||
|
||||
ProcessFields(reader, def, def.Fields, state);
|
||||
|
||||
if (reader.IsInterface)
|
||||
{
|
||||
foreach (var methodReader in reader.Interface_Methods)
|
||||
{
|
||||
var method = new Method()
|
||||
{
|
||||
DeclaringInterface = def,
|
||||
Id = def.Methods.Count,
|
||||
Name = methodReader.Name
|
||||
};
|
||||
foreach (var implicitParameterReader in methodReader.ImplicitParameters)
|
||||
{
|
||||
method.GenericParameters.Add(implicitParameterReader.Name);
|
||||
}
|
||||
state.currentMethod = method;
|
||||
|
||||
def.Methods.Add(method);
|
||||
|
||||
var paramNode = IdToNode(methodReader.ParamStructType);
|
||||
var paramType = ProcessParameterList(paramNode, methodReader.ParamBrand, method.Params, state);
|
||||
if (paramType != null)
|
||||
{
|
||||
paramType.SpecialName = SpecialName.MethodParamsStruct;
|
||||
paramType.UsingMethod = method;
|
||||
method.ParamsStruct = Types.FromDefinition(paramType);
|
||||
}
|
||||
else
|
||||
{
|
||||
method.ParamsStruct = method.Params[0].Type;
|
||||
}
|
||||
method.ParamsStruct.InheritFreeParameters(def);
|
||||
method.ParamsStruct.InheritFreeParameters(method);
|
||||
|
||||
var resultNode = IdToNode(methodReader.ResultStructType);
|
||||
var resultType = ProcessParameterList(resultNode, methodReader.ResultBrand, method.Results, state);
|
||||
if (resultType != null)
|
||||
{
|
||||
resultType.SpecialName = SpecialName.MethodResultStruct;
|
||||
resultType.UsingMethod = method;
|
||||
method.ResultStruct = Types.FromDefinition(resultType);
|
||||
}
|
||||
else
|
||||
{
|
||||
method.ResultStruct = method.Results[0].Type;
|
||||
}
|
||||
method.ResultStruct.InheritFreeParameters(def);
|
||||
method.ResultStruct.InheritFreeParameters(method);
|
||||
}
|
||||
|
||||
state.currentMethod = null;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
TypeDefinition ProcessStruct(Schema.Node.Reader structReader, TypeDefinition def, Pass2State state)
|
||||
{
|
||||
def.StructDataWordCount = structReader.Struct_DataWordCount;
|
||||
def.StructPointerCount = structReader.Struct_PointerCount;
|
||||
|
||||
if (structReader.Struct_DiscriminantCount > 0)
|
||||
{
|
||||
def.UnionInfo = new TypeDefinition.DiscriminationInfo(
|
||||
structReader.Struct_DiscriminantCount,
|
||||
16u * structReader.Struct_DiscriminantOffset);
|
||||
}
|
||||
|
||||
return ProcessInterfaceOrStructTail(def, structReader, state);
|
||||
}
|
||||
|
||||
TypeDefinition ProcessParameterList(Schema.Node.Reader reader, Schema.Brand.Reader brandReader, List<Field> list, Pass2State state)
|
||||
{
|
||||
//# If a named parameter list was specified in the method
|
||||
//# declaration (rather than a single struct parameter type) then a corresponding struct type is
|
||||
//# auto-generated. Such an auto-generated type will not be listed in the interface's
|
||||
//# `nestedNodes` and its `scopeId` will be zero -- it is completely detached from the namespace.
|
||||
//# (Awkwardly, it does of course inherit generic parameters from the method's scope, which makes
|
||||
//# this a situation where you can't just climb the scope chain to find where a particular
|
||||
//# generic parameter was introduced. Making the `scopeId` zero was a mistake.)
|
||||
|
||||
if (!reader.IsStruct)
|
||||
{
|
||||
throw new InvalidSchemaException("Expected a struct");
|
||||
}
|
||||
|
||||
var def = ProcessTypeDef(reader.Id, state, TypeTag.Struct);
|
||||
|
||||
if (reader.ScopeId == 0)
|
||||
{
|
||||
// Auto-generated => Named parameter list
|
||||
foreach (var field in def.Fields) list.Add(field);
|
||||
return def;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Single, anonymous, struct-typed parameter
|
||||
var type = Types.FromDefinition(def);
|
||||
ProcessBrand(brandReader, type, state);
|
||||
var anon = new Field() { Type = type };
|
||||
list.Add(anon);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
TypeDefinition ProcessInterface(Schema.Node.Reader ifaceReader, TypeDefinition def, Pass2State state)
|
||||
{
|
||||
foreach (var superClassReader in ifaceReader.Interface_Superclasses)
|
||||
{
|
||||
var superClass = ProcessTypeDef(superClassReader.Id, state, TypeTag.Interface);
|
||||
def.Superclasses.Add(Types.FromDefinition(superClass));
|
||||
}
|
||||
|
||||
return ProcessInterfaceOrStructTail(def, ifaceReader, state);
|
||||
}
|
||||
|
||||
TypeDefinition ProcessEnum(Schema.Node.Reader enumReader, TypeDefinition def, Pass2State state)
|
||||
{
|
||||
foreach (var fieldReader in enumReader.Enumerants)
|
||||
{
|
||||
var field = new Enumerant()
|
||||
{
|
||||
TypeDefinition = def,
|
||||
Literal = fieldReader.Name,
|
||||
CodeOrder = fieldReader.CodeOrder
|
||||
};
|
||||
|
||||
if (fieldReader.Ordinal_IsExplicit)
|
||||
{
|
||||
field.Ordinal = fieldReader.Ordinal_Explicit;
|
||||
}
|
||||
|
||||
def.Enumerants.Add(field);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
Constant ProcessConst(Schema.Node.Reader constReader, Constant @const, Pass2State state)
|
||||
{
|
||||
var value = ProcessValue(constReader.Const_Value);
|
||||
value.Type = ProcessType(constReader.Const_Type, state);
|
||||
@const.Value = value;
|
||||
return @const;
|
||||
}
|
||||
|
||||
TypeDefinition ProcessTypeDef(ulong id, Pass2State state, TypeTag tag = default)
|
||||
{
|
||||
var def = ProcessNode(id, state, true, tag);
|
||||
var typeDef = def as TypeDefinition;
|
||||
if (typeDef == null)
|
||||
throw new ArgumentException(
|
||||
$"Expected node {id.StrId()} to be a TypeDefinition but got {def.GetType().Name} instead.",
|
||||
nameof(id));
|
||||
return typeDef;
|
||||
}
|
||||
|
||||
IDefinition ProcessNode(ulong id, Pass2State state, bool mustExist, TypeTag tag = default)
|
||||
{
|
||||
if (!(IdToNode(id, mustExist) is Schema.Node.Reader node)) return null;
|
||||
var kind = node.GetKind();
|
||||
if (tag == TypeTag.Unknown) tag = kind.GetTypeTag();
|
||||
var def = _typeDefMgr.GetExistingDef(id, tag);
|
||||
if (state.processedNodes.Contains(id)) return def;
|
||||
state.processedNodes.Add(id);
|
||||
|
||||
switch (def)
|
||||
{
|
||||
case Annotation annotation:
|
||||
return annotation;
|
||||
case Constant constant:
|
||||
def.DeclaringElement.Constants.Add(ProcessConst(node, constant, state));
|
||||
return def;
|
||||
case TypeDefinition typeDef when kind == NodeKind.Enum:
|
||||
return ProcessEnum(node, typeDef, state);
|
||||
case TypeDefinition typeDef when kind == NodeKind.Interface:
|
||||
return ProcessInterface(node, typeDef, state);
|
||||
case TypeDefinition typeDef when kind == NodeKind.Struct || kind == NodeKind.Group:
|
||||
return ProcessStruct(node, typeDef, state);
|
||||
default:
|
||||
throw new InvalidProgramException($"An unexpected node {node.StrId()} was found during the 2nd schema model building pass.");
|
||||
}
|
||||
}
|
||||
|
||||
public static SchemaModel Create(Schema.CodeGeneratorRequest.Reader request)
|
||||
{
|
||||
var model = new SchemaModel(request);
|
||||
model.Build();
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
public enum NodeKind
|
||||
{
|
||||
Unknown,
|
||||
Annotation,
|
||||
Const,
|
||||
Enum,
|
||||
File,
|
||||
Interface,
|
||||
Struct,
|
||||
Group
|
||||
}
|
||||
|
||||
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)
|
||||
=> $"0x{node.Id:X}";
|
||||
|
||||
public static string StrId(this ulong nodeId)
|
||||
=> $"0x{nodeId:X}";
|
||||
|
||||
public static NodeKind GetKind(this Schema.Node.Reader node)
|
||||
{
|
||||
if (node.IsStruct)
|
||||
return node.Struct_IsGroup ? NodeKind.Group : NodeKind.Struct;
|
||||
if (node.IsInterface) return NodeKind.Interface;
|
||||
if (node.IsEnum) return NodeKind.Enum;
|
||||
if (node.IsConst) return NodeKind.Const;
|
||||
if (node.IsAnnotation) return NodeKind.Annotation;
|
||||
if (node.IsFile) return NodeKind.File;
|
||||
return NodeKind.Unknown;
|
||||
}
|
||||
|
||||
internal static TypeTag GetTypeTag(this NodeKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case NodeKind.Enum: return TypeTag.Enum;
|
||||
case NodeKind.Interface: return TypeTag.Interface;
|
||||
case NodeKind.Struct: return TypeTag.Struct;
|
||||
case NodeKind.Group: return TypeTag.Group;
|
||||
default: return TypeTag.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
internal static TypeTag GetTypeTag(this Schema.Node.Reader node)
|
||||
=> node.GetKind().GetTypeTag();
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
enum SpecialName
|
||||
{
|
||||
NothingSpecial,
|
||||
MethodParamsStruct,
|
||||
MethodResultStruct
|
||||
}
|
||||
}
|
@ -1,201 +0,0 @@
|
||||
using Capnp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
class Type: AbstractType
|
||||
{
|
||||
// Representation of a type expression in the schema language
|
||||
|
||||
public TypeDefinition Definition { get; set; }
|
||||
// The model for all nodes that are not file nodes - they define types
|
||||
|
||||
public GenericParameter Parameter { get; set; }
|
||||
// A reference to type parameter in this scope
|
||||
|
||||
public Type ElementType { get; set; }
|
||||
// The type of a list element, if this is a list.
|
||||
|
||||
readonly Dictionary<GenericParameter, Type> _parameterBindings =
|
||||
new Dictionary<GenericParameter, Type>();
|
||||
public Type(TypeTag tag)
|
||||
{
|
||||
Tag = tag;
|
||||
}
|
||||
|
||||
public bool IsValueType
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Tag)
|
||||
{
|
||||
case TypeTag.AnyPointer:
|
||||
case TypeTag.CapabilityPointer:
|
||||
case TypeTag.Data:
|
||||
case TypeTag.Group:
|
||||
case TypeTag.Interface:
|
||||
case TypeTag.List when ElementType.Tag != TypeTag.Void:
|
||||
case TypeTag.ListPointer:
|
||||
case TypeTag.Struct:
|
||||
case TypeTag.StructPointer:
|
||||
case TypeTag.Text:
|
||||
case TypeTag.Void:
|
||||
return false;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void InheritFreeParameters(IHasGenericParameters declaringType)
|
||||
{
|
||||
while (declaringType != null)
|
||||
{
|
||||
foreach (var p in declaringType.GetLocalTypeParameters())
|
||||
{
|
||||
if (!_parameterBindings.ContainsKey(p))
|
||||
{
|
||||
_parameterBindings[p] = Types.FromParameter(p);
|
||||
}
|
||||
}
|
||||
|
||||
declaringType = (declaringType as TypeDefinition)?.DeclaringElement as IHasGenericParameters;
|
||||
}
|
||||
}
|
||||
|
||||
Type SubstituteGenerics(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (type.Parameter != null)
|
||||
{
|
||||
if (_parameterBindings.TryGetValue(type.Parameter, out var boundType))
|
||||
{
|
||||
return boundType;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Types.AnyPointer;
|
||||
}
|
||||
}
|
||||
|
||||
var stype = new Type(type.Tag)
|
||||
{
|
||||
Definition = type.Definition,
|
||||
ElementType = SubstituteGenerics(type.ElementType)
|
||||
};
|
||||
|
||||
foreach (var kvp in type._parameterBindings)
|
||||
{
|
||||
var p = kvp.Value.Parameter;
|
||||
|
||||
if (p != null && _parameterBindings.TryGetValue(p, out var boundType))
|
||||
{
|
||||
stype._parameterBindings[kvp.Key] = boundType;
|
||||
}
|
||||
else
|
||||
{
|
||||
stype._parameterBindings[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return stype;
|
||||
}
|
||||
|
||||
Field SubstituteGenerics(Field field)
|
||||
{
|
||||
var result = field.Clone();
|
||||
result.Type = SubstituteGenerics(result.Type);
|
||||
return result;
|
||||
}
|
||||
|
||||
public new IReadOnlyList<Field> Fields => Definition.Fields.LazyListSelect(SubstituteGenerics);
|
||||
|
||||
public Type DeclaringType
|
||||
{
|
||||
get
|
||||
{
|
||||
var parentDef = Definition?.DeclaringElement as TypeDefinition;
|
||||
// FIXME: Will become more sophisticated as soon as generics are implemented
|
||||
return parentDef != null ? Types.FromDefinition(parentDef) : null;
|
||||
}
|
||||
}
|
||||
|
||||
public (int, Type) GetRank()
|
||||
{
|
||||
var cur = this;
|
||||
int rank = 0;
|
||||
|
||||
while (cur.Tag == TypeTag.List)
|
||||
{
|
||||
cur = cur.ElementType;
|
||||
++rank;
|
||||
}
|
||||
|
||||
return (rank, cur);
|
||||
}
|
||||
|
||||
public IEnumerable<Type> AllImplementedClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
var stk = new Stack<Type>();
|
||||
stk.Push(this);
|
||||
var set = new HashSet<Type>();
|
||||
while (stk.Count > 0)
|
||||
{
|
||||
var def = stk.Pop();
|
||||
|
||||
if (def == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (set.Add(def))
|
||||
{
|
||||
foreach (var super in def.Definition.Superclasses)
|
||||
{
|
||||
stk.Push(super);
|
||||
}
|
||||
}
|
||||
}
|
||||
return set;
|
||||
}
|
||||
}
|
||||
|
||||
public Type ResolveGenericParameter(GenericParameter genericParameter)
|
||||
{
|
||||
if (_parameterBindings.TryGetValue(genericParameter, out var type))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Types.AnyPointer;
|
||||
}
|
||||
}
|
||||
|
||||
public void BindGenericParameter(GenericParameter genericParameter, Type boundType)
|
||||
{
|
||||
_parameterBindings.Add(genericParameter, boundType);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Type other && Definition == other.Definition;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Definition?.GetHashCode() ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
enum TypeCategory
|
||||
{
|
||||
Value,
|
||||
Pointer
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
class TypeDefinition : AbstractType, IDefinition, IHasNestedDefinitions, IHasGenericParameters
|
||||
{
|
||||
public class DiscriminationInfo
|
||||
{
|
||||
public DiscriminationInfo(ushort numOptions, uint tagOffset)
|
||||
{
|
||||
NumOptions = numOptions;
|
||||
TagOffset = tagOffset;
|
||||
}
|
||||
|
||||
public ushort NumOptions { get; }
|
||||
public uint TagOffset { get; }
|
||||
}
|
||||
|
||||
public TypeDefinition(TypeTag tag, ulong id, IHasNestedDefinitions parent)
|
||||
{
|
||||
Trace.Assert(parent != null);
|
||||
Tag = tag;
|
||||
Id = id;
|
||||
IsGenerated = (parent as IDefinition).IsGenerated;
|
||||
DeclaringElement = parent;
|
||||
if (tag == TypeTag.Group)
|
||||
((TypeDefinition)parent).NestedGroups.Add(this);
|
||||
else
|
||||
parent.NestedDefinitions.Add(this);
|
||||
}
|
||||
|
||||
public ulong Id { get; }
|
||||
public bool IsGenerated { get; }
|
||||
public IHasNestedDefinitions DeclaringElement { get; }
|
||||
|
||||
public Method UsingMethod { get; set; }
|
||||
public string Name { get; set; }
|
||||
public SpecialName SpecialName { get; set; }
|
||||
public DiscriminationInfo UnionInfo { get; set; }
|
||||
public new List<Field> Fields => base.Fields;
|
||||
public List<Enumerant> Enumerants { get; } = new List<Enumerant>();
|
||||
public ICollection<IDefinition> NestedDefinitions { get; } = new List<IDefinition>();
|
||||
public IEnumerable<TypeDefinition> NestedTypes { get => this.GetNestedTypes(); }
|
||||
public List<TypeDefinition> NestedGroups { get; } = new List<TypeDefinition>();
|
||||
public ICollection<Constant> Constants { get; } = new List<Constant>();
|
||||
public List<Method> Methods { get; } = new List<Method>();
|
||||
public List<Type> Superclasses { get; } = new List<Type>();
|
||||
public List<string> GenericParameters { get; } = new List<string>();
|
||||
public bool IsGeneric { get; set; }
|
||||
public ushort StructDataWordCount { get; set; }
|
||||
public ushort StructPointerCount { get; set; }
|
||||
|
||||
public IEnumerable<TypeDefinition> DefinitionHierarchy
|
||||
{
|
||||
get
|
||||
{
|
||||
IHasNestedDefinitions cur = this;
|
||||
|
||||
while (cur is TypeDefinition def)
|
||||
{
|
||||
yield return def;
|
||||
cur = def.DeclaringElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GenFile File
|
||||
{
|
||||
get
|
||||
{
|
||||
IHasNestedDefinitions cur = this;
|
||||
while (cur is TypeDefinition def) cur = def.DeclaringElement;
|
||||
return cur as GenFile;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<GenericParameter> AllTypeParameters
|
||||
{
|
||||
get
|
||||
{
|
||||
return from def in DefinitionHierarchy
|
||||
from p in def.GetLocalTypeParameters()
|
||||
select p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
enum TypeTag
|
||||
{
|
||||
Unknown,
|
||||
Void,
|
||||
Bool,
|
||||
S8,
|
||||
U8,
|
||||
S16,
|
||||
U16,
|
||||
S32,
|
||||
U32,
|
||||
S64,
|
||||
U64,
|
||||
F32,
|
||||
F64,
|
||||
List,
|
||||
Data,
|
||||
Text,
|
||||
AnyPointer,
|
||||
StructPointer,
|
||||
ListPointer,
|
||||
CapabilityPointer,
|
||||
ParameterPointer,
|
||||
ImplicitMethodParameterPointer,
|
||||
Struct,
|
||||
Group,
|
||||
Interface,
|
||||
Enum,
|
||||
AnyEnum,
|
||||
Const,
|
||||
Annotation,
|
||||
File
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace CapnpC.Model
|
||||
{
|
||||
static class Types
|
||||
{
|
||||
public static readonly Type Void = new Type(TypeTag.Void);
|
||||
public static readonly Type Bool = new Type(TypeTag.Bool);
|
||||
public static readonly Type S8 = new Type(TypeTag.S8);
|
||||
public static readonly Type U8 = new Type(TypeTag.U8);
|
||||
public static readonly Type S16 = new Type(TypeTag.S16);
|
||||
public static readonly Type U16 = new Type(TypeTag.U16);
|
||||
public static readonly Type S32 = new Type(TypeTag.S32);
|
||||
public static readonly Type U32 = new Type(TypeTag.U32);
|
||||
public static readonly Type S64 = new Type(TypeTag.S64);
|
||||
public static readonly Type U64 = new Type(TypeTag.U64);
|
||||
public static readonly Type F32 = new Type(TypeTag.F32);
|
||||
public static readonly Type F64 = new Type(TypeTag.F64);
|
||||
public static readonly Type AnyPointer = new Type(TypeTag.AnyPointer);
|
||||
public static readonly Type StructPointer = new Type(TypeTag.StructPointer);
|
||||
public static readonly Type ListPointer = new Type(TypeTag.ListPointer);
|
||||
public static readonly Type CapabilityPointer = new Type(TypeTag.CapabilityPointer);
|
||||
public static readonly Type Data = new Type(TypeTag.Data);
|
||||
public static readonly Type Text = new Type(TypeTag.Text);
|
||||
public static readonly Type AnyEnum = new Type(TypeTag.AnyEnum);
|
||||
|
||||
public static Type List(Type elementType)
|
||||
{
|
||||
return new Type(TypeTag.List)
|
||||
{
|
||||
ElementType = elementType
|
||||
};
|
||||
}
|
||||
|
||||
public static Type FromDefinition(TypeDefinition def)
|
||||
{
|
||||
if (def.Tag == TypeTag.Unknown)
|
||||
{
|
||||
throw new InvalidOperationException("Oops, type definition is not yet valid, cannot create type");
|
||||
}
|
||||
|
||||
return new Type(def.Tag)
|
||||
{
|
||||
Definition = def
|
||||
};
|
||||
}
|
||||
|
||||
public static Type FromParameter(GenericParameter genericParameter)
|
||||
{
|
||||
return new Type(TypeTag.AnyPointer)
|
||||
{
|
||||
Parameter = genericParameter
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user