Skip to content

Commit

Permalink
Ensure string escape sequences work as expected
Browse files Browse the repository at this point in the history
  • Loading branch information
tannergooding committed Nov 10, 2023
1 parent 0720817 commit c8cb780
Show file tree
Hide file tree
Showing 17 changed files with 516 additions and 508 deletions.
32 changes: 20 additions & 12 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2271,21 +2271,29 @@ private string EscapeAndStripName(string name)
}

internal static string EscapeCharacter(char value) => value switch {
'\0' => "\\0",
'\\' => "\\\\",
'\r' => "\\r",
'\n' => "\\n",
'\t' => "\\t",
'\'' => "\\'",
'\0' => @"\0",
'\\' => @"\\",
'\r' => @"\r",
'\n' => @"\n",
'\t' => @"\t",
'\'' => @"\'",
_ => value.ToString(),
};

internal static string EscapeString(string value) => value.Replace("\0", "\\0", StringComparison.Ordinal)
.Replace("\\", "\\\\", StringComparison.Ordinal)
.Replace("\r", "\\r", StringComparison.Ordinal)
.Replace("\n", "\\n", StringComparison.Ordinal)
.Replace("\t", "\\t", StringComparison.Ordinal)
.Replace("\"", "\\\"", StringComparison.Ordinal);
// We first replace already escaped characters with their raw counterpart
// We then re-escape any raw characters. This ensures we don't end up with double escaped backslashes
internal static string EscapeString(string value) => value.Replace(@"\\", "\\", StringComparison.Ordinal)
.Replace(@"\0", "\0", StringComparison.Ordinal)
.Replace(@"\r", "\r", StringComparison.Ordinal)
.Replace(@"\n", "\n", StringComparison.Ordinal)
.Replace(@"\t", "\t", StringComparison.Ordinal)
.Replace(@"\""", "\"", StringComparison.Ordinal)
.Replace("\\", @"\\", StringComparison.Ordinal)
.Replace("\0", @"\0", StringComparison.Ordinal)
.Replace("\r", @"\r", StringComparison.Ordinal)
.Replace("\n", @"\n", StringComparison.Ordinal)
.Replace("\t", @"\t", StringComparison.Ordinal)
.Replace("\"", @"\""", StringComparison.Ordinal);

private AccessSpecifier GetAccessSpecifier(NamedDecl namedDecl, bool matchStar)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,16 @@ protected override Task NoInitializerTestImpl(string nativeType)

protected override Task Utf8StringLiteralMacroTestImpl()
{
var inputContents = $@"#define MyMacro1 ""Test""";
var inputContents = $@"#define MyMacro1 ""Test\0\\\r\n\t\""""";

var expectedOutputContents = $@"using System;
namespace ClangSharp.Test
{{
public static partial class Methods
{{
[NativeTypeName(""#define MyMacro1 \""Test\"""")]
public static ReadOnlySpan<byte> MyMacro1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
[NativeTypeName(""#define MyMacro1 \""Test\0\\\r\n\t\""\"""")]
public static ReadOnlySpan<byte> MyMacro1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
}}
}}
";
Expand All @@ -134,14 +134,14 @@ public static partial class Methods

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

