Me imagino que la mayoría de ustedes saben que goto es una palabra clave reservada en el lenguaje Java pero que en realidad no se usa. Y probablemente también sepa que goto es un código de operación de Java Virtual Machine (JVM). Considero que todas las estructuras de flujo de control sofisticadas de Java, Scala y Kotlin están, a nivel de JVM, implementadas usando alguna combinación de goto e ifeq , ifle , iflt , etc.
Mirando la especificación JVM https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.goto_w Veo que también hay un código de operación goto_w . Mientras que goto toma un desplazamiento de rama de 2 bytes, goto_w toma un desplazamiento de rama de 4 bytes. La especificación establece que
Aunque la instrucción goto_w toma un desplazamiento de rama de 4 bytes, otros factores limitan el tamaño de un método a 65535 bytes (§4.11). Este límite puede aumentar en una versión futura de Java Virtual Machine.
Me parece que goto_w está preparado para el futuro, como algunos de los otros códigos de *_w . Pero también se me ocurre que tal vez goto_w podría usarse con los dos bytes más significativos puestos a cero y los dos bytes menos significativos igual que para goto , con los ajustes necesarios.
Por ejemplo, dado este Java Switch-Case (o Scala Match-Case):
12: lookupswitch { 112785: 48 // case "red" 3027034: 76 // case "green" 98619139: 62 // case "blue" default: 87 } 48: aload_2 49: ldc #17 // String red 51: invokevirtual #18 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 54: ifeq 87 57: iconst_0 58: istore_3 59: goto 87 62: aload_2 63: ldc #19 // String green 65: invokevirtual #18 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 68: ifeq 87 71: iconst_1 72: istore_3 73: goto 87 76: aload_2 77: ldc #20 // String blue 79: invokevirtual #18 // etc.Podríamos reescribirlo como
12: lookupswitch { 112785: 48 3027034: 78 98619139: 64 default: 91 } 48: aload_2 49: ldc #17 // String red 51: invokevirtual #18 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 54: ifeq 91 // 00 5B 57: iconst_0 58: istore_3 59: goto_w 91 // 00 00 00 5B 64: aload_2 65: ldc #19 // String green 67: invokevirtual #18 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 70: ifeq 91 73: iconst_1 74: istore_3 75: goto_w 91 79: aload_2 81: ldc #20 // String blue 83: invokevirtual #18 // etc. En realidad, no he probado esto, ya que probablemente cometí un error al cambiar los "números de línea" para acomodar los goto_w s. Pero como está en la especificación, debería ser posible hacerlo.
Mi pregunta es si hay una razón por la que un compilador u otro generador de código de bytes podría usar goto_w con el límite actual de 65535 que no sea para mostrar que se puede hacer.
El tamaño del código del método puede ser tan grande como 64K.
El desplazamiento de rama del goto corto es un entero de 16 bits con signo: de -32768 a 32767.
Por lo tanto, el desplazamiento corto no es suficiente para dar un salto desde el principio del método de 65K hasta el final.
Incluso javac a veces emite goto_w . Aquí hay un ejemplo:
public class WideGoto { public static void main(String[] args) { for (int i = 0; i < 1_000_000_000; ) { i += 123456; // ... repeat 10K times ... } } } Descompilando con javap -c :
public static void main(java.lang.String[]); Code: 0: iconst_0 1: istore_1 2: iload_1 3: ldc #2 5: if_icmplt 13 8: goto_w 50018 // <<< Here it is! A jump to the end of the loop ...No hay razón para usar goto_w cuando la rama se ajusta a un goto . Pero parece que te has perdido que las ramas son relativas , usando un desplazamiento firmado, ya que una rama también puede ir hacia atrás.
No lo nota cuando mira la salida de una herramienta como javap , ya que calcula la dirección de destino absoluta resultante antes de imprimir.
Por lo tanto, el rango de -327678 … +32767 de goto no siempre es suficiente para abordar cada ubicación de destino posible en el rango de 0 … +65535 .
Por ejemplo, el siguiente método tendrá una instrucción goto_w al principio:
public static void methodWithLargeJump(int i) { for(; i == 0;) { try {x();} finally { switch(i){ case 1: try {x();} finally { switch(i){ case 1: try {x();} finally { switch(i){ case 1: try {x();} finally { switch(i){ case 1: try {x();} finally { switch(i){ case 1: try {x();} finally { switch(i){ case 1: try {x();} finally { switch(i){ case 1: try {x();} finally { switch(i){ case 1: try {x();} finally { switch(i){ case 1: try {x();} finally { switch(i){ case 1: } } } } } } } } } } } } } } } } } } } } } } static void x() {} Compiled from "Main.java" class LargeJump { public static void methodWithLargeJump(int); Code: 0: iload_0 1: ifeq 9 4: goto_w 57567 …Parece que en algunos compiladores (probado en 1.6.0 y 11.0.7), si un método es lo suficientemente grande como para necesitar goto_w, usa exclusivamente goto_w. Incluso cuando tiene saltos muy locales, todavía usa goto_w.