2012-01-21 7 views
26

L'istruzione invokespecial JVM viene utilizzata per chiamare i metodi di inizializzazione (<init>) durante la creazione di 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 nel file class.Qual è lo scopo del flag di accesso ACC_SUPER sui file di classe Java?

dalle specifiche Sun JVM:

Avanti, viene selezionato il metodo risolto per l'invocazione a meno che tutte le seguenti condizioni:

  • La bandiera ACC_SUPER (vedi Tabella 4.1, "Classe accesso e modificatori di proprietà ") è impostato per la classe corrente.

- Source (definizione invokespecial opcode)

L'impostazione del flag ACC_SUPER indica quale dei due semantiche alternative per la sua istruzione invokespecial la macchina virtuale Java è esprimere; esiste la flag ACC_SUPER per la compatibilità con le versioni precedenti per il codice compilato dai compilatori precedenti di Sun per 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 i flag ClassFile con ACC_SUPER unset. Le vecchie implementazioni Java di macchine virtuali di Sun ignorano il flag se è impostato.

- Source (formato ClassFile)

La definizione precisa che la bandiera è 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.

La flag è ancora utilizzata con l'opcode invokespecial? Da quello che posso dire, sembra non avere alcuno scopo e non riesco a trovare una risorsa che suggerisca di averlo mai fatto.

Grazie.

+1

Questa non è una risposta, ma per capire perché viene usata la bandiera penso che tu abbia anche bisogno di capire cosa significasse la bandiera sotto la specifica della JVM versione 1 - vedi http://java.sun.com/docs/books /jvms/first_edition/html/Instructions2.doc7.html#invokespecial –

+0

Fianco a fianco, non vedo alcuna differenza. – Jivings

risposta

33

ACC_SUPER è stato introdotto per correggere un problema con l'invocazione di metodi super. Il flag ACC_SUPER contrassegna una classe come compilata per la semantica modificata dell'istruzione 183 opcode. Lo 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 tale istruzione. Java 1.0.2 non ha impostato e ignorato ACC_SUPER mentre Java 1.1 e versioni successive impostano sempre ACC_SUPER.

Prima di Java 1.1 l'istruzione di codice byte con opcode 183 che ora viene chiamata invokespecial era denominata invokenonvirtual e aveva una specifica parzialmente diversa. È stato utilizzato ogni volta che i metodi di istanza dovevano essere richiamati senza una ricerca di metodo virtuale. Questo è il caso dei metodi privati, degli inizializzatori di istanze (costruttori) e dell'attuazione di invocazioni di metodi su super. Ma quest'ultimo caso ha causato problemi con l'evoluzione delle librerie di classi.

Un riferimento al metodo in byte code (CONSTANT_Methodref_info) non definisce solo il nome, l'argomento e i tipi restituiti di un metodo, ma anche la classe a cui appartiene.Opcode 183 ottiene tale parametro di riferimento del metodo e intendeva richiamare direttamente il metodo di riferimento dalla classe specificata senza ulteriori ricerche. Nel caso di invocazioni su super era compito 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 di riferimento in CONSTANT_Methodref_info e per eseguire la ricerca del metodo super più vicino con il nome del metodo e la firma specificati nella JVM. Questo di solito viene fatto ora quando la classe viene caricata o appena prima che l'istruzione venga eseguita o JIT compilato la prima volta.

Ecco un esempio del perché questo cambiamento era necessario. In Java 1.0.2 Container classi AWT e componenti 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 classe conatiner è stato cambiato per avere un proprio implementazione di paint:

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

Ora, quando si doveva una sottoclasse diretta o indiretta di Container che ha effettuato una chiamata su super.paint(g) e la 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 si compilava la classe per 1.1 e la si eseguiva su una JVM 1.0.2 si generava un AbstractMethodError o più probabilmente per le VM di quell'era semplicemente in crash. Per evitare il crash, è necessario scrivere ((Component)super).paint(g) e compilarlo con un compilatore 1.1 per ottenere il comportamento desiderato in entrambe le VM. Ciò imposterà ACC_SUPER ma genererà comunque l'istruzione per chiamare Component.paint. Una VM 1.0 ignorerebbe ACC_SUPER e andrà direttamente a invocare Component.paint che va bene mentre una macchina virtuale 1.1 troverà ACC_SUPER impostato e quindi eseguirà la ricerca stessa, il che farebbe invocare Container.paint anche se il riferimento al metodo del codice byte era Component.paint.

Potete trovare maggiori informazioni a riguardo in this old post on the ikvm.net weblog.

+0

Post interessante. Quindi tutti i compilatori dopo l'1.1 impostano il flag, facendo sì che la JVM controlli la gerarchia 'super' quando risolve i metodi? È fatto invece di usare 'objectref' spuntato dallo stack? – Jivings

+0

Questa è un'ottima risposta. Ora capisco come 'invokespecial' funzioni con la bandiera. Ma perché non hanno solo deprecato il vecchio metodo? – Jivings

+13

A partire dall'aggiornamento della sicurezza di Java 7u13, l'assenza del flag ACC_SUPER non viene più rispettata dalla JVM. Questo a causa del problema descritto qui: http://weblog.ikvm.net/PermaLink.aspx?guid=23cced47-ccdb-460d-acc9-ce16154ab6a5 –