Skip to content

Commit dea76b6

Browse files
Merge pull request #371 from tannergooding/main
Adding tests and validate handling for various global string literal variables
2 parents 6731145 + a5e2d0e commit dea76b6

13 files changed

Lines changed: 872 additions & 52 deletions

File tree

sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,16 @@ public void BeginValue(in ValueDesc desc)
144144
{
145145
Write(GetAccessSpecifierString(desc.AccessSpecifier, isNested: true));
146146
Write(" static ");
147+
148+
if (desc.TypeName is "byte[]" or "uint[]")
149+
{
150+
if (desc.IsConstant)
151+
{
152+
Write("readonly ");
153+
}
154+
isExpressionBody = false;
155+
}
156+
147157
Write(desc.TypeName);
148158
Write(' ');
149159
}

sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3227,18 +3227,27 @@ private void VisitVarDecl(VarDecl varDecl)
32273227
case CX_CharacterKind.CX_CLK_Ascii:
32283228
case CX_CharacterKind.CX_CLK_UTF8:
32293229
{
3230-
typeName = "ReadOnlySpan<byte>";
3230+
if (flags.HasFlag(ValueFlags.Constant))
3231+
{
3232+
typeName = "ReadOnlySpan<byte>";
3233+
}
3234+
else
3235+
{
3236+
typeName = "byte[]";
3237+
}
32313238
break;
32323239
}
32333240

32343241
case CX_CharacterKind.CX_CLK_Wide:
32353242
{
32363243
if (_config.GenerateUnixTypes)
32373244
{
3238-
goto default;
3245+
goto case CX_CharacterKind.CX_CLK_UTF32;
3246+
}
3247+
else
3248+
{
3249+
goto case CX_CharacterKind.CX_CLK_UTF16;
32393250
}
3240-
3241-
goto case CX_CharacterKind.CX_CLK_UTF16;
32423251
}
32433252

32443253
case CX_CharacterKind.CX_CLK_UTF16:
@@ -3248,6 +3257,19 @@ private void VisitVarDecl(VarDecl varDecl)
32483257
break;
32493258
}
32503259

