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

StackOverflow https://stackoverflow.com/questions/2135509

  •  22-09-2019
  •  | 
  •  

Вопрос

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

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

Исправление является простым и закомментировано под кодом, вызывающим нарушение.Но...Меня больше беспокоит то, что я, возможно, столкнулся с ошибкой в ​​ассемблере или, возможно, у кого-то другого есть объяснение, почему значение устанавливается в ноль.

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:   

Моя среда — это виртуальная машина под управлением Windows Server 2003 R2 с .NET 3.5 SP1.Использование командной системы VS2008.

Спасибо,

Брайан

Это было полезно?

Решение

Да, ваше выражение фатально сбивает с толку 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);
  }

Другие советы

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

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

value == new string[0]

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

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

Конечно, это похоже на ошибку, воспроизводится ли она при такой замене операндов оператора?

if (false == (null == value || new string[0] == value))
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top