2010-01-25 6 views
44

Encontré un error en el código que solo se reproduce cuando el código se genera con las optimizaciones habilitadas. He creado una aplicación de consola que replica la lógica para probar (código a continuación). Vas a ver que cuando se habilita la optimización de 'valor' se convierte en nula después de la ejecución de esta lógica no válida:Error que solo se produce cuando la optimización de compilación se habilitó

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

El arreglo es sencillo y se comenta a continuación el código erróneo. Pero ... me preocupa más que haya encontrado un error en el ensamblador o que alguien más tenga una explicación de por qué el valor se establece en nulo.

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); 
       //} 
      } 
     } 
    } 
} 

La salida normal debe ser:

Pre-check Value is: System.String[] 
Post-check Value is: System.String[] 
Key is: blah 
Value is: System.String[] 

La salida buggy es:

Pre-check Value is: System.String[] 
Post-check Value is: 
Key is: blah 
Value is: 

Mi Env es una máquina virtual que ejecuta Windows Server 2003 R2 con .NET 3.5 SP1 . Usando el sistema de equipo VS2008.

Gracias,

Brian

+12

Claramente, el optimizador está enojado con usted por usar '== falso' en lugar de aplicar el! operador a la expresión. – iandisme

+0

Ooh, that * is * interesting. Entrando en el reflector ... –

+0

Haha bmancini

Respuesta

45

Sí, su expresión confunde fatalmente el optimizador JIT. El código de máquina generada tiene el siguiente aspecto:

   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() 

El error se produce en la dirección 41, el optimizador ha llegado a la conclusión de que el valor siempre será nula por lo que pasa directamente a un nulo String.Concat().

Para la comparación, este es el código que se genera cuando la optimización JIT está apagado:

    Console.WriteLine("Post-check Value is: " + value); 
00000056 mov   ecx,dword ptr ds:[03342090h] 
0000005c mov   edx,dword ptr [ebp-8] 
0000005f call  6D77B790 

El código nos cambiaron, pero tenga en cuenta que en la dirección 5c ahora utiliza la variable local (valor) en lugar de nulo.

Puede informar este error en connect.microsoft.com. La solución es simple:

if (value != null) 
    { 
    Console.WriteLine("Post-check Value is: " + value); 
    new PrintManager().Print("blah", value); 
    } 
+2

Gracias por la gran explicación de lo que está pasando. – bmancini

+6

esta es fácilmente la respuesta más impresionante que he visto en SO ... O tal vez estoy impresionado fácilmente ... – Ben

+0

No debería ser "if (! String.IsNullOrEmpty (value))" en cambio como OP es comparando a una cadena vacía también. –

1

yo estoy en x64 y no podían reproducir el problema al principio. Luego especifiqué el objetivo como x86 y me sucedió a mí. De vuelta a x64, y se fue. No estoy seguro de lo que esto significa, exactamente, pero he ido y venido varias veces ahora.

+0

También estoy en x64 y no puedo reproducir esto con la optimización = true y target = x86. – nos

2
value == new string[0] 

Lo anterior me parece una declaración extraña. Está comparando dos matrices de cadenas con una declaración equals. Eso solo resultará verdadero si ambos apuntan a la misma matriz, lo cual es bastante improbable. Esto todavía no explica por qué este código se comporta de manera diferente en una versión optimizada.

+0

No puedo pensar en ninguna forma de inyectar una sobrecarga de operador para eso, pero ¿es eso definitivamente 100% cierto que me pregunto? Mientras tanto, eso es lo que pensé, así que +1 –

0

Sin duda se parece a un error, ¿se reproduce cuando intercambias los operandos del operador de esta manera?

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

Este error parece haberse corregido en .NET 4 (beta 2).Aquí está el desmontaje x86 optimizado para la nobugz poco destacó, por encima de:

    Console.WriteLine("Post-check Value is: " + value); 
00000056 mov   ecx,dword ptr ds:[033C2090h] 
0000005c mov   edx,dword ptr [ebp-8] 
0000005f call  65D8FE10 

El programa también muestra el resultado esperado en ambos modos optimizados y no optimizadas.

Cuestiones relacionadas