La presentación sobre MSIL en C# explica el flujo de compilación en .NET, desde el análisis del código hasta la generación de MSIL, destacando su importancia como puente entre el código fuente y el ejecutable. Se enfoca en la estructura y funciones del compilador, incluyendo la descomposición del código en tokens, la construcción del árbol sintáctico, y la conversión final a MSIL para ejecución por el CLR, subrayando el rol clave de MSIL en la portabilidad y optimización de código en diferentes plataformas.
18. var msLib = new MetadataFileReference(
typeof(object).Assembly.Location) ;
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
var comp = CSharpSyntaxTree.Create("DotNetConf")
.AddReferences(msLib)
.AddSyntaxTrees(tree);
var result = comp.Emit("DotNetConf.exe");
46. var myMethod = new DynamicMethod("DivideMethod",
typeof(double), new[] { typeof(int), typeof(int) },
typeof(Program).Module);
var il = myMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Div);
il.Emit(OpCodes.Ret);
var result = myMethod.Invoke(null, new object[] { 10, 2 });
var method =
(Func<int,int,int>)myMethod.CreateDelegate(typeof(DivideDelegate));
var result2 = method(6, 2);
Parser (Análisis Sintáctico): En esta fase, el compilador lee el código fuente y lo descompone en tokens que siguen la gramática del lenguaje. Se construye un árbol sintáctico que representa la estructura lógica del código. Esta fase se ocupa de la estructura sintáctica del código, asegurándose de que esté bien formado según las reglas del lenguaje de programación.
Symbol/Metadata (Símbolos y Metadatos): Durante esta etapa, el compilador analiza los símbolos definidos en el código, como variables, tipos y métodos, y los organiza en una tabla de símbolos. Esta fase también implica cargar y analizar los metadatos de las bibliotecas y ensamblados referenciados por el código fuente, formando una representación de todos los nombres y tipos utilizados.
Binder (Vinculación): En la fase de vinculación, el compilador asocia cada identificador en el código con su declaración correspondiente en la tabla de símbolos, resolviendo las referencias y comprobando la semántica del código, como el alcance de las variables, la corrección de los tipos y la resolución de sobrecargas de métodos.
IL Emitter (Emisión de IL): Esta es la fase final del proceso de compilación, donde el compilador convierte el código fuente analizado y procesado en MSIL (Microsoft Intermediate Language), el conjunto de instrucciones de bajo nivel que el CLR (Common Language Runtime) puede ejecutar. La salida de esta fase es un ensamblado que contiene el código MSIL junto con los metadatos necesarios para su ejecución.
Parser (Análisis Sintáctico): En esta fase, el compilador lee el código fuente y lo descompone en tokens que siguen la gramática del lenguaje. Se construye un árbol sintáctico que representa la estructura lógica del código. Esta fase se ocupa de la estructura sintáctica del código, asegurándose de que esté bien formado según las reglas del lenguaje de programación.
Symbol/Metadata (Símbolos y Metadatos): Durante esta etapa, el compilador analiza los símbolos definidos en el código, como variables, tipos y métodos, y los organiza en una tabla de símbolos. Esta fase también implica cargar y analizar los metadatos de las bibliotecas y ensamblados referenciados por el código fuente, formando una representación de todos los nombres y tipos utilizados.
Binder (Vinculación): En la fase de vinculación, el compilador asocia cada identificador en el código con su declaración correspondiente en la tabla de símbolos, resolviendo las referencias y comprobando la semántica del código, como el alcance de las variables, la corrección de los tipos y la resolución de sobrecargas de métodos.
IL Emitter (Emisión de IL): Esta es la fase final del proceso de compilación, donde el compilador convierte el código fuente analizado y procesado en MSIL (Microsoft Intermediate Language), el conjunto de instrucciones de bajo nivel que el CLR (Common Language Runtime) puede ejecutar. La salida de esta fase es un ensamblado que contiene el código MSIL junto con los metadatos necesarios para su ejecución.
IL_0000: nop - No operation (no hace nada, a menudo se incluye por motivos de depuración).
IL_0001: ldarg.0 - Carga el argumento en el índice 0 (el parámetro n) en el stack.
IL_0002: ldc.i4.1 - Carga la constante entera 1 en el stack.
IL_0003: cgt - Compara si el valor que está en el tope del stack es mayor que el siguiente valor debajo de él (en este caso, si n > 1). El resultado (true/false) se coloca en el stack.
IL_0005: ldc.i4.0 - Carga la constante entera 0 en el stack.
IL_0006: ceq - Compara si los dos valores en el tope del stack son iguales (si n > 1 es igual a 0, o si n <= 1). El resultado (true/false) se coloca en el stack.
IL_0008: stloc.0 - Almacena el resultado de la comparación en la variable local en el índice 0.
IL_0009: ldloc.0 - Carga el valor de la variable local en el índice 0 en el stack (el resultado de n <= 1).
IL_000a: brfalse.s IL_0010 - Si el valor es false (es decir, si n > 1), salta a la instrucción en IL_0010.
IL_000c: ldc.i4.1 - Si el valor fue true (es decir, si n <= 1), carga la constante 1 en el stack (caso base del factorial).
IL_000d: stloc.1 - Almacena el valor 1 en la variable local en el índice 1.
IL_000e: br.s IL_001d - Salta incondicionalmente a la instrucción en IL_001d.
IL_0010: ldarg.0 - Carga el argumento n en el stack.
IL_0011: ldarg.0 - Carga el argumento n en el stack nuevamente.
IL_0012: ldc.i4.1 - Carga la constante entera 1 en el stack.
IL_0013: sub - Resta 1 de n (lo que resulta en n - 1) y pone el resultado en el stack.
IL_0014: call int32 Program::'<<Main>$>g__Calculate|0_0'(int32) - Llama a la función Calculate recursivamente con el argumento n - 1.
IL_0019: mul - Multiplica n por el resultado de la llamada recursiva y coloca el resultado en el stack.
IL_001a: stloc.1 - Almacena el resultado de la multiplicación en la variable local en el índice 1.
IL_001b: br.s IL_001d - Salta incondicionalmente a la instrucción en IL_001d (esto se hace para unificar el flujo del programa después del if-else).
IL_001d: ldloc.1 - Carga el valor de la variable local en el índice 1 en el stack (el resultado final del factorial).
IL_001e: ret - Retorna el valor en el tope del stack (el resultado de la función).
IL_0000: nop - No operation (no hace nada, a menudo se incluye por motivos de depuración).
IL_0001: ldarg.0 - Carga el argumento en el índice 0 (el parámetro n) en el stack.
IL_0002: ldc.i4.1 - Carga la constante entera 1 en el stack.
IL_0003: cgt - Compara si el valor que está en el tope del stack es mayor que el siguiente valor debajo de él (en este caso, si n > 1). El resultado (true/false) se coloca en el stack.
IL_0005: ldc.i4.0 - Carga la constante entera 0 en el stack.
IL_0006: ceq - Compara si los dos valores en el tope del stack son iguales (si n > 1 es igual a 0, o si n <= 1). El resultado (true/false) se coloca en el stack.
IL_0008: stloc.0 - Almacena el resultado de la comparación en la variable local en el índice 0.
IL_0009: ldloc.0 - Carga el valor de la variable local en el índice 0 en el stack (el resultado de n <= 1).
IL_000a: brfalse.s IL_0010 - Si el valor es false (es decir, si n > 1), salta a la instrucción en IL_0010.
IL_000c: ldc.i4.1 - Si el valor fue true (es decir, si n <= 1), carga la constante 1 en el stack (caso base del factorial).
IL_000d: stloc.1 - Almacena el valor 1 en la variable local en el índice 1.
IL_000e: br.s IL_001d - Salta incondicionalmente a la instrucción en IL_001d.
IL_0010: ldarg.0 - Carga el argumento n en el stack.
IL_0011: ldarg.0 - Carga el argumento n en el stack nuevamente.
IL_0012: ldc.i4.1 - Carga la constante entera 1 en el stack.
IL_0013: sub - Resta 1 de n (lo que resulta en n - 1) y pone el resultado en el stack.
IL_0014: call int32 Program::'<<Main>$>g__Calculate|0_0'(int32) - Llama a la función Calculate recursivamente con el argumento n - 1.
IL_0019: mul - Multiplica n por el resultado de la llamada recursiva y coloca el resultado en el stack.
IL_001a: stloc.1 - Almacena el resultado de la multiplicación en la variable local en el índice 1.
IL_001b: br.s IL_001d - Salta incondicionalmente a la instrucción en IL_001d (esto se hace para unificar el flujo del programa después del if-else).
IL_001d: ldloc.1 - Carga el valor de la variable local en el índice 1 en el stack (el resultado final del factorial).
IL_001e: ret - Retorna el valor en el tope del stack (el resultado de la función).