You are on page 1of 7

Por Erick Bacallao, Miguel Katrib y Yoelvis Parodi (Grupo Weboo ) Universidad de La Habana

Copia, corta y pega en ensamblados .NET


Hasta dnde se puede llegar con la reflexin en el prximo .NET Framework 2.0?

<<

<<

Con los recursos de reflexin del .NET Framework 1.1 podemos inspeccionar un ensamblado y crear dinmicamente uno nuevo que mimetice los tipos y mtodos del original. Sin embargo, no podemos copiar cdigo IL de un ensamblado existente hacia otro que estemos creando. Cualquier intento de usar en el nuevo ensamblado funcionalidades de otro ensamblado, implica emitir un cdigo que use recursos del original y por tanto exigir que una aplicacin que se desarrolle con el nuevo ensamblado tenga que disponer tambin del original. Este es un patrn muy comn cuando se crean tipos proxy que van a hacer de intermediarios entre el cdigo cliente y el cdigo de un tipo servidor. El cdigo de la nueva aplicacin va a facilitarse el trabajo usando el ensamblado del proxy, pero tiene que cargar tambin con el otro ensamblado porque el proxy hace referencia a ste.

Podemos aspirar a formar un nuevo ensamblado copiando y pegando cdigo extrado de un ensamblado original para luego prescindir de ste? En este artculo veremos cules recursos aportar el .NET Framework 2.0 que pueden ayudarnos a extraer el cdigo IL de un mtodo y copiarlo hacia otro mtodo. Se muestra un ejemplo que ilustra bajo cules condiciones se podra "clonar" un ensamblado creando uno igual con la misma funcionalidad (los mismos tipos y mtodos) pero en el que se pueden haber cambiado los nombres originales. Se puede lograr un tal efecto de copia y pega ms general y ambicioso? Veremos en este trabajo cules son las limitaciones para esto; en un prximo artculo se propondr una biblioteca para facilitar esta edicin de ensamblados y se mostrar una herramienta (ILLEGO) desarrollada para realizar visualmente esta edicin.

Qu puedo hacer con .NET Framework 1.1?


La expresividad y generalidad del cdigo intermedio (IL), el concepto de ensamblado (assembly), los metadatos incluidos en el propio ensamblado para describir a ste, y las facilidades para hacer introspeccin o reflexin (reflection) de la informacin contenida en un ensamblado (a travs del System.Reflection), junto a la capacidad de emisin de cdigo IL (a travs del System.Reflection.Emit), nos permiten afirmar que .NET es una plataforma que nos ofrece, mejor que ninguna otra hasta ahora, la capacidad de hacer ingeniera inversa del cdigo generado por los compiladores. Un escenario donde son muy tiles e s t a s capacidades de inspeccionar un ensamblado, y emitir uno nuevo basndose en el original, es en la generacin de proxies . Como sabemos un proxy es un intermediario o

Figura 1. ILDSAM Aplicado a la clase Mathematics

<<dotNetMana
31

mediador entre un cdigo cliente y un cdigo servidor. Los objetivos del mediador pueden ser varios: dar una cara (interfaz) ms amigable o fcil de usar al cdigo cliente que la que puede dar el servidor, ocultar al cliente funcionalidades que ste no necesita usar del servidor, preprocesar la informacin que enva el cdigo cliente antes de invocar las funcionalidades del servidor, liberando por tanto al cdigo cliente de este tipo de trabajo y adaptando con ello funcionalidades existentes en el servi-

