Skip to content

Commit c8cb780

Browse files
committed
Ensure string escape sequences work as expected
1 parent 0720817 commit c8cb780

File tree

17 files changed

+516
-508
lines changed

17 files changed

+516
-508
lines changed

sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2271,21 +2271,29 @@ private string EscapeAndStripName(string name)
22712271
}
22722272

22732273
internal static string EscapeCharacter(char value) => value switch {
2274-
'\0' => "\\0",
2275-
'\\' => "\\\\",
2276-
'\r' => "\\r",
2277-
'\n' => "\\n",
2278-
'\t' => "\\t",
2279-
'\'' => "\\'",
2274+
'\0' => @"\0",
2275+
'\\' => @"\\",
2276+
'\r' => @"\r",
2277+
'\n' => @"\n",
2278+
'\t' => @"\t",
2279+
'\'' => @"\'",
22802280
_ => value.ToString(),
22812281
};
22822282

2283-
internal static string EscapeString(string value) => value.Replace("\0", "\\0", StringComparison.Ordinal)
2284-
.Replace("\\", "\\\\", StringComparison.Ordinal)
2285-
.Replace("\r", "\\r", StringComparison.Ordinal)
2286-
.Replace("\n", "\\n", StringComparison.Ordinal)
2287-
.Replace("\t", "\\t", StringComparison.Ordinal)
2288-
.Replace("\"", "\\\"", StringComparison.Ordinal);
2283+
// We first replace already escaped characters with their raw counterpart
2284+
// We then re-escape any raw characters. This ensures we don't end up with double escaped backslashes
2285+
internal static string EscapeString(string value) => value.Replace(@"\\", "\\", StringComparison.Ordinal)
2286+
.Replace(@"\0", "\0", StringComparison.Ordinal)
2287+
.Replace(@"\r", "\r", StringComparison.Ordinal)
2288+
.Replace(@"\n", "\n", StringComparison.Ordinal)
2289+
.Replace(@"\t", "\t", StringComparison.Ordinal)
2290+
.Replace(@"\""", "\"", StringComparison.Ordinal)
2291+
.Replace("\\", @"\\", StringComparison.Ordinal)
2292+
.Replace("\0", @"\0", StringComparison.Ordinal)
2293+
.Replace("\r", @"\r", StringComparison.Ordinal)
2294+
.Replace("\n", @"\n", StringComparison.Ordinal)
2295+
.Replace("\t", @"\t", StringComparison.Ordinal)
2296+
.Replace("\"", @"\""", StringComparison.Ordinal);
22892297

22902298
private AccessSpecifier GetAccessSpecifier(NamedDecl namedDecl, bool matchStar)
22912299
{

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

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -115,16 +115,16 @@ protected override Task NoInitializerTestImpl(string nativeType)
115115

116116
protected override Task Utf8StringLiteralMacroTestImpl()
117117
{
118-
var inputContents = $@"#define MyMacro1 ""Test""";
118+
var inputContents = $@"#define MyMacro1 ""Test\0\\\r\n\t\""""";
119119

120120
var expectedOutputContents = $@"using System;
121121
122122
namespace ClangSharp.Test
123123
{{
124124
public static partial class Methods
125125
{{
126-
[NativeTypeName(""#define MyMacro1 \""Test\"""")]
127-
public static ReadOnlySpan<byte> MyMacro1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
126+
[NativeTypeName(""#define MyMacro1 \""Test\0\\\r\n\t\""\"""")]
127+
public static ReadOnlySpan<byte> MyMacro1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
128128
}}
129129
}}
130130
";
@@ -134,14 +134,14 @@ public static partial class Methods
134134

