Ejecutando (un poco de) ActionScript 3 en JavaScript

Juan Mellado, 8 Mayo, 2010 - 09:00

Después de haber conseguido parsear con éxito un fichero Flash mediante un programa en JavaScript, me he animado a programar una máquina virtual, también en JavaScript, que ejecute el código ActionScript 3 leído del fichero SWF sin utilizar el plugin de Adobe.

La ventaja de hacerlo en JavaScript es que ambos lenguajes se basan en la misma especificación ECMA-262, por lo que muchas cosas pueden traducirse directamente de un lenguaje a otro. Pero eso es en un mundo ideal, en la vida real ocurre que cada cual implementa la especificación de una forma ligeramente distinta, aparte de que la propia especificación deja margen para que ciertas partes se implementen de forma arbitraria. De hecho, es algo bastante conocido que la implementación del propio JavaScript varía de un navegador a otro.

Para simplificar estoy suponiendo que el comportamiento del código ejecutado en JavaScript es lo suficientemente parecido al de ActionScript 3. Siempre habrá tiempo para ajustar las pequeñas diferencias que aparezcan. Para el código estándar que no fuerce la mezcla de tipos de forma innecesaria, ni se base en efectos laterales, debería bastar. Y de hecho, ya he conseguido ejecutar sin mayor problema pequeños programas ActionScript 3 compuestos por operaciones matemáticas, comparaciones y bucles.

Siguiendo las especificaciones de Adobe, la ejecución de una función en ActionScript 3 requiere que la máquina virtual instancie una estructura de datos locales a dicha ejecución compuesta por una pila de operandos, una pila de ámbitos (scope) y un conjunto de registros.

LocalDataArea = function(){
  this.stack = [];
  this.scopeStack = [];
  this.registers = [];
};

Para empezar me he centrado en los opcodes que manipulan los registros o la pila de operandos, ya que normalmente son los más fáciles de implementar en cualquier tipo de emulador, y permiten tener en muy poco tiempo un prototipo ejecutando código.

La estructura principal de la máquina vitual es un simple switch que determina el código a ejecutar en función del opcode actual sobre el contador de programa.

switch(opcode){
...
case 0x02: //nop
  break;
...
case 0x28: //pushnan
  lda.stack.push(NaN);
  break;
...
case 0xd0: //getlocal_0
  lda.stack.push( lda.registers[0] );
  break;
...

Dejo para más adelante las consideraciones acerca del rendimiento, y realizar pruebas para ver es si mejor, o no, tener una función para cada opcode con el objetivo de agilizar la localización e invocación del código asociado a cada opcode. De momento me he centrado en implementar la mayor cantidad de opcodes posibles, unos cincuenta en estos momentos, sobre un total de aproximadamente ciento cincuenta con los que cuenta la especificación.

Una de las cosas "divertidas" de este proyecto es que me permite depurar código ActionScript 3 directamente desde el propio navegador mediante Firebug. Un fichero SWF generado en modo debug incluye opcodes de depuración con nombres de ficheros, números de línea y nombres de variable. Cuando se produce un error, como el intento de ejecución de un opcode no implementado, me basta con poner un punto de parada y ver que línea concreta del código fuente original ha generado el error, e incluso el nombre de las variables que contiene cada registro local en ese momento.

Buscando proyectos similares por Internet me he encontrado con un fork de Gordon por Brian McKelvey. Tiene implementado el parser de ficheros abcFile (que contienen el código ActionScript 3 compilado) aprovechando la clase base de lectura de Gordon, pero aún poca cosa de la máquina virtual en si misma. De hecho, me ha dado la impresión de lo que pretende no es construir una máquina virtual que interprete el código en tiempo de ejecución, sino trasladar los opcodes a código JavaScript directamente en forma de texto para luego evaluar la cadena resultante. Habrá que seguirle la pista.

¿No encontró lo que buscaba?

Utilice el buscador para encontrar más páginas en esta web o en toda Internet.
 
Web www.inmensia.com