dor para que puedan ser utilizadas por el cliente. En dos artculos previos en dotNetMana [1][2] tratamos temas relacionados con la generacin dinmica de un tipo que sirviese de proxy, en ambos trabajos el tipo proxy se emite hacia un nuevo ensamblado que mantiene una referencia al original. Pudiramos generar un ensamblado que no mantuviese dependencias del original? Para ello habra que tener la posibilidad de copiar el cdigo IL del ensamblado inspeccionado hacia el nuevo ensamblado que se est creando. Los recursos del System.Reflection en el .NET Framework 1.1 nos permiten extraer de un ensamblado objetos de los tipos Type, MethodInfo, ParameterInfo, etc, y con ello inspeccionar el ensamblado. Sin embargo, no nos ofrece ningn tipo de facilidades para extraer, va programacin, el cdigo IL de un mtodo, luego si no se puede ni extraer cmo vamos a pretender copiarlo hacia otro? Podra ser de gran utilidad disponer de alguna infraestructura y herramienta para hacer algo como copia, edita, y pega de partes de un ensamblado hacia otro. Esto podra aplicarse en variados escenarios como los que listamos a continuacin (principalmente cuando no se dispusiese directamente del cdigo fuente que dio lugar al ensamblado original): Crear un nuevo ensamblado igual al original pero en el que se han cambiado los nombres de determinados recursos visibles al cdigo cliente: por ejemplo tipos y mtodos. Crear un ensamblado con los mismos tipos del original pero quitando o aadiendo nuevos mtodos a un tipo. Lograr un nuevo ensamblado que nos d el efecto de haber inyectado funcionalidad a uno ya existente. Por ejemplo aadiendo cdigo para ejecutar antes de un mtodo o para ejecutar despus de un mtodo. Obtener un nuevo ensamblado donde se ha cambiado la visibilidad

a algunos de sus miembros (por ejemplo, de privado a pblico) En los escenarios anteriores el nuevo ensamblado podra sustituir al original, es decir hacer las veces de intermediario pero sin requerir la presencia de ste ya que tendra dentro el cdigo que necesita.

El ILDSAM
.NET ofrece una herramienta para inspeccionar los ensamblados: ILDASM (Intermediate Language Disassembler) y su complemento ILASM (IL Assembler). El ILDASM toma un ensamblado y muestra su contenido a travs de una interfaz grfica amigable que nos posibilita navegar arbreamente por los miembros del ensamblado. La figura 1 nos muestra la aplicacin del ILDSAM al ensamblado que contiene la clase Mathematics siguiente:
class Mathematics { public static bool IsEven(int k) { return ((k%2)==0); } public static int Factorial(int k) { if (k==0) return 1; else return k*Factorial(k-1); } public static float Square(float x) { return x*x; } }

Fuente 1

