diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx
index 372af3bb5bc41..0ee9bd8f3670b 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.resx
+++ b/src/Compilers/CSharp/Portable/CSharpResources.resx
@@ -5309,7 +5309,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
Syntax tree should be created from a submission.
- Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.
+ Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals or try the EXPERIMENTAL feature flag 'experimental-data-section-string-literals'.
It is not legal to use nullable type '{0}?' in a pattern; use the underlying type '{0}' instead.
diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
index b9f065e75ff1d..26f9586b15fc1 100644
--- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
+++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
@@ -3481,13 +3481,39 @@ private void EmitConstantExpression(TypeSymbol type, ConstantValue constantValue
{
EmitInitObj(type, used, syntaxNode);
}
- else
+ else if (!TryEmitStringLiteralAsUtf8Encoded(constantValue, syntaxNode))
{
_builder.EmitConstantValue(constantValue);
}
}
}
+ private bool TryEmitStringLiteralAsUtf8Encoded(ConstantValue constantValue, SyntaxNode syntaxNode)
+ {
+ // Emit long strings into data section so they don't overflow the UserString heap.
+ if (constantValue.IsString &&
+ constantValue.StringValue.Length > _module.Compilation.DataSectionStringLiteralThreshold)
+ {
+ if (Binder.GetWellKnownTypeMember(_module.Compilation, WellKnownMember.System_Text_Encoding__get_UTF8, _diagnostics, syntax: syntaxNode) == null |
+ Binder.GetWellKnownTypeMember(_module.Compilation, WellKnownMember.System_Text_Encoding__GetString, _diagnostics, syntax: syntaxNode) == null)
+ {
+ return false;
+ }
+
+ Cci.IFieldReference field = _module.TryGetOrCreateFieldForStringValue(constantValue.StringValue, syntaxNode, _diagnostics.DiagnosticBag);
+ if (field == null)
+ {
+ return false;
+ }
+
+ _builder.EmitOpCode(ILOpCode.Ldsfld);
+ _builder.EmitToken(field, syntaxNode, _diagnostics.DiagnosticBag);
+ return true;
+ }
+
+ return false;
+ }
+
private void EmitInitObj(TypeSymbol type, bool used, SyntaxNode syntaxNode)
{
if (used)
diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
index 03176e8d1a351..632a09ab48501 100644
--- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
+++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
@@ -671,8 +671,11 @@ private void CompileSynthesizedMethods(PrivateImplementationDetails privateImplC
foreach (Cci.IMethodDefinition definition in privateImplClass.GetMethods(context).Concat(privateImplClass.GetTopLevelAndNestedTypeMethods(context)))
{
var method = (MethodSymbol)definition.GetInternalSymbol();
- Debug.Assert(method.SynthesizesLoweredBoundBody);
- method.GenerateMethodBody(compilationState, diagnostics);
+ if (method is not null)
+ {
+ Debug.Assert(method.SynthesizesLoweredBoundBody);
+ method.GenerateMethodBody(compilationState, diagnostics);
+ }
}
CompileSynthesizedMethods(compilationState);
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs
index ab0535e4e58d6..0dfa62d65d160 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs
@@ -122,18 +122,16 @@ private BoundExpression MakeUtf8Span(BoundExpression node, IReadOnlyList?
private byte[]? GetUtf8ByteRepresentation(BoundUtf8String node)
{
- var utf8 = new System.Text.UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
-
- try
+ if (node.Value.TryGetUtf8ByteRepresentation(out byte[]? result, out string? error))
{
- return utf8.GetBytes(node.Value);
+ return result;
}
- catch (Exception ex)
+ else
{
_diagnostics.Add(
ErrorCode.ERR_CannotBeConvertedToUtf8,
node.Syntax.Location,
- ex.Message);
+ error);
return null;
}
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
index f8bf057bf6f41..ea523fad2dafc 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
@@ -12710,8 +12710,8 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference
- Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.
- Kombinovaná délka uživatelských řetězců, které používá tento program, překročila povolený limit. Zkuste omezit použití řetězcových literálů.
+ Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals or try the EXPERIMENTAL feature flag 'experimental-data-section-string-literals'.
+ Kombinovaná délka uživatelských řetězců, které používá tento program, překročila povolený limit. Zkuste omezit použití řetězcových literálů.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
index 1f458cf89e14b..342940aa587ff 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
@@ -12710,8 +12710,8 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett
- Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.
- Die kombinierte Länge der vom Programm verwendeten Benutzerzeichenfolgen überschreitet den zulässigen Grenzwert. Versuchen Sie, die Verwendung von Zeichenfolgenliteralen zu verringern.
+ Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals or try the EXPERIMENTAL feature flag 'experimental-data-section-string-literals'.
+ Die kombinierte Länge der vom Programm verwendeten Benutzerzeichenfolgen überschreitet den zulässigen Grenzwert. Versuchen Sie, die Verwendung von Zeichenfolgenliteralen zu verringern.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
index 490afc99e2585..15d9d29b3033c 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
@@ -12710,8 +12710,8 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe
- Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.
- La longitud combinada de las cadenas de usuario que el programa utiliza supera el límite permitido. Intente disminuir el uso de literales de cadena.
+ Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals or try the EXPERIMENTAL feature flag 'experimental-data-section-string-literals'.
+ La longitud combinada de las cadenas de usuario que el programa utiliza supera el límite permitido. Intente disminuir el uso de literales de cadena.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
index 50de8c51ce9c0..506232c2dfe14 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
@@ -12710,8 +12710,8 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé
- Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.
- La longueur combinée des chaînes utilisateur que le programme utilise dépasse la limite autorisée. Essayez de réduire le nombre de littéraux de chaîne.
+ Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals or try the EXPERIMENTAL feature flag 'experimental-data-section-string-literals'.
+ La longueur combinée des chaînes utilisateur que le programme utilise dépasse la limite autorisée. Essayez de réduire le nombre de littéraux de chaîne.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
index 5b63726a67558..b81f6d15c0df9 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
@@ -12710,8 +12710,8 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr
- Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.
- La lunghezza combinata delle stringhe utente usate dal programma supera il limite consentito. Provare a ridurre l'uso di valori letterali stringa.
+ Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals or try the EXPERIMENTAL feature flag 'experimental-data-section-string-literals'.
+ La lunghezza combinata delle stringhe utente usate dal programma supera il limite consentito. Provare a ridurre l'uso di valori letterali stringa.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
index d1ccd40212a23..64d6155045c87 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
@@ -12710,8 +12710,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
- Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.
- プログラムで使うユーザー文字列の長さの合計が許可されている制限を超えています。文字列リテラルの使用を減らしてください。
+ Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals or try the EXPERIMENTAL feature flag 'experimental-data-section-string-literals'.
+ プログラムで使うユーザー文字列の長さの合計が許可されている制限を超えています。文字列リテラルの使用を減らしてください。
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
index 243fd2ac0b7c9..5880ea77267b9 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
@@ -12710,8 +12710,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
- Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.
- 프로그램에서 사용하는 사용자 문자열의 결합된 길이가 허용 한도를 초과합니다. 문자열 리터럴의 사용을 줄여 보세요.
+ Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals or try the EXPERIMENTAL feature flag 'experimental-data-section-string-literals'.
+ 프로그램에서 사용하는 사용자 문자열의 결합된 길이가 허용 한도를 초과합니다. 문자열 리터럴의 사용을 줄여 보세요.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
index 643f44629c419..b8aaf60f9547e 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
@@ -12710,8 +12710,8 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w
- Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.
- Całkowita długość ciągów użytkownika używanych przez program przekracza dozwolony limit. Spróbuj ograniczyć użycie literałów ciągów.
+ Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals or try the EXPERIMENTAL feature flag 'experimental-data-section-string-literals'.
+ Całkowita długość ciągów użytkownika używanych przez program przekracza dozwolony limit. Spróbuj ograniczyć użycie literałów ciągów.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
index 1407b0e3d35a4..f24658282f405 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
@@ -12710,8 +12710,8 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl
- Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.
- O comprimento combinado de cadeias do usuários usadas pelo programa excede o limite permitido. Tente diminuir o uso de literais de cadeia.
+ Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals or try the EXPERIMENTAL feature flag 'experimental-data-section-string-literals'.
+ O comprimento combinado de cadeias do usuários usadas pelo programa excede o limite permitido. Tente diminuir o uso de literais de cadeia.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
index feb729000ac8e..3f6ae5be291f2 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
@@ -12711,8 +12711,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
- Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.
- Общая длина пользовательских строк, используемых программой, превышает допустимый предел. Попробуйте сократить использование строковых литералов.
+ Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals or try the EXPERIMENTAL feature flag 'experimental-data-section-string-literals'.
+ Общая длина пользовательских строк, используемых программой, превышает допустимый предел. Попробуйте сократить использование строковых литералов.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
index 3aafdcd4d9e0a..5a2d1fdfe0517 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
@@ -12710,8 +12710,8 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T
- Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.
- Program tarafından kullanılan kullanıcı dizelerinin toplam uzunluğu, izin verilen sınırı aşıyor. Dize sabit değerlerinin kullanımını azaltmayı deneyin.
+ Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals or try the EXPERIMENTAL feature flag 'experimental-data-section-string-literals'.
+ Program tarafından kullanılan kullanıcı dizelerinin toplam uzunluğu, izin verilen sınırı aşıyor. Dize sabit değerlerinin kullanımını azaltmayı deneyin.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
index 11bd2b672e9b9..0e35fa2d08e26 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
@@ -12710,8 +12710,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
- Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.
- 该程序中的所有用户字符串在合并后,长度超出限制。请尝试减少字符串字面量的使用。
+ Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals or try the EXPERIMENTAL feature flag 'experimental-data-section-string-literals'.
+ 该程序中的所有用户字符串在合并后,长度超出限制。请尝试减少字符串字面量的使用。
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
index f43aeab538011..0d81591ccfca1 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
@@ -12710,8 +12710,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
- Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.
- 程式所使用的使用者字串加起來長度超過允許限制。請嘗試減少使用字串常值。
+ Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals or try the EXPERIMENTAL feature flag 'experimental-data-section-string-literals'.
+ 程式所使用的使用者字串加起來長度超過允許限制。請嘗試減少使用字串常值。
diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EmitErrorTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EmitErrorTests.cs
index aa1f4128b5f91..d522c10deec21 100644
--- a/src/Compilers/CSharp/Test/Emit/Emit/EmitErrorTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/Emit/EmitErrorTests.cs
@@ -311,9 +311,10 @@ public static int Main ()
}
[Fact, WorkItem(8287, "https://github.com/dotnet/roslyn/issues/8287")]
- public void ToManyUserStrings()
+ public void TooManyUserStrings()
{
var builder = new System.Text.StringBuilder();
+ var expectedOutputBuilder = new System.Text.StringBuilder();
builder.Append(@"
public class A
{
@@ -324,6 +325,8 @@ public static void Main ()
{
builder.Append("System.Console.WriteLine(\"");
builder.Append((char)('A' + i), 1000000);
+ expectedOutputBuilder.Append((char)('A' + i), 1000000);
+ expectedOutputBuilder.AppendLine();
builder.Append("\");");
builder.AppendLine();
}
@@ -333,12 +336,30 @@ public static void Main ()
}
");
- var compilation = CreateCompilation(builder.ToString());
+ var source = builder.ToString();
+ var expectedOutput = expectedOutputBuilder.ToString();
- compilation.VerifyEmitDiagnostics(
- // error CS8103: Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.
- Diagnostic(ErrorCode.ERR_TooManyUserStrings).WithLocation(1, 1)
- );
+ var expectedDiagnostics = new[]
+ {
+ // error CS8103: Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals or try the EXPERIMENTAL feature flag 'experimental-data-section-string-literals'.
+ Diagnostic(ErrorCode.ERR_TooManyUserStrings).WithLocation(1, 1)
+ };
+
+ CreateCompilation(source).VerifyEmitDiagnostics(expectedDiagnostics);
+
+ CreateCompilation(source,
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "1000000"))
+ .VerifyEmitDiagnostics(expectedDiagnostics);
+
+ CompileAndVerify(source,
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals"),
+ verify: Verification.Fails,
+ expectedOutput: expectedOutput).VerifyDiagnostics();
+
+ CompileAndVerify(source,
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "0"),
+ verify: Verification.Fails,
+ expectedOutput: expectedOutput).VerifyDiagnostics();
}
#endregion
diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadataTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadataTests.cs
index 738a07c592c94..234d9f222229f 100644
--- a/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadataTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadataTests.cs
@@ -2936,5 +2936,576 @@ public class Child : Parent, IParent
Assert.False(proxyChildParameters[1].IsMetadataIn); // User placed attributes are not copied.
});
}
+
+ [Fact]
+ public void DataSectionStringLiterals_MissingMembers()
+ {
+ var source = """
+ System.Console.Write("a");
+ System.Console.Write("bb");
+ System.Console.Write("ccc");
+ """;
+
+ var comp = CreateCompilation(source, parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "0"));
+ comp.MakeMemberMissing(WellKnownMember.System_Text_Encoding__get_UTF8);
+ comp.VerifyEmitDiagnostics(
+ // (1,22): error CS0656: Missing compiler required member 'System.Text.Encoding.get_UTF8'
+ // System.Console.Write("a");
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"""a""").WithArguments("System.Text.Encoding", "get_UTF8").WithLocation(1, 22),
+ // (2,22): error CS0656: Missing compiler required member 'System.Text.Encoding.get_UTF8'
+ // System.Console.Write("bb");
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"""bb""").WithArguments("System.Text.Encoding", "get_UTF8").WithLocation(2, 22),
+ // (3,22): error CS0656: Missing compiler required member 'System.Text.Encoding.get_UTF8'
+ // System.Console.Write("ccc");
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"""ccc""").WithArguments("System.Text.Encoding", "get_UTF8").WithLocation(3, 22));
+
+ comp = CreateCompilation(source, parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "0"));
+ comp.MakeMemberMissing(WellKnownMember.System_Text_Encoding__GetString);
+ comp.VerifyEmitDiagnostics(
+ // (1,22): error CS0656: Missing compiler required member 'System.Text.Encoding.GetString'
+ // System.Console.Write("a");
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"""a""").WithArguments("System.Text.Encoding", "GetString").WithLocation(1, 22),
+ // (2,22): error CS0656: Missing compiler required member 'System.Text.Encoding.GetString'
+ // System.Console.Write("bb");
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"""bb""").WithArguments("System.Text.Encoding", "GetString").WithLocation(2, 22),
+ // (3,22): error CS0656: Missing compiler required member 'System.Text.Encoding.GetString'
+ // System.Console.Write("ccc");
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"""ccc""").WithArguments("System.Text.Encoding", "GetString").WithLocation(3, 22));
+
+ comp = CreateCompilation(source, parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "0"));
+ comp.MakeMemberMissing(WellKnownMember.System_Text_Encoding__get_UTF8);
+ comp.MakeMemberMissing(WellKnownMember.System_Text_Encoding__GetString);
+ comp.VerifyEmitDiagnostics(
+ // (1,22): error CS0656: Missing compiler required member 'System.Text.Encoding.get_UTF8'
+ // System.Console.Write("a");
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"""a""").WithArguments("System.Text.Encoding", "get_UTF8").WithLocation(1, 22),
+ // (1,22): error CS0656: Missing compiler required member 'System.Text.Encoding.GetString'
+ // System.Console.Write("a");
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"""a""").WithArguments("System.Text.Encoding", "GetString").WithLocation(1, 22),
+ // (2,22): error CS0656: Missing compiler required member 'System.Text.Encoding.get_UTF8'
+ // System.Console.Write("bb");
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"""bb""").WithArguments("System.Text.Encoding", "get_UTF8").WithLocation(2, 22),
+ // (2,22): error CS0656: Missing compiler required member 'System.Text.Encoding.GetString'
+ // System.Console.Write("bb");
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"""bb""").WithArguments("System.Text.Encoding", "GetString").WithLocation(2, 22),
+ // (3,22): error CS0656: Missing compiler required member 'System.Text.Encoding.get_UTF8'
+ // System.Console.Write("ccc");
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"""ccc""").WithArguments("System.Text.Encoding", "get_UTF8").WithLocation(3, 22),
+ // (3,22): error CS0656: Missing compiler required member 'System.Text.Encoding.GetString'
+ // System.Console.Write("ccc");
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"""ccc""").WithArguments("System.Text.Encoding", "GetString").WithLocation(3, 22));
+
+ comp = CreateCompilation(source, parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "1"));
+ comp.MakeMemberMissing(WellKnownMember.System_Text_Encoding__get_UTF8);
+ comp.MakeMemberMissing(WellKnownMember.System_Text_Encoding__GetString);
+ comp.VerifyEmitDiagnostics(
+ // (2,26): error CS0656: Missing compiler required member 'System.Text.Encoding.get_UTF8'
+ // System.Console.Write("bb");
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"""bb""").WithArguments("System.Text.Encoding", "get_UTF8").WithLocation(2, 22),
+ // (2,26): error CS0656: Missing compiler required member 'System.Text.Encoding.GetString'
+ // System.Console.Write("bb");
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"""bb""").WithArguments("System.Text.Encoding", "GetString").WithLocation(2, 22),
+ // (3,22): error CS0656: Missing compiler required member 'System.Text.Encoding.get_UTF8'
+ // System.Console.Write("ccc");
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"""ccc""").WithArguments("System.Text.Encoding", "get_UTF8").WithLocation(3, 22),
+ // (3,22): error CS0656: Missing compiler required member 'System.Text.Encoding.GetString'
+ // System.Console.Write("ccc");
+ Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"""ccc""").WithArguments("System.Text.Encoding", "GetString").WithLocation(3, 22));
+
+ comp = CreateCompilation(source, parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "3"));
+ comp.MakeMemberMissing(WellKnownMember.System_Text_Encoding__get_UTF8);
+ comp.MakeMemberMissing(WellKnownMember.System_Text_Encoding__GetString);
+ CompileAndVerify(comp, expectedOutput: "abbccc").VerifyDiagnostics();
+ }
+
+ [Fact]
+ public void DataSectionStringLiterals_Threshold()
+ {
+ var source = """
+ System.Console.Write("a");
+ System.Console.Write("bb");
+ System.Console.Write("ccc");
+ """;
+
+ var expectedOutput = "abbccc";
+
+ var expectedIl = """
+ {
+ // Code size 31 (0x1f)
+ .maxstack 1
+ IL_0000: ldstr "a"
+ IL_0005: call "void System.Console.Write(string)"
+ IL_000a: ldstr "bb"
+ IL_000f: call "void System.Console.Write(string)"
+ IL_0014: ldstr "ccc"
+ IL_0019: call "void System.Console.Write(string)"
+ IL_001e: ret
+ }
+ """;
+
+ var verifier = CompileAndVerify(source, expectedOutput: expectedOutput)
+ .VerifyDiagnostics()
+ .VerifyIL("", expectedIl);
+ Assert.Null(verifier.Compilation.DataSectionStringLiteralThreshold);
+
+ foreach (var feature in new[] { "off", null })
+ {
+ verifier = CompileAndVerify(source,
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", null),
+ expectedOutput: expectedOutput)
+ .VerifyDiagnostics()
+ .VerifyIL("", expectedIl);
+ Assert.Null(verifier.Compilation.DataSectionStringLiteralThreshold);
+ }
+
+ // unrecognized input => default value 100
+ foreach (var feature in new[] { "true", "false", "", "-1", long.MaxValue.ToString() })
+ {
+ verifier = CompileAndVerify(source,
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", feature),
+ expectedOutput: expectedOutput)
+ .VerifyDiagnostics()
+ .VerifyIL("", expectedIl);
+ Assert.Equal(100, verifier.Compilation.DataSectionStringLiteralThreshold);
+ }
+
+ verifier = CompileAndVerify(source,
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "1000"),
+ expectedOutput: expectedOutput)
+ .VerifyDiagnostics()
+ .VerifyIL("", expectedIl);
+ Assert.Equal(1000, verifier.Compilation.DataSectionStringLiteralThreshold);
+
+ verifier = CompileAndVerify(source,
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "3"),
+ expectedOutput: expectedOutput)
+ .VerifyDiagnostics()
+ .VerifyIL("", expectedIl);
+ Assert.Equal(3, verifier.Compilation.DataSectionStringLiteralThreshold);
+
+ verifier = CompileAndVerify(source,
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "2"),
+ verify: Verification.Fails,
+ expectedOutput: expectedOutput)
+ .VerifyDiagnostics()
+ .VerifyIL("", """
+ {
+ // Code size 31 (0x1f)
+ .maxstack 1
+ IL_0000: ldstr "a"
+ IL_0005: call "void System.Console.Write(string)"
+ IL_000a: ldstr "bb"
+ IL_000f: call "void System.Console.Write(string)"
+ IL_0014: ldsfld "string .BE20CA004CC2993A396345E0D52DF013.s"
+ IL_0019: call "void System.Console.Write(string)"
+ IL_001e: ret
+ }
+ """);
+ Assert.Equal(2, verifier.Compilation.DataSectionStringLiteralThreshold);
+
+ verifier = CompileAndVerify(source,
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "1"),
+ verify: Verification.Fails,
+ expectedOutput: expectedOutput)
+ .VerifyDiagnostics()
+ .VerifyIL("", """
+ {
+ // Code size 31 (0x1f)
+ .maxstack 1
+ IL_0000: ldstr "a"
+ IL_0005: call "void System.Console.Write(string)"
+ IL_000a: ldsfld "string .DB1DE4B3DA6C7871B776D5CB968AA5A4.s"
+ IL_000f: call "void System.Console.Write(string)"
+ IL_0014: ldsfld "string .BE20CA004CC2993A396345E0D52DF013.s"
+ IL_0019: call "void System.Console.Write(string)"
+ IL_001e: ret
+ }
+ """);
+ Assert.Equal(1, verifier.Compilation.DataSectionStringLiteralThreshold);
+
+ verifier = CompileAndVerify(source,
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "0"),
+ verify: Verification.Fails,
+ expectedOutput: expectedOutput)
+ .VerifyDiagnostics()
+ .VerifyIL("", """
+ {
+ // Code size 31 (0x1f)
+ .maxstack 1
+ IL_0000: ldsfld "string .A96FAF705AF16834E6C632B61E964E1F.s"
+ IL_0005: call "void System.Console.Write(string)"
+ IL_000a: ldsfld "string .DB1DE4B3DA6C7871B776D5CB968AA5A4.s"
+ IL_000f: call "void System.Console.Write(string)"
+ IL_0014: ldsfld "string .BE20CA004CC2993A396345E0D52DF013.s"
+ IL_0019: call "void System.Console.Write(string)"
+ IL_001e: ret
+ }
+ """);
+ Assert.Equal(0, verifier.Compilation.DataSectionStringLiteralThreshold);
+ }
+
+ [Fact]
+ public void DataSectionStringLiterals_InvalidUtf8()
+ {
+ var source = """
+ System.Console.WriteLine("Hello \uD801\uD802");
+ """;
+ CompileAndVerify(source,
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "0"),
+ expectedOutput: "Hello \uD801\uD802",
+ symbolValidator: static (ModuleSymbol module) =>
+ {
+ // No types expected.
+ AssertEx.AssertEqualToleratingWhitespaceDifferences("""
+
+ EmbeddedAttribute
+ RefSafetyRulesAttribute
+ Program
+ """, module.TypeNames.Join("\n"));
+ })
+ .VerifyDiagnostics()
+ .VerifyIL("", $$"""
+ {
+ // Code size 11 (0xb)
+ .maxstack 1
+ IL_0000: ldstr "Hello {{"\uD801\uD802"}}"
+ IL_0005: call "void System.Console.WriteLine(string)"
+ IL_000a: ret
+ }
+ """);
+ }
+
+ [Fact]
+ public void DataSectionStringLiterals_SynthesizedTypes()
+ {
+ var source = """
+ System.Console.WriteLine("Hello");
+ """;
+ var verifier = CompileAndVerify(source,
+ targetFramework: TargetFramework.Mscorlib46,
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "0"),
+ verify: Verification.Fails,
+ expectedOutput: "Hello",
+ symbolValidator: static (ModuleSymbol module) =>
+ {
+ AssertEx.AssertEqualToleratingWhitespaceDifferences("""
+
+ EmbeddedAttribute
+ RefSafetyRulesAttribute
+ Program
+
+ __StaticArrayInitTypeSize=5
+ 1BFD09D1A433FB78117B4C7B1583D16D
+ """, module.TypeNames.Join("\n"));
+ });
+ verifier.VerifyDiagnostics();
+
+ verifier.VerifyIL("", """
+ {
+ // Code size 11 (0xb)
+ .maxstack 1
+ IL_0000: ldsfld "string .1BFD09D1A433FB78117B4C7B1583D16D.s"
+ IL_0005: call "void System.Console.WriteLine(string)"
+ IL_000a: ret
+ }
+ """);
+
+ var offset = ExecutionConditionUtil.IsUnix ? "00002890" : "00002850";
+ verifier.VerifyTypeIL("", $$"""
+ .class private auto ansi sealed ''
+ extends [mscorlib]System.Object
+ {
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
+ 01 00 00 00
+ )
+ // Nested Types
+ .class nested assembly explicit ansi sealed '__StaticArrayInitTypeSize=5'
+ extends [mscorlib]System.ValueType
+ {
+ .pack 1
+ .size 5
+ } // end of class __StaticArrayInitTypeSize=5
+ .class nested assembly auto ansi sealed beforefieldinit '1BFD09D1A433FB78117B4C7B1583D16D'
+ extends [mscorlib]System.Object
+ {
+ // Fields
+ .field assembly static initonly string s
+ // Methods
+ .method private hidebysig specialname rtspecialname static
+ void .cctor () cil managed
+ {
+ // Method begins at RVA 0x2089
+ // Code size 17 (0x11)
+ .maxstack 8
+ IL_0000: ldsflda valuetype ''/'__StaticArrayInitTypeSize=5' ''::'185F8DB32271FE25F561A6FC938B2E264306EC304EDA518007D1764826381969'
+ IL_0005: ldc.i4.5
+ IL_0006: call string ''::BytesToString(uint8*, int32)
+ IL_000b: stsfld string ''/'1BFD09D1A433FB78117B4C7B1583D16D'::s
+ IL_0010: ret
+ } // end of method '1BFD09D1A433FB78117B4C7B1583D16D'::.cctor
+ } // end of class 1BFD09D1A433FB78117B4C7B1583D16D
+ // Fields
+ .field assembly static initonly valuetype ''/'__StaticArrayInitTypeSize=5' '185F8DB32271FE25F561A6FC938B2E264306EC304EDA518007D1764826381969' at I_{{offset}}
+ .data cil I_{{offset}} = bytearray (
+ 48 65 6c 6c 6f
+ )
+ // Methods
+ .method private hidebysig static
+ string BytesToString (
+ uint8* bytes,
+ int32 length
+ ) cil managed
+ {
+ // Method begins at RVA 0x207b
+ // Code size 13 (0xd)
+ .maxstack 8
+ IL_0000: call class [mscorlib]System.Text.Encoding [mscorlib]System.Text.Encoding::get_UTF8()
+ IL_0005: ldarg.0
+ IL_0006: ldarg.1
+ IL_0007: callvirt instance string [mscorlib]System.Text.Encoding::GetString(uint8*, int32)
+ IL_000c: ret
+ } // end of method ''::BytesToString
+ } // end of class
+ """);
+ }
+
+ [Theory, CombinatorialData]
+ public void DataSectionStringLiterals_MetadataOnly(
+ [CombinatorialValues("0", "off")] string feature)
+ {
+ var source = """
+ class C
+ {
+ void M()
+ {
+ System.Console.WriteLine("Hello");
+ }
+ }
+ """;
+ CompileAndVerify(source,
+ emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true),
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", feature),
+ symbolValidator: static (ModuleSymbol module) =>
+ {
+ AssertEx.AssertEqualToleratingWhitespaceDifferences("""
+
+ EmbeddedAttribute
+ RefSafetyRulesAttribute
+ C
+ """, module.TypeNames.Join("\n"));
+ })
+ .VerifyDiagnostics();
+ }
+
+ [Fact]
+ public void DataSectionStringLiterals_SharedType()
+ {
+ var source = """
+ using static System.Console;
+
+ Write("a");
+ Write("b");
+ Write("ccc");
+ Write("ddd");
+ """;
+ CompileAndVerify(source,
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "0"),
+ options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All),
+ verify: Verification.Fails,
+ expectedOutput: "abcccddd",
+ symbolValidator: static (ModuleSymbol module) =>
+ {
+ var privateImplDetails = module.GlobalNamespace.GetTypeMember("");
+
+ // Data fields
+ AssertEx.AssertEqualToleratingWhitespaceDifferences("""
+ .__StaticArrayInitTypeSize=3 .64DAA44AD493FF28A96EFFAB6E77F1732A3D97D83241581B37DBD70A7A4900FE
+ .__StaticArrayInitTypeSize=3 .730F75DAFD73E047B86ACB2DBD74E75DCB93272FA084A9082848F2341AA1ABB6
+ System.Byte .3E23E8160039594A33894F6564E1B1348BBD7A0088D42C4ACB73EEAED59C009D
+ System.Byte .CA978112CA1BBDCAFAC231B39A23DC4DA786EFF8147C4E72B9807785AFEE48BB
+ """,
+ privateImplDetails.GetMembers().OfType().Select(f => f.ToTestDisplayString()).Order().Join("\n"));
+
+ // Nested types
+ AssertEx.AssertEqualToleratingWhitespaceDifferences("""
+ __StaticArrayInitTypeSize=3
+ A96FAF705AF16834E6C632B61E964E1F
+ 4B2212E31AC97FD4575A0B1C44D8843F
+ BE20CA004CC2993A396345E0D52DF013
+ 1F6CEF082E150274999DD6657C23A29E
+ """,
+ privateImplDetails.GetTypeMembers().Select(t => t.Name).Join("\n"));
+ })
+ .VerifyDiagnostics();
+ }
+
+ [Fact]
+ public void DataSectionStringLiterals_SharedValue()
+ {
+ var source = """
+ using static System.Console;
+
+ Write("a");
+ Write("a");
+ Write("bbb");
+ Write("bbb");
+ """;
+ CompileAndVerify(source,
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "0"),
+ options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All),
+ verify: Verification.Fails,
+ expectedOutput: "aabbbbbb",
+ symbolValidator: static (ModuleSymbol module) =>
+ {
+ var privateImplDetails = module.GlobalNamespace.GetTypeMember("");
+
+ // Data fields
+ AssertEx.AssertEqualToleratingWhitespaceDifferences("""
+ .__StaticArrayInitTypeSize=3 .3E744B9DC39389BAF0C5A0660589B8402F3DBB49B89B3E75F2C9355852A3C677
+ System.Byte .CA978112CA1BBDCAFAC231B39A23DC4DA786EFF8147C4E72B9807785AFEE48BB
+ """,
+ privateImplDetails.GetMembers().OfType().Select(f => f.ToTestDisplayString()).Order().Join("\n"));
+
+ // Nested types
+ AssertEx.AssertEqualToleratingWhitespaceDifferences("""
+ __StaticArrayInitTypeSize=3
+ A96FAF705AF16834E6C632B61E964E1F
+ A6DC9C19EFE4ABBCBE168A8B4D34D73A
+ """,
+ privateImplDetails.GetTypeMembers().Select(t => t.Name).Join("\n"));
+ })
+ .VerifyDiagnostics();
+ }
+
+ [Fact]
+ public void DataSectionStringLiterals_SharedType_ArrayInitializer()
+ {
+ var source = """
+ using System;
+ using static System.Console;
+
+ Write("abc");
+ M(new byte[] { 1, 2, 3 });
+
+ static void M(ReadOnlySpan x)
+ {
+ foreach (var b in x)
+ {
+ Write(b);
+ }
+ }
+ """;
+ CompileAndVerify(
+ CreateCompilationWithSpan(source,
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "0"),
+ options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)),
+ verify: Verification.Fails,
+ expectedOutput: "abc123",
+ symbolValidator: static (ModuleSymbol module) =>
+ {
+ // Data fields
+ AssertEx.AssertEqualToleratingWhitespaceDifferences("""
+ .__StaticArrayInitTypeSize=3 .039058C6F2C0CB492C533B0A4D14EF77CC0F78ABCCCED5287D84A1A2011CFB81
+ .__StaticArrayInitTypeSize=3 .BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD
+ """,
+ module.GlobalNamespace.GetTypeMember("").GetMembers()
+ .OfType().Select(f => f.ToTestDisplayString()).Order().Join("\n"));
+ })
+ .VerifyDiagnostics();
+ }
+
+ [Fact]
+ public void DataSectionStringLiterals_SharedValue_ArrayInitializer()
+ {
+ var source = """
+ using System;
+ using static System.Console;
+
+ Write("abc");
+ M(new byte[] { 97, 98, 99 });
+
+ static void M(ReadOnlySpan x)
+ {
+ foreach (var b in x)
+ {
+ Write((char)b);
+ }
+ }
+ """;
+ CompileAndVerify(
+ CreateCompilationWithSpan(source,
+ parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "0"),
+ options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)),
+ verify: Verification.Fails,
+ expectedOutput: "abcabc",
+ symbolValidator: static (ModuleSymbol module) =>
+ {
+ // Data fields
+ AssertEx.AssertEqualToleratingWhitespaceDifferences("""
+ .__StaticArrayInitTypeSize=3 .BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD
+ """,
+ module.GlobalNamespace.GetTypeMember("").GetMembers()
+ .OfType().Select(f => f.ToTestDisplayString()).Order().Join("\n"));
+ })
+ .VerifyDiagnostics();
+ }
+
+ ///
+ /// Tests a scenario that utilizes a private implementation detail class,
+ /// but doesn't use the string type, and the string type is not defined.
+ ///
+ [Fact]
+ public void PrivateImplDetailsWithoutString()
+ {
+ var source = """
+ #pragma warning disable CS8509 // The switch expression does not handle all possible values of its input type
+
+ class C
+ {
+ bool M(int i) => i switch { 1 => true };
+ }
+
+ namespace System
+ {
+ public class Object;
+ public class ValueType;
+ public struct Void;
+ public struct Boolean;
+ public struct Byte;
+ public struct Int16;
+ public struct Int32;
+ public struct Int64;
+ public class InvalidOperationException();
+ }
+ """;
+
+ var parseOptions = TestOptions.RegularPreview
+ .WithNoRefSafetyRulesAttribute()
+ .WithFeature("experimental-data-section-string-literals", "0");
+
+ CompileAndVerify(CreateEmptyCompilation(source, parseOptions: parseOptions),
+ verify: Verification.Skipped,
+ symbolValidator: static (ModuleSymbol module) =>
+ {
+ // PrivateImplementationDetails should be in the list.
+ AssertEx.AssertEqualToleratingWhitespaceDifferences("""
+
+ C
+ Object
+ ValueType
+ Void
+ Boolean
+ Byte
+ Int16
+ Int32
+ Int64
+ InvalidOperationException
+
+ """, module.TypeNames.Join("\n"));
+ })
+ .VerifyDiagnostics(
+ // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options.
+ Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1));
+ }
}
}
diff --git a/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs b/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs
index f1b3df903e4da..167f24f09be32 100644
--- a/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs
+++ b/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs
@@ -7,6 +7,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
+using System.IO.Hashing;
using System.Linq;
using System.Reflection.Metadata;
using System.Runtime.InteropServices;
@@ -14,6 +15,7 @@
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
+using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CodeGen
@@ -45,8 +47,10 @@ internal sealed class PrivateImplementationDetails : DefaultTypeDef, Cci.INamesp
internal const string SynthesizedInlineArrayFirstElementRefName = "InlineArrayFirstElementRef";
internal const string SynthesizedInlineArrayFirstElementRefReadOnlyName = "InlineArrayFirstElementRefReadOnly";
- private readonly CommonPEModuleBuilder _moduleBuilder; //the module builder
- private readonly Cci.ITypeReference _systemObject; //base type
+ internal const string SynthesizedBytesToStringFunctionName = "BytesToString";
+
+ internal readonly CommonPEModuleBuilder ModuleBuilder; //the module builder
+ internal readonly Cci.ITypeReference SystemObject; //base type
private readonly Cci.ITypeReference _systemValueType; //base for nested structs
private readonly Cci.ITypeReference _systemInt8Type; //for metadata init of byte arrays
@@ -91,8 +95,12 @@ internal sealed class PrivateImplementationDetails : DefaultTypeDef, Cci.INamesp
private readonly ConcurrentDictionary _synthesizedTopLevelTypes = new ConcurrentDictionary();
// field types for different block sizes.
- private ImmutableArray _orderedProxyTypes;
- private readonly ConcurrentDictionary<(uint Size, ushort Alignment), Cci.ITypeReference> _proxyTypes = new ConcurrentDictionary<(uint Size, ushort Alignment), Cci.ITypeReference>();
+ private readonly ConcurrentDictionary<(uint Size, ushort Alignment), Cci.ITypeReference> _dataFieldTypes = new ConcurrentDictionary<(uint Size, ushort Alignment), Cci.ITypeReference>();
+
+ // data section string literal holders (key is the full string literal)
+ private readonly ConcurrentDictionary _dataSectionStringLiteralTypes = new ConcurrentDictionary();
+
+ private ImmutableArray _orderedNestedTypes;
internal PrivateImplementationDetails(
CommonPEModuleBuilder moduleBuilder,
@@ -109,8 +117,8 @@ internal PrivateImplementationDetails(
RoslynDebug.Assert(systemObject != null);
RoslynDebug.Assert(systemValueType != null);
- _moduleBuilder = moduleBuilder;
- _systemObject = systemObject;
+ ModuleBuilder = moduleBuilder;
+ SystemObject = systemObject;
_systemValueType = systemValueType;
_systemInt8Type = systemInt8Type;
@@ -175,14 +183,15 @@ internal void Freeze()
// Sort top-level types.
_orderedTopLevelTypes = _synthesizedTopLevelTypes.OrderBy(kvp => kvp.Key).Select(kvp => (Cci.INamespaceTypeDefinition)kvp.Value).AsImmutable();
- // Sort proxy types.
- _orderedProxyTypes = _proxyTypes.OrderBy(kvp => kvp.Key.Size).ThenBy(kvp => kvp.Key.Alignment).Select(kvp => kvp.Value).AsImmutable();
+ // Sort nested types.
+ _orderedNestedTypes = _dataFieldTypes.OrderBy(kvp => kvp.Key.Size).ThenBy(kvp => kvp.Key.Alignment).Select(kvp => kvp.Value).OfType()
+ .Concat(_dataSectionStringLiteralTypes.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value)).AsImmutable();
}
internal bool IsFrozen => _frozen != 0;
///
- /// Gets a field that can be used to cache an array allocated to store data from a corresponding call.
+ /// Gets a field that can be used to cache an array allocated to store data from a corresponding call.
///
/// The data that will be used to initialize the field.
/// The type of the field, e.g. int[].
@@ -230,30 +239,16 @@ internal Cci.IFieldReference CreateArrayCachingField(ImmutableArray
- /// Gets a field that can be used to to store data directly in an RVA field.
+ /// Gets a struct type of the given size and alignment or creates it if it does not exist yet.
///
- /// The data for the field.
- ///
- /// The alignment value is the necessary alignment for addresses for the underlying element type of the array.
- /// The data is stored by using a type whose size is equal to the total size of the blob. If a built-in system
- /// type has an appropriate size and .pack, it can be used. Otherwise, a type is generated of the same size as
- /// the data, and that type needs its .pack set to the alignment required for the underlying data. While that
- /// .pack value isn't required by anything else in the compiler (the compiler always aligns RVA fields at 8-byte
- /// boundaries, which accomodates any element type that's relevant), it is necessary for IL rewriters. Such rewriters
- /// also need to ensure an appropriate alignment is maintained for the RVA field, and while they could also simplify
- /// by choosing a worst-case alignment as does the compiler, they may instead use the .pack value as the alignment
- /// to use for that field, since it's an opaque blob with no other indication as to what kind of data is
- /// stored and what alignment might be required.
- ///
- /// The field. This may have been newly created or may be an existing field previously created for the same data and alignment.
- internal Cci.IFieldReference CreateDataField(ImmutableArray data, ushort alignment)
+ private Cci.ITypeReference GetOrAddDataFieldType(int length, ushort alignment)
{
Debug.Assert(!IsFrozen);
Debug.Assert(alignment is 1 or 2 or 4 or 8);
- Debug.Assert(data.Length != 1 || alignment == 1);
+ Debug.Assert(length != 1 || alignment == 1);
- Cci.ITypeReference type = _proxyTypes.GetOrAdd(
- ((uint)data.Length, Alignment: alignment), key =>
+ return _dataFieldTypes.GetOrAdd(
+ ((uint)length, Alignment: alignment), key =>
{
// We need a type that's both the same size as the data and that has a .pack
// that matches the data's alignment requirements. If the size of the data
@@ -275,15 +270,39 @@ internal Cci.IFieldReference CreateDataField(ImmutableArray data, ushort a
// Use a custom type.
return new ExplicitSizeStruct(key.Size, key.Alignment, this, _systemValueType);
});
+ }
- return _mappedFields.GetOrAdd((data, alignment), key =>
+ ///
+ /// Gets a field that can be used to to store data directly in an RVA field.
+ ///
+ /// The data for the field.
+ ///
+ /// The alignment value is the necessary alignment for addresses for the underlying element type of the array.
+ /// The data is stored by using a type whose size is equal to the total size of the blob. If a built-in system
+ /// type has an appropriate size and .pack, it can be used. Otherwise, a type is generated of the same size as
+ /// the data, and that type needs its .pack set to the alignment required for the underlying data. While that
+ /// .pack value isn't required by anything else in the compiler (the compiler always aligns RVA fields at 8-byte
+ /// boundaries, which accommodates any element type that's relevant), it is necessary for IL rewriters. Such rewriters
+ /// also need to ensure an appropriate alignment is maintained for the RVA field, and while they could also simplify
+ /// by choosing a worst-case alignment as does the compiler, they may instead use the .pack value as the alignment
+ /// to use for that field, since it's an opaque blob with no other indication as to what kind of data is
+ /// stored and what alignment might be required.
+ ///
+ /// The field. This may have been newly created or may be an existing field previously created for the same data and alignment.
+ internal MappedField GetOrAddDataField(ImmutableArray data, ushort alignment)
+ {
+ return _mappedFields.GetOrAdd((data, alignment), static (key, @this) =>
{
+ var (data, alignment) = key;
+
+ Cci.ITypeReference type = @this.GetOrAddDataFieldType(data.Length, alignment);
+
// For alignment of 1 (which is used in cases other than in fields for ReadOnlySpan),
// just use the hex value of the data hash. For other alignments, tack on a '2', '4', or '8'
// accordingly. As every byte will yield two chars, the odd number of chars used for 2/4/8
// alignments will never produce a name that conflicts with names for an alignment of 1.
RoslynDebug.Assert(alignment is 1 or 2 or 4 or 8, $"Unexpected alignment: {alignment}");
- string hex = DataToHex(key.Data);
+ string hex = DataToHex(data);
string name = alignment switch
{
2 => hex + "2",
@@ -292,8 +311,82 @@ internal Cci.IFieldReference CreateDataField(ImmutableArray data, ushort a
_ => hex
};
- return new MappedField(name, this, type, key.Data);
- });
+ return new MappedField(name, @this, type, data);
+ },
+ this);
+ }
+
+ ///
+ /// Gets the field of or creates one
+ /// if the type does not exist yet for the given .
+ /// If the text cannot be encoded, returns .
+ ///
+ internal static Cci.IFieldReference? TryGetOrCreateFieldForStringValue(
+ string text,
+ CommonPEModuleBuilder moduleBuilder,
+ SyntaxNode syntaxNode,
+ DiagnosticBag diagnostics)
+ {
+ if (!text.TryGetUtf8ByteRepresentation(out byte[]? data, out _))
+ {
+ return null;
+ }
+
+ var @this = moduleBuilder.GetPrivateImplClass(syntaxNode, diagnostics);
+ return @this._dataSectionStringLiteralTypes.GetOrAdd(text, static (key, arg) =>
+ {
+ var (@this, data, diagnostics) = arg;
+
+ string name = "" + DataToHexViaXxHash128(data);
+
+ MappedField dataField = @this.GetOrAddDataField(data, alignment: 1);
+
+ Cci.IMethodDefinition bytesToStringHelper = @this.GetOrSynthesizeBytesToStringHelper(diagnostics);
+
+ return new DataSectionStringType(
+ name: name,
+ containingType: @this,
+ dataField: dataField,
+ bytesToStringHelper: bytesToStringHelper,
+ diagnostics: diagnostics);
+ },
+ (@this, ImmutableCollectionsMarshal.AsImmutableArray(data), diagnostics)).Field;
+ }
+
+ ///
+ /// Gets the or creates it if it does not exist yet.
+ ///
+ private Cci.IMethodDefinition GetOrSynthesizeBytesToStringHelper(DiagnosticBag diagnostics)
+ {
+ var method = GetMethod(SynthesizedBytesToStringFunctionName);
+
+ if (method is null)
+ {
+ var compilation = ModuleBuilder.CommonCompilation;
+ var encodingUtf8 = getWellKnownTypeMember(compilation, WellKnownMember.System_Text_Encoding__get_UTF8);
+ var encodingGetString = getWellKnownTypeMember(compilation, WellKnownMember.System_Text_Encoding__GetString);
+
+ TryAddSynthesizedMethod(BytesToStringHelper.Create(
+ moduleBuilder: (ITokenDeferral)ModuleBuilder,
+ containingType: this,
+ encodingUtf8: encodingUtf8,
+ encodingGetString: encodingGetString,
+ diagnostics: diagnostics));
+
+ method = GetMethod(SynthesizedBytesToStringFunctionName);
+ Debug.Assert(method is not null);
+ }
+
+ return method;
+
+ static Cci.IMethodReference getWellKnownTypeMember(
+ Compilation compilation,
+ WellKnownMember member)
+ {
+ ISymbolInternal? symbol = compilation.CommonGetWellKnownTypeMember(member);
+ Debug.Assert(symbol is not null, "The emit layer should check the helpers exist.");
+ return (Cci.IMethodReference)symbol.GetCciAdapter();
+ }
}
internal Cci.IFieldReference GetModuleVersionId(Cci.ITypeReference mvidType)
@@ -411,12 +504,12 @@ internal bool TryAddSynthesizedType(Cci.INamespaceTypeDefinition type)
public override IEnumerable GetNestedTypes(EmitContext context)
{
Debug.Assert(IsFrozen);
- return _orderedProxyTypes.OfType();
+ return _orderedNestedTypes;
}
public override string ToString() => this.Name;
- public override Cci.ITypeReference GetBaseClass(EmitContext context) => _systemObject;
+ public override Cci.ITypeReference GetBaseClass(EmitContext context) => SystemObject;
public override IEnumerable GetAttributes(EmitContext context)
{
@@ -443,8 +536,8 @@ public override void Dispatch(Cci.MetadataVisitor visitor)
public Cci.IUnitReference GetUnit(EmitContext context)
{
- Debug.Assert(context.Module == _moduleBuilder);
- return _moduleBuilder;
+ Debug.Assert(context.Module == ModuleBuilder);
+ return ModuleBuilder;
}
public string NamespaceName => string.Empty;
@@ -452,18 +545,26 @@ public Cci.IUnitReference GetUnit(EmitContext context)
private static string DataToHex(ImmutableArray data)
{
ImmutableArray hash = CryptographicHashProvider.ComputeSourceHash(data);
+ return HashToHex(hash.AsSpan());
+ }
+
+ private static string DataToHexViaXxHash128(ImmutableArray data)
+ {
+ Span hash = stackalloc byte[sizeof(ulong) * 2];
+ int bytesWritten = XxHash128.Hash(data.AsSpan(), hash);
+ Debug.Assert(bytesWritten == hash.Length);
return HashToHex(hash);
}
private static string ConstantsToHex(ImmutableArray constants)
{
ImmutableArray hash = CryptographicHashProvider.ComputeSourceHash(constants);
- return HashToHex(hash);
+ return HashToHex(hash.AsSpan());
}
- private static string HashToHex(ImmutableArray hash)
+ private static string HashToHex(ReadOnlySpan hash)
{
-#if NETCOREAPP2_1_OR_GREATER
+#if NET9_0_OR_GREATER
return string.Create(hash.Length * 2, hash, (destination, hash) => toHex(hash, destination));
#else
char[] c = new char[hash.Length * 2];
@@ -471,10 +572,10 @@ private static string HashToHex(ImmutableArray hash)
return new string(c);
#endif
- static void toHex(ImmutableArray source, Span destination)
+ static void toHex(ReadOnlySpan source, Span destination)
{
int i = 0;
- foreach (var b in source.AsSpan())
+ foreach (var b in source)
{
destination[i++] = hexchar(b >> 4);
destination[i++] = hexchar(b & 0xF);
@@ -562,7 +663,7 @@ public override int GetHashCode((ImmutableArray Constants, ushort
///
/// Simple struct type with explicit size and no members.
///
- internal sealed class ExplicitSizeStruct : DefaultTypeDef, Cci.INestedTypeDefinition
+ internal sealed class ExplicitSizeStruct : NestedTypeDefinition
{
private readonly uint _size;
private readonly ushort _alignment;
@@ -579,9 +680,6 @@ internal ExplicitSizeStruct(uint size, ushort alignment, PrivateImplementationDe
_sysValueType = sysValueType;
}
- public override string ToString()
- => _containingType.ToString() + "." + this.Name;
-
public override ushort Alignment => _alignment;
public override Cci.ITypeReference GetBaseClass(EmitContext context) => _sysValueType;
@@ -590,47 +688,143 @@ public override string ToString()
public override uint SizeOf => _size;
- public override void Dispatch(Cci.MetadataVisitor visitor)
- {
- visitor.Visit(this);
- }
-
- public string Name => _alignment == 1 ?
+ public override string Name => _alignment == 1 ?
$"__StaticArrayInitTypeSize={_size}" :
$"__StaticArrayInitTypeSize={_size}_Align={_alignment}";
- public Cci.ITypeDefinition ContainingTypeDefinition => _containingType;
+ public override Cci.ITypeDefinition ContainingTypeDefinition => _containingType;
- public Cci.TypeMemberVisibility Visibility => Cci.TypeMemberVisibility.Assembly;
+ public override Cci.TypeMemberVisibility Visibility => Cci.TypeMemberVisibility.Assembly;
public override bool IsValueType => true;
+ }
- public Cci.ITypeReference GetContainingType(EmitContext context) => _containingType;
+ ///
+ /// A type synthesized for each eligible string literal to hold the lazily-initialized string.
+ ///
+ /// https://github.com/dotnet/roslyn/blob/main/docs/features/string-literals-data-section.md
+ ///
+ internal sealed class DataSectionStringType : NestedTypeDefinition
+ {
+ private readonly string _name;
+ private readonly PrivateImplementationDetails _containingType;
+ private readonly ImmutableArray _fields;
+ private readonly ImmutableArray _methods;
+
+ public DataSectionStringType(
+ string name,
+ PrivateImplementationDetails containingType,
+ MappedField dataField,
+ Cci.IMethodDefinition bytesToStringHelper,
+ DiagnosticBag diagnostics)
+ {
+ _name = name;
+ _containingType = containingType;
+
+ var stringField = new DataSectionStringField("s", this);
+
+ var staticConstructor = synthesizeStaticConstructor((ITokenDeferral)containingType.ModuleBuilder, containingType, dataField, stringField, bytesToStringHelper, diagnostics);
+
+ _fields = [stringField];
+ _methods = [staticConstructor];
+
+ static Cci.IMethodDefinition synthesizeStaticConstructor(
+ ITokenDeferral module,
+ Cci.INamespaceTypeDefinition containingType,
+ MappedField dataField,
+ DataSectionStringField stringField,
+ Cci.IMethodDefinition bytesToStringHelper,
+ DiagnosticBag diagnostics)
+ {
+ var ilBuilder = new ILBuilder(
+ module,
+ new LocalSlotManager(slotAllocator: null),
+ OptimizationLevel.Release,
+ areLocalsZeroed: false);
+
+ // Push the `byte*` field's address.
+ ilBuilder.EmitOpCode(ILOpCode.Ldsflda);
+ ilBuilder.EmitToken(dataField, null, diagnostics);
+
+ // Push the byte size.
+ ilBuilder.EmitIntConstant(dataField.MappedData.Length);
+
+ // Call `.BytesToString(byte*, int)`.
+ ilBuilder.EmitOpCode(ILOpCode.Call, -1);
+ ilBuilder.EmitToken(bytesToStringHelper, null, diagnostics);
+
+ // Store into the corresponding `string` field.
+ ilBuilder.EmitOpCode(ILOpCode.Stsfld);
+ ilBuilder.EmitToken(stringField, null, diagnostics);
+
+ ilBuilder.EmitRet(isVoid: true);
+ ilBuilder.Realize();
+
+ return new Cci.StaticConstructor(containingType, ilBuilder.MaxStack, ilBuilder.RealizedIL);
+ }
+ }
+
+ public Cci.IFieldDefinition Field => _fields[0];
+ public override string Name => _name;
+ public override Cci.ITypeDefinition ContainingTypeDefinition => _containingType;
+ public override Cci.TypeMemberVisibility Visibility => Cci.TypeMemberVisibility.Assembly;
+ public override Cci.ITypeReference GetBaseClass(EmitContext context) => _containingType.SystemObject;
+ public override IEnumerable GetFields(EmitContext context) => _fields;
+ public override IEnumerable GetMethods(EmitContext context) => _methods;
+ public override bool IsBeforeFieldInit => true;
+
+ private sealed class DataSectionStringField(
+ string name, Cci.INamedTypeDefinition containingType)
+ : SynthesizedStaticFieldBase(name, containingType)
+ {
+ public override ImmutableArray MappedData => default;
+ public override bool IsReadOnly => true;
+
+ public override Cci.ITypeReference GetType(EmitContext context)
+ {
+ return context.Module.GetPlatformType(Cci.PlatformType.SystemString, context);
+ }
+
+ public override string ToString()
+ {
+ return $"string {(object?)ContainingTypeDefinition.GetInternalSymbol() ?? ContainingTypeDefinition}.{this.Name}";
+ }
+ }
+ }
- public override Cci.INestedTypeDefinition AsNestedTypeDefinition(EmitContext context) => this;
+ internal abstract class SynthesizedStaticField : SynthesizedStaticFieldBase
+ {
+ private readonly Cci.ITypeReference _type;
- public override Cci.INestedTypeReference AsNestedTypeReference => this;
+ internal SynthesizedStaticField(string name, Cci.INamedTypeDefinition containingType, Cci.ITypeReference type)
+ : base(name, containingType)
+ {
+ RoslynDebug.Assert(type != null);
+
+ _type = type;
+ }
+
+ internal Cci.ITypeReference Type => _type;
+
+ public override string ToString() => $"{(object?)_type.GetInternalSymbol() ?? _type} {(object?)ContainingTypeDefinition.GetInternalSymbol() ?? ContainingTypeDefinition}.{this.Name}";
+
+ public override Cci.ITypeReference GetType(EmitContext context) => _type;
}
- internal abstract class SynthesizedStaticField : Cci.IFieldDefinition
+ internal abstract class SynthesizedStaticFieldBase : Cci.IFieldDefinition
{
private readonly Cci.INamedTypeDefinition _containingType;
- private readonly Cci.ITypeReference _type;
private readonly string _name;
- internal SynthesizedStaticField(string name, Cci.INamedTypeDefinition containingType, Cci.ITypeReference type)
+ internal SynthesizedStaticFieldBase(string name, Cci.INamedTypeDefinition containingType)
{
RoslynDebug.Assert(name != null);
RoslynDebug.Assert(containingType != null);
- RoslynDebug.Assert(type != null);
_containingType = containingType;
- _type = type;
_name = name;
}
- public override string ToString() => $"{(object?)_type.GetInternalSymbol() ?? _type} {(object?)_containingType.GetInternalSymbol() ?? _containingType}.{this.Name}";
-
public MetadataConstant? GetCompileTimeValue(EmitContext context) => null;
public abstract ImmutableArray MappedData { get; }
@@ -685,14 +879,12 @@ public Cci.IDefinition AsDefinition(EmitContext context)
public bool IsContextualNamedEntity => false;
- public Cci.ITypeReference GetType(EmitContext context) => _type;
+ public abstract Cci.ITypeReference GetType(EmitContext context);
public ImmutableArray RefCustomModifiers => ImmutableArray.Empty;
public bool IsByReference => false;
- internal Cci.ITypeReference Type => _type;
-
public Cci.IFieldDefinition GetResolvedField(EmitContext context) => this;
public Cci.ISpecializedFieldReference? AsSpecializedFieldReference => null;
@@ -779,6 +971,29 @@ internal CachedArrayField(string name, Cci.INamedTypeDefinition containingType,
public override bool IsReadOnly => false;
}
+ internal abstract class NestedTypeDefinition : DefaultTypeDef, Cci.INestedTypeDefinition
+ {
+ public sealed override string ToString()
+ => ContainingTypeDefinition.ToString() + "." + this.Name;
+
+ public sealed override void Dispatch(Cci.MetadataVisitor visitor)
+ {
+ visitor.Visit(this);
+ }
+
+ public abstract string Name { get; }
+
+ public abstract Cci.ITypeDefinition ContainingTypeDefinition { get; }
+
+ public abstract Cci.TypeMemberVisibility Visibility { get; }
+
+ public Cci.ITypeReference GetContainingType(EmitContext context) => ContainingTypeDefinition;
+
+ public sealed override Cci.INestedTypeDefinition AsNestedTypeDefinition(EmitContext context) => this;
+
+ public sealed override Cci.INestedTypeReference AsNestedTypeReference => this;
+ }
+
///
/// Just a default implementation of a type definition.
///
@@ -807,7 +1022,7 @@ public IEnumerable GenericParameters
public bool IsAbstract => false;
- public bool IsBeforeFieldInit => false;
+ public virtual bool IsBeforeFieldInit => false;
public bool IsComObject => false;
@@ -883,19 +1098,13 @@ public TypeDefinitionHandle TypeDef
public virtual ushort Alignment => 0;
- public virtual Cci.ITypeReference GetBaseClass(EmitContext context)
- {
- throw ExceptionUtilities.Unreachable();
- }
+ public abstract Cci.ITypeReference GetBaseClass(EmitContext context);
public virtual LayoutKind Layout => LayoutKind.Auto;
public virtual uint SizeOf => 0;
- public virtual void Dispatch(Cci.MetadataVisitor visitor)
- {
- throw ExceptionUtilities.Unreachable();
- }
+ public abstract void Dispatch(Cci.MetadataVisitor visitor);
public virtual bool IsValueType => false;
@@ -911,4 +1120,104 @@ public sealed override int GetHashCode()
throw Roslyn.Utilities.ExceptionUtilities.Unreachable();
}
}
+
+ ///
+ /// A helper method used in static constructor of .
+ /// to share IL and hence save assembly size.
+ ///
+ ///
+ /// The method is equivalent to:
+ ///
+ /// private unsafe static string BytesToString(byte* bytes, int length)
+ /// {
+ /// return Encoding.UTF8.GetString(bytes, length);
+ /// }
+ ///
+ ///
+ file sealed class BytesToStringHelper : Cci.MethodDefinitionBase
+ {
+ private readonly ImmutableArray _parameters;
+
+ private BytesToStringHelper(
+ Cci.INamespaceTypeDefinition containingType,
+ Cci.IMethodReference encodingGetString,
+ ushort maxStack,
+ ImmutableArray il)
+ : base(containingType, maxStack, il)
+ {
+ _parameters =
+ [
+ new BytesParameter(encodingGetString), // byte* bytes
+ LengthParameterDefinition.Instance, // int length
+ ];
+ }
+
+ public static BytesToStringHelper Create(
+ ITokenDeferral moduleBuilder,
+ Cci.INamespaceTypeDefinition containingType,
+ Cci.IMethodReference encodingUtf8,
+ Cci.IMethodReference encodingGetString,
+ DiagnosticBag diagnostics)
+ {
+ var ilBuilder = new ILBuilder(
+ moduleBuilder,
+ new LocalSlotManager(slotAllocator: null),
+ OptimizationLevel.Release,
+ areLocalsZeroed: false);
+
+ // Call `Encoding.get_UTF8()`.
+ ilBuilder.EmitOpCode(ILOpCode.Call, 1);
+ ilBuilder.EmitToken(encodingUtf8, null, diagnostics);
+
+ // Push the `byte*`.
+ ilBuilder.EmitOpCode(ILOpCode.Ldarg_0);
+
+ // Push the byte size.
+ ilBuilder.EmitOpCode(ILOpCode.Ldarg_1);
+
+ // Call `Encoding.GetString(byte*, int)`.
+ ilBuilder.EmitOpCode(ILOpCode.Callvirt, -2);
+ ilBuilder.EmitToken(encodingGetString, null, diagnostics);
+
+ // Return.
+ ilBuilder.EmitRet(isVoid: false);
+ ilBuilder.Realize();
+
+ return new BytesToStringHelper(
+ containingType: containingType,
+ encodingGetString: encodingGetString,
+ maxStack: ilBuilder.MaxStack,
+ il: ilBuilder.RealizedIL);
+ }
+
+ public override string Name => PrivateImplementationDetails.SynthesizedBytesToStringFunctionName;
+ public override Cci.TypeMemberVisibility Visibility => Cci.TypeMemberVisibility.Private;
+ public override ImmutableArray Parameters => _parameters;
+ public override Cci.ITypeReference GetType(EmitContext context) => context.Module.GetPlatformType(Cci.PlatformType.SystemString, context);
+
+ private sealed class BytesParameter(
+ Cci.IMethodReference encodingGetString)
+ : Cci.ParameterDefinitionBase
+ {
+ private readonly Cci.IMethodReference _encodingGetString = encodingGetString;
+
+ public override ushort Index => 0;
+ public override string Name => "bytes";
+ public override Cci.ITypeReference GetType(EmitContext context)
+ {
+ return _encodingGetString.GetParameters(context)[0].GetType(context);
+ }
+ }
+
+ private sealed class LengthParameterDefinition : Cci.ParameterDefinitionBase
+ {
+ private LengthParameterDefinition() { }
+
+ public static readonly LengthParameterDefinition Instance = new LengthParameterDefinition();
+
+ public override ushort Index => 1;
+ public override string Name => "length";
+ public override Cci.ITypeReference GetType(EmitContext context) => context.Module.GetPlatformType(Cci.PlatformType.SystemInt32, context);
+ }
+ }
}
diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs
index b7210ad2b93fa..627e40d20e8f5 100644
--- a/src/Compilers/Core/Portable/Compilation/Compilation.cs
+++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs
@@ -70,6 +70,8 @@ public abstract partial class Compilation
// Protected for access in CSharpCompilation.WithAdditionalFeatures
protected readonly IReadOnlyDictionary _features;
+ private readonly Lazy _lazyDataSectionStringLiteralThreshold;
+
public ScriptCompilationInfo? ScriptCompilationInfo => CommonScriptCompilationInfo;
internal abstract ScriptCompilationInfo? CommonScriptCompilationInfo { get; }
@@ -91,6 +93,7 @@ internal Compilation(
_lazySubmissionSlotIndex = isSubmission ? SubmissionSlotIndexToBeAllocated : SubmissionSlotIndexNotApplicable;
_features = features;
+ _lazyDataSectionStringLiteralThreshold = new Lazy(ComputeDataSectionStringLiteralThreshold);
}
protected static IReadOnlyDictionary SyntaxTreeCommonFeatures(IEnumerable trees)
@@ -3519,6 +3522,33 @@ internal static bool SerializePeToStream(
///
internal bool CatchAnalyzerExceptions => Feature("debug-analyzers") == null;
+ internal int? DataSectionStringLiteralThreshold => _lazyDataSectionStringLiteralThreshold.Value;
+
+ private int? ComputeDataSectionStringLiteralThreshold()
+ {
+ if (Feature("experimental-data-section-string-literals") is { } s)
+ {
+ if (s == "off")
+ {
+ // disabled
+ return null;
+ }
+
+ if (int.TryParse(s, out var i) && i >= 0)
+ {
+ // custom non-negative threshold
+ // 0 can be used to enable for all strings
+ return i;
+ }
+
+ // default value
+ return 100;
+ }
+
+ // disabled
+ return null;
+ }
+
#endregion
private ConcurrentDictionary? _lazyTreeToUsedImportDirectivesMap;
diff --git a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs
index e6283ce8b07b2..1177f7add1a6d 100644
--- a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs
+++ b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs
@@ -214,6 +214,8 @@ public CommonPEModuleBuilder(
public abstract PrivateImplementationDetails? GetFrozenPrivateImplementationDetails();
+ internal abstract PrivateImplementationDetails GetPrivateImplClass(SyntaxNode? syntaxNode, DiagnosticBag diagnostics);
+
///
/// Additional top-level types injected by the Expression Evaluators.
///
@@ -1024,7 +1026,7 @@ Cci.IFieldReference ITokenDeferral.GetFieldForData(ImmutableArray data, us
var privateImpl = GetPrivateImplClass((TSyntaxNode)syntaxNode, diagnostics);
// map a field to the block (that makes it addressable via a token)
- return privateImpl.CreateDataField(data, alignment);
+ return privateImpl.GetOrAddDataField(data, alignment);
}
Cci.IFieldReference ITokenDeferral.GetArrayCachingFieldForData(ImmutableArray data, Cci.IArrayTypeReference arrayType, SyntaxNode syntaxNode, DiagnosticBag diagnostics)
@@ -1044,6 +1046,14 @@ public Cci.IFieldReference GetArrayCachingFieldForConstants(ImmutableArray
+ ///
+ ///
+ public Cci.IFieldReference TryGetOrCreateFieldForStringValue(string text, TSyntaxNode syntaxNode, DiagnosticBag diagnostics)
+ {
+ return PrivateImplementationDetails.TryGetOrCreateFieldForStringValue(text, this, syntaxNode, diagnostics);
+ }
+
public abstract Cci.IMethodReference GetInitArrayHelper();
public ArrayMethods ArrayMethods
@@ -1072,7 +1082,7 @@ public ArrayMethods ArrayMethods
#nullable enable
- internal PrivateImplementationDetails GetPrivateImplClass(TSyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics)
+ internal PrivateImplementationDetails GetPrivateImplClass(TSyntaxNode? syntaxNodeOpt, DiagnosticBag diagnostics)
{
var result = _lazyPrivateImplementationDetails;
@@ -1099,6 +1109,11 @@ internal PrivateImplementationDetails GetPrivateImplClass(TSyntaxNode syntaxNode
return result;
}
+ internal override PrivateImplementationDetails GetPrivateImplClass(SyntaxNode? syntaxNodeOpt, DiagnosticBag diagnostics)
+ {
+ return GetPrivateImplClass((TSyntaxNode?)syntaxNodeOpt, diagnostics);
+ }
+
public PrivateImplementationDetails? FreezePrivateImplementationDetails()
{
_lazyPrivateImplementationDetails?.Freeze();
diff --git a/src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs b/src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs
index fd9bb24e786c6..6a4329a22f876 100644
--- a/src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs
+++ b/src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs
@@ -7,12 +7,14 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+using System.Text;
namespace Roslyn.Utilities
{
internal static class StringExtensions
{
private static ImmutableArray s_lazyNumerals;
+ private static UTF8Encoding? s_lazyUtf8;
internal static string GetNumeral(int number)
{
@@ -275,5 +277,25 @@ public static int GetCaseSensitivePrefixLength(this string string1, string strin
return x;
}
+
+ internal static bool TryGetUtf8ByteRepresentation(
+ this string s,
+ [NotNullWhen(returnValue: true)] out byte[]? result,
+ [NotNullWhen(returnValue: false)] out string? error)
+ {
+ s_lazyUtf8 ??= new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
+ try
+ {
+ result = s_lazyUtf8.GetBytes(s);
+ error = null;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ result = null;
+ error = ex.Message;
+ return false;
+ }
+ }
}
}
diff --git a/src/Compilers/Core/Portable/PEWriter/Members.cs b/src/Compilers/Core/Portable/PEWriter/Members.cs
index 238be2f37fa65..c1c2cd6e5ecc6 100644
--- a/src/Compilers/Core/Portable/PEWriter/Members.cs
+++ b/src/Compilers/Core/Portable/PEWriter/Members.cs
@@ -415,7 +415,7 @@ ImmutableArray ExceptionRegions
///
/// Debugging information associated with a MoveNext method of a state machine.
///
- StateMachineMoveNextBodyDebugInfo MoveNextBodyInfo { get; }
+ StateMachineMoveNextBodyDebugInfo? MoveNextBodyInfo { get; }
///
/// The maximum number of elements on the evaluation stack during the execution of the method.
@@ -446,7 +446,7 @@ ImmutableArray ExceptionRegions
/// the namespace type name. For instance namespace type x.y.z will have two namespace scopes, the first is for the x and the second
/// is for the y.
///
- IImportScope ImportScope { get; }
+ IImportScope? ImportScope { get; }
DebugId MethodId { get; }
@@ -470,7 +470,7 @@ ImmutableArray ExceptionRegions
/// The name of the state machine generated for the method,
/// or null if the method isn't the kickoff method of a state machine.
///
- string StateMachineTypeName { get; }
+ string? StateMachineTypeName { get; }
///
/// Returns information relevant to EnC on slots of local variables hoisted to state machine fields,
diff --git a/src/Compilers/Core/Portable/PEWriter/MethodDefinitionBase.cs b/src/Compilers/Core/Portable/PEWriter/MethodDefinitionBase.cs
new file mode 100644
index 0000000000000..2ecd2e3d02edf
--- /dev/null
+++ b/src/Compilers/Core/Portable/PEWriter/MethodDefinitionBase.cs
@@ -0,0 +1,182 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Reflection;
+using Microsoft.CodeAnalysis.CodeGen;
+using Microsoft.CodeAnalysis.Debugging;
+using Microsoft.CodeAnalysis.Emit;
+using Roslyn.Utilities;
+
+namespace Microsoft.Cci;
+
+internal abstract class MethodDefinitionBase : IMethodDefinition, IMethodBody
+{
+ public MethodDefinitionBase(ITypeDefinition containingTypeDefinition, ushort maxStack, ImmutableArray il)
+ {
+ ContainingTypeDefinition = containingTypeDefinition;
+ MaxStack = maxStack;
+ IL = il;
+ }
+
+ // IMethodDefinition implementation
+
+ bool IDefinition.IsEncDeleted => false;
+
+ public ITypeDefinition ContainingTypeDefinition { get; }
+
+ public abstract string Name { get; }
+
+ public bool HasBody => true;
+
+ public IMethodBody GetBody(EmitContext context) => this;
+
+ public IEnumerable GenericParameters => SpecializedCollections.EmptyEnumerable();
+
+ public bool HasDeclarativeSecurity => false;
+
+ public bool IsAbstract => false;
+
+ public bool IsAccessCheckedOnOverride => false;
+
+ public bool IsConstructor => false;
+
+ public bool IsExternal => false;
+
+ public bool IsHiddenBySignature => true;
+
+ public bool IsNewSlot => false;
+
+ public bool IsPlatformInvoke => false;
+
+ public virtual bool IsRuntimeSpecial => false;
+
+ public bool IsSealed => false;
+
+ public virtual bool IsSpecialName => false;
+
+ public bool IsStatic => true;
+
+ public bool IsVirtual => false;
+
+ public virtual ImmutableArray Parameters => ImmutableArray.Empty;
+
+#nullable disable
+ public IPlatformInvokeInformation PlatformInvokeData => null;
+
+ public bool RequiresSecurityObject => false;
+
+ public bool ReturnValueIsMarshalledExplicitly => false;
+
+ public IMarshallingInformation ReturnValueMarshallingInformation => null;
+
+ public ImmutableArray ReturnValueMarshallingDescriptor => default;
+
+ public IEnumerable SecurityAttributes => null;
+
+ public INamespace ContainingNamespace => null;
+
+ public abstract TypeMemberVisibility Visibility { get; }
+
+ public bool AcceptsExtraArguments => false;
+
+ public ushort GenericParameterCount => 0;
+
+ public ImmutableArray ExtraParameters => ImmutableArray.Empty;
+
+ public IGenericMethodInstanceReference AsGenericMethodInstanceReference => null;
+
+ public ISpecializedMethodReference AsSpecializedMethodReference => null;
+
+ public CallingConvention CallingConvention => CallingConvention.Default;
+
+ public ushort ParameterCount => (ushort)Parameters.Length;
+
+ public ImmutableArray ReturnValueCustomModifiers => ImmutableArray.Empty;
+
+ public ImmutableArray RefCustomModifiers => ImmutableArray.Empty;
+
+ public bool ReturnValueIsByRef => false;
+
+ public IDefinition AsDefinition(EmitContext context) => this;
+
+ CodeAnalysis.Symbols.ISymbolInternal Cci.IReference.GetInternalSymbol() => null;
+
+ public void Dispatch(MetadataVisitor visitor) => visitor.Visit((IMethodDefinition)this);
+
+ public IEnumerable GetAttributes(EmitContext context) => SpecializedCollections.EmptyEnumerable();
+
+ public ITypeReference GetContainingType(EmitContext context) => ContainingTypeDefinition;
+
+ public MethodImplAttributes GetImplementationAttributes(EmitContext context) => default;
+
+ public ImmutableArray GetParameters(EmitContext context) => Parameters.CastArray();
+
+ public IMethodDefinition GetResolvedMethod(EmitContext context) => this;
+
+ public IEnumerable GetReturnValueAttributes(EmitContext context) => SpecializedCollections.EmptyEnumerable();
+
+ public virtual ITypeReference GetType(EmitContext context) => context.Module.GetPlatformType(PlatformType.SystemVoid, context);
+
+ // IMethodBody implementation
+
+ public ushort MaxStack { get; }
+
+ public ImmutableArray IL { get; }
+
+ public IMethodDefinition MethodDefinition => this;
+
+ public ImmutableArray ExceptionRegions => ImmutableArray.Empty;
+
+ public bool AreLocalsZeroed => false;
+
+ public bool HasStackalloc => false;
+
+ public ImmutableArray LocalVariables => ImmutableArray.Empty;
+
+ public StateMachineMoveNextBodyDebugInfo MoveNextBodyInfo => null;
+
+ public ImmutableArray SequencePoints => ImmutableArray.Empty;
+
+ public bool HasDynamicLocalVariables => false;
+
+ public ImmutableArray LocalScopes => ImmutableArray.Empty;
+
+ public IImportScope ImportScope => null;
+
+ public DebugId MethodId => default;
+
+ public ImmutableArray StateMachineHoistedLocalScopes => ImmutableArray.Empty;
+
+ public string StateMachineTypeName => null;
+
+ public ImmutableArray StateMachineHoistedLocalSlots => ImmutableArray.Empty;
+
+ public ImmutableArray StateMachineAwaiterSlots => ImmutableArray.Empty;
+
+ public ImmutableArray ClosureDebugInfo => ImmutableArray.Empty;
+
+ public ImmutableArray LambdaDebugInfo => ImmutableArray.Empty;
+
+ public ImmutableArray OrderedLambdaRuntimeRudeEdits => ImmutableArray.Empty;
+
+ public StateMachineStatesDebugInfo StateMachineStatesDebugInfo => default;
+
+ public ImmutableArray CodeCoverageSpans => ImmutableArray.Empty;
+
+ public bool IsPrimaryConstructor => false;
+
+ public sealed override bool Equals(object obj)
+ {
+ // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used.
+ throw ExceptionUtilities.Unreachable();
+ }
+
+ public sealed override int GetHashCode()
+ {
+ // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used.
+ throw ExceptionUtilities.Unreachable();
+ }
+}
diff --git a/src/Compilers/Core/Portable/PEWriter/ParameterDefinitionBase.cs b/src/Compilers/Core/Portable/PEWriter/ParameterDefinitionBase.cs
new file mode 100644
index 0000000000000..1eceff4aa72e7
--- /dev/null
+++ b/src/Compilers/Core/Portable/PEWriter/ParameterDefinitionBase.cs
@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis.CodeGen;
+using Microsoft.CodeAnalysis.Emit;
+using Microsoft.CodeAnalysis.Symbols;
+
+namespace Microsoft.Cci;
+
+internal abstract class ParameterDefinitionBase : Cci.IParameterDefinition
+{
+ public bool HasDefaultValue => false;
+ public bool IsIn => false;
+ public virtual bool IsMarshalledExplicitly => false;
+ public bool IsOptional => false;
+ public bool IsOut => false;
+ public virtual Cci.IMarshallingInformation? MarshallingInformation => null;
+ public virtual ImmutableArray MarshallingDescriptor => default;
+ public bool IsEncDeleted => false;
+ public abstract string Name { get; }
+ public virtual ImmutableArray CustomModifiers => [];
+ public virtual ImmutableArray RefCustomModifiers => [];
+ public virtual bool IsByReference => false;
+ public abstract ushort Index { get; }
+
+ public Cci.IDefinition? AsDefinition(EmitContext context) => this;
+ public void Dispatch(Cci.MetadataVisitor visitor) => visitor.Visit(this);
+ public virtual IEnumerable GetAttributes(EmitContext context) => [];
+ public MetadataConstant? GetDefaultValue(EmitContext context) => null;
+ public ISymbolInternal? GetInternalSymbol() => null;
+ public abstract Cci.ITypeReference GetType(EmitContext context);
+
+ public sealed override bool Equals(object? obj)
+ {
+ // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used.
+ throw Roslyn.Utilities.ExceptionUtilities.Unreachable();
+ }
+
+ public sealed override int GetHashCode()
+ {
+ // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used.
+ throw Roslyn.Utilities.ExceptionUtilities.Unreachable();
+ }
+}
diff --git a/src/Compilers/Core/Portable/PEWriter/ReturnValueParameter.cs b/src/Compilers/Core/Portable/PEWriter/ReturnValueParameter.cs
index 59d2416201a7c..8ec5fcc80a713 100644
--- a/src/Compilers/Core/Portable/PEWriter/ReturnValueParameter.cs
+++ b/src/Compilers/Core/Portable/PEWriter/ReturnValueParameter.cs
@@ -4,128 +4,67 @@
using System.Collections.Generic;
using System.Collections.Immutable;
-using Microsoft.CodeAnalysis.CodeGen;
-using EmitContext = Microsoft.CodeAnalysis.Emit.EmitContext;
+using Microsoft.CodeAnalysis.Emit;
namespace Microsoft.Cci
{
- internal class ReturnValueParameter : IParameterDefinition
+ internal sealed class ReturnValueParameter : ParameterDefinitionBase
{
internal ReturnValueParameter(IMethodDefinition containingMethod)
{
_containingMethod = containingMethod;
}
- bool IDefinition.IsEncDeleted => false;
-
- public IEnumerable GetAttributes(EmitContext context)
+ public override IEnumerable GetAttributes(EmitContext context)
{
return _containingMethod.GetReturnValueAttributes(context);
}
- public ISignature ContainingSignature
- {
- get { return _containingMethod; }
- }
-
private readonly IMethodDefinition _containingMethod;
- public MetadataConstant? Constant
- {
- get { return null; }
- }
-
- public ImmutableArray RefCustomModifiers
+ public override ImmutableArray RefCustomModifiers
{
get { return _containingMethod.RefCustomModifiers; }
}
- public ImmutableArray CustomModifiers
+ public override ImmutableArray CustomModifiers
{
get { return _containingMethod.ReturnValueCustomModifiers; }
}
- public MetadataConstant? GetDefaultValue(EmitContext context)
- {
- return null;
- }
-
- public void Dispatch(MetadataVisitor visitor)
- {
- }
-
- public bool HasDefaultValue
- {
- get { return false; }
- }
-
- public ushort Index
+ public override ushort Index
{
get { return 0; }
}
- public bool IsIn
- {
- get { return false; }
- }
-
- public bool IsByReference
+ public override bool IsByReference
{
get { return _containingMethod.ReturnValueIsByRef; }
}
- public bool IsMarshalledExplicitly
+ public override bool IsMarshalledExplicitly
{
get { return _containingMethod.ReturnValueIsMarshalledExplicitly; }
}
- public bool IsOptional
- {
- get { return false; }
- }
-
- public bool IsOut
- {
- get { return false; }
- }
-
- public IMarshallingInformation MarshallingInformation
+ public override IMarshallingInformation MarshallingInformation
{
get { return _containingMethod.ReturnValueMarshallingInformation; }
}
- public ImmutableArray MarshallingDescriptor
+ public override ImmutableArray MarshallingDescriptor
{
get { return _containingMethod.ReturnValueMarshallingDescriptor; }
}
- public string Name
+ public override string Name
{
get { return string.Empty; }
}
- public ITypeReference GetType(EmitContext context)
+ public override ITypeReference GetType(EmitContext context)
{
return _containingMethod.GetType(context);
}
-
- public IDefinition AsDefinition(EmitContext context)
- {
- return this as IDefinition;
- }
-
- CodeAnalysis.Symbols.ISymbolInternal? Cci.IReference.GetInternalSymbol() => null;
-
- public sealed override bool Equals(object? obj)
- {
- // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used.
- throw Roslyn.Utilities.ExceptionUtilities.Unreachable();
- }
-
- public sealed override int GetHashCode()
- {
- // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used.
- throw Roslyn.Utilities.ExceptionUtilities.Unreachable();
- }
}
}
diff --git a/src/Compilers/Core/Portable/PEWriter/RootModuleStaticConstructor.cs b/src/Compilers/Core/Portable/PEWriter/RootModuleStaticConstructor.cs
deleted file mode 100644
index e8d99093842c2..0000000000000
--- a/src/Compilers/Core/Portable/PEWriter/RootModuleStaticConstructor.cs
+++ /dev/null
@@ -1,186 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Collections.Generic;
-using System.Collections.Immutable;
-using System.Diagnostics.CodeAnalysis;
-using System.Reflection;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CodeGen;
-using Microsoft.CodeAnalysis.Debugging;
-using Microsoft.CodeAnalysis.Emit;
-using Roslyn.Utilities;
-
-namespace Microsoft.Cci
-{
- internal sealed partial class RootModuleStaticConstructor : IMethodDefinition, IMethodBody
- {
- public RootModuleStaticConstructor(ITypeDefinition containingTypeDefinition, ImmutableArray il)
- {
- ContainingTypeDefinition = containingTypeDefinition;
- IL = il;
- }
-
- // IMethodDefinition implementation
-
- bool IDefinition.IsEncDeleted => false;
-
- public ITypeDefinition ContainingTypeDefinition { get; }
-
- public string Name => WellKnownMemberNames.StaticConstructorName;
-
- public bool HasBody => true;
-
- public IMethodBody GetBody(EmitContext context) => this;
-
- public IEnumerable GenericParameters => SpecializedCollections.EmptyEnumerable();
-
- public bool IsImplicitlyDeclared => true;
-
- public bool HasDeclarativeSecurity => false;
-
- public bool IsAbstract => false;
-
- public bool IsAccessCheckedOnOverride => false;
-
- public bool IsConstructor => false;
-
- public bool IsExternal => false;
-
- public bool IsHiddenBySignature => true;
-
- public bool IsNewSlot => false;
-
- public bool IsPlatformInvoke => false;
-
- public bool IsRuntimeSpecial => true;
-
- public bool IsSealed => false;
-
- public bool IsSpecialName => true;
-
- public bool IsStatic => true;
-
- public bool IsVirtual => false;
-
- public ImmutableArray Parameters => ImmutableArray.Empty;
-
-#nullable disable
- public IPlatformInvokeInformation PlatformInvokeData => null;
-
- public bool RequiresSecurityObject => false;
-
- public bool ReturnValueIsMarshalledExplicitly => false;
-
- public IMarshallingInformation ReturnValueMarshallingInformation => null;
-
- public ImmutableArray ReturnValueMarshallingDescriptor => default;
-
- public IEnumerable SecurityAttributes => null;
-
- public INamespace ContainingNamespace => null;
-
- public TypeMemberVisibility Visibility => TypeMemberVisibility.Private;
-
- public bool AcceptsExtraArguments => false;
-
- public ushort GenericParameterCount => 0;
-
- public ImmutableArray ExtraParameters => ImmutableArray.Empty;
-
- public IGenericMethodInstanceReference AsGenericMethodInstanceReference => null;
-
- public ISpecializedMethodReference AsSpecializedMethodReference => null;
-
- public CallingConvention CallingConvention => CallingConvention.Default;
-
- public ushort ParameterCount => 0;
-
- public ImmutableArray ReturnValueCustomModifiers => ImmutableArray.Empty;
-
- public ImmutableArray RefCustomModifiers => ImmutableArray.Empty;
-
- public bool ReturnValueIsByRef => false;
-
- public IDefinition AsDefinition(EmitContext context) => this;
-
- CodeAnalysis.Symbols.ISymbolInternal Cci.IReference.GetInternalSymbol() => null;
-
- public void Dispatch(MetadataVisitor visitor) => visitor.Visit((IMethodDefinition)this);
-
- public IEnumerable GetAttributes(EmitContext context) => SpecializedCollections.EmptyEnumerable();
-
- public ITypeReference GetContainingType(EmitContext context) => ContainingTypeDefinition;
-
- public MethodImplAttributes GetImplementationAttributes(EmitContext context) => default;
-
- public ImmutableArray GetParameters(EmitContext context) => ImmutableArray.Empty;
-
- public IMethodDefinition GetResolvedMethod(EmitContext context) => this;
-
- public IEnumerable GetReturnValueAttributes(EmitContext context) => SpecializedCollections.EmptyEnumerable();
-
- public ITypeReference GetType(EmitContext context) => context.Module.GetPlatformType(PlatformType.SystemVoid, context);
-
- // IMethodBody implementation
-
- public ushort MaxStack => 0;
-
- public ImmutableArray IL { get; }
-
- public IMethodDefinition MethodDefinition => this;
-
- public ImmutableArray ExceptionRegions => ImmutableArray.Empty;
-
- public bool AreLocalsZeroed => false;
-
- public bool HasStackalloc => false;
-
- public ImmutableArray LocalVariables => ImmutableArray.Empty;
-
- public StateMachineMoveNextBodyDebugInfo MoveNextBodyInfo => null;
-
- public ImmutableArray SequencePoints => ImmutableArray.Empty;
-
- public bool HasDynamicLocalVariables => false;
-
- public ImmutableArray LocalScopes => ImmutableArray.Empty;
-
- public IImportScope ImportScope => null;
-
- public DebugId MethodId => default;
-
- public ImmutableArray StateMachineHoistedLocalScopes => ImmutableArray.Empty;
-
- public string StateMachineTypeName => null;
-
- public ImmutableArray StateMachineHoistedLocalSlots => ImmutableArray.Empty;
-
- public ImmutableArray StateMachineAwaiterSlots => ImmutableArray.Empty;
-
- public ImmutableArray ClosureDebugInfo => ImmutableArray.Empty;
-
- public ImmutableArray LambdaDebugInfo => ImmutableArray.Empty;
-
- public ImmutableArray OrderedLambdaRuntimeRudeEdits => ImmutableArray.Empty;
-
- public StateMachineStatesDebugInfo StateMachineStatesDebugInfo => default;
-
- public ImmutableArray CodeCoverageSpans => ImmutableArray.Empty;
-
- public bool IsPrimaryConstructor => false;
-
- public sealed override bool Equals(object obj)
- {
- // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used.
- throw ExceptionUtilities.Unreachable();
- }
-
- public sealed override int GetHashCode()
- {
- // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used.
- throw ExceptionUtilities.Unreachable();
- }
- }
-}
diff --git a/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs b/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs
index 3af909a47d7f8..9db1f14698f3a 100644
--- a/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs
+++ b/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs
@@ -30,7 +30,7 @@ public void SetStaticConstructorBody(ImmutableArray il)
Debug.Assert(_methods is null);
_methods = SpecializedCollections.SingletonReadOnlyList(
- new RootModuleStaticConstructor(containingTypeDefinition: this, il));
+ new StaticConstructor(containingTypeDefinition: this, maxStack: 0, il));
}
public IEnumerable GetMethods(EmitContext context)
diff --git a/src/Compilers/Core/Portable/PEWriter/StaticConstructor.cs b/src/Compilers/Core/Portable/PEWriter/StaticConstructor.cs
new file mode 100644
index 0000000000000..e53bfc160e385
--- /dev/null
+++ b/src/Compilers/Core/Portable/PEWriter/StaticConstructor.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+
+namespace Microsoft.Cci
+{
+ internal sealed class StaticConstructor(
+ ITypeDefinition containingTypeDefinition, ushort maxStack, ImmutableArray il)
+ : MethodDefinitionBase(containingTypeDefinition, maxStack, il)
+ {
+ public override string Name => WellKnownMemberNames.StaticConstructorName;
+ public override TypeMemberVisibility Visibility => TypeMemberVisibility.Private;
+ public override bool IsRuntimeSpecial => true;
+ public override bool IsSpecialName => true;
+ }
+}
diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs
index 33ece21e2439e..c3c78f3858415 100644
--- a/src/Compilers/Core/Portable/WellKnownMember.cs
+++ b/src/Compilers/Core/Portable/WellKnownMember.cs
@@ -715,6 +715,9 @@ internal enum WellKnownMember
System_Linq_Expressions_Expression__Default,
System_Linq_Expressions_Expression__Power_MethodInfo,
+ System_Text_Encoding__get_UTF8,
+ System_Text_Encoding__GetString,
+
Count,
// Remember to update the AllWellKnownTypeMembers tests when making changes here
diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs
index 37cf53ebccdb4..c3911dbb361d0 100644
--- a/src/Compilers/Core/Portable/WellKnownMembers.cs
+++ b/src/Compilers/Core/Portable/WellKnownMembers.cs
@@ -5181,6 +5181,22 @@ static WellKnownMembers()
(byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_Linq_Expressions_Expression,
(byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_Linq_Expressions_Expression,
(byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_Reflection_MethodInfo,
+
+ // System_Text_Encoding__get_UTF8
+ (byte)(MemberFlags.PropertyGet | MemberFlags.Static), // Flags
+ (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Text_Encoding - WellKnownType.ExtSentinel), // DeclaringTypeId
+ 0, // Arity
+ 0, // Method Signature
+ (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Text_Encoding - WellKnownType.ExtSentinel), // Return Type
+
+ // System_Text_Encoding__GetString
+ (byte)MemberFlags.Method, // Flags
+ (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Text_Encoding - WellKnownType.ExtSentinel), // DeclaringTypeId
+ 0, // Arity
+ 2, // Method Signature
+ (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_String, // Return Type
+ (byte)SignatureTypeCode.Pointer, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Byte, // Argument: byte*
+ (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, // Argument: int
};
string[] allNames = new string[(int)WellKnownMember.Count]
@@ -5805,6 +5821,8 @@ static WellKnownMembers()
"GreaterThanOrEqual", // System_Linq_Expressions_Expression__GreaterThanOrEqual_MethodInfo,
"Default", // System_Linq_Expressions_Expression__Default
"Power", // System_Linq_Expressions_Expression__Power_MethodInfo,
+ "get_UTF8", // System_Text_Encoding__get_UTF8
+ "GetString", // System_Text_Encoding__GetString
};
s_descriptors = MemberDescriptor.InitializeFromStream(new System.IO.MemoryStream(initializationBytes, writable: false), allNames);
diff --git a/src/Compilers/Core/Portable/WellKnownTypes.cs b/src/Compilers/Core/Portable/WellKnownTypes.cs
index 7c3f4f5fc301c..bf210b97a1ec2 100644
--- a/src/Compilers/Core/Portable/WellKnownTypes.cs
+++ b/src/Compilers/Core/Portable/WellKnownTypes.cs
@@ -355,6 +355,8 @@ internal enum WellKnownType
System_Linq_Expressions_NewArrayExpression,
System_Linq_Expressions_DefaultExpression,
+ System_Text_Encoding,
+
NextAvailable,
// Remember to update the AllWellKnownTypes tests when making changes here
}
@@ -695,6 +697,8 @@ internal static class WellKnownTypes
"System.Linq.Expressions.InvocationExpression",
"System.Linq.Expressions.NewArrayExpression",
"System.Linq.Expressions.DefaultExpression",
+
+ "System.Text.Encoding",
};
private static readonly Dictionary s_nameToTypeIdMap = new Dictionary((int)Count);
diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb
index a3de0df7eec4b..99df2dab956b3 100644
--- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb
+++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb
@@ -828,7 +828,9 @@ End Namespace
WellKnownMember.System_Collections_Immutable_ImmutableArray_T__Empty,
WellKnownMember.System_Span_T__ctor_ref_T,
WellKnownMember.System_ReadOnlySpan_T__ctor_ref_readonly_T,
- WellKnownMember.System_Runtime_CompilerServices_HotReloadException__ctorStringInt32
+ WellKnownMember.System_Runtime_CompilerServices_HotReloadException__ctorStringInt32,
+ WellKnownMember.System_Text_Encoding__get_UTF8,
+ WellKnownMember.System_Text_Encoding__GetString
' Not always available.
Continue For
End Select
@@ -1037,7 +1039,9 @@ End Namespace
WellKnownMember.System_Collections_Immutable_ImmutableArray_T__Empty,
WellKnownMember.System_Span_T__ctor_ref_T,
WellKnownMember.System_ReadOnlySpan_T__ctor_ref_readonly_T,
- WellKnownMember.System_Runtime_CompilerServices_HotReloadException__ctorStringInt32
+ WellKnownMember.System_Runtime_CompilerServices_HotReloadException__ctorStringInt32,
+ WellKnownMember.System_Text_Encoding__get_UTF8,
+ WellKnownMember.System_Text_Encoding__GetString
' Not always available.
Continue For
End Select