135135
protected override Task Utf16StringLiteralMacroTestImpl()
136136
{
137-
var inputContents = $@"#define MyMacro1 u""Test""";
137+
var inputContents = $@"#define MyMacro1 u""Test\0\\\r\n\t\""""";
138138

139139
var expectedOutputContents = $@"namespace ClangSharp.Test
140140
{{
141141
public static partial class Methods
142142
{{
143-
[NativeTypeName(""#define MyMacro1 u\""Test\"""")]
144-
public const string MyMacro1 = ""Test"";
143+
[NativeTypeName(""#define MyMacro1 u\""Test\0\\\r\n\t\""\"""")]
144+
public const string MyMacro1 = ""Test\0\\\r\n\t\"""";
145145
}}
146146
}}
147147
";
@@ -151,22 +151,22 @@ public static partial class Methods
151151

152152
protected override Task WideStringLiteralConstTestImpl()
153153
{
154-
var inputContents = $@"const wchar_t MyConst1[] = L""Test"";
155-
const wchar_t* MyConst2 = L""Test"";
156-
const wchar_t* const MyConst3 = L""Test"";";
154+
var inputContents = $@"const wchar_t MyConst1[] = L""Test\0\\\r\n\t\"""";
155+
const wchar_t* MyConst2 = L""Test\0\\\r\n\t\"""";
156+
const wchar_t* const MyConst3 = L""Test\0\\\r\n\t\"""";";
157157

158158
var expectedOutputContents = $@"namespace ClangSharp.Test
159159
{{
160160
public static partial class Methods
161161
{{
162-
[NativeTypeName(""const wchar_t[5]"")]
163-
public static readonly uint[] MyConst1 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
162+
[NativeTypeName(""const wchar_t[11]"")]
163+
public static readonly uint[] MyConst1 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000, 0x0000005C, 0x0000000D, 0x0000000A, 0x00000009, 0x00000022, 0x00000000 }};
164164
165165
[NativeTypeName(""const wchar_t *"")]
166-
public static uint[] MyConst2 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
166+
public static uint[] MyConst2 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000, 0x0000005C, 0x0000000D, 0x0000000A, 0x00000009, 0x00000022, 0x00000000 }};
167167
168168
[NativeTypeName(""const wchar_t *const"")]
169-
public static readonly uint[] MyConst3 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
169+
public static readonly uint[] MyConst3 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000, 0x0000005C, 0x0000000D, 0x0000000A, 0x00000009, 0x00000022, 0x00000000 }};
170170
}}
171171
}}
172172
";
@@ -176,24 +176,24 @@ public static partial class Methods
176176

177177
protected override Task StringLiteralConstTestImpl()
178178
{
179-
var inputContents = $@"const char MyConst1[] = ""Test"";
180-
const char* MyConst2 = ""Test"";
181-
const char* const MyConst3 = ""Test"";";
179+
var inputContents = $@"const char MyConst1[] = ""Test\0\\\r\n\t\"""";
180+
const char* MyConst2 = ""Test\0\\\r\n\t\"""";
181+
const char* const MyConst3 = ""Test\0\\\r\n\t\"""";";
182182

183183
var expectedOutputContents = $@"using System;
184184
185185
namespace ClangSharp.Test
186186
{{
187187
public static partial class Methods
188188
{{
189-
[NativeTypeName(""const char[5]"")]
190-
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
189+
[NativeTypeName(""const char[11]"")]
190+
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
191191
192192
[NativeTypeName(""const char *"")]
193-
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
193+
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
194194
195195
[NativeTypeName(""const char *const"")]
196-
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
196+
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
197197
}}
198198
}}
199199
";
@@ -203,22 +203,22 @@ public static partial class Methods
203203

204204
protected override Task WideStringLiteralStaticConstTestImpl()
205205
{
206-
var inputContents = $@"static const wchar_t MyConst1[] = L""Test"";
207-
static const wchar_t* MyConst2 = L""Test"";
208-
static const wchar_t* const MyConst3 = L""Test"";";
206+
var inputContents = $@"static const wchar_t MyConst1[] = L""Test\0\\\r\n\t\"""";
207+
static const wchar_t* MyConst2 = L""Test\0\\\r\n\t\"""";
208+
static const wchar_t* const MyConst3 = L""Test\0\\\r\n\t\"""";";
209209

210210
var expectedOutputContents = $@"namespace ClangSharp.Test
211211
{{
212212
public static partial class Methods
213213
{{
214-
[NativeTypeName(""const wchar_t[5]"")]
215-
public static readonly uint[] MyConst1 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
214+
[NativeTypeName(""const wchar_t[11]"")]
215+
public static readonly uint[] MyConst1 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000, 0x0000005C, 0x0000000D, 0x0000000A, 0x00000009, 0x00000022, 0x00000000 }};
216216
217217
[NativeTypeName(""const wchar_t *"")]
218-
public static uint[] MyConst2 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
218+
public static uint[] MyConst2 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000, 0x0000005C, 0x0000000D, 0x0000000A, 0x00000009, 0x00000022, 0x00000000 }};
219219
220220
[NativeTypeName(""const wchar_t *const"")]
221-
public static readonly uint[] MyConst3 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
221+
public static readonly uint[] MyConst3 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000, 0x0000005C, 0x0000000D, 0x0000000A, 0x00000009, 0x00000022, 0x00000000 }};
222222
}}
223223
}}
224224
";
@@ -228,24 +228,24 @@ public static partial class Methods
228228