<< dnm.plataforma.net
Si en el rbol de la figura 1 se hace doble clic sobre la hoja de un mtodo se muestra el cdigo IL correspondiente a ste. La figura 2 muestra el cdigo IL del mtodo Square. Hacer modificaciones tiles en el texto correspondiente al cdigo IL, para luego compilarlo es, adems de laborioso, complicado. Lamentablemente no hay ningn espacio de nombres (name space) para ello, ni ninguna herramienta integrada a Visual Studio que nos ayude. Por otra parte, en el System.Reflection del .NET Framework 1.1 no hay ningn tipo ni mtodo que nos ayude a extraer el cdigo IL de un mtodo. La funcionalidad existente nos permite extraer la informacin de un ensamblado para obtener los tipos, los atributos, las signaturas de los mtodos, etc. Pero no hay nada para sacar el cdigo IL del cuerpo del mtodo. Adems del ILDSAM hay algunas aplicaciones, que estn disponibles en la red, para extraer informacin de un ensamblado, ejemplos de ellas son Anakrino3 y Reflector4. stas extraen la informacin del cdigo IL y nos muestran el equivalente a ste en un lenguaje de alto nivel (C#, VB, Delphi en el caso de Reflector y C++, C# en el caso de Anakrino ) con bastante fidelidad al cdigo fuente que pudiera haber sido el origen del tal ensamblado. Sin embargo, estas aplicaciones no estn orientadas a nuestro propsito de ofrecer recursos para facilitar la edicin de un ensamblado, es decir, facilitar la creacin de un nuevo ensamblado que incluya funcionalidades del original.

Figura 2. Cdigo IL del mtodo Square

Esto est MUY bien, gracias a ello los ms curiosos pueden a travs del ILDASM llegar hasta las tripas de un ensamblado1. Pero SLO para ver y NO para tocar ni copiar. Si se quiere escribir algn cdigo que haga uso de la informacin que es capaz de mostrar el ILDASM habra que recurrir a la vieja tcnica del vaciado de cdigo. Es decir, salvar el resultado del ILDASM en un archivo texto (por ejemplo invocando al ILDASM desde la lnea de comandos ildasm Mathematics.dll /out: Mathematics.il.txt) para que despus nuestro cdigo "bucee" por su cuenta en dicho texto y entonces hacer cualquier copia, modificacin o composicin a riesgo, posiblemente generando otro texto que luego debe ensamblarse con el ILASM2.

Qu nos trae nuevo la reflexin en .NET Framework 2.0?


.NET Framework 2.0 nos trae nuevos recursos en
System.Reflection. En especial recursos para traba-

[
1 2

NOTA
Recuerde la mxima (que no acaban de interiorizar algunos fundamentalistas del Open Source): Siempre que para hacer cambios el programador tenga que meterse dentro de un texto fuente (en este caso el texto del cdigo IL) tiene libertad entonces para hacer los cambios que debera pero tambin para hacer los que no debiera.

jar con la genericidad. A diferencia de otros lenguajes y tecnologas anteriores, la genericidad en .NET Framework 2.0 est integrada, no slo a los lenguajes fuentes, sino tambin a la propia plataforma y queda expresada a nivel del ensamblado, del que puede ser analizada con la reflexin. Sin embargo, el tema de la genericidad se sale de los objetivos de este trabajo5. Lo que nos interesa ahora son aquellos recursos que vienen en el .NET Framework 2.0 y que podrn ayudarnos en nuestro propsito de trajinar con el cdigo IL. En el espacio de nombres System.Reflection del .NET Framework 2.0 viene un nuevo tipo: MethodBody. A un objeto de tipo MethodInfo se le puede aplicar ahora el mtodo GetMethodBody() y ste devuelve un objeto de tipo MethodBody. Luego a un objeto de tipo

<<dotNetMana
32

3 4 5

Y qu dicen los defensores del Open Source? Ver, a travs del ILDSAM, el cdigo IL de un ensamblado correspondiente a un cdigo fuente bien programado, puede ser tan legible o ms, que ver directamente el fuente de un crptico y maoso programa en C. Se debe aclarar que si se aplica esta tcnica para hacer modificaciones en ensamblados firmados, entonces habra que incluirle una modificacin extra al cdigo IL obtenido para eliminar la informacin referente a la firma digital. http://www.saurik.com/net/exemplar http://www.aisto.com/roeder/dotnet Y de seguro ser tratado en futuros artculos de dotNetMana.

<< dnm.plataforma.net
public static void Clone(Hashtable dicc) { Assembly asmSource = Assembly.LoadFrom("...nombre del ensamblado original..."); AssemblyName asmName = new AssemblyName("...nuevo nombre de ensamblado..."); AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Save); ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("Utilidades.dll", "Utilidades.dll"); //Copiar los tipos foreach (Type t in asmSource.GetTypes()) { string newTypeName = dicc[t.Name] == null ? t.FullName : t.Namespace + "." + (string)dicc[t.Name]; TypeBuilder typeBuilder = moduleBuilder.DefineType(newTypeName, t.Attributes, t.BaseType, t.GetInterfaces()); //Copiar los miembros de cada tipo foreach (MemberInfo mInfo in t.GetMembers( BindingFlags.DeclaredOnly | BindingFlags.CreateInstance| BindingFlags.Instance| BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static) ) { //Copiar las variables if (mInfo is FieldInfo) { FieldInfo f = (FieldInfo) mInfo; string newFieldName = dicc[f.Name] == null ? f.Name : (string)dicc[f.Name]; typeBuilder.DefineField(newFieldName, f.FieldType, f.Attributes); } //Copiar los constructores else if (mInfo is ConstructorInfo) { ConstructorInfo cInfo = (ConstructorInfo) mInfo; ConstructorBuilder cBuilder = typeBuilder.DefineConstructor(cInfo.Attributes, cInfo.CallingConvention, ParameterType(cInfo.GetParameters())); ILGenerator iLGen = cBuilder.GetILGenerator(); iLGen.Emit(OpCodes.Ldarg_0); iLGen.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[]{})); iLGen.Emit(OpCodes.Ret); } //Copiar los mtodos else if (mInfo is MethodInfo) { MethodInfo m = (MethodInfo) mInfo; string newMethodName = dicc[m.Name] == null ? m.Name : (string)dicc[m.Name]; MethodBuilder mBuilder = typeBuilder.DefineMethod( newMethodName, m.Attributes, m.CallingConvention, m.ReturnType, ParameterType(m.GetParameters())); MethodBody mBody = m.GetMethodBody(); byte[] ILByteArray = mBody.GetILAsByteArray(); //Emitir el mismo array de bytes mBuilder.CreateMethodBody(ILByteArray, ILByteArray.Length); } else ... } typeBuilder.CreateType(); //Concluye la emisin de un tipo } moduleBuilder.CreateGlobalFunctions(); //Concluye la emisin del mdulo assemblyBuilder.Save("Utilidades.dll"); } private static Type[] ParameterType(ParameterInfo[] pInfo) { Type[] t = new Type[pInfo.Length]; for (int i = 0; i < pInfo.Length; i++) t[i] = pInfo[i].ParameterType; return t; }

MethodBody se le puede aplicar el mtodo GetILAsByteArray() , y ste nos

Fuente 2
6

Sin embargo, esto s estar incluido en la herramienta en desarrollo que se pondr mas adelante a disposicin de los lectores de dotNetMana.

<<dotNetMana
33

devuelve el cdigo IL del cuerpo del mtodo (aunque como un simple y crudo array de bytes). Muy bien, si tenemos los bytes del cdigo de un mtodo entonces hemos llegado a las tripas del cdigo, pero, qu podemos hacer ahora con esto? Supongamos que a partir de un ensamblado Utils.dll, que contiene el tipo Mathematics visto anteriormente, queremos generar uno nuevo de nombre Utilidades.dll y que contenga un tipo anlogo a Mathematics pero cambiando los nombres respectivamente por Matemtica , Factorial , EsPar y ElevaAlCuadrado. El fuente 2 nos muestra un extracto del cdigo que nos permitira clonar un ensamblado original generando uno nuevo que tiene los mismos tipos y mtodos (e implementacin de los mtodos) pero tal vez con nuevos nombres. El mtodo Clone del fuente 2 recibe como parmetro un diccionario en el cual se indican los cambios de nombre (aquellos que no aparezcan en el diccionario mantendrn su nombre original). Por razones de simplicidad este cdigo no incluye ahora el tratamiento de clases internas, delegados, estructuras y enumeradores, para los cuales habra que hacer algo similar. Debemos notar que este cdigo no funciona como desearamos si un miembro del ensamblado usa un tipo que se encuentra dentro del propio ensamblado que estamos creando. Esto se debe a que en las definiciones de miembros donde usamos tipos bases, interfaces, tipos de variables, tipos de parmetros y tipos de retorno se hace referencia a los mismos tipos que los correspondientes miembros (antes de la edicin) se referan. Sin embargo, estos tipos pueden ser de los nuevos que precisamente se estn editando (a travs del TypeBuilder ) hacia el nuevo ensamblado. Esto no es problema en el ejemplo del ensamblado Utils porque los tipos que se usan son de los predefinidos. Incluir la solucin completa aqu hara muy extenso el fuente 26.

<< dnm.plataforma.net
Note las dos instrucciones siguientes dentro del cdigo del fuente 2:
MethodBody mBody = m.GetMethodBody(); byte[] ILByteArray = mBody.GetILAsByteArray();

Se est usando aqu el tipo MethodBody y el nuevo mtodo GetILAsByteArray() que nos permite extraer el cdigo del mtodo. De este modo cuando luego se hace:
mBuilder.CreateMethodBody( ILByteArray, ILByteArray.Length);

Lo que ocurre es una copia bruta del array de bytes hacia el nuevo mtodo que se est generando. Decimos copia bruta porque el mtodo CreateMethodBody no hace ninguna comprobacin de si el mtodo resultante servir realmente como un duplicado del original. Esto queda a riesgo del programador. La herramienta PEVerify (incluida en el .NET Framework SDK) comprueba que un ensamblado sea consistente y correcto desde el punto de la ejecucin y seguridad del cdigo, pero claro que no nos puede servir para garantizar que el ensamblado generado tenga las transformaciones que quisimos hacer del original, ni nos facilita el hacer tales transformaciones. Para un caso simple, como el del ejemplo del tipo Mathematics, la copia anterior dar el resultado deseado, pero ms adelante analizaremos las limitaciones de lo que hicimos en el cdigo del fuente 2 que nos impiden afirmar que esto vale de modo general.

No ser posible realizar las funcionalidades ms activas asociadas a la reflexin (note el prefijo ReflectionOnly en el nombre del mtodo). Es decir, no podemos hacer acciones que intentan ejecutar funcionalidades de lo inspeccionado, como crear instancias de los tipos extrados ni evaluar un MethodInfo por mediacin de un Invoke, ni obtener el valor de una variable de un objeto ya instanciado. En muchos casos cuando acudimos a la reflexin lo que se desea es solamente inspeccionar un ensamblado (eso es lo que hace, por ejemplo, el propio Visual Studio) pero no se pretende ejecutar funcionalidades del ensamblado. Con esta distincin en la reflexin, parece que el CLR podr ejecutar ms eficientemente. Aunque de momento en la documentacin de MSDN no se explica en qu se basa esto, puede ser que se deba a que se libera al CLR de NAME
IsEven

Podemos copiar cualquier mtodo simplemente pasando los bytes del cdigo IL de un ensamblado hacia otro?
La descripcin de los miembros que se encuentran declarados en un ensamblado, o que son referenciados por ste, se encuentra en los metadatos del propio ensamblado. Estos metadatos estn estructurados en tablas. Cuando en el cdigo IL del cuerpo de un mtodo (el cual es recuperado como array de bytes a travs del MethodBody) se hace referencia a otro miembro de un tipo, esta referencia aparece en el cdigo IL como un token. Este token no es ms que un nmero entero que indica la posicin de un registro dentro de una de las tablas de los metadatos. La tabla 1 ilustra la tabla con los registros de los mtodos del ensamblado en el que est el tipo Mathematics. IMPLFLAGS
[IL] [Managed]

FLAGS
[Public] [Static] [HideBySig] [ReuseSlot] [Public] [Static] [HideBySig] [ReuseSlot] [Public] [Static] [HideBySig] [ReuseSlot

RVA(Relative Virtual Address)


0x00002050

Factorial

[IL] [Managed]

0x00002058

Square

[IL] [Managed]

0x000020691

.ctor

[Public] [HideBySig] [ReuseSlot] [IL] [Managed] [SpecialName] [RTSpecialName] [.ctor]

0x0000206e

Tabla1

Contexto de slo reflexin


Otra nueva caracterstica muy atractiva vendr incluida en el .NET Framework 2.0, sta es la denominada Contexto de slo reflexin (ReflectionOnly context). Con este nuevo recurso si se carga un ensamblado por medio de:

<<dotNetMana

Assembly asm = Assembly. ReflectionOnlyLoad("Utils.dll");

verificaciones innecesarias si ste sabe que la informacin extrada no se va a ejecutar. Se obvian, por ejemplo, las verificaciones de nombrado fuerte (strong name) y los chequeos de la poltica de seguridad de acceso a cdigo (CAS)7. Como en el caso que nos ocupa no vamos a hacer ninguna ejecucin de lo inspeccionado, sino que slo vamos a generar IL a partir de ello, podemos suponer que el cdigo del fuente 2 ejecutara de modo ms eficiente si lo modificamos para que trabaje en este Contexto de Slo Reflexin.

Clonar un mtodo como IsEven o como Square (cuyo cdigo IL se muestra en la figura 2) no tiene mayor dificultad porque en el array de bytes extrado del MethodBody no hay ninguna referencia a ningn otro miembro. En este caso, si el array de bytes se copia hacia el nuevo mtodo, mantiene, a los efectos del CLR, su mismo significado. Sin embargo, note que el mtodo Factorial es recursivo y contiene una llamada a l mismo (ver figura 3):
IL_0009: call int32 ILLego.Mathematics::Factorial(int32)

http://blogs.msdn.com/junfeng/archive/2004/08/24/219691.aspx

34

<< dnm.plataforma.net
r Object que est en mscorlib.dll). Por este motivo en todo ensamblado que generemos, y que tenga tipos definidos por clases, siempre tendr que haber una referencia externa (al menos al constructor de Object). Estas referencias externas son colocadas en el ensamblado que se est emitiendo cuando, usando a travs de un objeto de tipo ConstructorBuilder, se emite el cdigo con el siguiente extracto de cdigo (fuente 2):
Figura 3. Cdigo IL del mtodo Factorial
ILGenerator iLGen = cBuilder.GetILGenerator(); iLGen.Emit(OpCodes.Ldarg_0); iLGen.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[]{})); iLGen.Emit(OpCodes.Ret);

