[Bug 46842] C# double.TryParse( String.Empty, out number) succeeds with .NET Framework 4.0 when it should not
wine-bugs at winehq.org
wine-bugs at winehq.org
Sat Mar 16 08:42:37 CDT 2019
https://bugs.winehq.org/show_bug.cgi?id=46842
Anastasius Focht <focht at gmx.net> changed:
What |Removed |Added
----------------------------------------------------------------------------
Ever confirmed|0 |1
Status|UNCONFIRMED |NEW
Component|-unknown |kernel32
Keywords| |source
--- Comment #10 from Anastasius Focht <focht at gmx.net> ---
Hello IanS,
confirming now - so I can sleep well again ;-)
The culprit seems to be .NET Framework 4.x and Windows version set to at least
'Windows 7' (default in new WINEPREFIXes). If you change it to Windows Vista or
lower (Windows XP) it works.
My WINEPREFIX was kept at 'Windows XP' due to winetricks recipe which is not
necessary anymore. .NET Framework 4.x CLR uses different ways on Windows 7+ to
determine various culture/localization information (GetLocaleInfoEx vs.
registry)
Suspecting this, I found some example code to test this here:
https://docs.microsoft.com/en-us/dotnet/api/system.globalization.numberformatinfo.currentinfo?view=netframework-4.7.2
I adapted it to build with lower .NET Frameworks/SDK and get rid of unneeded
stuff:
'numberformatinfo.cs'
--- snip ---
using System;
using System.Collections;
using System.Globalization;
using System.Reflection;
namespace Example
{
public class NumberFormatInfoExample
{
public static void Main()
{
NumberFormatInfo nfi = CultureInfo.CurrentCulture.NumberFormat;
Console.WriteLine("Properties of NumberFormat.CurrentInfo
object:");
PropertyInfo[] props =
nfi.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (PropertyInfo prop in props)
{
if (prop.PropertyType.IsArray)
{
Array arr = prop.GetValue(nfi, null) as Array;
Console.Write(String.Format(" {0}: ", prop.Name) + "{ ");
int ctr = 0;
foreach (object item in arr)
{
Console.Write("{0}{1}", item, ctr == arr.Length - 1 ?"
}" : ", ");
ctr++;
}
Console.WriteLine();
}
else
{
Console.WriteLine(" {0}: {1}", prop.Name,
prop.GetValue(nfi, null));
}
}
}
}
}
--- snip ---
You can use LC_ALL to override/check for specific locales.
Running the test app with 'Windows XP' setting:
--- snip ---
$ LC_ALL="en_US.utf8" wine ./numberformatinfo.exe
Properties of NumberFormat.CurrentInfo object:
CurrencyDecimalDigits: 2
CurrencyDecimalSeparator: .
IsReadOnly: True
CurrencyGroupSizes: { 3 }
NumberGroupSizes: { 3 }
PercentGroupSizes: { 3 }
CurrencyGroupSeparator: ,
CurrencySymbol: $
NaNSymbol: NaN
CurrencyNegativePattern: 0
NumberNegativePattern: 1
PercentPositivePattern: 0
PercentNegativePattern: 0
NegativeInfinitySymbol: -Infinity
NegativeSign: -
NumberDecimalDigits: 2
NumberDecimalSeparator: .
NumberGroupSeparator: ,
CurrencyPositivePattern: 0
PositiveInfinitySymbol: Infinity
PositiveSign: +
PercentDecimalDigits: 2
PercentDecimalSeparator: .
PercentGroupSeparator: ,
PercentSymbol: %
PerMilleSymbol: %
NativeDigits: { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
DigitSubstitution: None
--- snip ---
With 'Windows 7' setting:
--- snip ---
$ LC_ALL="en_US.utf8" wine ./numberformatinfo.exe
Properties of NumberFormat.CurrentInfo object:
CurrencyDecimalDigits: 2
CurrencyDecimalSeparator: ,
IsReadOnly: True
CurrencyGroupSizes: { 3 }
NumberGroupSizes: { 3 }
PercentGroupSizes: { 3 }
CurrencyGroupSeparator: .
CurrencySymbol: ?
NaNSymbol: NaN
CurrencyNegativePattern: 8
NumberNegativePattern: 1
PercentPositivePattern: 0
PercentNegativePattern: 0
NegativeInfinitySymbol:
NegativeSign: -
NumberDecimalDigits: 2
NumberDecimalSeparator: ,
NumberGroupSeparator: .
CurrencyPositivePattern: 3
PositiveInfinitySymbol:
PositiveSign: +
PercentDecimalDigits: 2
PercentDecimalSeparator: ,
PercentGroupSeparator: .
PercentSymbol:
PerMilleSymbol:
NativeDigits: { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
DigitSubstitution: None
--- snip ---
There are various number format properties missing/incorrect.
For reference .NET Core:
https://github.com/dotnet/corefx/blob/1f7c6362d3ad7cebeb748aabfc7e8303b703ed48/src/Common/src/CoreLib/System/Globalization/CultureData.cs
https://github.com/dotnet/corefx/blob/1f7c6362d3ad7cebeb748aabfc7e8303b703ed48/src/Common/src/CoreLib/System/Globalization/CultureData.cs#L2327
.NET number parsing code relies on it.
https://github.com/dotnet/corefx/blob/v2.2.3/src/Common/src/CoreLib/System/Number.Parsing.cs#L399
I also did some debugging of JIT code using cordbg - not for the faint hearted
;-) Just for reference, the simple C# example app already shows it.
--- snip ---
$ wine "c:\\Program Files\\Debugging Tools for Windows (x86)\\cdb.exe"
./bug46842.exe
...
0:000> sxe ld clrjit
0:000> g
0:000> .loadby sos clr
0:000> !name2ee *!System.Double.TryParse
Module: 79881000
Assembly: mscorlib.dll
Token: 06000b97
MethodDesc: 798f5440
Name: System.Double.TryParse(System.String, Double ByRef)
JITTED Code Address: 79b22cb0
-----------------------
Token: 06000b98
MethodDesc: 798f544c
Name: System.Double.TryParse(System.String,
System.Globalization.NumberStyles, System.IFormatProvider, Double ByRef)
JITTED Code Address: 79ac767c
-----------------------
Token: 06000bab
MethodDesc: 798f55ac
Name: System.Double.TryParse(System.String,
System.Globalization.NumberStyles, System.Globalization.NumberFormatInfo,
Double ByRef)
JITTED Code Address: 79ac76b4
--------------------------------------
Module: 00372e9c
Assembly: bug46842.exe
0:000> bp 79b22cb0
0:000> g
...
0:000> g
Unable to parse '123AE6'.
Breakpoint 0 hit
0:000> g
Unable to parse ''.
Breakpoint 0 hit
...
0:000> l+t
...
0:000> t
...
0:000> !IP2MD @eip
MethodDesc: 799b71d8
Method Name: System.Number.ParseNumber(Char* ByRef,
System.Globalization.NumberStyles, NumberBuffer ByRef,
System.Text.StringBuilder, System.Globalization.NumberFormatInfo, Boolean)
Class: 798dc1e4
MethodTable: 79ba6a48
mdToken: 06000e20
Module: 79881000
IsJitted: yes
CodeAddr: 79ac5db0
Transparency: Critical
0:000> !clrstack
OS Thread Id: 0x30 (0)
Child SP IP Call Site
0032f1a0 79ac5db0 System.Number.ParseNumber(Char* ByRef,
System.Globalization.NumberStyles, NumberBuffer ByRef,
System.Text.StringBuilder, System.Globalization.NumberFormatInfo, Boolean)
0032f1b4 79ac6394 System.Number.TryStringToNumber(System.String,
System.Globalization.NumberStyles, NumberBuffer ByRef,
System.Text.StringBuilder, System.Globalization.NumberFormatInfo, Boolean)
0032f1e0 79b11f12 System.Number.TryParseDouble(System.String,
System.Globalization.NumberStyles, System.Globalization.NumberFormatInfo,
Double ByRef)
0032f2a4 79ac76d3 System.Double.TryParse(System.String,
System.Globalization.NumberStyles, System.Globalization.NumberFormatInfo,
Double ByRef)
0032f2c0 79b22ccc System.Double.TryParse(System.String, Double ByRef)
0032f2d0 003b0182 Example.Main()
0032f538 791421db [GCFrame: 0032f538]
0:000> !DumpIL 799b71d8
ilAddr = 79c1fd28
IL_0000: ldarg.2
IL_0001: ldc.i4.0
IL_0002: stfld NumberBuffer::scale
IL_0007: ldarg.2
IL_0008: ldc.i4.0
IL_0009: stfld NumberBuffer::sign
IL_000e: ldnull
IL_000f: stloc.2
IL_0010: ldnull
IL_0011: stloc.3
IL_0012: ldnull
IL_0013: stloc.s VAR OR ARG 4
IL_0015: ldnull
IL_0016: stloc.s VAR OR ARG 5
IL_0018: ldc.i4.0
IL_0019: stloc.s VAR OR ARG 6
IL_001b: ldarg.1
IL_001c: ldc.i4 256
IL_0021: and
IL_0022: brfalse.s IL_0064
IL_0024: ldarg.s VAR OR ARG 4
IL_0026: callvirt System.Globalization.NumberFormatInfo::get_CurrencySymbol
IL_002b: stloc.2
IL_002c: ldarg.s VAR OR ARG 4
IL_002e: ldfld System.Globalization.NumberFormatInfo::ansiCurrencySymbol
IL_0033: brfalse.s IL_003d
IL_0035: ldarg.s VAR OR ARG 4
IL_0037: ldfld System.Globalization.NumberFormatInfo::ansiCurrencySymbol
IL_003c: stloc.3
IL_003d: ldarg.s VAR OR ARG 4
IL_003f: callvirt
System.Globalization.NumberFormatInfo::get_NumberDecimalSeparator
IL_0044: stloc.s VAR OR ARG 4
IL_0046: ldarg.s VAR OR ARG 4
IL_0048: callvirt
System.Globalization.NumberFormatInfo::get_NumberGroupSeparator
IL_004d: stloc.s VAR OR ARG 5
IL_004f: ldarg.s VAR OR ARG 4
IL_0051: callvirt
System.Globalization.NumberFormatInfo::get_CurrencyDecimalSeparator
IL_0056: stloc.0
IL_0057: ldarg.s VAR OR ARG 4
IL_0059: callvirt
System.Globalization.NumberFormatInfo::get_CurrencyGroupSeparator
IL_005e: stloc.1
IL_005f: ldc.i4.1
IL_0060: stloc.s VAR OR ARG 6
IL_0062: br.s IL_0074
IL_0064: ldarg.s VAR OR ARG 4
IL_0066: callvirt
System.Globalization.NumberFormatInfo::get_NumberDecimalSeparator
IL_006b: stloc.0
IL_006c: ldarg.s VAR OR ARG 4
IL_006e: callvirt
System.Globalization.NumberFormatInfo::get_NumberGroupSeparator
IL_0073: stloc.1
IL_0074: ldc.i4.0
IL_0075: stloc.s VAR OR ARG 7
IL_0077: ldc.i4.0
IL_0078: stloc.s VAR OR ARG 8
...
0:000> !clrstack -a
OS Thread Id: 0x30 (0)
Child SP IP Call Site
0032f150 79ac6260 System.Number.MatchChars(Char*, System.String)
PARAMETERS:
p (<CLR reg>) = 0x00c91230
str (<CLR reg>) = 0x00c91748
LOCALS:
<no data>
<no data>
<no data>
0032f154 79ac5e7d System.Number.ParseNumber(Char* ByRef,
System.Globalization.NumberStyles, NumberBuffer ByRef,
System.Text.StringBuilder, System.Globalization.NumberFormatInfo, Boolean)
PARAMETERS:
str (0x0032f16c) = 0x0032f1b8
options (0x0032f18c) = 0x000000e7
number (0x0032f1b0) = 0x0032f270
sb (0x0032f1ac) = 0x00000000
numfmt (0x0032f1a8) = 0x00c9b584
parseDecimal (0x0032f1a4) = 0x00000000
LOCALS:
0x0032f168 = 0x00c9b6a0
0x0032f164 = 0x00c9b6b0
0x0032f160 = 0x00000000
0x0032f15c = 0x00000000
0x0032f158 = 0x00000000
0x0032f154 = 0x00000000
0x0032f188 = 0x00000000
<CLR reg> = 0x00000000
<CLR reg> = 0x00000001
0x0032f184 = 0x00000000
0x0032f180 = 0x00000000
<no data>
0x0032f17c = 0x00c91230
<CLR reg> = 0x00000000
<no data>
<no data>
<no data>
<no data>
<no data>
<no data>
...
0:000> !do 0x00c91748
Name: System.String
MethodTable: 79b9f9ac
EEClass: 798d8bb0
Size: 16(0x10) bytes
File:
C:\windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String: +
Fields:
MT Field Offset Type VT Attr Value Name
79ba2978 40000ed 4 System.Int32 1 instance 1
m_stringLength
79ba1dc8 40000ee 8 System.Char 1 instance 2b
m_firstChar
79b9f9ac 40000ef 8 System.String 0 shared static Empty
>> Domain:Value 001430c0:00c91228 <<
--- snip ---
$ wine --version
wine-4.4
Regards
--
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.
More information about the wine-bugs
mailing list