Shu Yoshida y Adam Voyes explican como se comparten los juegos en la nueva PS4.
Sin comentarios xD
miércoles, 12 de junio de 2013
domingo, 9 de junio de 2013
Java - Como hacer una JVM | Parte 2
Continuamos este tutorial para realizar nuestra home made virtual machine, la primera parte la podéis encontrar en este enlace.
Habíamos preparado la mente para saber como empezar a programar, así que vamos allá, y empezaremos pasando a código lo que ya sabemos, como está estructurada nuestra JVM.
JVM.java
Ya dijimos que la JVM esta formada por 3 partes, la zona de clases, el heap y los frames, además sabemos que tendrá que ir leyendo instrucciones de los archivos .class y ejecutarlas en función de cual sea dicha instrucción, bien, pues pondremos eso en código. He decidido ir descomponiéndolo porque meterlo todo del tirón puede ser demasiao lioso.
Primero definimos las 3 estructuras tal y como ya sabemos, son clases que deberemos crear más adelante y establecemos un constructor para la JVM, además hacemos que implemente Runnable y por tanto el método run().
Dentro de este método run() podríamos tener distintos tipos de ejecución como se mencionó en la primera parte pero para no complicar el tema tomaremos solo la ejecución directa con OPCODE.
Código Base
Para esta ejecución tenemos un método runOPCODE(), que declara 2 variables, un array de short y un short, cada uno representará el código completo del método y el código de operación actual.
Y podréis preguntaros ¿Por qué son short si te has hartado de decir que la información en los .class esta en forma de bytes? Primero, con 8 bits tenemos 256 combinaciones, suficientes para los 212 opcodes, pero internamente los byte en Java están en complemento a 2, osea que van desde -2^7 a 2^7-1, así que bcel guarda la codificación de los opcodes superior a 2^7-1 como negativos, pero a la hora de la verdad son positivos, por lo que hacemos esa transformación de byte a short para permitir valores superior a 2^7-1, en la clase JMethod lo veremos.
Después de eso tenemos el bucle de ejecución, que debe seguir funcionando mientras tengamos algo que ejecutar (el frame actual no sea null), y no tengamos ningún error. Hacemos una distinción a la hora de ejecutar por si el método es nativo o no, si lo es el control pasa al método especial ejecutarNative(), sino, obtenemos el código del método, la instrucción a ejecutar y usamos el método ejecutarOPCODE(), todo esto haciendo uso de los métodos de StackFrame y de la clase JMethod, que todavía no hemos visto, pero que es fácil intuir que hacen.
Código runOPCODE()
Para el método de ejecución usamos una estructura switch-case, en función de qué tipo de opcode tengamos se realizará una ejecución u otra, he puesto solo algunas instrucciones aunque como digo son muchas más, pero para haceros una idea esta bien. Para saber como implementar cada una nada mejor que primero revisar la lista básica:
http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings
Y después revisar la ayuda de Jasmin (un ensamblador para la JVM algo más simple), por ejemplo el siguiente enlace es para putfield:
http://www.vmth.ucdavis.edu/incoming/Jasmin/ref-putfield.html
Código ejecutarOPCODE()
Mencionar el método getIndex(), cada opcode de la JVM ocupa un byte, y si usa parámetros van codificados a continuación usando otros byte, el tema esta que los índices a la ConstantPool van codificados como 16 bits, pero separados en 2 bytes (uno con los 8 bits más significados y otro con los 8 menos), asi que para formar el índice completo hay que coger cada byte por separado y generar el short (de 16bits), para ello hacemos uso de la clase ByteBuffer (probé distintas formas ésta es la que me funcionó).
Código getIndex()
Para la ejecución de métodos nativos, según leí hay distintas formas de abordarlo, yo elegí la más simple, cuando detectamos el método nativo, obtenemos cual es, mirando su signatura y nombre, y usando una clase de ayuda llamada Natives hacemos su ejecución utilizando código Java, aunque podría hacerse de otras formas. Por ejemplo nuestro método print() escribe por pantalla, y para ello usa el método System.out.print().
Código ejecutarNative()
Código print() en Natives.java
Código implementación algunas instrucciones
Por último la invocación de métodos, el mismo método lo usamos para los 3 tipos de invocación.
Código invocación de métodos
Alaaaa, sí, todo esto para invocación de métodos, primero tenemos que obtener que método vamos a llamar, esa información esta en una constante MethodRef de la ConstantPool así que lo obtenemos usando el índice de su posición que viene codificado en los dos siguientes bytes de la instrucción. El método getConstant() de BCEL nos facilita realizar esta operación, el segundo parámetro sirve para especificar que tipo de constante queremos obtener, saltando una excepción si no es ese (útil para debug).
Tras eso ya tenemos la referencia al método pero necesitamos su clase! He dejado comentado la manera "larga", la que sigue la cadena de referencias de la ConstantPool, pero por suerte BCEL nos proporciona un método llamado getClass() para obtener directamente ese valor.
Como dije tenemos la referencia al método pero ni su nombre ni su signatura, esa información esta en una constante NameAndType, de nuevo usamos la ConstantPool y voila, ya podemos crear nuestro JClass y JMethod. Además necesitamos saber el número de parámetros del método.
El tratamiento de métodos static lo hacemos ahora, cuando no es static, una referencia al objeto que lo llama se guarda en la primera posición de variables locales, si lo es, simplemente disminuimos el valor. Otro punto que hay que notar es que el paso de parámetros se hace en orden contrario al de la pila, de ahí el uso del método auxiliar getParamsReverse(), que nos da la pila de parámetros en orden para luego meterlos en el array de variables locales.
Luego creamos el frame en función de si es static o no, y por último el paso de parámetros en sí. Básicamente va sacando elementos de nuestra pila de argumentos y los mete en el array de locales, pero hay que resaltar varias cosas. Primero si es static o no, de ahí el valor desp para no sobreescribir la referencia al objeto, y además los valores long y double, que en la JVM ocupan dos "espacios". En esta implementación ocupa 1 pues todo son JValue, pero de todos modos tenemos que hacer que ocupen dos pues las referencias en el bytecode serán a su posición real.
UFF, bueno todo eso sería para nuestra clase JVM, mucho curro y todavía solo hemos empezado porque... ¿Cómo implementamos las 3 partes que no tenemos? ¿Dónde se hace la lectura de los .class? Adelanto que de eso se ocupará nuestra clase ClassArea.
Bueno, de esa clase y del resto de nuestra JVM hablaré en la tercera parte y final (espero) de este tutorial.
Un saludo.
Tercera parte ya disponible!
Habíamos preparado la mente para saber como empezar a programar, así que vamos allá, y empezaremos pasando a código lo que ya sabemos, como está estructurada nuestra JVM.
JVM.java
Ya dijimos que la JVM esta formada por 3 partes, la zona de clases, el heap y los frames, además sabemos que tendrá que ir leyendo instrucciones de los archivos .class y ejecutarlas en función de cual sea dicha instrucción, bien, pues pondremos eso en código. He decidido ir descomponiéndolo porque meterlo todo del tirón puede ser demasiao lioso.
Primero definimos las 3 estructuras tal y como ya sabemos, son clases que deberemos crear más adelante y establecemos un constructor para la JVM, además hacemos que implemente Runnable y por tanto el método run().
Dentro de este método run() podríamos tener distintos tipos de ejecución como se mencionó en la primera parte pero para no complicar el tema tomaremos solo la ejecución directa con OPCODE.
Código Base
public class JVM implements Runnable {
private StackFrame topFrame;
private Heap heap;
private ClassArea classes;
public JVM(StackFrame topFrame, Heap heap, ClassArea classes) {
this.topFrame = topFrame;
this.heap = heap;
this.classes = classes;
}
public void run() {
runOPCODE();
}
}
Para esta ejecución tenemos un método runOPCODE(), que declara 2 variables, un array de short y un short, cada uno representará el código completo del método y el código de operación actual.
Y podréis preguntaros ¿Por qué son short si te has hartado de decir que la información en los .class esta en forma de bytes? Primero, con 8 bits tenemos 256 combinaciones, suficientes para los 212 opcodes, pero internamente los byte en Java están en complemento a 2, osea que van desde -2^7 a 2^7-1, así que bcel guarda la codificación de los opcodes superior a 2^7-1 como negativos, pero a la hora de la verdad son positivos, por lo que hacemos esa transformación de byte a short para permitir valores superior a 2^7-1, en la clase JMethod lo veremos.
Después de eso tenemos el bucle de ejecución, que debe seguir funcionando mientras tengamos algo que ejecutar (el frame actual no sea null), y no tengamos ningún error. Hacemos una distinción a la hora de ejecutar por si el método es nativo o no, si lo es el control pasa al método especial ejecutarNative(), sino, obtenemos el código del método, la instrucción a ejecutar y usamos el método ejecutarOPCODE(), todo esto haciendo uso de los métodos de StackFrame y de la clase JMethod, que todavía no hemos visto, pero que es fácil intuir que hacen.
Código runOPCODE()
private void runOPCODE() {
short[] code;
short opcode;
while (topFrame != null && topFrame.error == 0) {
if (topFrame.getMethod().isNative())
ejecutarNative();
else {
// ejecucion acceso directo bytecode
code = topFrame.getMethod().getCode();
opcode = code[topFrame.getPC()];
ejecutarOPCODE(code, opcode);
}
}
}
Para el método de ejecución usamos una estructura switch-case, en función de qué tipo de opcode tengamos se realizará una ejecución u otra, he puesto solo algunas instrucciones aunque como digo son muchas más, pero para haceros una idea esta bien. Para saber como implementar cada una nada mejor que primero revisar la lista básica:
http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings
Y después revisar la ayuda de Jasmin (un ensamblador para la JVM algo más simple), por ejemplo el siguiente enlace es para putfield:
http://www.vmth.ucdavis.edu/incoming/Jasmin/ref-putfield.html
Código ejecutarOPCODE()
private void ejecutarOPCODE(short[] code, short opcode) {
short pos, parteA, parteB, cons, type;
// segun opcode exec instruccion
switch (opcode) {
case Constants.NOP:
ejecutarNOP();
break;
// /////////////// Stack Operations ////////////////
// Instructions that push a constant onto the stack
case Constants.ICONST_M1:
case Constants.ICONST_0:
case Constants.ICONST_1:
case Constants.ICONST_2:
case Constants.ICONST_3:
case Constants.ICONST_4:
case Constants.ICONST_5:
ejecutarICONST(Integer.valueOf(opcode - Constants.ICONST_0));
break;
// Instructions that load a local variable onto the stack
case Constants.ILOAD:
pos = code[topFrame.getPC() + 1];
ejecutarILOAD((byte) pos);
topFrame.incPC();// saltar index
break;
// Instructions that load a local variable onto the stack
case Constants.ILOAD:
pos = code[topFrame.getPC() + 1];
ejecutarILOAD((byte) pos);
topFrame.incPC();// saltar index
break;
case Constants.ISTORE:
pos = code[topFrame.getPC() + 1];
ejecutarISTORE(pos);
topFrame.incPC();// saltar index
break;
// Integer arithmetic
case Constants.IADD:
ejecutarIADD();
break;
// ///////////// Objects and Arrays ////////////
// Instructions that deal with objects
case Constants.NEW:
parteA = code[topFrame.getPC() + 1];
parteB = code[topFrame.getPC() + 2];
ejecutarNEW(getIndex(parteA, parteB));
break;
case Constants.GETFIELD:
parteA = code[topFrame.getPC() + 1];
parteB = code[topFrame.getPC() + 2];
ejecutarGETFIELD(getIndex(parteA, parteB), lastDec);
break;
// ////////////Control Flow /////////////////////
// Conditional branch instructions
case Constants.IFEQ:
parteA = code[topFrame.getPC() + 1];
parteB = code[topFrame.getPC() + 2];
ejecutarIFEQ(getIndex(parteA, parteB), lastDec);
break;
// ////////////////////// Method Invocation and Return ////////
// Method return instructions
case Constants.RETURN:
ejecutarRETURN();
break;
case Constants.IRETURN:
ejecutarIRETURN();
break;
// Method invocation instructions
case Constants.INVOKESPECIAL:
ejecutarINVOKEBC(null, code);
break;
case Constants.INVOKESTATIC:
ejecutarINVOKEBC(Constants.INVOKESTATIC, code);
break;
case Constants.INVOKEVIRTUAL:
ejecutarINVOKEBC(null, code);
break;
default:
error(topFrame, "opcode no reconocido");
break;
}
}
Mencionar el método getIndex(), cada opcode de la JVM ocupa un byte, y si usa parámetros van codificados a continuación usando otros byte, el tema esta que los índices a la ConstantPool van codificados como 16 bits, pero separados en 2 bytes (uno con los 8 bits más significados y otro con los 8 menos), asi que para formar el índice completo hay que coger cada byte por separado y generar el short (de 16bits), para ello hacemos uso de la clase ByteBuffer (probé distintas formas ésta es la que me funcionó).
Código getIndex()
/**
* Construye un indice de 16 bits separado en 2 bytes
*
* @param subi1
* primera parte indice
* @param subi2
* segunda parte indice
* @return el indice completo
*/
private int getIndex(short subi1, short subi2) {
ByteBuffer bb = ByteBuffer.allocate(2);
bb.order(ByteOrder.BIG_ENDIAN);
bb.put((byte) subi1);
bb.put((byte) subi2);
short index = bb.getShort(0);
return index;
}
Para la ejecución de métodos nativos, según leí hay distintas formas de abordarlo, yo elegí la más simple, cuando detectamos el método nativo, obtenemos cual es, mirando su signatura y nombre, y usando una clase de ayuda llamada Natives hacemos su ejecución utilizando código Java, aunque podría hacerse de otras formas. Por ejemplo nuestro método print() escribe por pantalla, y para ello usa el método System.out.print().
Código ejecutarNative()
private int ejecutarNative() {
JValue val = null;
String sig = topFrame.getMethod().getFullNameAndSig();
if (sig.equals(Natives.SIGS[0])) {
val = Natives.fillInStackTrace(this);
} else if (sig.equals(Natives.SIGS[1])) {
val = Natives.print(this);
} else if (sig.equals(Natives.SIGS[2])) {
val = Natives.println(this);
} else if (sig.equals(Natives.SIGS[3])) {
val = Natives.getIntFromStr(this);
}
if (val != null) {
topFrame.push(val);
ejecutarXRETURN();
} else
ejecutarRETURN();
return 0;
}
Código print() en Natives.java
public static JValue print(JVM jvm) {
JObject ostr = (JObject) jvm.topFrame.locals[0];
JArray arraystr = (JArray) ostr.getField(0).getValue();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < arraystr.nElems; i++) {
sb.append(arraystr.get(i));
}
System.out.print(sb);
return null;
}
También os dejo la implementación de algunas de las instrucciones, he comentado el código para que quede más claro y más o menos creo que debería entenderse.Código implementación algunas instrucciones
private void ejecutarNEW(int index) {
JObject object;
//usamos constant pool para obtener nombre de clase en base a su indice
ConstantPool cp = topFrame.getConstantPool();
ConstantClass cc = (ConstantClass) cp.getConstant(index,
Constants.CONSTANT_Class);
String className = cc.getBytes(cp);
//cargamos clase y creamos objeto a traves de heap
JClass jClass = classes.loadClass(className);
object = heap.createObject(jClass);
//añadimos objeto a la pila y saltamos parametros
topFrame.push(object);
topFrame.incPC(3);
}
private void ejecutarRETURN() {
//return sin valor, simplemente volvemos al frame padre
setTopFrame(topFrame.getCf());
}
private void ejecutarIRETURN() {
if (topFrame.size() != 1)
error(topFrame, "Tamaño de pila para return distinto de 1");
JValue val = topFrame.pop();
StackFrame previous = topFrame.getCf();
if (previous == null) {
error(topFrame, "No hay frame previo para return");
} else {
previous.push(val);
setTopFrame(previous);
}
}
//para este metodo usamos un truco a la hora de llamarlo,
//las instrucciones const estan codificadas de manera consecutiva,
//asi obtenemos el valor correcto restando cada una a la primera
private void ejecutarICONST(int n) {
topFrame.push(new JInteger(n));
topFrame.incPC();
}
private void ejecutarILOAD(byte pos) {
topFrame.push(topFrame.locals[pos]);
topFrame.incPC();
}
private void ejecutarISTORE(int pos) {
JValue val = topFrame.operands.pop();
topFrame.locals[pos] = val;
topFrame.incPC();
}
private void ejecutarIADD() {
//cada tipo tiene su clase concreta, asà para sumar
//dos enteros usamos el metodo de clase y add a la pila
JInteger op1 = (JInteger) topFrame.operands.pop();
JInteger op2 = (JInteger) topFrame.operands.pop();
topFrame.push(op1.add(op2));
topFrame.incPC();
}
private void ejecutarIFEQ(int index) {
//en base al valor en la pila saltamos a index o no
JInteger val = (JInteger) topFrame.pop();
if (val.getValue() == 0) {
topFrame.incPC(index);
} else {
topFrame.incPC(3);
}
}
Por último la invocación de métodos, el mismo método lo usamos para los 3 tipos de invocación.
Código invocación de métodos
private void ejecutarINVOKEBC(Short stat, short[] code) {
// get clase y method info a partir de BC
ConstantPool cp = topFrame.getConstantPool();
ConstantMethodref methodIndex = (ConstantMethodref) cp
.getConstant(
getIndex(code[topFrame.getPC() + 1],
code[topFrame.getPC() + 2]),
Constants.CONSTANT_Methodref);
// get clase
// modo largo, siguiendo cadena referencias
// ConstantClass classIndex =(ConstantClass)
// cp.getConstant(methodIndex.getClassIndex(),Constants.CONSTANT_Class);
// ConstantString stringClassIndex=(ConstantString)
// cp.getConstant(classIndex.getNameIndex(),Constants.CONSTANT_String);
// ConstantUtf8 utf8ClassIndex=(ConstantUtf8)
// cp.getConstant(stringClassIndex.getStringIndex(),Constants.CONSTANT_Utf8);
// String className =utf8ClassIndex.toString();
// USANDO ATAJO
String className = methodIndex.getClass(cp);
// get method
ConstantNameAndType nameAndTypeIndex = (ConstantNameAndType) cp
.getConstant(methodIndex.getNameAndTypeIndex());
String methodName = nameAndTypeIndex.getName(cp);
String signature = nameAndTypeIndex.getSignature(cp);
JClass jClass = classes.loadClass(className);
JMethod jMethod = jClass.getMethod(methodName, signature);
// maximo argumentos
int maxArgs = jMethod.getMaxArgs() + 1;
// si es estatico disminuyo params
if (stat != null)
maxArgs--;
// get pila en orden para parametros
Stack<JValue> stack = topFrame.getParamsReverse(maxArgs);
topFrame.clearParams(maxArgs);
StackFrame newTopFrame;
int desp = 0;
if (stat != null) {
newTopFrame = new StackFrame(topFrame, jMethod, jClass);
} else {
// si no es estatico, meto referencia en locals[0]
desp = 1;
JObject ref = (JObject) stack.pop();
newTopFrame = new StackFrame(topFrame, jMethod, jClass, ref);
}
// paso parametros tras que se ha creado la estructura
for (int j = 0; j < maxArgs - desp && !stack.empty(); j++) {
JValue val = stack.pop();
// si no es el primero y el anterior ocupa 2 espacios, lo desplazo
if (j != 0
&& (newTopFrame.getLocals()[j - 1].type == Type.LONG || newTopFrame
.getLocals()[j - 1].type == Type.DOUBLE)) {
newTopFrame.getLocals()[j + desp + 1] = val;
} else
newTopFrame.getLocals()[j + desp] = val;
}
topFrame.incPC(3);
setTopFrame(newTopFrame);
}
Alaaaa, sí, todo esto para invocación de métodos, primero tenemos que obtener que método vamos a llamar, esa información esta en una constante MethodRef de la ConstantPool así que lo obtenemos usando el índice de su posición que viene codificado en los dos siguientes bytes de la instrucción. El método getConstant() de BCEL nos facilita realizar esta operación, el segundo parámetro sirve para especificar que tipo de constante queremos obtener, saltando una excepción si no es ese (útil para debug).
Tras eso ya tenemos la referencia al método pero necesitamos su clase! He dejado comentado la manera "larga", la que sigue la cadena de referencias de la ConstantPool, pero por suerte BCEL nos proporciona un método llamado getClass() para obtener directamente ese valor.
Como dije tenemos la referencia al método pero ni su nombre ni su signatura, esa información esta en una constante NameAndType, de nuevo usamos la ConstantPool y voila, ya podemos crear nuestro JClass y JMethod. Además necesitamos saber el número de parámetros del método.
El tratamiento de métodos static lo hacemos ahora, cuando no es static, una referencia al objeto que lo llama se guarda en la primera posición de variables locales, si lo es, simplemente disminuimos el valor. Otro punto que hay que notar es que el paso de parámetros se hace en orden contrario al de la pila, de ahí el uso del método auxiliar getParamsReverse(), que nos da la pila de parámetros en orden para luego meterlos en el array de variables locales.
Luego creamos el frame en función de si es static o no, y por último el paso de parámetros en sí. Básicamente va sacando elementos de nuestra pila de argumentos y los mete en el array de locales, pero hay que resaltar varias cosas. Primero si es static o no, de ahí el valor desp para no sobreescribir la referencia al objeto, y además los valores long y double, que en la JVM ocupan dos "espacios". En esta implementación ocupa 1 pues todo son JValue, pero de todos modos tenemos que hacer que ocupen dos pues las referencias en el bytecode serán a su posición real.
UFF, bueno todo eso sería para nuestra clase JVM, mucho curro y todavía solo hemos empezado porque... ¿Cómo implementamos las 3 partes que no tenemos? ¿Dónde se hace la lectura de los .class? Adelanto que de eso se ocupará nuestra clase ClassArea.
Bueno, de esa clase y del resto de nuestra JVM hablaré en la tercera parte y final (espero) de este tutorial.
Un saludo.
Tercera parte ya disponible!
jueves, 6 de junio de 2013
iPhone - Eliminar cifrado de una copia de seguridad de iPhone
Primero, si la copia esta cifrada no se puede descifrar, solo puedes tener acceso a ella teniendo la contraseña, pero si lo que te interesa es desactivar el cifrado para posteriores copias y por algún motivo no puedes deseleccionar la casilla de verificación para el cifrado de copias de seguridad de iPhone aun sabiendo la contraseña, el problema es que tienes que borrar los perfiles que tengas guardados en el iPhone.
Accedemos a ajustes->general->perfiles y vamos borrándolos, una vez hecho esto conectamos de nuevo el móvil y ya debería dejarnos, Apple siempre complicándonos la vida xD.
Accedemos a ajustes->general->perfiles y vamos borrándolos, una vez hecho esto conectamos de nuevo el móvil y ya debería dejarnos, Apple siempre complicándonos la vida xD.
miércoles, 5 de junio de 2013
Webs que molan - Recommended Games
Alguna vez te has visto sin saber a que jugar, tanto juego y por donde empiezo? seguro que sí, pues bien os traigo la solución!
En esta web tienes organizados por consolas/plataformas una gran cantidad de juegos. Además para compatibles en Windows los tienes organizados por épocas!
Un sueño para todo jugón, sin más os dejo el enlace, ala!
En esta web tienes organizados por consolas/plataformas una gran cantidad de juegos. Además para compatibles en Windows los tienes organizados por épocas!
Un sueño para todo jugón, sin más os dejo el enlace, ala!
martes, 4 de junio de 2013
Parodia Mago David Blaine
No sé porque hace unos días me vino a la cabeza esta parodia del mago David Blaine que cuando la vi me escojoné ampliamente, os la dejo por aquí mientras preparo la segunda parte de lo de la JVM.
Esta es la primera parte, hay unos cuantas más igual de buenas!
Un saludoo!
Esta es la primera parte, hay unos cuantas más igual de buenas!
Un saludoo!
viernes, 24 de mayo de 2013
Java - Como hacer una JVM | Parte 1
He estado trabajando en un proyecto en el que tenia que diseñar una JVM (Java Virtual Machine), y fue complicado encontrar información [1][2] sobre como hacerlo, (en español ya ni os cuento). Tengo que agradecer a un profesor llamado Jon Pearce de Saint José State University que me pasó el código fuente de parte de su especificación que me hizo saber por donde empezar [3][4].
Vista la falta de información, me he decidido a realizar este tutorial para hacer nuestra propia JVM, algo así como una Home Made Java Virtual Machine!
Conforme iba creciendo el contenido me decidí a dividir el tutorial en 3 partes, esta primera será más de teoría y preparación para tener cierta idea de que estamos haciendo, en las otras 2 partes nos meteremos con el código.
Hay muchas implementaciones de la JVM, algunas de código abierto que puedes mirar y repasar pero son muy complejas para tomar como una primera aproximación, la que expondré aquí será bastante "simple", sin soporte para varios threads ni recolector de basura y probablemente bastante ineficiente, pero que funcionará!.
La JVM
Lo primero, la JVM (Java Virtual Machine), es, como su propio nombre indica, una máquina virtual para programas Java, permite que cualquier programa Java se pueda ejecutar sobre cualquier plataforma (siempre y cuando disponga de su JVM), aquí entra el famoso 'Write once, Run anywhere' o WORA [5].
Ahora bien, para hacer eso es necesario que la JVM ejecute algo que sea común a todas las plataformas, y aquí entra el Bytecode, una especie de lenguaje ensamblador que la JVM entiende y puede ejecutar.
Los .class
¿Pero donde esta ese Bytecode? En los archivos .class, cualquiera que haya programado en Java sabrá que los archivos fuente son los .java y que al compilar se crea un archivo .class, estos .class (agrupados en bytes), son los que la JVM usa para la ejecución, conteniendo las instrucciones Bytecode además de la información de cada clase, almacenada en una estructura llamada Constant Pool [6]. Estos archivos son cargados dinámicamente en un intérprete para su ejecución (JVM).
Los .class son un mundo en sí, y si bien en este tutorial haremos referencia a ellos y habrá que entender ciertos aspectos de su estructura, será solo para su uso aplicado en la JVM, la elaboración de un parser para .class no es el objetivo de este tutorial, por suerte hay gente que se ha dedicado a hacer eso, y lo ha puesto a distribución del publico, para todo ese trabajo con .class usaremos la librería BCEL que comentaremos más adelante.
Estructura de la JVM
Podemos ver a la JVM como una maquina con tres partes, una zona de memoria llamada ClassArea, otra llamada Heap y otra para frames. La primera guarda la información de clases y métodos, en la segunda se guarda la información de objetos dinámicos cada vez que se invoca a new, además cuando un objeto deja de ser útil es eliminado por el recolector de basura, la tercera guarda la información de frames, cada frame cuenta con una pila propia, un espacio para variables locales y parámetros, e información de control (frame padre, PC y método operando).
La JVM sigue el patrón de un intérprete basado en pila creando un frame por cada ejecución de método, cuando el método termina el control pasa al frame padre.
La ejecución
La ejecución se hace a través de 212 instrucciones [7] que se ocupan de distintas tareas, si existen para distintos tipos usan un prefijo (i para integer, d para double, etc).
BCEL
La Librería BCEL (Byte Code Engineering Library) [8] es una librería para analizar, manipular y crear archivos Java Class. En nuestro contexto es una ayuda para parsear los archivos .class y obtener toda la información que necesitamos para la ejecución del bytecode en nuestra máquina virtual de Java.
Necesitaremos hacer uso de dos de los paquetes principales de BCEL.
El primero se encarga de la parte estática teniendo su clase principal en JavaClass (org.apache.bcel.classfile). Con ella podemos acceder a la información guardada en .class sin preocuparnos de lecturas de bajo nivel, aunque no es posible ningún tipo de modificación. Podemos ver un diagrama de su estructura en la siguiente imagen.
Con la segunda ClassGen (org.apache.bcel.generic) podemos acceder a alguna de las opciones de la primera además de poder llevar a cabo modificaciones.
Ambas puedes ser útiles según el contexto, aunque no es necesario hacer modificaciones en el bytecode pues solo vamos a ejecutarlo, ClassGen da acceso a algunos elementos que son necesarios a la hora de realizar la ejecución del mismo de una manera más simple, como la lista de instrucciones encapsulada en InstructionHandle. A pesar de eso hablaremos de otra forma de ejecución que lee instrucciones directamente del bytecode a través del method attribute code.
Bueno hasta aquí esta introducción teórica, bastante densa y con mucho tema pero que me parece imprescindible para poder meternos en la siguiente parte con el código.
Un saludo!
Segunda Parte ya disponible!
Tercera Parte ya disponible!
Vista la falta de información, me he decidido a realizar este tutorial para hacer nuestra propia JVM, algo así como una Home Made Java Virtual Machine!
Conforme iba creciendo el contenido me decidí a dividir el tutorial en 3 partes, esta primera será más de teoría y preparación para tener cierta idea de que estamos haciendo, en las otras 2 partes nos meteremos con el código.
Hay muchas implementaciones de la JVM, algunas de código abierto que puedes mirar y repasar pero son muy complejas para tomar como una primera aproximación, la que expondré aquí será bastante "simple", sin soporte para varios threads ni recolector de basura y probablemente bastante ineficiente, pero que funcionará!.
La JVM
Lo primero, la JVM (Java Virtual Machine), es, como su propio nombre indica, una máquina virtual para programas Java, permite que cualquier programa Java se pueda ejecutar sobre cualquier plataforma (siempre y cuando disponga de su JVM), aquí entra el famoso 'Write once, Run anywhere' o WORA [5].
![]() |
| El .java se convierte en .class para la JVM |
Los .class
¿Pero donde esta ese Bytecode? En los archivos .class, cualquiera que haya programado en Java sabrá que los archivos fuente son los .java y que al compilar se crea un archivo .class, estos .class (agrupados en bytes), son los que la JVM usa para la ejecución, conteniendo las instrucciones Bytecode además de la información de cada clase, almacenada en una estructura llamada Constant Pool [6]. Estos archivos son cargados dinámicamente en un intérprete para su ejecución (JVM).
Los .class son un mundo en sí, y si bien en este tutorial haremos referencia a ellos y habrá que entender ciertos aspectos de su estructura, será solo para su uso aplicado en la JVM, la elaboración de un parser para .class no es el objetivo de este tutorial, por suerte hay gente que se ha dedicado a hacer eso, y lo ha puesto a distribución del publico, para todo ese trabajo con .class usaremos la librería BCEL que comentaremos más adelante.
Estructura de la JVM
Podemos ver a la JVM como una maquina con tres partes, una zona de memoria llamada ClassArea, otra llamada Heap y otra para frames. La primera guarda la información de clases y métodos, en la segunda se guarda la información de objetos dinámicos cada vez que se invoca a new, además cuando un objeto deja de ser útil es eliminado por el recolector de basura, la tercera guarda la información de frames, cada frame cuenta con una pila propia, un espacio para variables locales y parámetros, e información de control (frame padre, PC y método operando).
![]() |
| Pila de parámetros, variables locales, información de control |
La JVM sigue el patrón de un intérprete basado en pila creando un frame por cada ejecución de método, cuando el método termina el control pasa al frame padre.
La ejecución
La ejecución se hace a través de 212 instrucciones [7] que se ocupan de distintas tareas, si existen para distintos tipos usan un prefijo (i para integer, d para double, etc).
- Operaciones con la pila: poner elementos (bipush), quitarlos (pop), duplicarlos (dup), etc.
- Aritméticas: sumas, restas, multiplicaciones de distintos tipos (iadd, fadd, dmul).
- Carga y guardado: cargar (iload) o guardar elementos desde memoria local (istore).
- Control de flujo: salto mediante comparación de valores (if_icmpeq), a subrutina, por excepciones (athrow).
- Acceso a campos: para acceder a campos de clases (getfield, putfield, etc).
- Invocación de métodos: para invocar métodos ya sean estáticos (invokestatic), de interfaces (invokeinterface), etc.
- Objetos: para reservar memoria para objetos (new) o arrays (newarray).
- Conversión de tipos: cambio de un tipo (f2i) a otro o comprobación de tipos (checkcast).
BCEL
La Librería BCEL (Byte Code Engineering Library) [8] es una librería para analizar, manipular y crear archivos Java Class. En nuestro contexto es una ayuda para parsear los archivos .class y obtener toda la información que necesitamos para la ejecución del bytecode en nuestra máquina virtual de Java.
Necesitaremos hacer uso de dos de los paquetes principales de BCEL.
El primero se encarga de la parte estática teniendo su clase principal en JavaClass (org.apache.bcel.classfile). Con ella podemos acceder a la información guardada en .class sin preocuparnos de lecturas de bajo nivel, aunque no es posible ningún tipo de modificación. Podemos ver un diagrama de su estructura en la siguiente imagen.
![]() |
| Estructura JavaClass |
![]() |
| Estructura ClassGen |
Bueno hasta aquí esta introducción teórica, bastante densa y con mucho tema pero que me parece imprescindible para poder meternos en la siguiente parte con el código.
Un saludo!
Segunda Parte ya disponible!
Tercera Parte ya disponible!
Algunas referencias que queda bonico
[1] Especificación oficial, mu bonica pero un tochaco.
[2] El punto de referencia que usé para desarrollar la JVM, ésta hecha en en C++ y con parser incluido.
[3] Información bastante básica, empecé por aquí.
[4] Esqueleto básico de una JVM.
[5] El lema de Java.
[6] La Constant Pool y el mundo de las referencias indirectas (ya entenderéis xd).
[7] Lista de instrucciones de la JVM
[8] Explicaciones sobre el uso de BCEL y la JVM, algo como esto pero en inglés .
[1] Especificación oficial, mu bonica pero un tochaco.
[2] El punto de referencia que usé para desarrollar la JVM, ésta hecha en en C++ y con parser incluido.
[3] Información bastante básica, empecé por aquí.
[4] Esqueleto básico de una JVM.
[5] El lema de Java.
[6] La Constant Pool y el mundo de las referencias indirectas (ya entenderéis xd).
[7] Lista de instrucciones de la JVM
[8] Explicaciones sobre el uso de BCEL y la JVM, algo como esto pero en inglés .
viernes, 10 de mayo de 2013
Windows - Abrir CMD en un directorio concreto
Si os encontráis en la necesidad de usar el cmd de Windows y tenéis que navegar para ir a un directorio que no es el que se abre por defecto y no tenéis ganas de escribir toda la ruta haced lo siguiente:
Os creáis un archivo de texto y escribis:
Ahora bien si tenéis que ir a otra unidad que no sea C: necesitáis añadir un parámetro al cd:
Renombráis el .txt a .bat y listo, rápido y sencillo aunque he estado un rato para encontrar lo de puñetero /d
Añadir también que con Windows Vista/7 hay una opción desde el menú contextual pulsando shift+Mouse2 sobre una carpeta para abrir directamente el cmd en ella, como se ve en la imagen.
Os dejo algunos enlaces de interés:
Os creáis un archivo de texto y escribis:
cmd /K "cd RUTA"Ahora bien si tenéis que ir a otra unidad que no sea C: necesitáis añadir un parámetro al cd:
cmd /K "cd /d D:\ruta"Renombráis el .txt a .bat y listo, rápido y sencillo aunque he estado un rato para encontrar lo de puñetero /d
Añadir también que con Windows Vista/7 hay una opción desde el menú contextual pulsando shift+Mouse2 sobre una carpeta para abrir directamente el cmd en ella, como se ve en la imagen.
Os dejo algunos enlaces de interés:
Suscribirse a:
Entradas (Atom)