Se pudiera pensar que el hecho de copiar ciegamente el array de bytes no funcionara porque el nombre Mathematics ya no existe en el nuevo ensamblado, ha sido cambiado por Matemtica. Pero lo que mostramos en la figura 3 es la informacin que simblicamente nos reproduce el ILDASM. Lo que verdaderamente est en el array de bytes del mtodo original es una llamada en la forma:
IL_009: call 2

<<dotNetMana
35

Aqu el nmero 2 lo que indica es que se quiere llamar al mtodo de la segunda entrada de la tabla de mtodos en los metadatos, que es en este caso Factorial. Esto implica que hay que garantizar, para que las referencias a miembros dentro del cdigo se mantengan consistentes, que la emisin de tipos, y sus mtodos, hacia el nuevo ensamblado se debe hacer en el mismo orden en que estn en el ensamblado original. Felizmente el mecanismo del System.Reflection nos ofrece los miembros en el mismo orden en que aparecen en las tablas de metadatos, por lo que la ejecucin del cdigo del fuente 2 nos produce el efecto deseado. Si hacia el ensamblado que estamos generando hubisemos emitido primero el cdigo de Factorial y luego el de Square , entonces al copiar los bytes correspondientes a la instruccin IL_009: call 2 (que est dentro del cuerpo del Factorial original) la llamada dentro del cdigo de Factorial en el nuevo ensamblado quedara como una llamada a Square. Qu pasara enton-