3260+
case CX_CharacterKind.CX_CLK_UTF32:
3261+
{
3262+
if (_config.GeneratePreviewCode && flags.HasFlag(ValueFlags.Constant))
3263+
{
3264+
typeName = "ReadOnlySpan<uint>";
3265+
}
3266+
else
3267+
{
3268+
typeName = "uint[]";
3269+
}
3270+
break;
3271+
}
3272+
32513273
default:
32523274
{
32533275
AddDiagnostic(DiagnosticLevel.Error, $"Unsupported string literal kind: '{stringLiteral.Kind}'. Generated bindings may be incomplete.", stringLiteral);
@@ -3327,7 +3349,7 @@ private void VisitVarDecl(VarDecl varDecl)
33273349
{
33283350
StartUsingOutputBuilder(className);
33293351

3330-
if ((kind == ValueKind.String) && (typeName == "ReadOnlySpan<byte>"))
3352+
if ((kind == ValueKind.String) && typeName.StartsWith("ReadOnlySpan<"))
33313353
{
33323354
_outputBuilder.EmitSystemSupport();
33333355
}

sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Diagnostics;
66
using System.Linq;
77
using System.Runtime.CompilerServices;
8+
using System.Runtime.InteropServices;
89
using System.Text;
910
using ClangSharp.Abstractions;
1011
using ClangSharp.Interop;
@@ -2321,10 +2322,12 @@ private void VisitStringLiteral(StringLiteral stringLiteral)
23212322
{
23222323
if (_config.GenerateUnixTypes)
23232324
{
2324-
goto default;
2325+
goto case CX_CharacterKind.CX_CLK_UTF32;
2326+
}
2327+
else
2328+
{
2329+
goto case CX_CharacterKind.CX_CLK_UTF16;
23252330
}
2326-
2327-
goto case CX_CharacterKind.CX_CLK_UTF16;
23282331
}
23292332

23302333
case CX_CharacterKind.CX_CLK_UTF16:
@@ -2335,6 +2338,24 @@ private void VisitStringLiteral(StringLiteral stringLiteral)
23352338
break;
23362339
}
23372340

2341+
case CX_CharacterKind.CX_CLK_UTF32:
2342+
{
2343+
outputBuilder.Write("new uint[] { ");
2344+
2345+
var bytes = Encoding.UTF32.GetBytes(stringLiteral.String);
2346+
var codepoints = MemoryMarshal.Cast<byte, uint>(bytes);
2347+
2348+
foreach (var codepoint in codepoints)
2349+
{
2350+
outputBuilder.Write("0x");
2351+
outputBuilder.Write(codepoint.ToString("X8"));
2352+
outputBuilder.Write(", ");
2353+
}
2354+
2355+
outputBuilder.Write("0x00000000 }");
2356+
break;
2357+
}
2358+
23382359
default:
23392360
{
23402361
AddDiagnostic(DiagnosticLevel.Error, $"Unsupported string literal kind: '{stringLiteral.Kind}'. Generated bindings may be incomplete.", stringLiteral);

sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,21 @@ public void EndUnchecked()
3434

3535
public void BeginValue(in ValueDesc desc)
3636
{
37-
_ = _sb.Append((desc.Kind != ValueKind.Enumerator)
38-
? $"<constant name=\"{desc.EscapedName}\" access=\"{desc.AccessSpecifier.AsString()}\">"
39-
: $"<enumerator name=\"{desc.EscapedName}\" access=\"{desc.AccessSpecifier.AsString()}\">");
37+
if (desc.Kind == ValueKind.Enumerator)
38+
{
39+
_ = _sb.Append("<enumerator");
40+
}
41+
else if (desc.IsConstant)
42+
{
43+
_ = _sb.Append("<constant");
44+
}
45+
else
46+
{
47+
_ = _sb.Append("<field");
48+
}
49+
50+
_ = _sb.Append($" name=\"{desc.EscapedName}\" access=\"{desc.AccessSpecifier.AsString()}\">");
51+
4052
desc.WriteCustomAttrs?.Invoke(desc.CustomAttrGeneratorData);
4153
_ = _sb.Append($"<type primitive=\"{desc.Kind == ValueKind.Primitive}\">");
4254
_ = _sb.Append(EscapeText(desc.TypeName));
@@ -58,7 +70,18 @@ public void EndValue(in ValueDesc desc)
5870
_ = _sb.Append("</value>");
5971
}
6072

61-
_ = _sb.Append((desc.Kind != ValueKind.Enumerator) ? "</constant>" : "</enumerator>");
73+
if (desc.Kind == ValueKind.Enumerator)
74+
{
75+
_ = _sb.Append("</enumerator>");
76+
}
77+
else if (desc.IsConstant)
78+
{
79+
_ = _sb.Append("</constant>");
80+
}
81+
else
82+
{
83+
_ = _sb.Append("</field>");
84+
}
6285
}
6386

6487
public void BeginEnum(in EnumDesc desc)

tests/ClangSharp.PInvokeGenerator.UnitTests/Base/VarDeclarationTest.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ public abstract class VarDeclarationTest : PInvokeGeneratorTest
5353
[Test]
5454
public Task StringLiteralConstTest() => StringLiteralConstTestImpl();
5555

56+
[Test]
57+
public Task WideStringLiteralStaticConstTest() => WideStringLiteralStaticConstTestImpl();
58+
59+
[Test]
60+
public Task StringLiteralStaticConstTest() => StringLiteralStaticConstTestImpl();
61+
5662
[Test]
5763
public Task UncheckedConversionMacroTest() => UncheckedConversionMacroTestImpl();
5864

@@ -94,6 +100,10 @@ public abstract class VarDeclarationTest : PInvokeGeneratorTest
94100

95101
protected abstract Task StringLiteralConstTestImpl();
96102

103+
protected abstract Task WideStringLiteralStaticConstTestImpl();
104+
105+
protected abstract Task StringLiteralStaticConstTestImpl();
106+
97107
protected abstract Task UncheckedConversionMacroTestImpl();
98108

99109
protected abstract Task UncheckedFunctionLikeCastMacroTestImpl();

tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpCompatibleUnix/VarDeclarationTest.cs

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,88 @@ public static partial class Methods
151151
return ValidateGeneratedCSharpCompatibleUnixBindingsAsync(inputContents, expectedOutputContents);
152152
}
153153

