Skip to content

Commit 62dbff4

Browse files
[release/dev17.13] Emit opted-in string literals into data section as UTF8 (#76785)
* Add feature flag for emitting string literals into data section as UTF8 * Address feedback * Simplify some APIs * Lift well known type member check * Obtain the string type lazily * Consolidate static constructor implementations * Encapsulate IL emit of BytesToStringHelper * Extract NestedTypeDefinition * Share ParameterDefinitionBase * Add more doc comments * Remove unnecessary `using`s * Avoid passing text and data together * Avoid immutable array allocation * Remove ITokenDeferral and SystemStringType passing * Simplify parameter definition * Cache threshold * Simplify code * Re-add Opt suffix * Remove an unnecessary using * Extend tests * Improve tests * Improve code --------- Co-authored-by: Jan Jones <janjones@microsoft.com>
1 parent 911cf5f commit 62dbff4

34 files changed

+1409
-384
lines changed

src/Compilers/CSharp/Portable/CSharpResources.resx

+1-1
Original file line numberDiff line numberDiff line change
@@ -5309,7 +5309,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
53095309
<value>Syntax tree should be created from a submission.</value>
53105310
</data>
53115311
<data name="ERR_TooManyUserStrings" xml:space="preserve">
5312-
<value>Combined length of user strings used by the program exceeds allowed limit. Try to decrease use of string literals.</value>
5312+
<value>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'.</value>
53135313
</data>
53145314
<data name="ERR_PatternNullableType" xml:space="preserve">
53155315
<value>It is not legal to use nullable type '{0}?' in a pattern; use the underlying type '{0}' instead.</value>

src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs

+27-1
Original file line numberDiff line numberDiff line change
@@ -3481,13 +3481,39 @@ private void EmitConstantExpression(TypeSymbol type, ConstantValue constantValue
34813481
{
34823482
EmitInitObj(type, used, syntaxNode);
34833483
}
3484-
else
3484+
else if (!TryEmitStringLiteralAsUtf8Encoded(constantValue, syntaxNode))
34853485
{
34863486
_builder.EmitConstantValue(constantValue);
34873487
}
34883488
}
34893489
}
34903490

3491+
private bool TryEmitStringLiteralAsUtf8Encoded(ConstantValue constantValue, SyntaxNode syntaxNode)
3492+
{
3493+
// Emit long strings into data section so they don't overflow the UserString heap.
3494+
if (constantValue.IsString &&
3495+
constantValue.StringValue.Length > _module.Compilation.DataSectionStringLiteralThreshold)
3496+
{
3497+
if (Binder.GetWellKnownTypeMember(_module.Compilation, WellKnownMember.System_Text_Encoding__get_UTF8, _diagnostics, syntax: syntaxNode) == null |
3498+
Binder.GetWellKnownTypeMember(_module.Compilation, WellKnownMember.System_Text_Encoding__GetString, _diagnostics, syntax: syntaxNode) == null)
3499+
{
3500+
return false;
3501+
}
3502+
3503+
Cci.IFieldReference field = _module.TryGetOrCreateFieldForStringValue(constantValue.StringValue, syntaxNode, _diagnostics.DiagnosticBag);
3504+
if (field == null)
3505+
{
3506+
return false;
3507+
}
3508+
3509+
_builder.EmitOpCode(ILOpCode.Ldsfld);
3510+
_builder.EmitToken(field, syntaxNode, _diagnostics.DiagnosticBag);
3511+
return true;
3512+
}
3513+
3514+
return false;
3515+
}
3516+
34913517
private void EmitInitObj(TypeSymbol type, bool used, SyntaxNode syntaxNode)
34923518
{
34933519
if (used)

src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -671,8 +671,11 @@ private void CompileSynthesizedMethods(PrivateImplementationDetails privateImplC
671671
foreach (Cci.IMethodDefinition definition in privateImplClass.GetMethods(context).Concat(privateImplClass.GetTopLevelAndNestedTypeMethods(context)))
672672
{
673673
var method = (MethodSymbol)definition.GetInternalSymbol();
674-
Debug.Assert(method.SynthesizesLoweredBoundBody);
675-
method.GenerateMethodBody(compilationState, diagnostics);
674+
if (method is not null)
675+
{
676+
Debug.Assert(method.SynthesizesLoweredBoundBody);
677+
method.GenerateMethodBody(compilationState, diagnostics);
678+
}
676679
}
677680

678681
CompileSynthesizedMethods(compilationState);

src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs

+4-6
Original file line numberDiff line numberDiff line change
@@ -122,18 +122,16 @@ private BoundExpression MakeUtf8Span(BoundExpression node, IReadOnlyList<byte>?
122122

123123
private byte[]? GetUtf8ByteRepresentation(BoundUtf8String node)
124124
{
125-
var utf8 = new System.Text.UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
126-
127-
try
125+
if (node.Value.TryGetUtf8ByteRepresentation(out byte[]? result, out string? error))
128126
{
129-
return utf8.GetBytes(node.Value);
127+
return result;
130128
}
131-
catch (Exception ex)
129+
else
132130
{
133131
_diagnostics.Add(
134132
ErrorCode.ERR_CannotBeConvertedToUtf8,
135133
node.Syntax.Location,
136-
ex.Message);
134+
error);
137135

138136
return null;
139137
}

src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)