Cómo crear un Compact Framework (I)

Imaginemos los siguientes escenarios:

  • tenemos un gran framework super completo del cual queremos obtener una versión más pequeña para correr en dispositivos móviles, y tenemos que quitar ciertas características que el dispositivo no soporta. Además la idea del concepto de “Compact” Framework también hace referencia al tamaño, por lo que queremos eliminar todo el código del las características no soportadas.
  • queremos lanzar varias versiones de nuestra aplicación como Free Edition, Profesional Edition, Enterprise Edition, y no deseamos que en cada una de las ediciones solo vaya el código necesario para esa edición (y no todo).

¿Cómo lo hacemos?

La idea que propongo, si bien no es original ya que tengo entendido que .Net Compact Framework se ha realizado con una técnica similar, es simplemente identificar en los assemblies todos aquellos fragmentos de código (IL) que se utilizan para implementar las características deseadas y una vez conocido esto, eliminar el resto. Es decir, eliminar todos los métodos, propiedades, campos, eventos, tipos y demás elementos que no tomen parte en la realización de las características que queremos.

Por ejemplo:

En un procesador de texto tenemos 2 features: Save y Print. Y la interacción como se ve abajo.

image 

Del diagrama queda claro que no todas las funcionalidades hacen uso de todos los tipos ni de todos los métodos ni de todas las propiedades, etc. Entonces, si solo quisiésemos permitir guardar un documento, sería seguro eliminar los tipos DocumentReader y Printer, verdad?

Esto es posible hacerlo reescribiendo los ensamblados con Mono.Cecil, solo hay que conocer algo de IL para hacer cosas sencillas aunque para hacerlo realmente bien hay que conocer IL a la perfección.

Veamos el código del ejemplo del cual queremos eliminar la feature que permite imprimir es documento.

image

Ahora bien, antes de continuar quiero mostrar el resultado de como queda el assembly antes y después de ser procesado:

image

Lo que se eliminó es la posibilidad de imprimir y por eso, en el assembly procesado no existe el método TargetClass.Print(), ni los field _reader y printer ya que estos solo eran usados por el método TargetClass.Print(). Los tipos Printer y DocumentReader también han sido eliminados ya que no eran usados por otro método más que el TargetClass.Print().

Por otra parte, todos los tipos, campos, propiedades y eventos que son utilizados por otras características, aún cuando sean utilizados además por el TargetClass.Print(), permanecen intactos. Para esto solo hace falta comprobar que la clase Document aún sigue allí (aunque modificada ya que la propiedad Title se ha quitado porque solo era relevante al imprimir).

Bien, la idea entonces es que una vez que conocemos cuales son los métodos que implementan las features que deseamos mantener, la técnica consiste en seguir todas y cada una de las llamadas que ese método realiza. Luego, hacer lo mismo con cada uno de estos de manera recursiva hasta tener la lista de todos los métodos que participan en la realización de la características deseada. Abajo puede verse el mecanismo:

image

Y con esto ya tenemos el listado de todos los métodos usados, ahora lo que hacemos es eliminar todos aquellos métodos que no se encuentren en la lista:

image

Esto elimina todos los métodos no usados incluyendo getters, setters, los add y remove de los eventos. Por esta razón es que de seguro nos quedan propiedades sin gettes ni setters y que por lo tanto tenemos que eliminar para que el ensamblado nos quede funcional.

image

Por último, si existen clases sin métodos, ni campos, ni propiedades ni eventos… la eliminamos también.

image

Como esto es solo una prueba de concepto, quizás cambie muchas cosas en el código, voy a continuar con la explicación en la siguiente entrada.

Sin categoría

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *