Подтвердить что ты не робот

Ошибка возникает только при включенной оптимизации компиляции

Я столкнулся с ошибкой в ​​коде, который воспроизводится только тогда, когда код построен с включенными оптимизациями. Я создал консольное приложение, которое реплицирует логику тестирования (код ниже). Вы увидите, что когда оптимизация включена, значение "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.

Спасибо,

Брайан

4b9b3361

Ответ 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))