Skip to content

Commit b1079a0

Browse files
authored
[Xamarin.Android.Build.Tasks] Fix -int.ToString() for locales (#7941)
Fixes: #7939 Context: dotnet/runtime#13363 Context: https://www.unicode.org/charts/PDF/U2200.pdf (page 2) Context: 5271f3e The LLVM IR generator (5271f3e) outputs a number of integer values, relying on the standard `int.ToString()` method to convert the value to a string. However, since .NET 6, this conversion is culture- sensitive and in certain cultures (list below) it will output the minus sign not as the "standard" ASCII \u002d character `-` but instead as something else (see complete list below for details). This breaks LLVM LLC: llc: environment.arm64-v8a.ll:554:7: error: expected value token stderr | i32 −1, ; apk_fd Fix the problem by using invariant culture when converting ***all*** integers and unknown objects to strings. The reason all of them are converted in this way is to avoid future changes to ICU and/or .NET to-string conversion that might affect the resulting integer format. Workaround: export `$LANG` to a locale that uses 0x2D `-` for negation, e.g. `LANG=C`. Locales/cultures affected by this issue are as follows: * ARABIC LETTER MARK + HYPHEN-MINUS `؜-` (\u061c \u002d): 26 cultures * ar * ar-001 * ar-BH * ar-DJ * ar-EG * ar-ER * ar-IL * ar-IQ * ar-JO * ar-KM * ar-KW * ar-LB * ar-MR * ar-OM * ar-PS * ar-QA * ar-SA * ar-SD * ar-SO * ar-SS * ar-SY * ar-TD * ar-YE * sd * sd-Arab * sd-Arab-PK * LEFT-TO-RIGHT MARK + HYPHEN-MINUS `‎-` (\u200e \u002d): 10 cultures * ar-AE * ar-DZ * ar-EH * ar-LY * ar-MA * ar-TN * he * he-IL * ur * ur-PK * RIGHT-TO-LEFT MARK + HYPHEN-MINUS `‏-` (\u200f \u002d): 3 cultures * ckb * ckb-IQ * ckb-IR * MINUS SIGN `−` (\u2212): 38 cultures * et * et-EE * eu * eu-ES * fi * fi-FI * fo * fo-DK * fo-FO * gsw * gsw-CH * gsw-FR * gsw-LI * hr * hr-BA * hr-HR * ksh * ksh-DE * lt * lt-LT * nb * nb-NO * nb-SJ * nn * nn-NO * no * rm * rm-CH * se * se-FI * se-NO * se-SE * sl * sl-SI * sv * sv-AX * sv-FI * sv-SE * LEFT-TO-RIGHT MARK + MINUS SIGN `‎−` (\u200e \u2212): 3 cultures * fa * fa-AF * fa-IR * LEFT-TO-RIGHT MARK + HYPHEN-MINUS + LEFT-TO-RIGHT MARK `‎-‎` (\u200e \u002d \u002e): 16 cultures * ks * ks-Arab * ks-Arab-IN * lrc * lrc-IQ * lrc-IR * mzn * mzn-IR * pa-Arab * pa-Arab-PK * ps * ps-AF * ps-PK * ur-IN * uz-Arab * uz-Arab-AF Update `BuildTest2.BuildBasicApplication()` to export `LANG=sv_SE.UTF-8` as part of the `dotnet build` command to test this scenario on macOS and Linux. (This change is ignored on Windows.)
1 parent bff7242 commit b1079a0

File tree

8 files changed

+80
-47
lines changed

8 files changed

+80
-47
lines changed

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,22 @@ public static string [] SupportedTargetFrameworks ()
105105
}
106106