var expectedOutputContents = $@"namespace ClangSharp.Test
{{
public static partial class Methods
{{
[NativeTypeName(""#define MyMacro1 u\""Test\"""")]
public const string MyMacro1 = ""Test"";
[NativeTypeName(""#define MyMacro1 u\""Test\0\\\r\n\t\""\"""")]
public const string MyMacro1 = ""Test\0\\\r\n\t\"""";
}}
}}
";
Expand All @@ -151,22 +151,22 @@ public static partial class Methods

protected override Task WideStringLiteralConstTestImpl()
{
var inputContents = $@"const wchar_t MyConst1[] = L""Test"";
const wchar_t* MyConst2 = L""Test"";
const wchar_t* const MyConst3 = L""Test"";";
var inputContents = $@"const wchar_t MyConst1[] = L""Test\0\\\r\n\t\"""";
const wchar_t* MyConst2 = L""Test\0\\\r\n\t\"""";
const wchar_t* const MyConst3 = L""Test\0\\\r\n\t\"""";";

var expectedOutputContents = $@"namespace ClangSharp.Test
{{
public static partial class Methods
{{
[NativeTypeName(""const wchar_t[5]"")]
public static readonly uint[] MyConst1 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
[NativeTypeName(""const wchar_t[11]"")]
public static readonly uint[] MyConst1 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000, 0x0000005C, 0x0000000D, 0x0000000A, 0x00000009, 0x00000022, 0x00000000 }};
[NativeTypeName(""const wchar_t *"")]
public static uint[] MyConst2 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
public static uint[] MyConst2 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000, 0x0000005C, 0x0000000D, 0x0000000A, 0x00000009, 0x00000022, 0x00000000 }};
[NativeTypeName(""const wchar_t *const"")]
public static readonly uint[] MyConst3 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
public static readonly uint[] MyConst3 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000, 0x0000005C, 0x0000000D, 0x0000000A, 0x00000009, 0x00000022, 0x00000000 }};
}}
}}
";
Expand All @@ -176,24 +176,24 @@ public static partial class Methods

protected override Task StringLiteralConstTestImpl()
{
var inputContents = $@"const char MyConst1[] = ""Test"";
const char* MyConst2 = ""Test"";
const char* const MyConst3 = ""Test"";";
var inputContents = $@"const char MyConst1[] = ""Test\0\\\r\n\t\"""";
const char* MyConst2 = ""Test\0\\\r\n\t\"""";
const char* const MyConst3 = ""Test\0\\\r\n\t\"""";";

var expectedOutputContents = $@"using System;
namespace ClangSharp.Test
{{
public static partial class Methods
{{
[NativeTypeName(""const char[5]"")]
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
[NativeTypeName(""const char[11]"")]
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
[NativeTypeName(""const char *"")]
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
[NativeTypeName(""const char *const"")]
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
}}
}}
";
Expand All @@ -203,22 +203,22 @@ public static partial class Methods

protected override Task WideStringLiteralStaticConstTestImpl()
{
var inputContents = $@"static const wchar_t MyConst1[] = L""Test"";
static const wchar_t* MyConst2 = L""Test"";
static const wchar_t* const MyConst3 = L""Test"";";
var inputContents = $@"static const wchar_t MyConst1[] = L""Test\0\\\r\n\t\"""";
static const wchar_t* MyConst2 = L""Test\0\\\r\n\t\"""";
static const wchar_t* const MyConst3 = L""Test\0\\\r\n\t\"""";";

var expectedOutputContents = $@"namespace ClangSharp.Test
{{
public static partial class Methods
{{
[NativeTypeName(""const wchar_t[5]"")]
public static readonly uint[] MyConst1 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
[NativeTypeName(""const wchar_t[11]"")]
public static readonly uint[] MyConst1 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000, 0x0000005C, 0x0000000D, 0x0000000A, 0x00000009, 0x00000022, 0x00000000 }};
[NativeTypeName(""const wchar_t *"")]
public static uint[] MyConst2 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
public static uint[] MyConst2 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000, 0x0000005C, 0x0000000D, 0x0000000A, 0x00000009, 0x00000022, 0x00000000 }};
[NativeTypeName(""const wchar_t *const"")]
public static readonly uint[] MyConst3 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000 }};
public static readonly uint[] MyConst3 = new uint[] {{ 0x00000054, 0x00000065, 0x00000073, 0x00000074, 0x00000000, 0x0000005C, 0x0000000D, 0x0000000A, 0x00000009, 0x00000022, 0x00000000 }};
}}
}}
";
Expand All @@ -228,24 +228,24 @@ public static partial class Methods

protected override Task StringLiteralStaticConstTestImpl()
{
var inputContents = $@"static const char MyConst1[] = ""Test"";
static const char* MyConst2 = ""Test"";
static const char* const MyConst3 = ""Test"";";
var inputContents = $@"static const char MyConst1[] = ""Test\0\\\r\n\t\"""";
static const char* MyConst2 = ""Test\0\\\r\n\t\"""";
static const char* const MyConst3 = ""Test\0\\\r\n\t\"""";";

var expectedOutputContents = $@"using System;
namespace ClangSharp.Test
{{
public static partial class Methods
{{
[NativeTypeName(""const char[5]"")]
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
[NativeTypeName(""const char[11]"")]
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
[NativeTypeName(""const char *"")]
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
[NativeTypeName(""const char *const"")]
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
}}
}}
";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,16 @@ protected override Task NoInitializerTestImpl(string nativeType)

protected override Task Utf8StringLiteralMacroTestImpl()
{
var inputContents = $@"#define MyMacro1 ""Test""";
var inputContents = $@"#define MyMacro1 ""Test\0\\\r\n\t\""""";

var expectedOutputContents = $@"using System;
namespace ClangSharp.Test
{{
public static partial class Methods
{{
[NativeTypeName(""#define MyMacro1 \""Test\"""")]
public static ReadOnlySpan<byte> MyMacro1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
[NativeTypeName(""#define MyMacro1 \""Test\0\\\r\n\t\""\"""")]
public static ReadOnlySpan<byte> MyMacro1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
}}
}}
";
Expand All @@ -135,14 +135,14 @@ public static partial class Methods

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

