-
Die Erfindung bezieht sich im Allgemeinen auf
Verfahren und Computersysteme zum Übersetzen (oder Übersetzen
und Ausführen)
objektorientierter Computerprogramme. Insbesondere, obwohl nicht
ausschließlich,
beschäftigt
sich die Erfindung mit objektorientierten Programmen, bei welchen
der Code in Form von Klassendateien bereitgestellt wird.
-
Ein bekanntes Beispiel für eine objektorientierte
Programmiersprache ist "Java" (eine Marke von Sun
Microsystems Inc.). Eine "Java-Implementierung" ist ein Software-System,
welches gestattet, daß eine
Software-Anwendung abläuft,
die aus einer oder mehreren Klassendateien besteht. Diese Klassendateien
müssen
irgendeiner Version der Standard-Spezifikation für die Virtuelle Java-Maschine [Java
Virtual Machine Specification] entsprechen, wie sie von Sun Microsystems
Inc. veröffentlicht
wurde. Eine Klassendatei definiert die Daten und den Programmcode,
der für
eine bestimmte Klasse erforderlich ist.
-
Obwohl eine gewisse Interaktion besteht, kann
die Java-Implementierung
konzeptionell in zwei Bestandteile eingeteilt werden:
- • Die
Virtuelle Java-Maschine (JVM – Java
Virtual Machine). Diese liest die Klassendateien und führt die
in ihnen enthaltenen Anweisungen auf eine Weise aus, welche irgendeiner
Version der Spezifikation für
die Virtuelle Java-Maschine von Sun entspricht. Einige der Anweisungen,
die in einer Klasse enthalten sind, können sich auf die Daten oder
den Programmcode anderer Klassen beziehen; die JVM managt auch solche
Beziehungen zwischen den Klassen.
- • Die
Java-Klassenbibliothek [Java Class Library]. Dies ist ein Satz von
vorher definierten Klassen mit Daten und Programmcode, welche sich
auf eine Weise verhalten, die der Spezifikation für die Java-Klassenbibliothek
[Java Class Library Specification] von Sun entspricht. Diese Bibliotheksklassen
könnten
auf irgendeine andere Weise als als reale Klassendateien implementiert
werden, beispielsweise unter Verwendung der C- oder Assembler-Programmiersprachen,
in welchem Fall die JVM gewährleisten
muß, daß Bezüge auf ihre Daten
und ihren Programmcode auf dieselbe Weise funktionieren wie Bezüge auf Klassen,
die von realen Klassendateien stammten.
-
Der Programmcode in einer Klassendatei liegt
in einem Anweisungsformat vor, das als Java-Byte-Code (JBC), oder
einfach als Byte-Code, bekannt ist. Jedes Verfahren in der Klasse
hat seine eigene Abfolge des Byte-Codes. Der Byte-Code für ein Verfahren
besteht aus einer Abfolge von JBC-Anweisungen.
-
Es gibt zwei Modelle, welche die
JVMs zur Ausführung
des Byte-Codes verwenden:
- • Ein Interpreter. Bei diesem
Modell enthält
die JVM einen Interpreter, welcher Teil des Programmcodes ist, welcher
den Byte-Code ausführt, indem
er jede JBC-Anweisung der Reihe nach anschaut, sie decodiert und
dann die Aktionen ausführt,
die sie verlangt. Während
diese Vorgehensweise am einfachsten zu implementieren ist, besteht
ihr Nachteil darin, daß sie
langsamer als die Alternative ist, da viele Schritte des Interpreter-Programms
erforderlich sind, um eine einzelne JBC-Anweisung zu interpretieren.
- • Ein
Compiler. Bei diesem Modell wandelt die JVM die JBC-Anweisungen des Byte-Codes
in Maschinencode-Anweisungen um, die von der CPU verstanden werden,
auf der sie ablaufen (nativer Maschinencode), bevor irgendeine Ausführung beginnt.
Dann wird zur Ausführung
des Programmcodes für
ein Verfahren statt dessen der kompilierte Maschinencode ausgeführt. Es
gibt einen zeitlichen Mehraufwand für die anfängliche Kompilierung von JBC-Anweisungen
in Maschinencode-Anweisungen,
obwohl dies während
der Vorbereitung der Anwendung – und
nicht, wenn die Anwendung begonnen wird – erfolgen kann. Wenn die Kompilierung
ausgeführt
worden ist, läuft
der Programmcode des Verfahrens viel schneller, bei einer Geschwindigkeit,
die mit anderen traditionell kompilierten Sprachen, wie beispielsweise
C, vergleichbar ist. Ein Sonderfall des Compiler-Modells ist ein
ausführungssynchroner Compiler
(JIT – just-in-time
compiler), bei welchem der Byte-Code für eine Klasse unmittelbar vor
seiner ersten Verwendung kompiliert wird.
-
Einige JVMs verwenden eine Kombination beider
Modelle, wobei nur ein Programmcode, der viele Male ausgeführt wird,
kompiliert und der Rest interpretiert wird.
-
Verbinden ist der Prozess, durch
welchen ein Bezug von einer Klasse C1 auf eine andere Klasse C2
(oder Daten oder ein Verfahren in C2) aufgelöst wird. Wenn C2 nicht bereits
geladen ist, wird sie geladen und bei Verwendung des Compiler-Modells kompiliert
und selbst verbunden. Dann wird der Bezug in C1 auf C2 (oder irgendein
Datenelement oder ein Verfahren in C2) derart modifiziert, daß es nun
einen direkten Zeiger auf das gibt, worauf auch immer in C2 Bezug
genommen wird.
-
Die Spezifikation für die Virtuelle
Java-Maschine von Sun sieht eine Reihe von Verbindungsmodellen vor:
- • Statische
Verbindung: Das Laden und Verbinden aller Klassen der Anwendung
wird ausgeführt, wenn
die Anwendung vorbereitet wird. Dieses Modell wird üblicherweise
verwendet, wenn eine Anwendung permanent in eine Einrichtung eingebettet
ist.
- • Dynamische
Ladezeit-Verbindung: Klasse C2 wird zum ersten Mal geladen, wenn
eine andere Klasse geladen wird, welche auf C2 (oder irgendein Datenelement
oder Verfahren in C2) Bezug nimmt.
- • Dynamische
späte Verbindung:
Klasse C2 wird zum ersten Mal geladen, wenn eine JBC-Anweisung (oder
ihr kompiliertes Äquivalent),
welche auf C2 (oder irgendein Datenelement oder Verfahren in C2)
Bezug nimmt, ausgeführt
wird.
-
Wenn im Betrieb ein bestimmtes Verfahren einer
bestimmten Klasse aufgerufen wird, kann die bestimmte erforderliche
Klasse bereits in der JVM vorhanden sein oder auch nicht. Wenn die
erforderliche Klasse nicht vorhanden ist, dann muß die Klassendatei
für diese
Klasse zuerst von außerhalb
der JVM geladen werden (beispielsweise von einer Platte oder aus
einem Netzwerk), verknüpft
und in die JVM initialisiert werden. Dann kann das erforderliche Verfahren
gefunden werden, indem die Liste der Verfahren für die Klasse durchgesehen wird.
Wenn das erforderliche Verfahren gefunden worden ist, wird der Java-Byte-Code dieses
Verfahrens ausgeführt,
bis ein Return vorgefunden wird, woraufhin das Verfahren beendet
ist und die Steuerung an den Aufrufenden des Verfahrens zurückgegeben
wird. Ein Verfahrensaufruf kann auch beendet werden, indem eine Ausnahme
ausgeworfen wird, welche nicht in dem Verfahren erfaßt ist.
-
1 stellt
eine typische, dem Stand der Technik entsprechende Implementierung
dar, bei welcher die JVM sich eines JIT-Compilers bedient. Der JIT-Compiler 120 nimmt
den Klassen-Byte-Code 110,
unmittelbar bevor er verwendet werden soll, und übersetzt ihn in den nativen
Code 130, der fertig zur Ausführung auf einem speziellen
Prozessor ist. Die Reste der Klasse 140 (oder möglicherweise
die Gesamtheit der Klasse) bleibt in dem Speicher verfügbar, für den Fall,
daß der
native Code 130 während des
Ablaufens darauf Bezug nehmen müßte.
-
3 stellt
eine typische, dem Stand der Technik entsprechende JVM-Implementierung
in einer Mehrprozessorumgebung dar. Ein Server 210 hält einen
Klassenspeicher 220 zum Halten des Byte-Codes der verschiedenen
Klassen bereit, die von den Client-Prozessoren 230, 240 benötigt werden
können.
Bei diesem Beispiel repräsentieren
die Prozessoren 230, 240 zwei verschiedene Typen – und zwar
Client-Typ 1 beziehungsweise Client-Typ 2. Der
Server stellt die Klassendateien je nach Bedarf über ein Kommunikationsnetzwerk
bereit, das den Clients 230, 240 im Allgemeinen
bei 250 angezeigt wird. Jeder der Clients 230, 240 hält seinen
eigenen JIT bereit, beziehungsweise 231, 241,
der ihn in die Lage versetzt, die Klassendatei in seine eigene Version
des nativen Codes zu kompilieren und diese in seinem eigenen nativen
Code-Speicher 232, 242 zu speichern. Der native
Code kann dann auf den einzelnen Clients ablaufen.
-
Ein Problem bei der obigen Anordnung
ist, daß sie
eine große
Ausführungsumgebung
auf jedem der Clients benötigt.
JIT-Compiler sind üblicherweise groß, und es
kann schwierig – wenn
nicht unmöglich – sein,
den erforderlichen Platz auf dem Client bereitzustellen, insbesondere
bei einem eingebetteten System, bei welchem der Client beispielsweise
aus einem Mobiltelefon besteht. Eine alternative (nicht dargestellte)
Methode wäre,
die JIT-Kompilierung auf dem Server auszuführen und die Clients nach Bedarf mit
der nativen Version jeder Klasse zu beliefern. Während dies weniger Platz auf
jedem der Clients erfordert, hat es andere Nachteile, und zwar eine
substantielle zusätzliche
Komplexität
beim Server. Da jeder JIT prozessorabhängig ist, müßte der Server bei einer derartigen
Anordnung für
jeden Prozessortyp, der möglicherweise
bedient werden müßte, einen
anderen JIT bereithalten. Während
dies in einem Festnetz möglich
wäre, ist
es beispielsweise in einem Mobiltelefonsystem unrealistisch, in
welchem eine große
Anzahl verschiedener Telefone verschiedener Typen und Marken ständig mit
dem Server verbunden und von diesem getrennt wird. Die Besitzer
des Servers hätten
die überaus
schwierige Aufgabe, JITs für alle
bekannten Typen von Mobiltelefonen bereit zu halten und diese JITs
bereit zu halten und zu aktualisieren, wenn neue Telefone auf den
Markt kommen. Aus diesem Grund ist es vorzuziehen, daß der Server einfach
einen allgemeinen Klassenspeicher 220 bereithält, und
daß die
einzelnen Clients prozessorabhängige Übersetzungen
ausführen.
Wie oben erwähnt
wurde, hat es sich jedoch wegen der begrenzten Speicher, die in
eingebetteten Systemen verfügbar
sind, als schwierig erwiesen, dies in der Praxis zu implementieren.
-
Der nächstliegende Stand der Technik
ist die EP-A-0913769 (SUN MICROSYSTEMS, Inc.), die das Herunterladen
von Klassendateien von einem Server auf eine Client-Plattform und
das Ausführen des
Byte-Codes entweder direkt durch einen Interpreter oder unter Verwendung
eines JIT offenbart. Die relevanten Merkmale sind in den Oberbegriffen der
unabhängigen
Ansprüche
dargelegt. Ein weiteres dem Stand der Technik entsprechendes Dokument ist
Bertil Folliot et al: "Virtual
Virtual Machines",
CABERNET RADICALS WORKSHOP, 17. September 1997, XP0002135231 Kreta.
-
Es ist ein Ziel der vorliegenden
Erfindung, zumindest einige der oben erwähnten Probleme mit dem Stand
der Technik zu verringern.
-
Gemäß einem ersten Aspekt der Erfindung wird
ein Verfahren der Übersetzung
eines objektorientierten Computerprogramms bereitgestellt, wie es im
kennzeichnenden Teil von Anspruch 1 angegeben ist. Gemäß einem
zweiten Aspekt der Erfindung wird ein verteiltes Computersystem
bereitgestellt, wie es im kennzeichnenden Teil von Anspruch 19 angegeben
ist.
-
Es wird selbstverständlich davon
ausgegangen, daß in
der Beschreibung und in den Ansprüchen das Wort "Code" Daten einschließt, wobei
diese Daten Teil des Programms selbst sind.
-
Dementsprechend, aber ohne Beschränkung, schließt der Ausdruck "Code" solche Dinge wie Konstanten,
Variablennamen und -typen, Flags, Zeiger, Objektnamen und so weiter
ein.
-
Bei einem bevorzugten Ausführungsbeispiel ist
das objektorientierte Computerprogramm in Java (Marke von Sun Microsystems
Inc.) geschrieben und ist für
die Implementierung auf einer Virtuellen Java-Maschine (JVM) vorgesehen.
-
Unabhängig davon jedoch, ob Java
verwendet wird oder nicht, stellt der Zweistufen-Übersetzungsprozeß der vorliegenden
Erfindung eine erste Stufe bereit, welche vollkommen plattformunabhängig ist,
und eine zweite Stufe, welche plattformabhängig ist. Der Virtueller-Prozeß-Code (VP-Code),
welcher aus der ersten Übersetzung
resultiert, ist maschinenunabhängig
und ist folglich vollständig
portabel. Die Arbeit, die zum Schreiben eines neuen Übersetzers
zur Übersetzung
des VP-Codes auf
eine neue Plattform erforderlich ist, ist weitaus geringer, als
diejenige, welche erforderlich wäre,
um den Byte-Code-JIT auf die neue Plattform zu portieren. Wenn Java
verwendet wird, können
neue Anwendungen von Java-erfahrenen Programmierern, welche keine,
wie auch immer geartete Kenntnisse über den VP-Code haben müssen, direkt
in Java geschrieben werden. Ein neuer nativer Übersetzer zum Übersetzen
des VP-Codes in nativen Code wird nicht für jedes neue Anwendungsprogramm
benötigt,
sondern nur für
jede neue Plattform, auf welcher das Programm ausgeführt werden
soll. Dies führt
zu signifikanten Zeiteinsparungen für die Anwendungsprogrammierer.
Es ist vorgesehen, daß die
nativen Übersetzer
für jeden üblichen
Prozessortyp problemlos verfügbar
sind, so müssen
die Anwendungslieferanten nur die relevanten Übersetzer für die Prozessoren kaufen, auf
welchen die Ausführung
ihrer Anwendungen vorgesehen ist; alternativ könnten die nativen Übersetzer
als Standard mit den Endnutzer-Maschinen selbst geliefert werden.
-
Bei einem Ausführungsbeispiel ist die vorliegende
Erfindung besonders hilfreich, wenn eine übliche Anwendung auf mehreren
vernetzten Prozessoren ausgeführt
werden soll. Die Übersetzung
von Byte-Code in VP-Code kann auf einem zentralen Server ausgeführt werden,
welcher auch die Verifizierung der Klassendateien des Virtuellen
Prozessors bereitstellen kann. Wenn der VP-Code verifiziert worden
ist, kann er an die einzelnen Client-Prozessoren – entweder
durch eine Netzwerksendung oder alternativ auf Anforderung – verteilt
werden. Jeder Client-Prozessor hält
seinen eigenen nativen Übersetzer
bereit, den er verwendet, um den empfangenen VP-Code in seine eigene
bestimmte Variante des nativen Codes zu übersetzen. Die vernetzten Client-Einrichtungen
können
heterogen sein, das heißt, sie
können
verschiedene Prozessortypen verwenden. Das ist bei dem bevorzugten
Ausführungsbeispiel
der Erfindung kein Problem, da jede Client-Einrichtung in den entsprechenden
nativen Code für
ihren eigenen Prozessortyp übersetzt.
-
Bei einer derartigen Anordnung muß der Server
nur den VP-Code
bereithalten. Er muß nicht
wissen, oder sich darum kümmern,
welche Prozessoren oder Prozessortypen auf den vernetzten Client-Einrichtungen
verwendet werden.
-
Es wird erwartet, daß die vorliegende
Erfindung auf dem Gebiet der drahtlosen Kommunikation (drahtlose
Client-Netzwerke), und speziell, obwohl nicht ausschließlich, auf
dem Gebiet der mobilen zellularen Telefonnetze, besondere Anwendung
findet. Bei einem bevorzugten Ausführungsbeispiel der Erfindung
wird die Anwendungs-Software auf jedem Mobiltelefon automatisch
aktualisiert – auf
eine Weise, die vollkommen benutzertransparent ist – indem die
notwendigen Aktualisierungen im VP-Code von dem zentralen Server
heruntergeladen werden. Das Herunterladen könnte in regelmäßigen Intervallen, oder
auf Anforderung, oder wenn erforderlich erfolgen – beispielsweise,
wenn der Benutzer zum ersten Mal versucht, irgendeine spezielle
Funktionalität
zu verwenden, für
deren Bereitstellung das Telefon noch nicht programmiert worden
ist. Eine ähnliche
Methode kann bei anderen vernetzten Einrichtungen verwendet werden,
wie beispielsweise (ohne Beschränkung)
bei Handheld-Computern, Spielkonsolen, Kameras oder praktisch bei
jedem beliebigen anderen Typ von vernetzter oder netzwerkfähiger Einrichtung. Bei
einem Ausführungsbeispiel
kann das Netzwerk aus einem drahtlosen Netzwerk bestehen oder dieses
einbeziehen, wohingegen es bei anderen Ausführungsbeispielen ein privates
oder öffentliches Festnetz
oder das Internet einbeziehen kann. Wenn die Client-Einrichtungen
nicht die Fähigkeit
zur drahtlosen Kommunikation aufweisen, können Vorkehrungen für sie getroffen
werden, damit sie je nach Bedarf mit dem Internet gekoppelt werden
können
(beispielsweise über
ein Standardmodem oder eine ISDN-Verbindung). Auf eine solche weise
könnte
die Erfindung auf einen großen
Bereich eingebetteter Einrichtungen anwendbar sein, einschließlich beispielsweise
auf Kameras, Fernseher, Waschmaschinen, Kraftfahrzeuge oder tatsächlich praktisch
auf jeden beliebigen anderen Typ einer computergesteuerten Einrichtung,
die man sich vorstellen kann.
-
Die Erfindung erstreckt sich auf
ein Computersystem zur Ausführung
jedes der beschriebenen Verfahren und auf ein entsprechendes Computerprogramm,
das auf einem Datenträger
realisiert ist oder nicht. Die Erfindung erstreckt sich ferner auf
einen Datenstrom, der für
ein Computerprogramm zur Ausführung
des beschriebenen Verfahrens repräsentativ ist.
-
Die Erfindung kann auf verschiedene
Arten und Weisen in der Praxis ausgeführt werden, und es wird nun
ein spezielles Ausführungsbeispiel – um ein Beispiel
zu geben – unter
Bezugnahme auf die zugehörigen
Zeichnungen beschrieben, in welchen:
-
1 die
Operation eines herkömmlichen JIT-Compilers
in einer JVM darstellt;
-
2 den
Zweistufen-Übersetzungsprozeß des bevorzugten
Ausführungsbeispiels
der vorliegenden Erfindung darstellt;
-
3 ein
typisches, dem Stand der Technik entsprechendes Client-Server-System
darstellt;
-
4 die
Operation des bevorzugten Ausführungsbeispiels
der Erfindung in einem Client-Server-System darstellt;
-
5 die
Operation der vorliegenden Erfindung in einem drahtlosen Netzwerk
darstellt; und
-
6 bestimmte
Aspekte der Übersetzung von
Byte-Code in Zwischen-VP-Code gemäß dem bevorzugten Ausführungsbeispiel
der Erfindung darstellt.
-
1,
welche oben beschrieben wurde, zeigt die Art und Weise, auf welche
ein JIT-Compiler in einer JVM von prozessorunabhängigem Byte-Code in prozessorabhängigen nativen
Code zur Ausführung
auf einem bestimmten Prozessor übersetzt.
Bei der vorliegenden Erfindung findet die Umwandlung von Byte-Code
in nativen Code in zwei separaten Stufen statt:
- 1.
Umwandlung aus der Klassendatei in eine prozessorunabhängige Zwischenform.
Diese wird als Virtueller-Prozessor- oder als VP-Code bezeichnet. Der Wandler
selbst ist bei der bevorzugten Implementierung als der "Jcode-Übersetzer" bekannt.
- 2. Umwandlung aus der Zwischen-VP-Form in den nativen Maschi
nencode. Hier ist der Wandler als der "native Übersetzer" bekannt.
-
2 stellt
die Übersetzung
aus Klassen-Byte-Code in nativen Code detaillierter dar. Der Klassen-Byte-Code 210 wird
von einem Klassenverifizierer 211 zuerst auf Validität geprüft. Dieser überprüft nicht
nur die einzelnen Bytes selbst, sondern er prüft ebenfalls auf gültige externe
und interne Bezüge.
Der Klassenverifizierer lädt
je nach Bedarf zusätzliche
Klassen, um die externen Bezüge
zu prüfen.
-
Wenn der Code geprüft worden
ist, wird er an den Jcode-Übersetzer 212 weitergeleitet,
welcher ihn in VP-Code 213 umwandelt, wie nachfolgend detaillierter
beschrieben wird. Der VP-Code 213 wird dann von dem nativen Übersetzer 214 in
den nativen Code 230 umgewandelt.
-
Es ist wichtig zu erkennen, daß der Klassenverifizierer 211,
der Jcode-Übersetzer 212 und
der VP-Code 213 alle prozes sorunabhängig sind. Nur der native Übersetzer 214 und
natürlich
der endgültige
native Code 230 sind prozessorspezifisch.
-
Die Verwendung des bevorzugten Ausführungsbeispiels
in einer heterogenen Mehrprozessorumgebung wird in 4 schematisch dargestellt. Dies sollte
mit der entsprechenden, dem Stand der Technik entsprechenden Methode
verglichen werden, die in 3 gezeigt
wird.
-
In 4 bedient
der Server 410 zwei Clients 430, 440 (die
unterschiedliche Prozessoren haben) über ein Kommunikationsnetzwerk 450.
Die gesamte prozessorunabhängige
Berechnung wird auf dem Server ausgeführt; im Einzelnen hält der Server
einen Klassenspeicher 420, einen Klassenverifizierer 421,
einen Jcode-Übersetzer 422 und
einen VP-Speicher 423 bereit. Der VP (prozessorunabhängige)-Code
kann dann, wenn erforderlich, über
das Netzwerk 450 an die einzelnen Clients übermittelt werden.
Der VP-Code wird dann von den einzelnen Client-Übersetzern 424, 425 übersetzt
und der entsprechende native Code für den speziellen Prozessor
in den nativen Codespeichern 432, 442 gespeichert.
-
Die Verwendung von VP auf dem Server,
wie in 4 gezeigt, gestattet,
daß die
Verifizierung der Klassendateien und die erste Stufe der Kompilierung (die
Umwandlung in VP-Code) nur einmal von dem Server ausgeführt werden.
Dann muß nur
die native Übersetzung
(welche je nach Prozessortyp verschieden ist) vor der Ausführung von
dem Client ausgeführt
werden. Eine derartige Anordnung macht es einfach, aktualisierte
Klassen auf dem Server bereitzustellen, ohne daß der Server irgend etwas über die Details
der einzelnen Clients wissen muß,
die von diesen Klassen Gebrauch machen wollen. Eine aktualisierte
Klasse muß nur
einmal in dem Klassen-Byte-Code geändert werden und dann nur einmal
in VP übersetzt
werden. Der VP wird an die Client-Einrichtungen je nach Bedarf übermittelt,
und die endgültige Übersetzung
in nativen Code kann auf einem Client auf eine Weise ausgeführt werden,
welche für
den Endbenutzer vollkommen transparent ist. Außerdem ist keine Änderung
am Server oder an dem VP-Code für
den Fall erforderlich, daß ein
neuer Client-Typ auf den Markt kommt, welcher einen anderen nativen Code
benötigt.
Ein Client-Hersteller stattet den Client einfach mit einem entsprechenden
nativen Übersetzer
aus, und die Einrichtung sollte ohne irgendeinen manuellen Eingriff
am Server funktionieren.
-
Eine spezielle Implementierung, dargestellt in 5, ist die eines mobilen
Telefonnetzes. Die einzelnen Mobiltelefone 530, 550,
die das Netzwerk nutzen, enthalten jeweils einen entsprechenden
nativen Übersetzer 524, 525 und
einen nativen Code-Speicher 532, 542. Wenn es
erforderlich ist, die Funktionalität der Telefone zu erweitern,
wird ein aktualisierter VP-Code von einem VP-Speicher 523 auf
einem zentralen Server 520 bereitgestellt. Der aktualisierte VP-Code
wird über
ein landgestütztes
Kommunikationsnetzwerk 511 an einen drahtlosen Sender 512 gesendet.
Der Code wird dann paketiert und über eine drahtlose Verbindung 513 an
die einzelnen Telefone gesendet. Beim Empfang wird der VP-Code automatisch
in nativen Code übersetzt
und in dem Speicher für
nativen Code gespeichert. Der gesamte Prozeß kann für den Telefonbenutzer transparent
sein; oder alternativ kann der aktualisierte Code beim Empfang einer
speziellen Anforderung vom Telefonbenutzer über die drahtlose Verbindung 513 gesendet
werden.
-
Es erfolgt die Rückkehr zu 2; es werden weitere Details über die
Zweistufen-Übersetzung
von Byte-Code 210 in nativen Code 230 erläutert. Wie vorher
beschrieben, überprüft der Klassenverifizierer 211 den
Klassen-Byte-Code auf Validität.
Der Klassenverifizierer kann bei einigen Ausführungsbeispielen in dem Jcode-Übersetzer
enthalten sein, in welchem Fall der Klassen-Byte-Code 210 direkt
an den Jcode-Übersetzer 212 weitergeleitet
wird, wie durch den Pfeil 240 gezeigt wird.
-
Die JVM und die Byte-Code-Anweisungen, die
sie implementiert, sind Stapel-basiert, was bedeutet, daß Operanden
(Zahlen, Zeiger auf Objekte) auf einem Stapel gehalten werden, auf
welchem das letzte Element, das darauf geschoben wird, das erste ist,
das heruntergenommen wird. Eine Byte-Code-Anweisung entfernt üblicherweise
einen oder mehrere Operanden von dem Stapel, führt irgendeine Aktion aus und
schiebt den Ergebnisoperanden (sofern vorhanden) zurück auf den
Stapel. Anderer seits ist der VP insofern Register-basiert, daß er über einen
Satz von Registern verfügt,
die direkt von den VP-Anweisungen adressiert werden. Eine Anweisung
nimmt ihren/ihre Operanden üblicherweise
von dem Register/den Registern, die in der Anweisung festgelegt
werden, führt
irgendeine Aktion aus und legt den Ergebnisoperanden (sofern vorhanden)
in ein weiteres Register ab, das in der Anweisung festgelegt wird.
Diese Registerbasierte Architektur ist den meisten realen Prozessoren ähnlicher,
mit der Ausnahme, daß der
VP eine sehr große
Anzahl von Registern aufweist, die groß genug sind, daß irgendein
beliebiges System, das in. VP umwandelt, sich keine Gedanken darum
machen muß,
wie viele es sind.
-
VP-Anweisungen basieren auf Ausdrücken. Eine
einzelne Anweisung weist üblicherweise
einen oder zwei Operanden auf, und jeder Operand kann eine Konstante,
ein Register oder ein Ausdruck sein. Ein Ausdruck weist dann einen
oder zwei Operanden auf, von welchen jeder eine Konstante, ein Register oder
ein Ausdruck sein kann. Auf diese Weise kann eine willkürlich komplexe
Anweisung aufgebaut werden.
-
Es folgt nun eine detailliertere
Beschreibung dazu, wie Teile einer Klassendatei umgewandelt werden.
Die Beschreibung verwendet den Begriff "Fixup" bzw. "Sprunganweisungsmarkierung"; dies ist ein kleines
Datenelement, das an eine bestimmte Stelle in dem Ausgabecode oder
den Ausgabedaten des Compilers angefügt wird, welches die JVM anweist, daß der Code
oder die Daten an dieser Stelle auf irgendeine Weise modifiziert
werden müssen,
bevor sie verwendet werden können.
Sprunganweisungsmarkierungen werden verwendet, um eine native Anweisung
oder ein Datenelement derart zu verändern, daß der native Code einen direkten
Bezug zu einer anderen Klasse oder zu einem Feld oder einem Verfahren
darin erlangen kann.
-
Eine Java-Klassendatei besteht aus
den folgenden Bestandteilen:
- • Ein Konstanten-Pool,
welcher die Konstantennummern und -Namen in anderen Bestandteilen der
Klassendatei enthält,
anstelle eines Namens gibt es einen Bezug zu einem Namen, der hier
gespeichert ist.
- • Informationen,
wie beispielsweise der Name dieser Klasse, die Überklasse und jegliche direkten Überschnittstellen.
- • Eine
Liste von Feldern mit Informationen über jedes Feld.
- • Eine
Liste von Verfahren mit Informationen über jedes Verfahren. Diese
Informationen beinhalten seinen Codeabschnitt. So gibt es mehrere
Codeabschnitte, einen für
jedes Verfahren.
-
Die Java-Klassendatei wird wie folgt
in VP-Werkzeuge bzw. -Tools umgewandelt:
- • Ein Datenwerkzeug.
Trotz seines Namens hat dies nichts mit den von der Klasse zu verwendenden
Daten zu tun. Statt dessen enthält
es Informationen über
eine Klasse, welche die Namen, Parameter und Typen aller Konstruktoren,
Felder, Verfahren und andere Einheiten, welche das API einer Klasse
bilden, beinhalten, aber nicht auf diese beschränkt sind. Eine typische Verwendung
für dieses
wäre zur
Reflexion (d.h. die Funktionalität in
java.lang.reflect in einer Java-Bibliothek).
Reflexion ist eine programmatische Schnittstelle, die einem Programmierer
gestattet, die Konstruktoren, Felder, Verfahren und anderen Einheiten,
die zu einer Klassen gehören,
zu spezifizieren und zu manipulieren. Das Datenwerkzeug wird auch
in Situationen, wenn entweder die Klassendatei nicht verfügbar ist
oder wenn die Klassendatei bereits übersetzt worden ist, von den
verifizierenden Jcode-Übersetzern
verwendet. Wenn die Klasse in VP geschrieben ist, gibt es ohnehin
keine Klassendatei.
- • Ein
Klassenwerkzeug. Dieses enthält
einige Organisationsinformationen, die von der JVM verwendet werden
(eingeschlossen die Größe des zuzuweisenden
Objekts, die Größe der statischen Daten
der Klasse – sofern
vorhanden – und
die Überklasse
und Überschnittstellen),
und den Code für
keine, einige oder alle Verfahren.
- • Null
oder mehr Verfahrenswerkzeuge. Verfahren, die nicht im Klassenwerkzeug
erscheinen, haben ihre eigenen individuellen Werkzeuge. Die Entscheidung
darüber,
ob ein Verfahren in seinem eigenen Werkzeug plaziert wird, kann
auf einer Reihe von Faktoren basieren, wie beispielsweise dem Umfang
des Verfahrens.
- • Ein
Sprunganweisungsmarkierungswerkzeug bzw. Fixpup-Tool. Das Sprunganweisungsmarkierungswerkzeug
gibt üblicherweise
einen konstanten Sprunganweisungsmarkierungswert zurück, welcher
verwendet wird, um die Verschiebungen in einem Objekt eines bestimmten
Feldes zu bestimmen. Das Werkzeug wird zur Sprunganweisungsmarkierungszeit
aufgerufen, um die Verschiebung bereitzustellen, und der Binder/Verbinder
setzt diese Verschiebung in den Code ein, der es verwenden will.
Es wird somit verwendet, um sowohl "ein Feld erhalten" als auch "ein Feld setzen" in dem Byte-Code zu implementieren.
Allgemeiner ausgedrückt,
das Sprunganweisungsmarkierungswerkzeug gibt Daten zurück, die
für Sprunganweisungsmarkierungen
verwendet werden. Diese können
nur zur Sprunganweisungsmarkierungszeit, und nicht zur Kompilierzeit,
bestimmt werden. Die Daten können
die Größe einer Klasseninstanz
und die Verschiebung in einer Klasseninstanz eines Feldes enthalten,
sind aber nicht darauf beschränkt.
-
Das Datenwerkzeug kann verworfen
werden, wenn von der Java-Anwendung bekannt ist, daß sie bestimmte
Möglichkeiten
nicht verwendet (weitgehend reflektiert), und das Sprunganweisungsmarkierungswerkzeug
kann verworfen werden, wenn die Java-Anwendung in eine Einrichtung eingebettet werden
soll, die weitere Java-Klassen nicht dynamisch lädt.
-
Der Jcode-Übersetzer verwendet ein VP-Register
für jedes
Element auf dem Stapel.
-
Der VP-Code implementiert die Mechanismen
der Klassendatei für
den Zugriff auf eine andere Klasse, ein Verfahren oder ein Feld
aus dem Byte-Code nicht direkt. In dem Byte-Code sind – ohne auf
diese beschränkt
zu sein – Anweisungen
für das
Aufrufen eines Verfahrens (in dieser oder einer anderen Klasse),
das Erhalten des Inhalts eines Feldes (in dieser oder einer anderen
Klasse), das Schieben eines Wertes auf den Stapel, das Herunternehmen
eines Wertes von dem Stapel und das Einstellen des Inhalts eines
Feldes enthalten. Der Jcode-Übersetzer
wandelt diese in VP-Anweisungen um, welche eine der folgenden Aktivitäten ausführen können (dies
ist keine vollständige
Liste):
- • Aufruf
eines nicht-statischen Verfahrens (d.h. ein Verfahren, an welches
ein Objektzeiger weitergeleitet werden muß) in einer Klasse. VP weist
das Konzept einer Klasse mit Verfahren auf, welches verwendet wird,
um Java-Klassen zu implementieren. Derartige Verfahren können virtuell
aufgerufen werden (das konkrete Verfahren, das aufgerufen wird,
hängt von
der Klasse des Objekts ab, dessen Zeiger weitergeleitet wird), oder
sie können
nicht-virtuell aufgerufen werden (das aufgerufene Verfahren befindet
sich in der Klasse, die in dem Aufruf bestimmt wird).
- • Aufruf
eines Unterprogramms. Dies wird verwendet, um den Byte-Code-Aufruf
eines statischen Verfahren (d.h. eines Verfahrens, an welches kein Objektzeiger
weitergeleitet werden muß)
zu implementieren, und in einigen Fällen ein nichtstatisches Verfahren.
- • Erhalt
des Wertes der Konstanten-Sprunganweisungsmarkierung von dem Sprunganweisungsmarkierungswerkzeug.
-
Der Konstanten-Pool in einer Klassendatei wird
wie folgt umgewandelt:
- • Ein Konstanten-Pool-Eintrag,
der eine Konstantennummer (Ganzzahl oder Gleitkomma) enthält, wird
in die kompilierte Version der JBC-Anweisung aufgenommen, welche
auf die Konstantennummer verweist.
- • Ein
Konstanten-Pool-Eintrag, der Zeichenkettendaten enthält, welche
von einer JBC-Anweisung direkt verwendet werden, wird in die Daten kopiert,
die an den Ausgabecode des Compilers angefügt werden.
- • Andere
Konstanten-Pool-Einträge,
die Zeichenkettendaten enthalten, werden nicht direkt verwendet,
werden aber verwendet, wenn von den nachstehenden Konstanten-Pool-Typen
oder von anderen Teilen der Klassendatei auf sie Bezug genommen
wird.
- • Ein
auf eine Klasse C bezugnehmender Konstanten-Pool-Eintrag veranlaßt, daß eine Sprunganweisungsmarkierung,
die auf die Klasse C (oder den internen Namen der JVM für die Klasse) Bezug
nimmt, an den/die Ausgabecode/-daten des Compilers derart angefügt wird,
daß eine JBC-Anweisung,
die diesen Konstanten-Pool-Eintrag verwendet, um auf C Bezug zu
nehmen, in eine native Code-Abfolge kompiliert wird, welche nach
Anwendung der Sprunganweisungsmarkierung Zugriff auf Code und Daten
der Klasse C erlangt.
- • Ein
auf ein Feld F in einer Klasse C bezugnehmender Konstanten-Pool-Eintrag,
veranlaßt,
daß eine
Sprunganweisungsmarkierung, die auf F in C (oder auf den internen
Namen der JVM für
F in C) Bezug nimmt, an den/die Ausgabecode/-daten des Compilers
derart angefügt
wird, daß eine JBC-Anweisung,
die diesen Konstanten-Pool-Eintrag verwendet, um auf F Bezug zu
nehmen, in eine native Code-Abfolge kompiliert wird, welche nach
Anwendung der Sprunganweisungsmarkierung Zugriff auf Feld F erlangt.
- • Ein
auf ein Verfahren M in einer Klasse C bezugnehmender Konstanten-Pool-Eintrag
veranlaßt, daß eine Sprunganweisungsmarkierung,
die auf M in C (oder auf den internen Namen der JVM für M in C)
Bezug nimmt, an den/die Ausgabecode/-daten des Compilers derart angefügt wird, daß eine JBC-Anweisung, die diesen
Konstanten-Pool-Eintrag verwendet, um auf M Bezug zu nehmen, in
eine native Code-Abfolge kompiliert wird, welche nach Anwendung
der Sprunganweisungsmarkierung Zugriff auf Verfahren M erlangt.
- • Ein
Konstanten-Pool-Eintrag, der einen Namen und Typ eines Feldes oder
eines Verfahrens angibt, wird nicht direkt verwendet, wird aber
verwendet, wenn auf ihn durch andere Typen von Konstanten-Pool-Einträgen oder
anderen Teilen der Klassendatei Bezug genommen wird.
-
Der Codeabschnitt in einer Klassendatei
wird wie folgt umgewandelt:
- • Code für rein numerische
Berechnungen (d.h. wenn es keinen Bezug auf ein externes Verfahren gibt)
wird direkt aus dem Byte-Code in ein entsprechendes Werkzeug in
VP übersetzt.
- • Wie
in 6 gezeigt, wo der
Byte-Code 600 einen Bezug 610 auf ein Feld hat,
das zur Sprunganweisungsmarkierungszeit durch einen Aufruf 611 an
das Sprunganweisungsmarkierungs werkzeug umgewandelt wird. Der Aufruf
an das Sprunganweisungsmarkierungswerkzeug gibt einen Wert zurück, welcher
auf die Speicherstelle des Feldes Bezug nimmt. Somit ist es bis
zu dem Zeitpunkt, wenn die Anweisung ausgeführt wird, eingesetzt bzw. geändert worden,
damit die korrekte Verschiebung enthalten ist.
- • Ein
statisches Verfahren 620 wird in ein entsprechendes VP-Werkzeug umgewandelt,
aber mit angefügtem
Sprunganweisungsmarkierungscode 621.
- • An
ein nicht-statisches Verfahren 630 wird eine Sprunganweisungsmarkierung
für einen
Verfahrensaufruf (d.h. ein Bezug auf den Verfahrensnamen) angefügt. Dies
wird schließlich
ein Teilchen in dem endgültigen
nativen Code.
- • Die
Aufrufkonventionen sind bei Byte-Code und VP recht unterschiedlich.
Bei einem herkömmlichen
Byte-Code, wie beispielsweise Java-Byte-Code, werden die an ein
Unterprogramm weiterzuleitenden Parameter auf einem Stapel plaziert,
gefolgt von einem Bezug auf das aufzurufende Verfahren. Eine Byte-Code-Anweisung zum
Aufrufen eines Verfahrens wird dann ausgeführt, welche den Verfahrensbezug
von dem Stapel nimmt, ihn auflöst
und mit der Ausführung
des neuen Verfahrens mit den Parametern von dem Stapel beginnt.
Die Steuerung wird an das ursprüngliche
Verfahren zurückgegeben,
wenn eine Return-Anweisung
ausgeführt
wird. Diese wird in VP umgewandelt, welcher alle Parameter in VP-Register
lädt, bevor
eine gos (gehe zu Unterprogramm)-Anweisung ausgeführt wird,
welche mit Sprunganweisungsmarkierung versehen worden ist, damit
sie auf das Zielverfahren zeigt (diese Sprunganweisungsmarkierung
kann statistisch oder dynamisch gebunden sein). Die Ausführung wird
an das Unterprogramm weitergeleitet und kehrt zurück, wenn
eine 'ret'-Anweisung ausgeführt wird.
-
Sonstige Bestandteile der Datei werden
wie folgt umgewandelt:
- • Der Name der Klasse bestimmt
den Namen, den die JVM verwendet, um auf Code und Daten Bezug zu
nehmen, die vom Compiler ausgegeben werden.
- • Der
Name der Überklasse
wird zu irgendeinem Bezug auf die Überklasse in dem Code und den Daten,
die vom Compiler ausgegeben werden. Bei der bevorzugten Implementierung
enthalten die Ausgabedaten einen Zeiger, an welchen eine Sprunganweisungsmarkierung
derart angefügt ist,
daß nach
dem Verbinden der Zeiger auf Code und Daten der Überklasse zeigt.
- • Der
Name jeder Schnittstelle wird zu irgendeinem Bezug auf die Schnittstelle
in dem Code und den Daten, die ausgegeben werden. Bei der bevorzugten
Implementierung enthalten die Ausgabedaten einen Zeiger für jede Schnittstelle,
an welche eine Sprunganweisungsmarkierung derart angefügt ist,
daß nach
dem Verbinden der Zeiger auf Code und Daten der Schnittstelle zeigt.
- • Die
Fehlersuch-Informationen, die an jedes Verfahren (und den Quelldateinamen,
der in der Klassendatei gespeichert ist) angefügt sind, werden, wenn vorhanden,
in ein Format umgewandelt, das für
die Umgebung geeignet ist, in welcher die JVM läuft. Bei der bevorzugten Implementierung
werden die Fehlersuch-Informationen in dasselbe Format umgewandelt,
das für
Nicht-Java-Bestandteile des Systems verwendet wird.
-
Die endgültige VP-Klasse enthält ein oder mehr
benannte Werkzeuge, welche normalerweise wenigstens das Datenwerkzeug,
das Klassenwerkzeug, das Sprunganweisungsmarkierungswerkzeug und
null oder weitere Verfahrenswerkzeuge umfassen. Die Werkzeugnamen
werden automatisch vom Jcode-Übersetzer
erzeugt, wobei jeder Name mit dem Namen der Klasse und der Funktion
jedes Werkzeugs in der Implementierung dieser Klasse in Beziehung
steht.
-
Es erfolgt erneut die Rückkehr zu 2; es werden nun weitere
Details über
den nativen Übersetzer
dargelegt, welcher den VP-Code in nativen Code übersetzt. Es sollte selbstverständlich klar
sein, daß der
VP-Code selbst in einer echten Anwendung nie direkt ausgeführt wird;
er wird von dem prozessorabhängigen
nativen Übersetzer
immer in den entsprechenden nativen Code für den Prozessor umgewandelt,
auf welchem er ausgeführt
werden soll.
-
Der native Übersetzer 214 ist
ein ziemlich kleiner Code (ungefähr
150k, in Abhängigkeit
vom Prozessor), so daß er
leicht in einem Speicher innerhalb eines eingebetteten Systems gespeichert
werden kann. Der Übersetzer 214 bildet
die VP-Register auf
die Register des bestimmten verwendeten Prozessors ab. Der Übersetzer
nutzt seine Kenntnisse über
die Registerarchitektur des realen Prozessors, um an jeder Stelle
des ausgegebenen nativen Codes zu entscheiden, welche VP-Register
auf die Register des realen Prozessors abgebildet werden sollten
und welche im Speicher (auf welchen langsamer zugegriffen werden
kann) verbleiben sollten. Der Übersetzer
ermöglicht
auch die maschinenabhängige
Optimierung von Anweisungen. Bis der native Code eingebunden wird,
enthält
er normalerweise noch Abschnitte des Sprunganweisungsmarkierungscodes. Beim
Einbinden (oder manchmal zur Laufzeit) wird der Sprunganweisungsmarkierungscode
durch entsprechende maschinenabhängige
Anweisungen ersetzt. Beispielsweise wird die Sprunganweisungsmarkierung
für ein
nicht-statisches Verfahren zu einem Teilchen in dem nativen Code
umgewandelt.
-
Sowohl der Jcode-Übersetzer als auch der native Übersetzer
sind selbst vorzugsweise in VP-Code geschrieben und können somit übersetzt werden
(unter Verwendung des nativen Übersetzers selbst),
um auf jeder gewünschten
Plattform zu laufen. Von dem anfänglichen
VP-Code können
kompilierte Versionen von beiden Übersetzern in nativem Code
bereitgestellt werden, die für
den bestimmten Prozessor optimiert sind, auf welchem der Übersetzer
auszuführen
ist. Um den VP-Code für
den Jcode-Übersetzer
zu kompilieren, wird dieser Code durch den nativen Übersetzer
geführt.
Um den VP-Code für
den nativen Übersetzer
zu kompilieren, wird dieser Code durch den nativen Übersetzer selbst
geführt.
-
Obwohl das bevorzugte Ausführungsbeispiel die
Virtuelle Java-Maschine verwendet, ist das Gesamtkonzept der Erfindung
allgemeiner und die Verwendung der JVM oder praktisch von Java überhaupt ist
nicht wesentlich. Wenn jedoch Java verwendet wird, gestattet die
beschriebene Erfindung Java-erfahrenen Anwendungsprogrammierern,
Programme in ihrer bevorzugten Spra che zu entwickeln, ohne irgend
etwas von dem VP-Code verstehen oder gar wissen zu müssen. Die
einzige Anforderung ist, daß es
einen nativen Code-Übersetzer
für jeden
physischen Prozessor gibt, auf welchem die Anwendung ausgeführt werden
soll. Es wird davon ausgegangen, daß derartige native Übersetzer
allgemein verfügbar sein
werden, und entsprechende Übersetzer
könnten entweder
von dem Anwendungsentwickler gekauft oder alternativ als Standard
auf einzelnen Client-Einrichtungen, wie beispielsweise Mobiltelefonen
oder Spielkonsolen, zur Verfügung
gestellt werden.