229229
protected override Task StringLiteralStaticConstTestImpl()
230230
{
231-
var inputContents = $@"static const char MyConst1[] = ""Test"";
232-
static const char* MyConst2 = ""Test"";
233-
static const char* const MyConst3 = ""Test"";";
231+
var inputContents = $@"static const char MyConst1[] = ""Test\0\\\r\n\t\"""";
232+
static const char* MyConst2 = ""Test\0\\\r\n\t\"""";
233+
static const char* const MyConst3 = ""Test\0\\\r\n\t\"""";";
234234

235235
var expectedOutputContents = $@"using System;
236236
237237
namespace ClangSharp.Test
238238
{{
239239
public static partial class Methods
240240
{{
241-
[NativeTypeName(""const char[5]"")]
242-
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
241+
[NativeTypeName(""const char[11]"")]
242+
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
243243
244244
[NativeTypeName(""const char *"")]
245-
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
245+
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
246246
247247
[NativeTypeName(""const char *const"")]
248-
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
248+
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
249249
}}
250250
}}
251251
";

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

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -116,16 +116,16 @@ protected override Task NoInitializerTestImpl(string nativeType)
116116

117117
protected override Task Utf8StringLiteralMacroTestImpl()
118118
{
119-
var inputContents = $@"#define MyMacro1 ""Test""";
119+
var inputContents = $@"#define MyMacro1 ""Test\0\\\r\n\t\""""";
120120

121121
var expectedOutputContents = $@"using System;
122122
123123
namespace ClangSharp.Test
124124
{{
125125
public static partial class Methods
126126
{{
127-
[NativeTypeName(""#define MyMacro1 \""Test\"""")]
128-
public static ReadOnlySpan<byte> MyMacro1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
127+
[NativeTypeName(""#define MyMacro1 \""Test\0\\\r\n\t\""\"""")]
128+
public static ReadOnlySpan<byte> MyMacro1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
129129
}}
130130
}}
131131
";
@@ -135,14 +135,14 @@ public static partial class Methods
135135