107107
[Test]
108-
public void BuildBasicApplication ([ValueSource (nameof (SupportedTargetFrameworks))] string tfv, [Values (true, false)] bool isRelease)
108+
public void BuildBasicApplication ([ValueSource (nameof (SupportedTargetFrameworks))] string tfv, [Values (true, false)] bool isRelease, [Values ("", "en_US.UTF-8", "sv_SE.UTF-8")] string langEnvironmentVariable)
109109
{
110110
var proj = new XamarinAndroidApplicationProject {
111111
IsRelease = isRelease,
112112
TargetFrameworkVersion = tfv,
113113
};
114+
115+
Dictionary<string, string> envvar = null;
116+
if (!String.IsNullOrEmpty (langEnvironmentVariable)) {
117+
envvar = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase) {
118+
{"LANG", langEnvironmentVariable},
119+
};
120+
}
121+
114122
using (var b = CreateApkBuilder ()) {
115-
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
123+
Assert.IsTrue (b.Build (proj, environmentVariables: envvar), "Build should have succeeded.");
116124
}
117125
}
118126

src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Globalization;
34
using System.IO;
45

56
using Java.Interop.Tools.TypeNameMappings;
@@ -253,7 +254,7 @@ List<StructureInstance<DSOCacheEntry>> InitDSOCache ()
253254
continue;
254255
}
255256

256-
dsos.Add ((name, $"dsoName{dsos.Count}", ELFHelper.IsEmptyAOTLibrary (log, item.ItemSpec)));
257+
dsos.Add ((name, $"dsoName{dsos.Count.ToString (CultureInfo.InvariantCulture)}", ELFHelper.IsEmptyAOTLibrary (log, item.ItemSpec)));
257258
}
258259

259260
var dsoCache = new List<StructureInstance<DSOCacheEntry>> ();

src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/FunctionAttributes.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Text;
3+
using System.Globalization;
34

45
namespace Xamarin.Android.Tasks.LLVMIR
56
{
@@ -113,7 +114,7 @@ public AlignstackFunctionAttribute (uint powerOfTwoAlignment)
113114

114115
protected override void RenderParams (StringBuilder sb)
115116
{
116-
sb.Append (alignment);
117+
sb.Append (alignment.ToString (CultureInfo.InvariantCulture));
117118
}
118119
}
119120

@@ -165,13 +166,13 @@ public AllocsizeFunctionAttribute (uint elementSize, uint? numberOfElements = nu
165166

166167
protected override void RenderParams (StringBuilder sb)
167168
{
168-
sb.Append (elementSize);
169+
sb.Append (elementSize.ToString (CultureInfo.InvariantCulture));
169170
if (!numberOfElements.HasValue) {
170171
return;
171172
}
172173

173174
sb.Append (", ");
174-
sb.Append (numberOfElements.Value);
175+
sb.Append (numberOfElements.Value.ToString (CultureInfo.InvariantCulture));
175176
}
176177
}
177178

@@ -701,13 +702,13 @@ public VscaleRangeFunctionAttribute (uint min, uint? max = null)
701702

702703
protected override void RenderParams (StringBuilder sb)
703704
{
704-
sb.Append (min);
705+
sb.Append (min.ToString (CultureInfo.InvariantCulture));
705706
if (!max.HasValue) {
706707
return;
707708
}
708709

709710
sb.Append (", ");
710-
sb.Append (max.Value);
711+
sb.Append (max.Value.ToString (CultureInfo.InvariantCulture));
711712
}
712713
}
713714

@@ -721,7 +722,7 @@ public MinLegalVectorWidthFunctionAttribute (uint size)
721722
this.size = size;
722723
}
723724

724-
protected override void RenderAssignedValue (StringBuilder sb) => sb.Append (size);
725+
protected override void RenderAssignedValue (StringBuilder sb) => sb.Append (size.ToString (CultureInfo.InvariantCulture));
725726
}
726727

727728
class StackProtectorBufferSizeFunctionAttribute : LLVMFunctionAttribute
@@ -734,7 +735,7 @@ public StackProtectorBufferSizeFunctionAttribute (uint size)
734735
this.size = size;
735736
}
736737

