Je pense que la plupart d'entre vous savent que goto
c'est un mot-clé réservé dans le langage Java mais qu'il n'est pas réellement utilisé. Et vous savez probablement aussi qu'il goto
s'agit d'un opcode Java Virtual Machine (JVM). Je pense toutes les structures sophistiquées de contrôle de flux de Java, Scala et Kotlin sont, au niveau JVM, mis en œuvre en utilisant une combinaison de goto
et ifeq
, ifle
, iflt
, etc.
En regardant la spécification JVM https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.goto_w Je vois qu'il y a aussi un goto_w
opcode. Alors que goto
prend un décalage de branche de 2 octets, goto_w
prend un décalage de branche de 4 octets. La spécification indique que
Bien que l' instruction goto_w prenne un décalage de branche de 4 octets, d'autres facteurs limitent la taille d'une méthode à 65 535 octets (§4.11). Cette limite pourrait être relevée dans une future version de la machine virtuelle Java.
Cela me semble être à l' goto_w
épreuve du temps, comme certains des autres *_w
opcodes. Mais il me vient également à l'esprit que peut-être goto_w
pourrait être utilisé avec les deux octets plus significatifs mis à zéro et les deux octets moins significatifs identiques à goto
, avec des ajustements selon les besoins.
Par exemple, étant donné ce Java Switch-Case (ou 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.
nous pourrions le réécrire
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.
Je n'ai pas vraiment essayé cela, car j'ai probablement fait une erreur en changeant les "numéros de ligne" pour tenir compte du par goto_w
. Mais puisque c'est dans la spécification, il devrait être possible de le faire.
Ma question est de savoir s'il y a une raison qu'un compilateur ou un autre générateur de bytecode pourrait utiliser goto_w
avec la limite 65535 actuelle autre que pour montrer que cela peut être fait?
// ... repeat 10K times ...
Ça compile? Je sais qu'il y a une limite à la taille d'une classe source unique ... mais je ne sais pas ce que c'est précisément (la génération de code est la seule fois où j'ai vu quelque chose l'atteindre).Il n'y a aucune raison de l'utiliser
goto_w
lorsque la branche s'inscrit dans ungoto
. Mais vous semblez avoir oublié que les branches sont relatives , en utilisant un décalage signé, car une branche peut aussi reculer.Vous ne le remarquez pas lorsque vous regardez la sortie d'un outil comme
javap
, car il calcule l'adresse cible absolue résultante avant l'impression.Ainsi,
goto
la plage de-327678 … +32767
n'est pas toujours suffisante pour adresser chaque emplacement cible possible dans la0 … +65535
plage.Par exemple, la méthode suivante aura une
goto_w
instruction au début:Démo sur Ideone
la source
Main
avecmethodWithLargeJump()
compile à près de 400 Ko.finally
blocs sont dupliqués pour un flux normal et exceptionnel (obligatoire depuis Java 6). L'imbrication de dix d'entre eux implique donc × 2¹⁰, puis, le commutateur a toujours une cible par défaut, donc avec l'iload, il a besoin de dix octets plus le remplissage. J'ai également ajouté une déclaration non triviale dans chaque branche pour éviter les optimisations. L'exploitation des limites est un sujet récurrent, les expressions imbriquées , les lambdas , les champs , les constructeurs …Il semble que dans certains compilateurs (essayés en 1.6.0 et 11.0.7), si une méthode est suffisamment grande pour avoir toujours besoin de goto_w, elle utilise exclusivement goto_w. Même lorsqu'il a des sauts très locaux, il utilise toujours goto_w.
la source