diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 572249b346afc68976826ea6c4994c771ed4d0d7..c7dbd03b04bff33f229eeac3cba35b6c3cde8feb 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -8125,6 +8125,15 @@ internal class CSharpResources { } } + /// + /// Looks up a localized string similar to Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.. + /// + internal static string ERR_TooManyUserStrings { + get { + return ResourceManager.GetString("ERR_TooManyUserStrings", resourceCulture); + } + } + /// /// Looks up a localized string similar to A format specifier may not contain trailing whitespace.. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 84721832f6d45946bafc464f6f475da5d766ab63..1d96642596d6c61325fe6867b685c3d3661c2d57 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -4672,4 +4672,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. + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index d04f2ee42fa4cbd2ab2d80f7a07291730b3bccef..729180bee5ef743f743c960146263b4cb9667538 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1319,5 +1319,6 @@ internal enum ErrorCode ERR_BadAwaitInStaticVariableInitializer = 8100, ERR_InvalidPathMap = 8101, ERR_PublicSignButNoKey = 8102, + ERR_TooManyUserStrings = 8103, } } diff --git a/src/Compilers/CSharp/Portable/Errors/MessageProvider.cs b/src/Compilers/CSharp/Portable/Errors/MessageProvider.cs index 50305e95cf87cb84b20cedbd1aa5a5cad9fec789..4a4d0ff96aac294715613a3ccd942484a7987ac6 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageProvider.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageProvider.cs @@ -201,6 +201,7 @@ public override void ReportDuplicateMetadataReferenceWeak(DiagnosticBag diagnost // PE Writer: public override int ERR_MetadataNameTooLong { get { return (int)ErrorCode.ERR_MetadataNameTooLong; } } public override int ERR_EncReferenceToAddedMember { get { return (int)ErrorCode.ERR_EncReferenceToAddedMember; } } + public override int ERR_TooManyUserStrings { get { return (int)ErrorCode.ERR_TooManyUserStrings; } } public override void ReportInvalidAttributeArgument(DiagnosticBag diagnostics, SyntaxNode attributeSyntax, int parameterIndex, AttributeData attribute) { diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EmitErrorTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EmitErrorTests.cs index 58122d4def6903ea70bbe47da40795cd4ba25ece..02da9e4e21284c4caed44983de919654c1815491 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EmitErrorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EmitErrorTests.cs @@ -300,6 +300,37 @@ public static int Main () Diagnostic(ErrorCode.ERR_TooManyLocals, "Main")); } + [Fact, WorkItem(8287, "https://github.com/dotnet/roslyn/issues/8287")] + public void ToManyUserStrings() + { + var builder = new System.Text.StringBuilder(); + builder.Append(@" +public class A +{ + public static void Main () + { +"); + for (int i = 0; i < 11; i++) + { + builder.Append("System.Console.WriteLine(\""); + builder.Append((char)('A' + i), 1000000); + builder.Append("\");"); + builder.AppendLine(); + } + + builder.Append(@" + } +} +"); + + var compilation = CreateCompilationWithMscorlib(builder.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) + ); + } + #endregion } } diff --git a/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs b/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs index dfc35a5d3842ac307c1d188b2f684634935804e7..573ecf52a0cc27742586b17bd5fd48cfd7642737 100644 --- a/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs +++ b/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs @@ -202,6 +202,7 @@ public DiagnosticInfo FilterDiagnosticInfo(DiagnosticInfo diagnosticInfo, Compil // PE writing: public abstract int ERR_MetadataNameTooLong { get; } public abstract int ERR_EncReferenceToAddedMember { get; } + public abstract int ERR_TooManyUserStrings { get; } public abstract void ReportInvalidAttributeArgument(DiagnosticBag diagnostics, SyntaxNode attributeSyntax, int parameterIndex, AttributeData attribute); public abstract void ReportInvalidNamedArgument(DiagnosticBag diagnostics, SyntaxNode attributeSyntax, int namedArgumentIndex, ITypeSymbol attributeClass, string parameterName); diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataHeapsBuilder.cs b/src/Compilers/Core/Portable/PEWriter/MetadataHeapsBuilder.cs index 12f4acc0797de29ce1247a9d34daa041bfee2874..e03c475b0fc0d4ceb3d12f4a63bcdca9980e0b33 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataHeapsBuilder.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataHeapsBuilder.cs @@ -261,7 +261,7 @@ public int ResolveBlobIndex(BlobIdx index) return (index.HeapPosition == 0) ? 0 : _blobHeapStartOffset + index.HeapPosition; } - public int GetUserStringToken(string str) + public bool TryGetUserStringToken(string str, out int token) { int index; if (!_userStrings.TryGetValue(str, out index)) @@ -269,6 +269,14 @@ public int GetUserStringToken(string str) Debug.Assert(!_streamsAreComplete); index = _userStringWriter.Position + _userStringHeapStartOffset; + + // User strings are referenced by metadata tokens (8 bits of which are used for the token type) leaving only 24 bits for the offset. + if ((index & 0xFF000000) != 0) + { + token = 0; + return false; + } + _userStrings.Add(str, index); _userStringWriter.WriteCompressedInteger((uint)str.Length * 2 + 1); @@ -327,7 +335,8 @@ public int GetUserStringToken(string str) _userStringWriter.WriteByte(stringKind); } - return 0x70000000 | index; + token = 0x70000000 | index; + return true; } public void Complete() diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index 327e59b685697b4aeeae37a428dc11a90bda8188..e0adf6778db264b56f1ba694b86520327da46163 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -422,6 +422,7 @@ private bool IsMinimalDelta private int[] _pseudoSymbolTokenToTokenMap; private IReference[] _pseudoSymbolTokenToReferenceMap; private int[] _pseudoStringTokenToTokenMap; + private bool _userStringTokenOverflow; private List _pseudoStringTokenToStringMap; private ReferenceIndexer _referenceVisitor; @@ -4479,7 +4480,23 @@ private int ResolveStringTokenFromPseudoStringToken(int pseudoStringToken) var str = _pseudoStringTokenToStringMap[index]; if (str != null) { - var token = heaps.GetUserStringToken(str); + const int overflowToken = 0x70000000; // 0x70 is a token type for a user string + int token; + if (!_userStringTokenOverflow) + { + if(!heaps.TryGetUserStringToken(str, out token)) + { + this.Context.Diagnostics.Add(this.messageProvider.CreateDiagnostic(this.messageProvider.ERR_TooManyUserStrings, + NoLocation.Singleton)); + _userStringTokenOverflow = true; + token = overflowToken; + } + } + else + { + token = overflowToken; + } + _pseudoStringTokenToTokenMap[index] = token; _pseudoStringTokenToStringMap[index] = null; // Set to null to bypass next lookup return token; diff --git a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb index 10da36f5cef45ec5d951c38308edfd64c132655a..14ee1be641237f34356a3b4e65cbff05a8e4759b 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb @@ -1681,6 +1681,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERR_DebugEntryPointNotSourceMethodDefinition = 37252 ERR_InvalidPathMap = 37253 ERR_PublicSignNoKey = 37254 + ERR_TooManyUserStrings = 37255 ERR_LastPlusOne diff --git a/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb b/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb index 10fec0ee5a902df742e0abae3fb7db3123675865..9d03f9a7ca288afd3fc225f4acc61fef3fdd816c 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb @@ -491,6 +491,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return ERRID.ERR_EncReferenceToAddedMember End Get End Property + + Public Overrides ReadOnly Property ERR_TooManyUserStrings As Integer + Get + Return ERRID.ERR_TooManyUserStrings + End Get + End Property End Class End Namespace diff --git a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb index a6e95c3ead87a229b6070cd360fe442682903c44..05d122330a6875262b8f2c72159443a3a794d43d 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb +++ b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb @@ -10253,6 +10253,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + ''' + ''' Looks up a localized string similar to Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string or XML literals.. + ''' + Friend ReadOnly Property ERR_TooManyUserStrings() As String + Get + Return ResourceManager.GetString("ERR_TooManyUserStrings", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Method cannot contain both a 'Try' statement and an 'On Error' or 'Resume' statement.. ''' diff --git a/src/Compilers/VisualBasic/Portable/VBResources.resx b/src/Compilers/VisualBasic/Portable/VBResources.resx index 466e80fab4216fab08c0d750e05e8e95f079f183..a9dcac8d3a5c60be083ca388436529498741c413 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.resx +++ b/src/Compilers/VisualBasic/Portable/VBResources.resx @@ -5344,4 +5344,7 @@ 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 or XML literals. + \ No newline at end of file diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/EmitErrorTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/EmitErrorTests.vb index cfe055ac6ddabb4b2578a2c1df8efc84f55b4036..42f729c18b62f43e0f908a8099f2f30c2088827e 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/EmitErrorTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/EmitErrorTests.vb @@ -574,6 +574,35 @@ BC35000: Requested operation is not available because the runtime library functi ]]>) End Sub + + Public Sub ToManyUserStrings() + + Dim source As New System.Text.StringBuilder() + source.Append(" +Module C + Sub Main() +") + + For i As Integer = 1 To 11 + source.Append( +" System.Console.WriteLine(""") + source.Append(ChrW(AscW("A"c) + i), 1000000) + source.Append(""") +") + Next + + source.Append(" + End Sub +End Module") + + Dim compilation = CreateCompilationWithReferences(VisualBasicSyntaxTree.ParseText(source.ToString()), {MscorlibRef, SystemRef, MsvbRef}) + + AssertTheseEmitDiagnostics(compilation, + +BC37255: Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string or XML literals. +) + End Sub + #End Region End Class diff --git a/src/Test/Utilities/Shared/Mocks/TestMessageProvider.cs b/src/Test/Utilities/Shared/Mocks/TestMessageProvider.cs index 7f84d161c656f51fbd7f79edb9c4d4fde7a884a2..4e459dcebf6e1f7ccc60de3ca8fa76ab193ebc6f 100644 --- a/src/Test/Utilities/Shared/Mocks/TestMessageProvider.cs +++ b/src/Test/Utilities/Shared/Mocks/TestMessageProvider.cs @@ -354,5 +354,13 @@ public override int ERR_BadCompilationOptionValue throw new NotImplementedException(); } } + + public override int ERR_TooManyUserStrings + { + get + { + throw new NotImplementedException(); + } + } } }