ces? Una hecatombe en ejecucin? Felizmente no, porque cuando el CLR intente, en una aplicacin, cargar este nuevo ensamblado, reportar un error por incompatibilidad en la firma de los mtodos (el CLR detecta que hay una llamada a un mtodo 2, que segn su registro en la tabla de metadatos tiene un parmetro de tipo float, cuando realmente lo que se le va a pasar es un int). Sin embargo, el lector puede imaginar, que si los mtodos tuviesen igual firma, el CLR no puede adivinar los errores que usted, al generar el ensamblado, ha cometido si alter el orden. El nuevo ensamblado podr ser consistente y correcto pero no tendra la funcionalidad que se pretenda. Una tal dislocacin de los mtodos en la tabla de metadatos, en correspondencia con las referencias que aparecen a los mismos en el cdigo IL, podra ocurrir si queremos que en el nuevo ensamblado no aparezcan algunos mtodos que s estn el original o si queremos aadir nuevos mtodos. Por razones de espacio la solucin para esto no se ver ahora aqu y se dejar para un prximo artculo.

Y la clonacin de constructores?
En el modelo de herencia de .NET, todo constructor debe invocar al constructor correspondiente en la clase base (recuerde que en caso de no tener definida explcitamente tal clase base entonces se considera que es Object). Por lo tanto, algn tipo base, en la jerarqua de herencia del tipo que estemos queriendo clonar, estar en un ensamblado externo (porque en el caso extremo esta-

En este caso el mtodo Emit cuando emite un OpCodes.Call (tercera lnea del extracto de cdigo anterior) comprueba si lo que se va a emitir es una llamada a un mtodo (constructor en este caso) que est en un ensamblado externo, y en tal caso coloca en la tabla de metadatos la referencia a dicho ensamblado y miembro externo. Por lo tanto la copia de constructores no puede resolverse copiando simplemente el array de bytes de un constructor hacia otro, porque, quin pondra entonces esta referencia en los metadatos. De hecho (y tal vez por ello) no hay en ConstructorBuilder un equivalente al CreateMethodBody (como s lo hay en MethodBuilder) que es el mtodo que se usa para poder emitir el array de bytes. Cmo facilitar esta copia de los constructores se dejar para un prximo artculo. Sin embargo, para un caso simple, como el de esta clase Mathematics , que slo tiene el constructor por defecto, basta con emitir el cdigo para aplicar este constructor de Object. Esto es lo que hace el extracto de cdigo anterior (que es parte del fuente 2). El cdigo que se emitir es entonces:
// Poner en la pila una referencia a this. ldarg.0 //Llamada al constructor de object Call instance void [mscorlib]System.Object::.ctor() Ret

<< dnm.plataforma.net
Esto es suficiente para el caso de la clase Mathematics ya que sta no hereda de nadie salvo de Object. No hubo aqu necesidad de copiar el array de bytes del constructor (lo que de hecho no hubiese servido como ya explicamos).

Conclusiones y trabajos uturos


En este artculo hemos ilustrado algunas de las bondades del nuevo .NET Framework 2.0 en cuanto a la reflexin. Con el MethodBody y el GetILAsByteArray tendremos acceso a las interioridades de un mtodo, pero con la responsabilidad de que lo que hagamos despus con ello, correr a nuestra cuenta. Hemos ilustrado cmo estos recursos se pueden aprovechar para generar nuevos ensamblados copiando cdigo de un ensamblado original hacia uno nuevo. De este modo un nuevo ensamblado puede reproducir funcionalidades de uno ya existente. Vimos el ejemplo de cmo cambiarle los nombres a funcionalidades sin que en el ensamblado final quede dependencia del original. Tambin hemos analizado algunas de las insuficiencias de esta nueva oferta. Se explic en cules limitadas situaciones esta copia ciega de cdigo es correcta. Si por ejemplo queremos algo tan simple como despejar de obsolescencias un ensamblado, generando un nuevo ensamblado con los mtodos del original, pero sin incluir aquellos que ya no nos interesan, habra que garantizar que en el nuevo cdigo las referencias a los recursos sean correctas con respecto a la posicin de stos en la tabla de metadatos. Otro escenario an ms complicado, sera si queremos inyectar pedazos de cdigo en algunos mtodos. Un patrn interesante de esta inyeccin es el de intercalar cdigo que se quiere ejecutar antes que el mtodo o que se quiere ejecutar despus del mtodo. Tal inyeccin de cdigo puede significar que haya que cambiar, en la parte del cdigo que se copia del original, algunas referencias que puedan existir porque al intercalar cdigo stas pueden haberse desplazado de lugar. Si lo nico que podemos hacer es trabajar crudamente sobre la secuencia de bytes que recuperamos va el MethodBody entonces todo lo que hemos dicho en los prrafos anteriores sera complejo de programar y por supuesto que propenso a errores. En un prximo trabajo propondremos el espacio de nombres Reflection.Editor que nos proporcionar una forma de ms alto nivel para ayudarnos a hacer esta plomera en el cdigo IL, sobre este Reflection.Editor se desarrollar una herramienta IL-LEGO que nos ayudar en la edicin y composicin visual de los ensamblados.

Clases estticas
Una clase como sta del ejemplo Mathematics es una clase de la que no nos interesa crear instancias. El nuevo .NET Framework 2.0 trae tambin el concepto de clase esttica. Una clase esttica (en C# se indicar precediendo la definicin de la clase con la palabra static) es una clase que slo tiene mtodos y variables estticas. Por tanto no tiene constructores, ni est obligado a tener un constructor por defecto que lo que haga sea llamar al constructor de Object. El cdigo del fuente 2 podra modificarse para tener en cuenta tal tipo de clases.

.NET es una plataforma que nos ofrece, mejor que ninguna otra hasta ahora, la capacidad de hacer ingeniera inversa del cdigo generado por los compiladores

Y si el mtodo tiene variables locales?


En la copia de los mtodos de Mathematics no tuvimos problemas porque estos mtodos no tienen variables locales. Si un mtodo tiene variables locales, entonces, para que la copia del array de bytes tenga utilidad, habra tambin que haber generado las variables locales en el mtodo correspondiente del nuevo ensamblado. Pero cmo nos enteramos de cules son estas variables locales? Segn MSDN Online8 la clase MethodBody del .NET Framework 2.0 tiene la propiedad LocalVariables, que nos dara las variables locales del mtodo. Sin embargo, en la versin Beta 1 de Visual Studio 20059 sta no aparece an. En caso que se incluya en la versin final (lo cual es lo esperado), entonces, una vez que podamos conocer las variables locales, las podramos reproducir en el nuevo mtodo a travs del ILGenerator.

Referencias
[1] Mario del Valle, Miguel Katrib, El poder de la reflexin en .NET, dotNetMana No 3, Abril 2004. [2] Miguel Katrib, Yamil Hernndez, Aspectos e intercepcin de mtodos en .NET o cmo poner la guinda sin estropear el merengue , dotNetMana No 10, Diciembre 2004.

<<dotNetMana

8 9

http://msdn2.microsoft.com/library/System.Reflection.MethodBody.aspx La disponible en el momento de escribir este artculo.

36

You might also like