737-
protected override void RenderAssignedValue (StringBuilder sb) => sb.Append (size);
738+
protected override void RenderAssignedValue (StringBuilder sb) => sb.Append (size.ToString (CultureInfo.InvariantCulture));
738739
}
739740

740741
class TargetCpuFunctionAttribute : LLVMFunctionAttribute

src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunction.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Globalization;
44
using System.Linq;
55
using System.Text;
6-
using System.Reflection;
76

87
namespace Xamarin.Android.Tasks.LLVMIR
98
{

src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.Code.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Globalization;
34
using System.IO;
45
using System.Text;
56

@@ -52,7 +53,7 @@ public void WriteFunctionStart (LlvmIrFunction function, string? comment = null)
5253
WriteFunctionParameters (function.Parameters, writeNames: true);
5354
Output.Write(") local_unnamed_addr ");
5455
if (attributes != null) {
55-
Output.Write ($"#{function.AttributeSetID}");
56+
Output.Write ($"#{function.AttributeSetID.ToString (CultureInfo.InvariantCulture)}");
5657
}
5758
Output.WriteLine ();
5859
Output.WriteLine ("{");
@@ -184,7 +185,7 @@ public void EmitStoreInstruction (LlvmIrFunction function, LlvmIrFunctionLocalVa
184185
CodeRenderType (source);
185186
Output.Write ($" %{source.Name}, ");
186187
CodeRenderType (destination);
187-
Output.WriteLine ($"* {destination.Reference}, align {GetTypeSize (destination.Type)}");
188+
Output.WriteLine ($"* {destination.Reference}, align {GetTypeSize (destination.Type).ToString (CultureInfo.InvariantCulture)}");
188189
}
189190

190191
/// <summary>
@@ -201,7 +202,7 @@ public LlvmIrFunctionLocalVariable EmitLoadInstruction (LlvmIrFunction function,
201202

202203
string variableType = sb.ToString ();
203204
LlvmIrFunctionLocalVariable result = function.MakeLocalVariable (source, resultVariableName);
204-
Output.WriteLine ($"{function.Indent}%{result.Name} = load {variableType}, {variableType}* @{source.Name}, align {PointerSize}");
205+
Output.WriteLine ($"{function.Indent}%{result.Name} = load {variableType}, {variableType}* @{source.Name}, align {PointerSize.ToString (CultureInfo.InvariantCulture)}");
205206

206207
return result;
207208
}
@@ -378,7 +379,8 @@ public void EmitLabel (LlvmIrFunction function, string labelName)
378379
Output.Write ("nonnull ");
379380
}
380381

381-
Output.Write ($"align {PointerSize} dereferenceable({PointerSize}) ");
382+
string ptrSize = PointerSize.ToString (CultureInfo.InvariantCulture);
383+
Output.Write ($"align {ptrSize} dereferenceable({ptrSize}) ");
382384

383385
if (argument.Value is LlvmIrVariableReference variableRef) {
384386
bool needBitcast = parameter.Type != argument.Type;
@@ -409,7 +411,7 @@ public void EmitLabel (LlvmIrFunction function, string labelName)
409411
if (!FunctionAttributes.ContainsKey (AttributeSetID)) {
410412
throw new InvalidOperationException ($"Unknown attribute set ID {AttributeSetID}");
411413
}
412-
Output.Write ($" #{AttributeSetID}");
414+
Output.Write ($" #{AttributeSetID.ToString (CultureInfo.InvariantCulture)}");
413415
}
414416
Output.WriteLine ();
415417

@@ -538,7 +540,7 @@ void WriteAttributeSets ()
538540

539541
void WriteSet (int id, TextWriter output)
540542
{
541-
output.Write ($"attributes #{id} = {{ ");
543+
output.Write ($"attributes #{id.ToString (CultureInfo.InvariantCulture)} = {{ ");
542544
foreach (LLVMFunctionAttribute attr in FunctionAttributes[id]) {
543545
output.Write (attr.Render ());
544546
output.Write (' ');

0 commit comments

Comments
 (0)