136136
protected override Task Utf16StringLiteralMacroTestImpl()
137137
{
138-
var inputContents = $@"#define MyMacro1 u""Test""";
138+
var inputContents = $@"#define MyMacro1 u""Test\0\\\r\n\t\""""";
139139

140140
var expectedOutputContents = $@"namespace ClangSharp.Test
141141
{{
142142
public static partial class Methods
143143
{{
144-
[NativeTypeName(""#define MyMacro1 u\""Test\"""")]
145-
public const string MyMacro1 = ""Test"";
144+
[NativeTypeName(""#define MyMacro1 u\""Test\0\\\r\n\t\""\"""")]
145+
public const string MyMacro1 = ""Test\0\\\r\n\t\"""";
146146
}}
147147
}}
148148
";
@@ -152,22 +152,22 @@ public static partial class Methods
152152

153153
protected override Task WideStringLiteralConstTestImpl()
154154
{
155-
var inputContents = $@"const wchar_t MyConst1[] = L""Test"";
156-
const wchar_t* MyConst2 = L""Test"";
157-
const wchar_t* const MyConst3 = L""Test"";";
155+
var inputContents = $@"const wchar_t MyConst1[] = L""Test\0\\\r\n\t\"""";
156+
const wchar_t* MyConst2 = L""Test\0\\\r\n\t\"""";
157+
const wchar_t* const MyConst3 = L""Test\0\\\r\n\t\"""";";
158158

159159
var expectedOutputContents = $@"namespace ClangSharp.Test
160160
{{
161161
public static partial class Methods
162162
{{
163-
[NativeTypeName(""const wchar_t[5]"")]
164-
public const string MyConst1 = ""Test"";
163+
[NativeTypeName(""const wchar_t[11]"")]
164+
public const string MyConst1 = ""Test\0\\\r\n\t\"""";
165165
166166
[NativeTypeName(""const wchar_t *"")]
167-
public static string MyConst2 = ""Test"";
167+
public static string MyConst2 = ""Test\0\\\r\n\t\"""";
168168
169169
[NativeTypeName(""const wchar_t *const"")]
170-
public const string MyConst3 = ""Test"";
170+
public const string MyConst3 = ""Test\0\\\r\n\t\"""";
171171
}}
172172
}}
173173
";
@@ -177,24 +177,24 @@ public static partial class Methods
177177

178178
protected override Task StringLiteralConstTestImpl()
179179
{
180-
var inputContents = $@"const char MyConst1[] = ""Test"";
181-
const char* MyConst2 = ""Test"";
182-
const char* const MyConst3 = ""Test"";";
180+
var inputContents = $@"const char MyConst1[] = ""Test\0\\\r\n\t\"""";
181+
const char* MyConst2 = ""Test\0\\\r\n\t\"""";
182+
const char* const MyConst3 = ""Test\0\\\r\n\t\"""";";
183183

184184
var expectedOutputContents = $@"using System;
185185
186186
namespace ClangSharp.Test
187187
{{
188188
public static partial class Methods
189189
{{
190-
[NativeTypeName(""const char[5]"")]
191-
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
190+
[NativeTypeName(""const char[11]"")]
191+
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
192192
193193
[NativeTypeName(""const char *"")]
194-
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
194+
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
195195
196196
[NativeTypeName(""const char *const"")]
197-
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
197+
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
198198
}}
199199
}}
200200
";
@@ -204,22 +204,22 @@ public static partial class Methods
204204

205205
protected override Task WideStringLiteralStaticConstTestImpl()
206206
{
207-
var inputContents = $@"static const wchar_t MyConst1[] = L""Test"";
208-
static const wchar_t* MyConst2 = L""Test"";
209-
static const wchar_t* const MyConst3 = L""Test"";";
207+
var inputContents = $@"static const wchar_t MyConst1[] = L""Test\0\\\r\n\t\"""";
208+
static const wchar_t* MyConst2 = L""Test\0\\\r\n\t\"""";
209+
static const wchar_t* const MyConst3 = L""Test\0\\\r\n\t\"""";";
210210

211211
var expectedOutputContents = $@"namespace ClangSharp.Test
212212
{{
213213
public static partial class Methods
214214
{{
215-
[NativeTypeName(""const wchar_t[5]"")]
216-
public const string MyConst1 = ""Test"";
215+
[NativeTypeName(""const wchar_t[11]"")]
216+
public const string MyConst1 = ""Test\0\\\r\n\t\"""";
217217
218218
[NativeTypeName(""const wchar_t *"")]
219-
public static string MyConst2 = ""Test"";
219+
public static string MyConst2 = ""Test\0\\\r\n\t\"""";
220220
221221
[NativeTypeName(""const wchar_t *const"")]
222-
public const string MyConst3 = ""Test"";
222+
public const string MyConst3 = ""Test\0\\\r\n\t\"""";
223223
}}
224224
}}
225225
";
@@ -229,24 +229,24 @@ public static partial class Methods
229229

230230
protected override Task StringLiteralStaticConstTestImpl()
231231
{
232-
var inputContents = $@"static const char MyConst1[] = ""Test"";
233-
static const char* MyConst2 = ""Test"";
234-
static const char* const MyConst3 = ""Test"";";
232+
var inputContents = $@"static const char MyConst1[] = ""Test\0\\\r\n\t\"""";
233+
static const char* MyConst2 = ""Test\0\\\r\n\t\"""";
234+
static const char* const MyConst3 = ""Test\0\\\r\n\t\"""";";
235235

236236
var expectedOutputContents = $@"using System;
237237
238238
namespace ClangSharp.Test
239239
{{
240240
public static partial class Methods
241241
{{
242-
[NativeTypeName(""const char[5]"")]
243-
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
242+
[NativeTypeName(""const char[11]"")]
243+
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
244244
245245
[NativeTypeName(""const char *"")]
246-
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
246+
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
247247
248248
[NativeTypeName(""const char *const"")]
249-
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
249+
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
250250
}}
251251
}}
252252
";

0 commit comments

Comments
 (0)