Ошибка возникает только при включенной оптимизации компиляции
Я столкнулся с ошибкой в коде, который воспроизводится только тогда, когда код построен с включенными оптимизациями. Я создал консольное приложение, которое реплицирует логику тестирования (код ниже). Вы увидите, что когда оптимизация включена, значение "value" становится нулевым после выполнения этой недействительной логики:
if ((value == null || value == new string[0]) == false)
Исправление является прямым и прокомментировано ниже кода нарушения. Но... меня больше беспокоит, что я, возможно, столкнулся с ошибкой в ассемблере или, возможно, у кого-то есть объяснение, почему значение получает значение null.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace memory_testing
{
class Program
{
sta tic void Main(string[] args)
{
while(true)
{
Console.Write("Press any key to start...");
Console.ReadKey();
Console.WriteLine();
PrintManagerUser c = new PrintManagerUser();
c.MyProperty = new string[1];
}
}
}
public class PrintManager
{
public void Print(string key, object value)
{
Console.WriteLine("Key is: " + key);
Console.WriteLine("Value is: " + value);
}
}
public class PrintManagerUser
{
public string[] MyProperty
{
get { return new string[100]; }
set
{
Console.WriteLine("Pre-check Value is: " + value);
if ((value == null || value == new string[0]) == false)
{
Console.WriteLine("Post-check Value is: " + value);
new PrintManager().Print("blah", value);
}
//if (value != null && value.Length > 0)
//{
// new PrintManager().Print("blah", value);
//}
}
}
}
}
Нормальный выход должен быть:
Pre-check Value is: System.String[]
Post-check Value is: System.String[]
Key is: blah
Value is: System.String[]
Выход с ошибкой:
Pre-check Value is: System.String[]
Post-check Value is:
Key is: blah
Value is:
My Env - это виртуальная машина под управлением Windows Server 2003 R2 с .NET 3.5 SP1. Использование VS2008 Team System.
Спасибо,
Брайан
Ответы
Ответ 1
Да, ваше выражение фатально смущает оптимизатор JIT. Сгенерированный машинный код выглядит следующим образом:
if ((value == null || value == new string[0]) == false)
00000027 test esi,esi ; value == null?
00000029 je 00000075
0000002b xor edx,edx ; new string[0]
0000002d mov ecx,6D913BD2h
00000032 call FFD20BC8
00000037 cmp eax,esi ; (value == new string[0]) == false?
00000039 je 00000075
{
Console.WriteLine("Post-check Value is: " + value);
0000003b mov ecx,dword ptr ds:[03532090h] ; "Post-check value is: "
00000041 xor edx,edx ; BUGBUG not null!
00000043 call 6D70B7E8 ; String.Concat()
00000048 mov esi,eax ;
0000004a call 6D72BE08 ; get Console.Out
0000004f mov ecx,eax
00000051 mov edx,esi
00000053 mov eax,dword ptr [ecx]
00000055 call dword ptr [eax+000000D8h] ; Console.WriteLine()
Ошибка возникает по адресу 41, оптимизатор пришел к выводу, что значение всегда будет нулевым, поэтому оно напрямую передает null в String.Concat().
Для сравнения, это код, который генерируется при отключении оптимизации JIT:
Console.WriteLine("Post-check Value is: " + value);
00000056 mov ecx,dword ptr ds:[03342090h]
0000005c mov edx,dword ptr [ebp-8]
0000005f call 6D77B790
Код был перемещен, но обратите внимание, что по адресу 5c он теперь использует локальную переменную (значение) вместо нулевого.
Вы можете сообщить об этой ошибке на сайте connect.microsoft.com. Обходной путь прост:
if (value != null)
{
Console.WriteLine("Post-check Value is: " + value);
new PrintManager().Print("blah", value);
}
Ответ 2
Эта ошибка, по-видимому, исправлена в .NET 4 (бета 2). Здесь оптимизированная разборка x86 для бит nobugz, выделенная выше:
Console.WriteLine("Post-check Value is: " + value);
00000056 mov ecx,dword ptr ds:[033C2090h]
0000005c mov edx,dword ptr [ebp-8]
0000005f call 65D8FE10
Программа также отображает ожидаемый результат как в оптимизированных, так и в неоптимизированных режимах.
Ответ 3
value == new string[0]
Вышеприведенное выглядит для меня странным выражением. Вы сравниваете два строковых массива с выражением равенства. Это приведет только к истине, если оба указывают на один и тот же массив, что маловероятно. Это еще не объясняет, почему этот код ведет себя по-разному в оптимизированной версии.
Ответ 4
Я нахожусь на x64 и не могу воспроизвести проблему сначала. Затем я указал цель как x86, и это случилось со мной. Вернемся к x64, и он ушел. Не уверен, что это значит, но я уже несколько раз возвращался назад и вперед.
Ответ 5
Конечно, выглядит как ошибка, воспроизводится ли она, когда вы меняете операнды оператора следующим образом?
if (false == (null == value || new string[0] == value))