Qual è lo scopo del flag ACC SUPER access sui file di classe Java?


L'istruzione JVM invokespecial viene utilizzata per chiamare i metodi di inizializzazione (<init>) quando si creano nuovi oggetti. La descrizione dell'istruzione suggerisce (ma non chiarisce) che la decisione se chiamare il costruttore di una superclasse o un costruttore della classe corrente dipende dallo stato del flag ACC_SUPER impostato all'interno del file class.

Dal Sole JVM Specifica:

Successivamente, il metodo risolto viene selezionato per l'invocazione a meno che le condizioni sono vere:

  • Il flag ACC_SUPER (vedere Tabella 4.1, "Accesso alla classe e modificatori di proprietà") è impostato per la classe corrente.

-- Fonte (invokespecial definizione opcode)

L'impostazione del flag ACC_SUPER indica quale delle due semantiche alternative per la sua istruzione invokespecial la macchina virtuale Java deve esprimere; il flag ACC_SUPER esiste per la compatibilità con le versioni precedenti per il codice compilato dai compilatori precedenti di Sun il linguaggio di programmazione Java. Tutte le nuove implementazioni della Java virtual machine dovrebbero implementare la semantica per invokespecial documentata in questa specifica. Tutti i nuovi compilatori del set di istruzioni della macchina virtuale Java devono impostare il flag ACC_SUPER. I compilatori precedenti di Sun hanno generato flag ClassFile con ACC_SUPER unset. Le implementazioni della macchina virtuale Java più vecchie di Sun ignorano il flag se è impostato.

-- Fonte (ClassFile formato)

La definizione afferma che il flag è per la compatibilità con i vecchi compilatori. Tuttavia continua a contraddire con Sun's older Java virtual machine implementations ignore the flag if it is set.

Il flag è ancora usato con l'opcode invokespecial? Da quello che posso dire, sembra non avere alcuno scopo e non riesco a trovare una risorsa per suggerire che abbia mai fatto.

Grazie.

Author: Jivings, 2012-01-21

1 answers

ACC_SUPER è stato introdotto per correggere un problema con l'invocazione di super metodi. Il flag ACC_SUPER contrassegna una classe come compilata per la semantica modificata dell'istruzione opcode 183. Il suo scopo è simile a quello del numero di versione del file di classe in quanto consente alla JVM di rilevare se una classe è stata compilata per la semantica più vecchia o più recente di quell'istruzione. Java 1.0.2 non ha impostato e ignorato ACC_SUPER mentre Java 1.1 e versioni successive imposta sempre ACC_SUPER.

Prima di Java 1.1 il istruzione codice byte con opcode 183 che ora è chiamato invokespecial è stato chiamato invokenonvirtual e aveva una specifica parzialmente diversa. Veniva utilizzato ogni volta che i metodi di istanza dovevano essere richiamati senza una ricerca di metodo virtuale. Questo è stato il caso di metodi privati, inizializzatori di istanze (costruttori) e di implementare invocazioni di metodi su super. Ma quest'ultimo caso ha causato problemi con le librerie di classi in evoluzione.

Un riferimento al metodo nel codice byte (CONSTANT_Methodref_info) non solo definisce il nome e l'argomento e restituisce i tipi di un metodo ma anche la classe a cui appartiene. Opcode 183 ottiene tale parametro di riferimento al metodo e ha lo scopo di richiamare direttamente il metodo di riferimento dalla classe specificata senza ulteriori ricerche. Nel caso di invocazioni su super era responsabilità dei compilatori risolvere la super classe più vicina che implementa questo metodo e generare un riferimento ad esso nel codice byte.

Da Java 1.1 è stato modificato per ignorare essenzialmente la classe a cui si fa riferimento in CONSTANT_Methodref_info e fare invece la ricerca del metodo super più vicino con il nome e la firma del metodo specificato nella JVM. Questo di solito viene fatto ora quando la classe viene caricata o subito prima che l'istruzione venga eseguita o JIT compilata la prima volta.

Ecco un esempio per cui questo cambiamento era necessario. In Java 1.0.2 il Contenitore e il componente delle classi AWT sono stati definiti in questo modo:

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

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

In Java 1.1 la classe Conatiner è stata modificata per avere la propria implementazione di paint:

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

Ora, quando hai avuto una sottoclasse diretta o indiretta di Container che ha effettuato una chiamata su super.paint(g) e l'ha compilata per 1.0.2, ha generato un'istruzione invokenonvirtual per Component.paint poiché questo era il primo genitore che aveva questo metodo. Ma se hai usato questa classe compilata su una JVM che aveva anche Container.paint avrebbe comunque chiamato Component.paint che non è quello che ti aspetteresti.

D'altra parte, quando hai compilato la classe per 1.1 e l'hai eseguita su una JVM 1.0.2, lancerebbe un AbstractMethodError o più probabilmente per le VM di quell'epoca semplicemente in crash. Per evitare il crash è stato necessario scrivere ((Component)super).paint(g) e compilarlo con un compilatore 1.1 per ottenere il comportamento desiderato in entrambe le VM. Questo imposterebbe ACC_SUPER ma genererebbe comunque l'istruzione per chiamare Component.paint. Una VM 1.0.2 ignorerebbe ACC_SUPER e andrebbe direttamente a invocare Component.paint che va bene mentre una VM 1.1 troverebbe ACC_SUPER set e quindi eseguirebbe la ricerca stessa che lo farebbe invocare Container.paint anche se il riferimento al metodo del codice byte era Component.paint.

Puoi trovare maggiori informazioni su questo in questo vecchio post sul ikvm.net weblog.

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