var expectedOutputContents = $@"namespace ClangSharp.Test
{{
public static partial class Methods
{{
[NativeTypeName(""#define MyMacro1 u\""Test\"""")]
public const string MyMacro1 = ""Test"";
[NativeTypeName(""#define MyMacro1 u\""Test\0\\\r\n\t\""\"""")]
public const string MyMacro1 = ""Test\0\\\r\n\t\"""";
}}
}}
";
Expand All @@ -152,22 +152,22 @@ public static partial class Methods

protected override Task WideStringLiteralConstTestImpl()
{
var inputContents = $@"const wchar_t MyConst1[] = L""Test"";
const wchar_t* MyConst2 = L""Test"";
const wchar_t* const MyConst3 = L""Test"";";
var inputContents = $@"const wchar_t MyConst1[] = L""Test\0\\\r\n\t\"""";
const wchar_t* MyConst2 = L""Test\0\\\r\n\t\"""";
const wchar_t* const MyConst3 = L""Test\0\\\r\n\t\"""";";

var expectedOutputContents = $@"namespace ClangSharp.Test
{{
public static partial class Methods
{{
[NativeTypeName(""const wchar_t[5]"")]
public const string MyConst1 = ""Test"";
[NativeTypeName(""const wchar_t[11]"")]
public const string MyConst1 = ""Test\0\\\r\n\t\"""";
[NativeTypeName(""const wchar_t *"")]
public static string MyConst2 = ""Test"";
public static string MyConst2 = ""Test\0\\\r\n\t\"""";
[NativeTypeName(""const wchar_t *const"")]
public const string MyConst3 = ""Test"";
public const string MyConst3 = ""Test\0\\\r\n\t\"""";
}}
}}
";
Expand All @@ -177,24 +177,24 @@ public static partial class Methods

protected override Task StringLiteralConstTestImpl()
{
var inputContents = $@"const char MyConst1[] = ""Test"";
const char* MyConst2 = ""Test"";
const char* const MyConst3 = ""Test"";";
var inputContents = $@"const char MyConst1[] = ""Test\0\\\r\n\t\"""";
const char* MyConst2 = ""Test\0\\\r\n\t\"""";
const char* const MyConst3 = ""Test\0\\\r\n\t\"""";";

var expectedOutputContents = $@"using System;
namespace ClangSharp.Test
{{
public static partial class Methods
{{
[NativeTypeName(""const char[5]"")]
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
[NativeTypeName(""const char[11]"")]
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
[NativeTypeName(""const char *"")]
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
[NativeTypeName(""const char *const"")]
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
}}
}}
";
Expand All @@ -204,22 +204,22 @@ public static partial class Methods

protected override Task WideStringLiteralStaticConstTestImpl()
{
var inputContents = $@"static const wchar_t MyConst1[] = L""Test"";
static const wchar_t* MyConst2 = L""Test"";
static const wchar_t* const MyConst3 = L""Test"";";
var inputContents = $@"static const wchar_t MyConst1[] = L""Test\0\\\r\n\t\"""";
static const wchar_t* MyConst2 = L""Test\0\\\r\n\t\"""";
static const wchar_t* const MyConst3 = L""Test\0\\\r\n\t\"""";";

var expectedOutputContents = $@"namespace ClangSharp.Test
{{
public static partial class Methods
{{
[NativeTypeName(""const wchar_t[5]"")]
public const string MyConst1 = ""Test"";
[NativeTypeName(""const wchar_t[11]"")]
public const string MyConst1 = ""Test\0\\\r\n\t\"""";
[NativeTypeName(""const wchar_t *"")]
public static string MyConst2 = ""Test"";
public static string MyConst2 = ""Test\0\\\r\n\t\"""";
[NativeTypeName(""const wchar_t *const"")]
public const string MyConst3 = ""Test"";
public const string MyConst3 = ""Test\0\\\r\n\t\"""";
}}
}}
";
Expand All @@ -229,24 +229,24 @@ public static partial class Methods

protected override Task StringLiteralStaticConstTestImpl()
{
var inputContents = $@"static const char MyConst1[] = ""Test"";
static const char* MyConst2 = ""Test"";
static const char* const MyConst3 = ""Test"";";
var inputContents = $@"static const char MyConst1[] = ""Test\0\\\r\n\t\"""";
static const char* MyConst2 = ""Test\0\\\r\n\t\"""";
static const char* const MyConst3 = ""Test\0\\\r\n\t\"""";";

var expectedOutputContents = $@"using System;
namespace ClangSharp.Test
{{
public static partial class Methods
{{
[NativeTypeName(""const char[5]"")]
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
[NativeTypeName(""const char[11]"")]
public static ReadOnlySpan<byte> MyConst1 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
[NativeTypeName(""const char *"")]
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
public static byte[] MyConst2 = new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
[NativeTypeName(""const char *const"")]
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00 }};
public static ReadOnlySpan<byte> MyConst3 => new byte[] {{ 0x54, 0x65, 0x73, 0x74, 0x00, 0x5C, 0x0D, 0x0A, 0x09, 0x22, 0x00 }};
}}
}}
";
Expand Down
Loading

0 comments on commit c8cb780

Please sign in to comment.