154-
protected override Task WideStringLiteralConstTestImpl() =>
155-
// Unsupported string literal kind: 'CX_CLK_Wide'
156-
Task.CompletedTask;
154+
protected override Task WideStringLiteralConstTestImpl()
155+
{
156+
var inputContents = $@"const wchar_t MyConst1[] = L""Test"";
157+
const wchar_t* MyConst2 = L""Test"";
158+
const wchar_t* const MyConst3 = L""Test"";";
159+
160+
var expectedOutputContents = $@"namespace ClangSharp.Test
161+
{{
162+
public static partial class Methods
163+
{{
164+
[NativeTypeName(""const wchar_t[5]"")]
165+
public static readonly uint[] MyConst1 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
166+
167+
[NativeTypeName(""const wchar_t *"")]
168+
public static uint[] MyConst2 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
169+
170+
[NativeTypeName(""const wchar_t *const"")]
171+
public static readonly uint[] MyConst3 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
172+
}}
173+
}}
174+
";
175+
176+
return ValidateGeneratedCSharpCompatibleUnixBindingsAsync(inputContents, expectedOutputContents);
177+
}
157178

158179
protected override Task StringLiteralConstTestImpl()
159180
{
160-
var inputContents = $@"const char MyConst1[] = ""Test"";";
181+
var inputContents = $@"const char MyConst1[] = ""Test"";
182+
const char* MyConst2 = ""Test"";
183+
const char* const MyConst3 = ""Test"";";
184+
185+
var expectedOutputContents = $@"using System;
186+
187+
namespace ClangSharp.Test
188+
{{
189+
public static partial class Methods
190+
{{
191+
[NativeTypeName(""const char[5]"")]
192+
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
193+
194+
[NativeTypeName(""const char *"")]
195+
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
196+
197+
[NativeTypeName(""const char *const"")]
198+
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
199+
}}
200+
}}
201+
";
202+
203+
return ValidateGeneratedCSharpCompatibleUnixBindingsAsync(inputContents, expectedOutputContents);
204+
}
205+
206+
protected override Task WideStringLiteralStaticConstTestImpl()
207+
{
208+
var inputContents = $@"static const wchar_t MyConst1[] = L""Test"";
209+
static const wchar_t* MyConst2 = L""Test"";
210+
static const wchar_t* const MyConst3 = L""Test"";";
211+
212+
var expectedOutputContents = $@"namespace ClangSharp.Test
213+
{{
214+
public static partial class Methods
215+
{{
216+
[NativeTypeName(""const wchar_t[5]"")]
217+
public static readonly uint[] MyConst1 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
218+
219+
[NativeTypeName(""const wchar_t *"")]
220+
public static uint[] MyConst2 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
221+
222+
[NativeTypeName(""const wchar_t *const"")]
223+
public static readonly uint[] MyConst3 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
224+
}}
225+
}}
226+
";
227+
228+
return ValidateGeneratedCSharpCompatibleUnixBindingsAsync(inputContents, expectedOutputContents);
229+
}
230+
231+
protected override Task StringLiteralStaticConstTestImpl()
232+
{
233+
var inputContents = $@"static const char MyConst1[] = ""Test"";
234+
static const char* MyConst2 = ""Test"";
235+
static const char* const MyConst3 = ""Test"";";
161236

162237
var expectedOutputContents = $@"using System;
163238
@@ -167,6 +242,12 @@ public static partial class Methods
167242
{{
168243
[NativeTypeName(""const char[5]"")]
169244
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
245+
246+
[NativeTypeName(""const char *"")]
247+
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
248+
249+
[NativeTypeName(""const char *const"")]
250+
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
170251
}}
171252
}}
172253
";

0 commit comments

Comments
 (0)