mirror of
https://github.com/FabInfra/capnproto-dotnetcore_Runtime.git
synced 2025-03-12 23:01:44 +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
|
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}"
|
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
|
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
|
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
|
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
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{C3A3BB49-356E-4762-A190-76D877BE18F7}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
<RootNamespace>capnpc_csharp.Tests</RootNamespace>
|
<RootNamespace>CapnpC.CSharp.Generator.Tests</RootNamespace>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@ -16,17 +16,18 @@
|
|||||||
<PackageReference Include="SpecFlow.Tools.MsBuild.Generation" Version="3.0.225" />
|
<PackageReference Include="SpecFlow.Tools.MsBuild.Generation" Version="3.0.225" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\capnpc-csharp\capnpc-csharp.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="*.cs" />
|
<Compile Remove="*.cs" />
|
||||||
<Compile Remove="Embedded Resources\test.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\null.bin" />
|
||||||
<None Remove="Embedded Resources\test.capnp" />
|
<None Remove="Embedded Resources\test.capnp" />
|
||||||
<None Remove="Embedded Resources\test.capnp.bin" />
|
<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>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -34,9 +35,13 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<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\null.bin" />
|
||||||
<EmbeddedResource Include="Embedded Resources\test.capnp.bin" />
|
<EmbeddedResource Include="Embedded Resources\test.capnp.bin" />
|
||||||
<EmbeddedResource Include="Embedded Resources\test.cs" />
|
<EmbeddedResource Include="Embedded Resources\test.cs" />
|
||||||
|
<EmbeddedResource Include="Embedded Resources\UnitTest1.capnp" />
|
||||||
<EmbeddedResource Include="Embedded Resources\UnitTest1.capnp.bin" />
|
<EmbeddedResource Include="Embedded Resources\UnitTest1.capnp.bin" />
|
||||||
<EmbeddedResource Include="Embedded Resources\UnitTest2.capnp.bin" />
|
<EmbeddedResource Include="Embedded Resources\UnitTest2.capnp.bin" />
|
||||||
<EmbeddedResource Include="Embedded Resources\UnitTest3.capnp.bin" />
|
<EmbeddedResource Include="Embedded Resources\UnitTest3.capnp.bin" />
|
||||||
@ -47,6 +52,10 @@
|
|||||||
<EmbeddedResource Include="Embedded Resources\schema-with-offsets.capnp.bin" />
|
<EmbeddedResource Include="Embedded Resources\schema-with-offsets.capnp.bin" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\CapnpC.CSharp.Generator\CapnpC.CSharp.Generator.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<SpecFlowFeatureFiles Update="CodeGenerator.feature">
|
<SpecFlowFeatureFiles Update="CodeGenerator.feature">
|
||||||
<Generator>SpecFlowSingleFileGenerator</Generator>
|
<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
|
#region Designer generated code
|
||||||
#pragma warning disable
|
#pragma warning disable
|
||||||
namespace capnpc_csharp.Tests
|
namespace CapnpC.CSharp.Generator.Tests
|
||||||
{
|
{
|
||||||
using TechTalk.SpecFlow;
|
using TechTalk.SpecFlow;
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ namespace capnpc_csharp.Tests
|
|||||||
if (((testRunner.FeatureContext != null)
|
if (((testRunner.FeatureContext != null)
|
||||||
&& (testRunner.FeatureContext.FeatureInfo.Title != "CodeGenerator")))
|
&& (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)));
|
this.InvalidBinaryCodeGeneratorRequests("test.cs", ((string[])(null)));
|
||||||
#line hidden
|
#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
|
#pragma warning restore
|
@ -1,13 +1,13 @@
|
|||||||
using Capnp;
|
using Capnp;
|
||||||
using Model = CapnpC.Model;
|
using Model = CapnpC.CSharp.Generator.Model;
|
||||||
using Generator = CapnpC.Generator;
|
using CodeGen = CapnpC.CSharp.Generator.CodeGen;
|
||||||
using CodeGeneratorRequest = CapnpC.Schema.CodeGeneratorRequest;
|
using CodeGeneratorRequest = CapnpC.CSharp.Generator.Schema.CodeGeneratorRequest;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace capnpc_csharp.Tests
|
namespace CapnpC.CSharp.Generator.Tests
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class CodeGeneratorUnitTests
|
public class CodeGeneratorUnitTests
|
||||||
@ -98,13 +98,13 @@ namespace capnpc_csharp.Tests
|
|||||||
struct Run
|
struct Run
|
||||||
{
|
{
|
||||||
public Model.SchemaModel Model;
|
public Model.SchemaModel Model;
|
||||||
public Generator.CodeGenerator CodeGen;
|
public CodeGen.CodeGenerator CodeGen;
|
||||||
public Model.GenFile FirstFile;
|
public Model.GenFile FirstFile;
|
||||||
public string Code;
|
public string Code;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Generator.CodeGenerator NewGeneratorFor(Model.SchemaModel model)
|
static CodeGen.CodeGenerator NewGeneratorFor(Model.SchemaModel model)
|
||||||
=> new Generator.CodeGenerator(model, new Generator.GeneratorOptions());
|
=> new CodeGen.CodeGenerator(model, new CodeGen.GeneratorOptions());
|
||||||
|
|
||||||
Run LoadAndGenerate(string inputName, int? testNum = null)
|
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">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFrameworks>netstandard2.0;netcoreapp2.1</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
using Capnp;
|
using Capnp;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("CapnpC.CSharp.Generator.Tests")]
|
||||||
|
|
||||||
namespace CapnpC.CSharp.Generator
|
namespace CapnpC.CSharp.Generator
|
||||||
{
|
{
|
||||||
@ -23,13 +28,7 @@ namespace CapnpC.CSharp.Generator
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
WireFrame segments;
|
var segments = Framing.ReadSegments(input);
|
||||||
|
|
||||||
using (input)
|
|
||||||
{
|
|
||||||
segments = Framing.ReadSegments(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
var dec = DeserializerState.CreateRoot(segments);
|
var dec = DeserializerState.CreateRoot(segments);
|
||||||
var reader = Schema.CodeGeneratorRequest.Reader.Create(dec);
|
var reader = Schema.CodeGeneratorRequest.Reader.Create(dec);
|
||||||
var model = Model.SchemaModel.Create(reader);
|
var model = Model.SchemaModel.Create(reader);
|
||||||
@ -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 Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||||
using static SyntaxHelpers;
|
using static SyntaxHelpers;
|
||||||
|
|
||||||
class CodeGenerator
|
internal class CodeGenerator
|
||||||
{
|
{
|
||||||
readonly SchemaModel _model;
|
readonly SchemaModel _model;
|
||||||
readonly GenNames _names;
|
readonly GenNames _names;
|
||||||
|
@ -40,5 +40,15 @@ namespace CapnpC.CSharp.Generator
|
|||||||
/// true iff generation was successful
|
/// true iff generation was successful
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsSuccess => GeneratedFiles != null;
|
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.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Capnpc.Csharp.MsBuild.Generation
|
namespace CapnpC.CSharp.MsBuild.Generation
|
||||||
{
|
{
|
||||||
public class CapnpCodeBehindGenerator : IDisposable
|
public class CapnpCodeBehindGenerator : IDisposable
|
||||||
{
|
{
|
||||||
//private SpecFlowProject _specFlowProject;
|
|
||||||
//private ITestGenerator _testGenerator;
|
|
||||||
|
|
||||||
public void InitializeProject(string projectPath)
|
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);
|
// Works around a weird capnp.exe behavior: When the input file is empty, it will spit out an exception dump
|
||||||
//var generatedFeatureFileName = Path.GetFileName(_testGenerator.GetTestFullPath(featureFileInput));
|
// 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(
|
var result = CapnpCompilation.InvokeCapnpAndGenerate(new string[] { capnpFile });
|
||||||
new TestGeneratorResult() { GeneratedTestCode = "//dummy" },
|
|
||||||
capnpFile + ".cs");
|
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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
//_testGenerator?.Dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,16 +5,13 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Build.Utilities;
|
using Microsoft.Build.Utilities;
|
||||||
|
|
||||||
namespace Capnpc.Csharp.MsBuild.Generation
|
namespace CapnpC.CSharp.MsBuild.Generation
|
||||||
{
|
{
|
||||||
public class CapnpFileCodeBehindGenerator : ICapnpcCsharpGenerator
|
public class CapnpFileCodeBehindGenerator : ICapnpcCsharpGenerator
|
||||||
{
|
{
|
||||||
private readonly FilePathGenerator _filePathGenerator;
|
|
||||||
|
|
||||||
public CapnpFileCodeBehindGenerator(TaskLoggingHelper log)
|
public CapnpFileCodeBehindGenerator(TaskLoggingHelper log)
|
||||||
{
|
{
|
||||||
Log = log ?? throw new ArgumentNullException(nameof(log));
|
Log = log ?? throw new ArgumentNullException(nameof(log));
|
||||||
_filePathGenerator = new FilePathGenerator();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TaskLoggingHelper Log { get; }
|
public TaskLoggingHelper Log { get; }
|
||||||
@ -39,33 +36,44 @@ namespace Capnpc.Csharp.MsBuild.Generation
|
|||||||
|
|
||||||
foreach (var capnpFile in capnpFiles)
|
foreach (var capnpFile in capnpFiles)
|
||||||
{
|
{
|
||||||
var capnpFileItemSpec = capnpFile;
|
var generatorResult = capnpCodeBehindGenerator.GenerateCodeBehindFile(capnpFile);
|
||||||
var generatorResult = capnpCodeBehindGenerator.GenerateCodeBehindFile(capnpFileItemSpec);
|
|
||||||
|
|
||||||
if (!generatorResult.Success)
|
if (!generatorResult.Success)
|
||||||
{
|
{
|
||||||
foreach (var error in generatorResult.Errors)
|
if (!string.IsNullOrEmpty(generatorResult.Error))
|
||||||
{
|
{
|
||||||
//Log.LogError(
|
Log.LogError("{0}", generatorResult.Error);
|
||||||
// null,
|
|
||||||
// null,
|
|
||||||
// null,
|
|
||||||
// featureFile,
|
|
||||||
// error.Line,
|
|
||||||
// error.LinePosition,
|
|
||||||
// 0,
|
|
||||||
// 0,
|
|
||||||
// error.Message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetFilePath = _filePathGenerator.GenerateFilePath(
|
var resultedFile = codeBehindWriter.WriteCodeBehindFile(generatorResult.Filename, generatorResult);
|
||||||
projectFolder,
|
|
||||||
capnpFile,
|
|
||||||
generatorResult.Filename);
|
|
||||||
|
|
||||||
var resultedFile = codeBehindWriter.WriteCodeBehindFile(targetFilePath, capnpFile, generatorResult);
|
|
||||||
|
|
||||||
yield return FileSystemHelper.GetRelativePath(resultedFile, projectFolder);
|
yield return FileSystemHelper.GetRelativePath(resultedFile, projectFolder);
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net471;netcoreapp2.1</TargetFrameworks>
|
<TargetFrameworks>net471;netcoreapp2.1</TargetFrameworks>
|
||||||
<SignAssembly>false</SignAssembly>
|
<SignAssembly>false</SignAssembly>
|
||||||
<NuspecFile>$(MSBuildThisFileDirectory)Capnpc.Csharp.MsBuild.Generation.nuspec</NuspecFile>
|
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
|
||||||
|
|
||||||
|
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
<NoPackageAnalysis>true</NoPackageAnalysis>
|
<NoPackageAnalysis>true</NoPackageAnalysis>
|
||||||
@ -12,6 +9,13 @@
|
|||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
<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>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -54,6 +58,12 @@
|
|||||||
<Folder Include="FrameworkDependent\FullFramework\" />
|
<Folder Include="FrameworkDependent\FullFramework\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\CapnpC.CSharp.Generator\CapnpC.CSharp.Generator.csproj">
|
||||||
|
<Private>true</Private>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.Build">
|
<Reference Include="Microsoft.Build">
|
||||||
<HintPath>Microsoft.Build</HintPath>
|
<HintPath>Microsoft.Build</HintPath>
|
||||||
@ -68,11 +78,28 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<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">
|
<None Update="build\CPS\Buildsystem\Rules\CapnpFileType.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="build\CPS\Buildsystem\Rules\ProjectItemsSchema.xaml">
|
<None Update="build\CPS\Buildsystem\Rules\ProjectItemsSchema.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Capnpc.Csharp.MsBuild.Generation</id>
|
<id>CapnpC.CSharp.MsBuild.Generation</id>
|
||||||
<version>1.0.0</version>
|
<version>$version$</version>
|
||||||
<title>Capnpc.Csharp.MsBuild.Generation</title>
|
<title>CapnpC.CSharp.MsBuild.Generation</title>
|
||||||
<authors>Christian Köllner and contributors</authors>
|
<authors>Christian Köllner and contributors</authors>
|
||||||
<owners>Christian Köllner</owners>
|
<owners>Christian Köllner</owners>
|
||||||
<description>Package to enable the .capnp -> .cs file generation during build time</description>
|
<description>Package to enable the .capnp -> .cs file generation during build time</description>
|
||||||
@ -15,6 +15,7 @@
|
|||||||
<tags>capnproto csharp msbuild</tags>
|
<tags>capnproto csharp msbuild</tags>
|
||||||
<copyright>Christian Köllner and contributors</copyright>
|
<copyright>Christian Köllner and contributors</copyright>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency id="Capnp.Net.Runtime" version="1.0" />
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.Build.Utilities;
|
using Microsoft.Build.Utilities;
|
||||||
|
|
||||||
namespace Capnpc.Csharp.MsBuild.Generation
|
namespace CapnpC.CSharp.MsBuild.Generation
|
||||||
{
|
{
|
||||||
public class CodeBehindWriter
|
public class CodeBehindWriter
|
||||||
{
|
{
|
||||||
@ -13,14 +13,8 @@ namespace Capnpc.Csharp.MsBuild.Generation
|
|||||||
|
|
||||||
public TaskLoggingHelper Log { get; }
|
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();
|
string directoryPath = Path.GetDirectoryName(outputPath) ?? throw new InvalidOperationException();
|
||||||
Log?.LogWithNameTag(Log.LogMessage, directoryPath);
|
Log?.LogWithNameTag(Log.LogMessage, directoryPath);
|
||||||
|
|
||||||
@ -35,11 +29,6 @@ namespace Capnpc.Csharp.MsBuild.Generation
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(directoryPath))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(directoryPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
File.WriteAllText(outputPath, testFileGeneratorResult.GeneratedCode);
|
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.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Capnpc.Csharp.MsBuild.Generation
|
namespace CapnpC.CSharp.MsBuild.Generation
|
||||||
{
|
{
|
||||||
public static class FileSystemHelper
|
public static class FileSystemHelper
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,7 @@ using System.Resources;
|
|||||||
using Microsoft.Build.Framework;
|
using Microsoft.Build.Framework;
|
||||||
using Microsoft.Build.Utilities;
|
using Microsoft.Build.Utilities;
|
||||||
|
|
||||||
namespace Capnpc.Csharp.MsBuild.Generation
|
namespace CapnpC.CSharp.MsBuild.Generation
|
||||||
{
|
{
|
||||||
public class GenerateCapnpFileCodeBehindTask : Task
|
public class GenerateCapnpFileCodeBehindTask : Task
|
||||||
{
|
{
|
||||||
|
@ -5,7 +5,7 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Build.Utilities;
|
using Microsoft.Build.Utilities;
|
||||||
|
|
||||||
namespace Capnpc.Csharp.MsBuild.Generation
|
namespace CapnpC.CSharp.MsBuild.Generation
|
||||||
{
|
{
|
||||||
public static class LogExtensions
|
public static class LogExtensions
|
||||||
{
|
{
|
||||||
@ -15,7 +15,7 @@ namespace Capnpc.Csharp.MsBuild.Generation
|
|||||||
string message,
|
string message,
|
||||||
params object[] messageArgs)
|
params object[] messageArgs)
|
||||||
{
|
{
|
||||||
string fullMessage = $"[SpecFlow] {message}";
|
string fullMessage = $"[Cap'n Proto] {message}";
|
||||||
loggingMethod?.Invoke(fullMessage, messageArgs);
|
loggingMethod?.Invoke(fullMessage, messageArgs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Capnpc.Csharp.MsBuild.Generation
|
namespace CapnpC.CSharp.MsBuild.Generation
|
||||||
{
|
{
|
||||||
public interface ICapnpcCsharpGenerator
|
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="Link" Visible="false" />
|
||||||
<StringProperty Name="Generator" Visible="true" DisplayName="Custom Tool"/>
|
<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>
|
<StringProperty.DataSource>
|
||||||
<DataSource Persistence="ProjectFile" ItemType="WorkingDirectory" PersistedName="WorkingDirectory" />
|
<DataSource Persistence="ProjectFile" ItemType="WorkingDirectory" PersistedName="WorkingDirectory" />
|
||||||
</StringProperty.DataSource>
|
</StringProperty.DataSource>
|
||||||
@ -39,5 +39,5 @@
|
|||||||
<StringProperty.DataSource>
|
<StringProperty.DataSource>
|
||||||
<DataSource Persistence="ProjectFile" ItemType="AdditionalOptions" PersistedName="AdditionalOptions" />
|
<DataSource Persistence="ProjectFile" ItemType="AdditionalOptions" PersistedName="AdditionalOptions" />
|
||||||
</StringProperty.DataSource>
|
</StringProperty.DataSource>
|
||||||
</StringProperty>
|
</StringProperty>-->
|
||||||
</Rule>
|
</Rule>
|
@ -54,7 +54,7 @@
|
|||||||
- after deletion of a feature file
|
- after deletion of a feature file
|
||||||
- after pulling latest changes from version control with above changes
|
- 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
|
<!-- Support for Visual Studio Incremental Build
|
||||||
https://github.com/techtalk/SpecFlow/issues/1319
|
https://github.com/techtalk/SpecFlow/issues/1319
|
||||||
@ -69,9 +69,9 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<_CapnpcCsharp_TaskFolder Condition=" '$(MSBuildRuntimeType)' == 'Core' And '$(_CapnpcCsharp_TaskFolder)' == ''">netcoreapp2.1</_CapnpcCsharp_TaskFolder>
|
<_CapnpcCsharp_TaskFolder Condition=" '$(MSBuildRuntimeType)' == 'Core' And '$(_CapnpcCsharp_TaskFolder)' == ''">netcoreapp2.1</_CapnpcCsharp_TaskFolder>
|
||||||
<_CapnpcCsharp_TaskFolder Condition=" '$(MSBuildRuntimeType)' != 'Core' And '$(_CapnpcCsharp_TaskFolder)' == ''">net471</_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>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Import Project="Capnpc.Csharp.MsBuild.Generation.tasks"/>
|
<Import Project="CapnpC.CSharp.MsBuild.Generation.tasks"/>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<Import Project="Capnpc.Csharp.MsBuild.Generation.props" Condition="'$(_CapnpcCsharpPropsImported)'==''"/>
|
<Import Project="CapnpC.CSharp.MsBuild.Generation.props" Condition="'$(_CapnpcCsharpPropsImported)'==''"/>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(BuildServerMode)' == ''">
|
<PropertyGroup Condition="'$(BuildServerMode)' == ''">
|
||||||
<BuildServerMode Condition="'$(BuildingInsideVisualStudio)'=='true'">false</BuildServerMode>
|
<BuildServerMode Condition="'$(BuildingInsideVisualStudio)'=='true'">false</BuildServerMode>
|
||||||
@ -48,9 +48,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
<Target Name="WarnForFeatureCodeBehindFilesWithoutCorrespondingFeatureFile" AfterTargets="CoreCompile"
|
<Target Name="WarnForCapnpCsharpCodeBehindFilesWithoutCorrespondingCapnpFile" AfterTargets="CoreCompile"
|
||||||
Condition="'$(CapnpcCsharp_EnableWarnForFeatureCodeBehindFilesWithoutCorrespondingCapnpFile)' == 'true'">
|
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>
|
</Target>
|
||||||
|
|
||||||
|
|
||||||
@ -100,7 +100,7 @@
|
|||||||
Condition="'%(Compile.Identity)' == '@(CapnpFiles->'%(CodeBehindFile)')'" />
|
Condition="'%(Compile.Identity)' == '@(CapnpFiles->'%(CodeBehindFile)')'" />
|
||||||
|
|
||||||
<!-- remove files which got obsolete, typically after rename operation, or getting changes from source control -->
|
<!-- remove files which got obsolete, typically after rename operation, or getting changes from source control -->
|
||||||
<Compile Remove="@(SpecFlowObsoleteCodeBehindFiles)" />
|
<Compile Remove="@(CapnpCsharpObsoleteCodeBehindFiles)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Target>
|
</Target>
|
||||||
@ -128,6 +128,6 @@
|
|||||||
- after deletion of a capnp file
|
- after deletion of a capnp file
|
||||||
- after pulling latest changes from version control with above changes
|
- after pulling latest changes from version control with above changes
|
||||||
-->
|
-->
|
||||||
<Delete Files="@(SpecFlowObsoleteCodeBehindFiles)" ContinueOnError="true" />
|
<Delete Files="@(CapnpCsharpObsoleteCodeBehindFiles)" ContinueOnError="true" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
@ -1,3 +1,3 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<UsingTask TaskName="Capnpc.Csharp.MsBuild.Generation.GenerateCapnpFileCodeBehindTask" AssemblyFile="$(_CapnpcCsharp_TaskAssembly)" />
|
<UsingTask TaskName="CapnpC.CSharp.MsBuild.Generation.GenerateCapnpFileCodeBehindTask" AssemblyFile="$(_CapnpcCsharp_TaskAssembly)" />
|
||||||
</Project>
|
</Project>
|
@ -1,5 +1,5 @@
|
|||||||
<Project TreatAsLocalProperty="TaskFolder;TaskAssembly">
|
<Project TreatAsLocalProperty="TaskFolder;TaskAssembly">
|
||||||
|
|
||||||
<Import Project="..\build\Capnpc.Csharp.MsBuild.Generation.props"/>
|
<Import Project="..\build\CapnpC.CSharp.MsBuild.Generation.props"/>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
@ -53,7 +53,7 @@ Solution/project structure is as follows:
|
|||||||
* Capnp.Net.Runtime is the runtime implementation, a .NET assembly.
|
* Capnp.Net.Runtime is the runtime implementation, a .NET assembly.
|
||||||
* capnpc-csharp is the generator backend for C# language.
|
* 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.
|
* 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.
|
- 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
|
## 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/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 ./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/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:
|
build_script:
|
||||||
- cmd: msbuild ./Capnp.Net.sln /p:Configuration="Debug"
|
- cmd: msbuild ./Capnp.Net.sln /p:Configuration="Debug"
|
||||||
- cmd: msbuild ./Capnp.Net.sln /p:Configuration="Release"
|
- cmd: msbuild ./Capnp.Net.sln /p:Configuration="Release"
|
||||||
@ -50,11 +50,12 @@ artifacts:
|
|||||||
type: NuGetPackage
|
type: NuGetPackage
|
||||||
clone_depth: 1
|
clone_depth: 1
|
||||||
test_script:
|
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: cd %APPVEYOR_BUILD_FOLDER%\chocolatey\install
|
||||||
- cmd: choco install capnpc-csharp --source=".;https://chocolatey.org/api/v2" --force -y
|
- cmd: choco install capnpc-csharp --source=".;https://chocolatey.org/api/v2" --force -y
|
||||||
- cmd: cd %APPVEYOR_BUILD_FOLDER%\install-test
|
- cmd: cd %APPVEYOR_BUILD_FOLDER%\install-test
|
||||||
- cmd: compile-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: choco uninstall capnpc-csharp -y
|
||||||
- cmd: notinstalled-test
|
- cmd: notinstalled-test
|
||||||
- cmd: cd %APPVEYOR_BUILD_FOLDER%\chocolatey\install
|
- 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