Ошибка возникает только при включенной оптимизации компиляции - PullRequest
44 голосов
/ 25 января 2010

Я обнаружил ошибку в коде, которая воспроизводится только тогда, когда код создается с включенной оптимизацией.Я сделал консольное приложение, которое копирует логику для тестирования (код ниже).Вы увидите, что, когда оптимизация включена, «значение» становится нулевым после выполнения этой недопустимой логики:

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.

Спасибо,

Брайан

Ответы [ 5 ]

45 голосов
/ 26 января 2010

Да, ваше выражение смертельно смущает оптимизатор 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, оптимизатор пришел к выводу, что значение всегда будет нулевым, поэтому он напрямую передает ноль в 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);
  }
3 голосов
/ 28 января 2010

Эта ошибка, кажется, была исправлена ​​в .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

Программа также отображает ожидаемый результат как в оптимизированном, так и в неоптимизированном режимах.

2 голосов
/ 26 января 2010
value == new string[0]

Вышесказанное выглядит для меня странным заявлением. Вы сравниваете два строковых массива с оператором equals. Это приведет только к истине, если они оба будут указывать на один и тот же массив, что маловероятно. Это еще не объясняет, почему этот код ведет себя по-разному в оптимизированной версии.

1 голос
/ 26 января 2010

Я на x64 и сначала не смог воспроизвести проблему.Затем я указал цель как x86, и это случилось со мной.Вернуться к x64, и это ушло.Не знаю точно, что это значит, но я уже несколько раз ходил туда-сюда.

0 голосов
/ 26 января 2010

Конечно, выглядит как ошибка, воспроизводится ли она, когда вы меняете операнды оператора, как это?

if (false == (null == value || new string[0] == value))
...