Quel est le but de l'indicateur ACC SUPER access sur les fichiers de classe Java?


L'instruction invokespecial JVM est utilisée pour appeler des méthodes d'initialisation (<init>) lors de la création de nouveaux objets. La description de l'instruction suggère (mais ne clarifie pas) que la décision d'appeler le constructeur d'une superclasse ou un constructeur de la classe actuelle dépend de l'état de l'indicateur ACC_SUPER défini dans le fichier class.

De la spécification Sun JVM:

Ensuite, la méthode résolue est sélectionnée pour l'invocation à moins que tous les éléments suivants les conditions sont vraies:

  • L'indicateur ACC_SUPER (voir Tableau 4.1, "Accès à la classe et modificateurs de propriété") est défini pour la classe en cours.

-- Source (invokespecial définition de l'opcode)

Le réglage de l'indicateur ACC_SUPER indique laquelle des deux sémantiques alternatives pour son instruction invokespecial la machine virtuelle Java doit exprimer; l'indicateur ACC_SUPER existe pour la rétrocompatibilité du code compilé par les anciens compilateurs de Sun pour le langage de programmation Java. Toutes les nouvelles implémentations de la machine virtuelle Java doivent implémenter la sémantique d'invokespecial documentée dans cette spécification. Tous les nouveaux compilateurs du jeu d'instructions de la machine virtuelle Java doivent définir l'indicateur ACC_SUPER. Les anciens compilateurs de Sun ont généré des drapeaux ClassFile avec ACC_SUPER unset. Les anciennes implémentations de machines virtuelles Java de Sun ignorent l'indicateur s'il est défini.

-- Source (ClassFile format)

La définition indique que le drapeau est pour la rétrocompatibilité avec les anciens compilateurs. Cependant, il continue à contredire avec Sun's older Java virtual machine implementations ignore the flag if it is set.

Le drapeau est-il toujours utilisé avec l'opcode invokespecial? D'après ce que je peux dire, cela semble n'avoir aucun but et je ne trouve pas de ressource pour le suggérer.

Merci.

Author: Jivings, 2012-01-21

1 answers

ACC_SUPER a été introduit pour corriger un problème avec l'invocation de super méthodes. L'indicateur ACC_SUPER marque une classe comme compilée pour la sémantique modifiée de l'instruction opcode 183. Son but est similaire à celui du numéro de version du fichier de classe car il permet à la JVM de détecter si une classe a été compilée pour la sémantique plus ancienne ou plus récente de cette instruction. Java 1.0.2 n'a pas défini et ignoré ACC_SUPER tandis que Java 1.1 et versions ultérieures définit toujours ACC_SUPER.

Avant Java 1.1 le l'instruction byte code avec l'opcode 183 qui s'appelle maintenant invokespecial a été appelée invokenonvirtual et avait une spécification partiellement différente. Il était utilisé chaque fois que des méthodes d'instance devaient être invoquées sans recherche de méthode virtuelle. C'était le cas pour les méthodes privées, les initialiseurs d'instance (constructeurs) et pour implémenter des invocations de méthode sur super. Mais ce dernier cas a causé des problèmes avec l'évolution des bibliothèques de classes.

Une référence de méthode dans le code octet (CONSTANT_Methodref_info) ne définit pas seulement le nom et l'argument et renvoie les types d'une méthode mais aussi la classe à laquelle elle appartient. L'opcode 183 obtient un tel paramètre de référence de méthode et était destiné à appeler directement la méthode référencée à partir de la classe spécifiée sans autres recherches. Dans le cas des invocations sur super, il était de la responsabilité des compilateurs de résoudre la super classe la plus proche qui implémente cette méthode et de générer une référence à celle-ci dans le code octet.

Depuis Java 1.1, il a été modifié pour ignorer essentiellement la classe référencée dans CONSTANT_Methodref_info et à la place faire la recherche de la super méthode la plus proche avec le nom de méthode et la signature donnés dans la JVM. Cela se fait généralement maintenant lorsque la classe est chargée ou juste avant l'exécution de l'instruction ou la compilation JIT la première fois.

Voici un exemple pourquoi ce changement était nécessaire. Dans Java 1.0.2, les classes AWT Container et Component ont été définies de cette façon:

class Component
{
    public void paint( Graphics g ) {}
}

class Container extends Component
{
    // inherits paint from Component but doesn't override it
}

Dans Java 1.1, la classe Conatiner a été modifiée pour avoir sa propre implémentation de paint:

class Container extends Component
{
    public void paint( Graphics g ) {/*...*/}
}

Maintenant, lorsque vous aviez une sous-classe directe ou indirecte de Conteneur qui a fait un appel sur super.paint(g) et l'a compilé pour 1.0.2, elle a généré une instruction invokenonvirtual pour Component.paint car c'était le premier parent qui avait cette méthode. Mais si vous utilisiez cette classe compilée sur une JVM qui avait également Container.paint, elle aurait toujours appelé Component.paint, ce qui n'est pas ce à quoi vous vous attendiez.

D'autre part, lorsque vous avez compilé la classe pour 1.1 et l'avez exécutée sur une JVM 1.0.2, elle lancerait un AbstractMethodError ou plus probablement pour les machines virtuelles de cette époque se bloque simplement. Pour éviter le plantage, vous deviez écrire ((Component)super).paint(g) et le compiler avec un compilateur 1.1 pour obtenir le comportement souhaité dans l'une ou l'autre machine virtuelle. Cela définirait ACC_SUPER mais générerait toujours l'instruction pour appeler Component.paint. Une machine virtuelle 1.0.2 ignorerait ACC_SUPER et irait directement invoquer Component.paint, ce qui est bien tandis qu'une machine virtuelle 1.1 trouverait ACC_SUPER set et ferait donc la recherche elle-même ce qui la ferait invoquer Container.paint même si la référence de la méthode byte code était Component.paint.

Vous pouvez en trouver plus à ce sujet dans cet ancien post sur le ikvm.net weblog .

 34
Author: x4u, 2012-01-21 17:06:50