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