-
Gebiet der
Erfindung
-
Die
vorliegende Erfindung betrifft das Gebiet der objektorientierten
Programmiersprachen für
Computerprogramme und insbesondere das Auffinden der Änderbarkeit
von Feldern und Klassen in einer beliebigen Programmkomponente.
-
Hintergrund
der Erfindung
-
Als
die Programmiersprache Java gegen Ende des Jahres 1995 eingeführt wurde,
eroberte sie das Internet im Sturm. Ein Hauptgrund dafür war die
Tatsache, dass Java eine interpretierte Programmiersprache war,
was hauptsächlich
bedeutete, dass sie ein anderes Kompilierungs-/Ausführungsparadigma
verwendete als Programmiersprachen wie z.B. C oder C++. Ein in einer
höheren
Programmiersprache wie C oder C++ geschriebenes Programm, das von
Menschen gelesen, geschrieben und verstanden werden kann, muss in
Maschinencode übersetzt
werden, der von dem Computer verstanden werden kann, der das Programm
tatsächlich
ausführt.
Dies ist es, was ein Compiler macht. Zusätzlich zum Übersetzen optimieren Compiler
den Code. Das Endprodukt des Kompilierens, der Maschinencode, ist
per Definitionem maschinenspezifisch, was bedeutet, dass der Code
einzig an den Computertyp adressiert ist, der ihn ausführt, und
von einem anderen Computertyp nicht verstanden werden wird. Ein
einfaches Beispiel hierfür
ist die Tatsache, dass ein Programm, das für einen Apple Macintosh kompiliert worden
ist, auf einem International Business Machines (IBM) Klon-PC nicht
laufen wird. Dies nennt man plattformabhängig.
-
Auf
der anderen Seite werden interpretierte Programmiersprachen wie
z.B. Java nicht für
einen bestimmten Computertyp kompiliert (Java ist ein Warenzeichen
der Sun Microsystems Inc.). Sie sind plattformunabhängig. Dies
wird dadurch erreicht, dass ein Vermittler, die Java Virtual Machine
(JVM), zwischen das kompilierte Programm und die spezifische Plattform
gesetzt wird. Mit anderen Worten, wenn ein Javaprogramm kompiliert
wird, ist das Endergebnis nicht Maschinencode, sondern Bytecode,
der von der JVM verstanden wird. Die JVM ist maschinenspezifisch
und fungiert als Übersetzer
des Bytecodes für
die bestimmte Maschine, auf der die JVM installiert ist. Dadurch
können
Javaprogramme kompiliert und an jede Maschine portiert werden, solange
auf der Maschine eine JVM installiert ist.
-
Es
ist diese Plattformunabhängigkeit,
die Java so einzigartig geeignet für das Internet macht. Ist auf einem
Computer einmal JVM installiert, kommt es nicht darauf an, ob er
ein Apple, Wintel PC, Sun, Digital usw. ist, ein über das
Internet heruntergeladenes Java-kompiliertesBytecodeprogramm wird
auf ihm laufen.
-
Obwohl
Java im Allgemeinen als eine übersetzte
Programmiersprache ausgeführt
wird, sollte beachtet werden, das sie statisch oder während der
Laufzeit (Just-In-Time-Compiler)
optimiert und kompiliert werden kann.
-
Java
ist eine objektorientierte Programmiersprache (OOP). Dies bedeutet,
dass der Schwerpunkt auf Objekten und nicht auf Prozessen (wie in
C oder Basic) liegt. Grob gesagt enthält ein Objekt Daten und die Methoden,
mit denen diese Daten bearbeitet werden. Programmieren in Java kann
als Schreiben von Beschreibungen verschiedener Objekte verstanden
werden.
-
Insbesondere
ist in OOP eine „Klasse" eine Sammlung von
Daten und Methoden, die die Ausführungsform
einer bestimmten Art von Objekt definiert. Eine Klassendefinition
definiert „Instanzvariablen" und „Klassenvariablen", ebenso wie sie
die „Schnittstellen" spezifiziert, die
die Klasse realisiert, und die unmittelbare „Oberklasse" der Klasse. In groben
Zügen kann
eine Klasse als eine allgemeine Definition verstanden werden, und
ein Objekt ist eine „Instanz" einer Klasse. Zum
Beispiel könnte
eine Klasse mit dem Namen „Kreis" mit Variablen für den Radius
und die Lage des Mittelpunktes definiert werden. Ein bestimmter
Kreis c mit bestimmten Werten für
den Radius und die Lage des Mittelpunktes könnten durch Aufsuchen der Klasse „Kreis" instanziert werden.
Da der Radius und der Mittelpunkt speziell zu dieser Instanz c der
Kreisklasse gehören,
sind sie „Instanzvariablen". Im Unterschied
dazu ist eine „Klassenvariable" ein Datenelement,
das mit der Klasse als ganze verbunden ist. Zum Beispiel könnte der
Wert pi=3,14 eine Klassenvariable in der Klasse „Kreis" sein. Ein anderes Beispiel wäre eine
Variable num_circles, die in der Klasse „Kreis" definiert wird und die jedes Mal, wenn
ein Kreis instanziert wird, um eins erhöht wird. Diese Klassenvariablen
sind mit der ganzen Klasse und nicht mit einer Instanz verbunden
und werden über
den Modifikator als statisch erklärt. Klassen in Java bilden eine
Klassenhierarchie, in der eine Klasse eine „Oberklasse" oder eine „Unterklasse" einer anderen sein
kann. Zum Beispiel könnte „Formen" eine Oberklasse
von „Kreis" sein, und „GrafikKreis" eine Klasse, die
die Fähigkeit
ermöglicht,
instanzierte Objekte der Kreisklasse zu bearbeiten und zu zeichnen,
könnte
eine Unterklasse von „Kreis" sein. Eine Unterklasse
erbt Verhalten von ihrer Oberklasse.
-
In
Java ist ein „Paket" eine umfassende
Gruppe von Klassen, und Java hat Standardpakete, die Programmierer
für übliche Aufgaben
verwenden. Zum Beispiel hat das Paket java.io Klassen, die Eingabe
und Ausgabe verarbeiten, das Paket java.net hat Klassen, die Vernetzungsfunktionalitäten unterstützen, und
das Paket java.awt stellt Klassen zur Verfügung, die Komponenten der grafischen
Benutzeroberfläche
erstellen.
-
Fortfahrend
mit einigen der einzigartigen Wesensmerkmale von Java sollte angemerkt
werden, dass Java eine dynamische Sprache ist. Dies bedeutet, dass
jede Javaklasse zu jeder Zeit in einen laufenden Java-Interpreter
geladen werden kann. Diese dynamisch geladenen Klassen können dann
dynamisch instanziert werden. Java ist auch eine Sprache, die für die Vernetzung
erschaffen wurde. Unter Verwendung des Pakets Java.net ist es ebenso
leicht, auf Dateien oder Ressourcen über ein Netzwerk zuzugreifen
wie auf lokale Dateien oder Ressourcen. Da Java sowohl dynamisch
als auch für
die Vernetzung erschaffen ist, ist es für einen Java-Interpreter möglich, Code
aus der Breite des Internets herunterzuladen und laufen zu lassen.
Dies ist es, was passiert, wenn ein Web-Browser ein Java-Applet
herunterlädt
und laufen lässt
(ein Applet ist eine Klasse, die von einer bereits laufenden Java-Anwendung
geladen und ausgeführt
wird). Zur Zeit sind Internet-Java-Applets die allgegenwärtige Anwendung
von Java, aber Java hat die Fähigkeit,
jeden Programmtyp zu erstellen, der die verteilten Ressourcen eines
Netzwerks dynamisch verwendet.
-
Wegen
der Sicherheitsrisiken, die einem System innewohnen, das aktiven
Code über
ein Netzwerk herunterladen kann, hat Java verschiedene Verteidigungslinien
gegen Malicious Code (bösartigen
Code). Als Erstes hat Java, anders als C oder C++, keine Zeiger,
die benutzt werden können,
um auf Speicher außerhalb der
Grenzen eines String oder Array zuzugreifen. Dem Fehlen von Zeigern
entsprechend lässt
Java keinen direkten Zugriff auf Speicher zu und beendet somit jeden
Sicherheitsangriff aus dieser Richtung. Zweitens führt der
Java-Interpreter an jedem unsicheren Code, den er lädt, einen
Bytecode-Überprüfungsprozess
durch, was verhindert, dass Malicious Code Vorteile aus Realisierungsschwächen im
Java-Interpreter
zieht. Drittens verwendet Java ein Sicherheits-„Sandboxinodell", in dem unsicherer
Code in eine „Sandbox" gestellt wird, wo
er sicher spielen kann, ohne der vollständigen Java-Umgebung irgendeinen
Schaden zuzufügen
(siehe US-Patentschrift 5 974 549). Wenn ein Applet in der Sandbox
läuft,
gibt es zahlreiche Sicherheitsbeschränkungen in dem, was es tun
kann. Auf diese Weise wird Malicious Code daran gehindert, sich
in andere in derselben Java-Umgebung laufende Anwendungen einzumischen,
oder unautorisierten Zugriff auf Ressourcen im zugrundeliegenden
Betriebssystem oder Netzwerk zu erhalten. Eine vierte Sicherheitsebene
kann durch Anhängen digitaler
Signaturen an Java-Code zur Verfügung
gestellt werden. Diese digitalen Signaturen können die Herkunft des Codes
in einer kryptographisch sicheren und unfälschbaren Weise nachweisen.
Ein Benutzer ermittelt, ob eine bestimmte Quelle sicher ist, und
wenn von einer sicheren Quelle Code empfangen wird, wird er angenommen
und läuft.
-
Ein
anderes Merkmal von Java ist sein Verfahren der Speicherbelegung
und -freigabe. In C oder C++ belegt der Programmierer Speicher bewusst
und gibt dann Speicher bewusst frei. Mit anderen Worten, der C++-Programmierer
belegt zu Beginn eines Objektes oder einer Methode ausdrücklich Speicher
für Arrays, Variablen
usw. und gibt diesen Speicher dann ausdrücklich frei, wenn er nicht
mehr länger
gebraucht werden wird. Im Unterschied dazu belegt der Java-Programmierer
weder Speicher, noch gibt er Speicher frei. Stattdessen verwendet
Java Speicherbereinigung, die wie folgt arbeitet: der Java-Interpreter weiß, welche
Objekte er zugeordnet hat. Er kann auch herausfinden, welche Variablen
sich auf welche Objekte beziehen und welche Objekte sich auf welche
anderen Objekte beziehen. Aufgrund dessen kann er herausfinden,
wann sich irgendein anderes Objekt oder eine Variable nicht mehr
länger
auf ein zugeordnetes Objekt bezieht. Wenn solch ein Objekt gefunden
wird, kann es durch einen „Speicherbereiniger" sicher zerstört werden.
-
Schließlich verwendet
Java Komponenten, Softwareeinheiten auf Anwendungsebene, die zur
Einsatzzeit konfigurierbar sind. Derzeit gibt es vier Arten von
Komponenten: Enterprise Beans, Webkomponenten, Applets und Application
Clients. Enterprise Beans führen
eine betriebliche Aufgabe oder eine betriebliche Gesamtheit ein.
Webkomponenten wie z.B. Servlets stellen in Reaktion auf Anfragen
Dienste bereit. Applets laufen wie zuvor erwähnt typischerweise in einem
Web-Browser ab, können
aber auch in einer Vielzahl anderer Anwendungen oder Vorrichtungen
ablaufen, die das Applet-Programmierungsmodell unterstützen. Application Clients
sind Erste-Ebene-Clientprogramme, die in ihrer eigenen Java Virtual
Machine ablaufen. Den Komponenten werden Lebensdauermanagement,
Sicherheits-, Einsatz- und Laufzeitdienste durch Container zur Verfügung gestellt.
Jede Art von Container (Enterprise Java Bean (EJB), Web, Java Server
Page (JSP), Servlet, Applet und Application Client) stellt auch
komponentenspezifische Dienste zur Verfügung.
-
Wie
durch die obige Beschreibung von Java deutlich gemacht wurde, ist
eine wesentliche Eigenschaft von Java die Lokalisierung von Wissen
innerhalb eines Moduls, was als „Kapselung" bekannt ist. Da Objekte Daten und Realisierungen
einkapseln, kann der Nutzer eines Objekts das Objekt als eine Blackbox
betrachten, die Dienste zur Verfügung
stellt. Instanzvariablen und Methoden können hinzugefügt, gelöscht oder
verändert werden,
aber solange die durch das Objekt zur Verfügung gestellten Dienste dieselben
bleiben, kann Code, der das Objekt verwendet, fortfahren, es zu
verwenden, ohne umgeschrieben zu werden.
-
Probleme
treten jedoch auf, wenn ein Objekt oder eine Komponente vom Zustand
einer gemeinsamen Variablen oder eines gemeinsamen Objektes abhängt und
eine andere Komponente oder ein anderes Objekt den Zustand dieser
variablen oder dieses Objekts verändert. Mit anderen Worten,
in diesem Fall ist das gemeinsame Objekt nicht eingekapselt. Dies
wird manchmal als Isolationsfehler bezeichnet. Der Mechanismus zum
gemeinsamen Benutzen eines Zustands in Java läuft über Klassenvariablen, d.h.
mit dem statischen Modifikator deklarierte Felder. Auf eine Klassenvariable
wird über
den Klassennamen zugegriffen und nicht über einen Objektbezug. Daher
wird die Variable von all dem Code gemeinsam benutzt, der auf die
deklarierende Klasse zugreifen kann.
-
Diese
Isolationsfehler sind von besonderer Wichtigkeit aufgrund der schnellen
Entwicklung des Marktes der Java-Komponenten
(Applets, Servlets, Java Beans und Enterprise JavaBeans) und der
Verwendung von Java, um Middleware zu entwickeln, wie z.B. den AppletViewer,
der von Web-Browsern verwendet wird, um Applets laufen zu lassen,
das Java Server Toolkit (JST), um Servlets auf Servern laufen zu
lassen, und Container, um EJBs laufen zu lassen. Die Bezugsrealisierungen
dieser Middlewaresysteme basieren auf der gleichzeitigen Ausführung mehrerer
Komponenten in einer einzigen Instanz des Java-Laufzeitsystems.
Das Java-Laufzeitsystem ist die Softwareumgebung, in der von der
JVM kompilierte Programme laufen können. Das Laufzeitsystem enthält all den
Code, der notwendig ist, um Programme zu laden, die in der Programmiersprache
Java geschrieben wurden, systemeigene Methoden dynamisch zu verbinden,
Speicher zu steuern sowie mit Ausnahmen umzugehen, und eine Ausführung der
JVM, die ein Java-Interpreter sein kann.
-
Isolationsfehler
unter mehreren gleichzeitig oder nacheinander ablaufenden Programmen
können
zu zahlreichen Problemen führen,
insbesondere in den im folgenden aufgelisteten Gebieten:
Integrität – einige
Zustandsinformationen in globalen Feldern/Objekten können durch
jedes Programm, das in der JVM läuft,
verändert
werden. Ein solches Beispiel ist die Standard-Ländereinstellung (Land, Sprache,
Variante). Wenn zwei oder mehrere Programme von diesen globalen
Zustandsinformationen abhängen
und sie beide versuchen, den Standardwert zu verändern, sind Ergebnisse ihrer
Ausführung
wahrscheinlich unvorhersehbar.
Sicherheit – die Fähigkeit, Zustände zu verändern oder
Zustandsveränderungen
zu beobachten, führt
zu Sicherheitsaussetzungen. In einigen Fällen gehören globale Felder zu Klassen,
die zur Laufzeit Bezüge
zu Objekten enthalten können,
die Instanzen von Unterklassen sind, die Methoden zum Außer-Kraft-Setzen
definieren. Diese Methoden könne
Operationen ausführen,
die vom Entwickler der Anwendung nicht beabsichtigt sind, und können zu
schädlichem
Verhalten führen
(z.B. Öffnen
eines Fensters einer grafischen Benutzeroberfläche mit Nutzer-ID- oder Passwortanforderung).
Malicious Code kann auch den Zustand der Java-Laufzeit in unvorhersehbarer
Weise verändern.
Ein tatsächliches
Realisierungsproblem im Java Development Kit (JDK), das in Version
1.1.1 auftrat, ergab sich durch gemeinsames Benutzen von Objekten.
Als Folge konnte ein unprivilegiertes Applet eine sichere Signatur
entpersonalisieren, was zu einer ernsten Sicherheitslücke führte.
Zusammenarbeit
mit dem Komponentenmodell – Anwendungscode
kann Skalierbarkeitsprobleme bekommen. Oft verwendet Anwendungscode
globale Variablen, um Zustandsinformationen für Instanzen der Klasse gemeinsam
zu benutzen. Das Problem liegt darin, das in einigen der Anwendungsmodelle
eine Instanz eines EJB in einem Container erzeugt, im Sekundärspeicher
geparkt und dann in einem anderen Container reaktiviert werden kann.
Wenn sie reaktiviert wurde, wird die Zustandsinformation der Klassenvariable/Instanzvariable
in einem anderen Container gespeichert. Die Folge für das Netz
ist, dass es Speicherundichtigkeiten gibt – Informationen werden in Variablen
erzeugt und gespeichert, aber niemals freigegeben – und die
EJBs nicht mehr länger
standorttransparent sind.
-
Deswegen
gibt es einen Bedarf, änderbare
Variablen zu identifizieren, jene Variablen, die durch mehr als
eine Komponente verändert
werden können,
um Isolationsfehler zu identifizieren und zu beenden.
-
OFFENBARUNG
DER ERFINDUNG
-
Gemäß der vorliegenden
Erfindung werden ein System und Verfahren zum Auffinden der Änderbarkeit von
Klassen in einer Komponente einer objektorientierten Programmiersprache
bereitgestellt. In dem System und Verfahren wird eine Gruppe von
Klassen erhalten, wobei jede der Klassen entweder als änderbar,
nichtänderbar
oder unentschieden klassifiziert wird, und dann wird jede unentschiedene
Klasse untersucht. Die Untersuchung der unentschiedenen Klassen
umfasst die Untersuchung jedes nichtstatischen Feldes in der unentschiedenen
Klasse, das Umklassifizieren jeder unentschiedenen Klasse als änderbar,
wenn irgendein nichtstatisches Feld in der unentschiedenen Klasse änderbar
ist; und das Umklassifizieren der unentschiedenen Klasse als nicht änderbar,
wenn alle nichtstatischen Felder in der unentschiedenen Klasse nicht änderbar sind.
Die Untersuchung jedes Feldes in der unentschiedenen Klasse umfasst
das Ermitteln möglicher
Zustandsveränderungen
jeder Variable und jedes Objekts in jedem Feld, wobei die Zustandsveränderungen durch
Methoden innerhalb des Analysebereichs hervorgerufen werden; das
Ermitteln möglicher
Kapselungsaufbrüche
jeder Variable und jedes Objekts in jedem Feld, wobei die Kapselungsaufbrüche von
Methoden hervorgerufen wurden, die sich innerhalb des Analysebereichs
befinden; das Klassifizieren jedes Feldes als nichtänderbar,
wenn keine möglichen
Zustandsveränderungen
oder Kapselungsaufbrüche gefunden
werden; das Klassifizieren jedes Feldes als änderbar, wenn mögliche Zustandsveränderungen
oder Kapselungsaufbrüche gefunden
werden; und das Klassifizieren jedes Feldes als unentschieden, wenn
es unzureichende Informationen über
die Klassenänderbarkeit
gibt. Der Schritt der Untersuchung jeder unentschiedenen Klasse
wird wiederholt, bis eine Anzahl unentschiedener Klassen nach einer
Wiederholung des Untersuchungsschrittes mit einer Anzahl unentschiedener
Klassen vor der Wiederholung des Untersuchungsschrittes identisch
ist; und dann werden die verbleibenden unentschiedenen Klassen als änderbare
Klassen umklassifiziert.
-
Gemäß der vorliegenden
Erfindung werden ein System und eine Einheit zum Auffinden der Änderbarkeit
von Variablen, Objekten, Feldern und Klassen in einer Komponente
einer objektorientierten Programmiersprache bereitgestellt. Das
System und die Einheit umfassen einen Änderbarkeitsanalysator mit
drei Ebenen: eine erste Ebene mit mindestens einer Kernbibliothek
und mindestens einer Maschine zur Datenflussanalyse, um eine bestimmte
Abstraktion der Programmkomponente zu ermöglichen; eine zweite Ebene
mit mindestens einem Dienstprogrammmodul zum Verwenden der Resultate
der mindestens einen Maschine zur Datenflussanalyse, um grundlegende
Ergebnisse zu erzeugen; und eine dritte Ebene mit mindestens Änderbarkeits-Subanalysemodul
zum Erzeugen von Endergebnissen.
-
KURZE BESCHREIBUNG
DER ZEICHNUNGEN
-
Die
Erfindung wird nun beispielhaft mit Bezug auf die begleitenden Zeichnungen
beschrieben, in denen:
-
1 eine Übersichtsdarstellung
eines Änderbarkeitsanalysators
gemäß der bevorzugten
Ausführungsform
der vorliegenden Erfindung ist;
-
2 ein
Schaubild der Verteilung änderbarer
und nichtänderbarer
statischer Felder ist, das die Ergebnisse für einen auf Zugriff basierenden
Algorithmus mit den Ergebnissen des Änderbarkeitsanalysator-Werkzeugs
gemäß der bevorzugten
Ausführungsform
vergleicht;
-
3 ein
Schaubild der Analysezeiten und der Größen der analysierten Bibliothek
für verschiedene Teile
des rt.jar der Analyse des Änderbarkeitsanalysators
gemäß der bevorzugten
Ausführungsform
der vorliegenden Erfindung ist; und
-
4 ein
Flussdiagramm der Teilanalysen der Änderbarkeitsanalyse gemäß der bevorzugten
Ausführungsform
der vorliegenden Erfindung ist.
-
DETAILLIERTE
BESCHREIBUNG DER ERFINDUNG
-
Die
bevorzugte Ausführungsform
der vorliegenden Erfindung soll eine Vorrichtung und ein Verfahren zum
Auffinden der Änderbarkeit
von Feldern und Klassen in einer beliebigen Java-Komponente bereitstellen. Insbesondere
wird eine statische Analyse beschrieben, die alle nicht änderbaren
Felder und Klassen auflisten sowie Standorte identifizieren kann,
wo Änderungen
möglich
sein können.
In der bevorzugten Ausführungsform
der vorliegenden Erfindung wird eine Variable als änderbar
angesehen, wenn ihr Wert oder der Wert irgendeiner von ihr erreichbaren
variable nach ihrem Initialisierungspunkt verändert wird. Insbesondere konzentrieren
sich die Algorithmen auf einen offenen Analysebereich (oder Komponentenanalyse),
in dem zur Zeit der Analyse nicht notwendigerweise der gesamte auszuführende Code
zur Verfügung
steht. Die bevorzugte Ausführungsform
ist im Zusammenhang mit Java zu sehen, aber sie ist auch auf andere
objektorientierte Programmiersprachen (OOP) anwendbar.
-
Die
bevorzugte Ausführungsform
der vorliegenden Erfindung verwendet eine Gruppe von Algorithmen,
die für
die Änderbarkeitsanalyse
von Java-Komponenten Datenflusstechniken einfügen. Der Schwerpunkt liegt
auf der Analyse von Softwarekomponenten (z.B. Bibliotheken oder
Beans) anstatt von ganzen Programmen. Dies wird erreicht durch Verbinden
des Begriffs der Änderbarkeit
mit den Begriffen der Kapselung und des Scopings. In der bevorzugten
Ausführungsform
entstehen Isolationsfehler, wenn man Komponenten zusammensetzt,
bei denen eine Komponente vom Zustand einer gemeinsamen Variable
oder eines gemeinsamen Objekts abhängt, und eine andere Komponente
diesen Zustand verändert.
-
In
der bevorzugten Ausführungsform
der vorliegenden Erfindung wird ein Änderbarkeitsanalysator verwendet,
um eine statische Analyse eines offenen Bereichs an einer Java-Komponente
durchzuführen,
um die Gruppe von Algorithmen als gültig zu erklären. Der Änderbarkeitsanalysator
klassifiziert jedes statische Feld und jede Klasse in der Komponente
als änderbar
oder nichtänderbar.
Obwohl der Änderbarkeitsanalysator
gemäß der bevorzugten
Ausführungsform
der vorliegenden Erfindung sehr konservativ ist, könnten andere
Ausführungsformen
weniger konservativ sein.
-
Der
verbleibende Teil dieser Patentanmeldung ist wie folgt aufgebaut.
Abschnitt I enthält
die in der Änderbarkeitsanalyse
verwendeten Definitionen. Abschnitt II stellt eine statische Analyse
zum Bestimmen der Änderbarkeit
auf dem Gebiet des Komponentenprogrammierens vor. Der Algorithmus
wird über
eine Gruppe von Teilanalysen beschrieben. Abschnitt III beschreibt
das Änderbarkeitsanalysator-Werkzeug
und zeigt Versuchsergebnisse. Abschnitt IV enthält eine Schlussfolgerung und
stellt Ideen vor, wie man das Werkzeug weiter verbessert.
-
I. Definitionen
-
In
diesem Abschnitt werden Definitionen des Zustands in Java-Programmen eingeführt, in
denen Variablen primitive Werte (z.B. Schriftzeichenwerte oder ganzzahlige
Werte) oder Bezüge
zu Objekten enthalten können.
Als Nächstes
werden formale Definitionen vorgestellt, um Änderbarkeitsfragen anzusprechen.
In der bevorzugten Ausführungsform
der vorliegenden Erfindung werden die Typsicherheit und die Zugriffsmodifikatoren
der Sprache Java vorausgesetzt und ausgenutzt. Wie unten erörtert werden
wird, richtet sich die Analyse nach Klassendateien, und die Terminologie
der Klassendateien wird verwendet werden.
-
A. Variablenzustand und
Objektzustand
-
Ein
Objekt in Java ist entweder eine Klasseninstanz oder ein Array.
Objektinstanzierung bringt die Erzeugung einer Gruppe von Instanzvariablen
bzw. Arraykomponenten mit sich. Somit stellt ein Objekt einen Zusammenschluss
von verbundenen Variablen dar. Eine Klassenvariable in Java entspricht
einem Feld, das innerhalb einer Klassendeklaration unter Verwendung
des statischen Modifikators deklariert wurde. Man beachte, dass
die innerhalb einer Schnittstellendeklaration deklarierten Felder
notwendigerweise als statisch definiert sind. Im Gegensatz dazu
entspricht eine Instanzvariable einem Feld, das innerhalb einer
Klassendeklaration ohne den statischen Modifikator deklariert wurde.
Eine Klasseninstanz ist ein Zusammenschluss von Variablen, die nichtstatischen
Feldern entsprechen, die in dieser Klasse und allen ihren Vorfahren
deklariert wurden. Ein Klassentyp führt ein nichtstatisches Feld
ein, wenn seine Instanzen eine Variable enthalten, die mit diesem
Feld verbunden ist.
-
Für primitive
Typen ist der Zustand einer Variable definiert durch den primitiven
Wert, den sie enthält. Eine
variable, die einen Bezug zu einem Objekt enthält, ist jedoch komplexer, da
sie sich indirekt auf andere Variablen beziehen kann. Mit anderen
Worten, der Zustand einer Variable des Bezugstyps hängt rückbeziehend
vom Zustand des bezogenen Objekts ab. Die folgenden Begriffe werden
formal unterschieden:
-
Der
Wertzustand einer Variable ist der Wert, den die Variable enthält.
-
Der
Zustand einer variable wird definiert durch die Gruppe aller Wertzustände der
Variablen, die von ihr erreichbar sind.
-
Der
Zustand eines Objekts wird definiert durch die Gruppe von Zuständen aller
seiner verbundenen Variablen.
-
Es
ist offensichtlich, dass der Zustand einer primitiven Variable mit
ihrem Wertzustand übereinstimmt. Der
Zustand einer Variable des Bezugstyps, deren Wertzustand nicht Null ist,
wird rückbeziehend
definiert durch den Wertzustand der Variable zusammen mit dem Zustand
des bezogenen Objekts.
-
B. Definitionen der Nicht-Änderbarkeit
-
Um
die Nicht-Änderbarkeit
einer Variable oder eines Objekts zu definieren, muss man sich auf
einen bestimmten Ausführungszeitpunkt
beziehen, an dem sie/es als „vollständig erzeugt" angesehen wird.
Dieser Punkt wird als Initialisierungspunkt der Variable oder des
Objekts bezeichnet und wird im folgenden definiert:
Der Initialisierungspunkt
einer Klassenvariable folgt auf Vollendung ihrer entsprechenden <clinit>-Methode.
Der
Initialisierungspunkt einer Instanzvariable oder einer Klasseninstanz
folgt auf die Vollendung einer entsprechenden <init>-Methode.
Der
Initialisierungspunkt einer Arrayinstanz oder ihrer Komponenten
folgt auf Ausführung
eines entsprechenden Array-Erzeugungsbefehls
(z.B. newarray, anewarray oder multianewarray).
-
Nachdem
nun der Initialisierungspunkt definiert ist, können die Definitionen der Nicht-Änderbarkeit
erstellt werden:
Eine Variable oder ein Objekt ist dann und
nur dann nicht änderbar,
wenn ihr/sein Zustand sich nach dem entsprechenden Initialisierungspunkt
niemals ändert.
Ein
Feld ist nicht änderbar,
wenn und nur wenn alle Variablen, die diesem Feld entsprechen, nicht änderbar sind.
Eine
Klasse ist dann und nur dann nicht änderbar, wenn alle von ihr
eingeführten
nichtstatischen Felder nichtänderbar
sind.
-
Man
beachte: Da abstrakte Klassen und Schnittstellen nicht instanziert
werden können,
wird deren Änderbarkeit
hier nicht betrachtet.
-
Für die Zwecke
der bevorzugten Ausführungsform
wird der Zustand eines Objekts durch die Variablen bestimmt, die
ausdrücklich
nichtstatischen Feldern entsprechen. Die JVM kann jedoch ohne weiteres
andere Speichersegmente mit dem Objekt verknüpfen. Eine solche implizite
Variable oder ein solch „verstecktes
Feld" ist das mit
jedem Java-Objekt verbundene Schloss. Die Definitionen besagen jedoch,
dass ein Objekt nicht änderbar
ist, wenn es keine Instanzvariablen hat. Da java.lang.Object keine
deklarierten nichtstatischen Felder hat, ist es somit als nichtänderbare
Klasse definiert.
-
C. Zugriffsmodifikatoren
und Zugriffskontrolle
-
Java
stellt über
Zugriffsmodifikatoren grundlegende Mittel zur Kontrolle des Zugriffs
auf Objekte zur Verfügung:
privat, öffentlich
und geschützt.
Diese Modifikatoren beschränken
die Sichtbarkeit von Klassen und Feldern. Ein anderer Zugriffsmodifikator
ist final. Dieser Modifikator stellt partielle Unterstützung zur
Verhinderung ungewünschter Änderbarkeit
von Variablen zur Verfügung.
Zuweisungen zu Variablen, die einem finalen Feld entsprechen, werden
von der JVM verboten. Wenn also die JVM die Felder einmal initialisiert,
können deren
Wertzustände
nicht verändert
werden. Wenn solch eine Variable jedoch einen Bezug zu einer Klasseninstanz
enthält,
kann der Zustand des bezogenen Objekts durch Operationen an dem
Objekt verändert
werden, obwohl sich die Variable immer auf dasselbe Objekt beziehen
wird. Dies geschieht auch mit Arrays. Eine Arraykomponente kann
verändert
werden, obwohl der Wertzustand der Variable, die einem finalen Feld
entspricht, das sich auf die Arrayinstanz bezieht, nicht änderbar
bleiben wird. Man beachte, dass der finale Modifikator für Methoden
und Klassen eine völlig
unterschiedliche Bedeutung hat, er beeinflusst die Vererbung und setzt
Beziehungen zwischen Klassen und Methoden außer Kraft. Eine als final deklarierte
Klasse kann keine Unterklassen bekommen. Eine der Folgen für die Änderbarkeitsanalyse
ist es, dass immer, wenn eine finale Klasse der deklarierte Typ
einer Variablen ist, sie auch ihr Laufzeittyp ist. Somit ist ein
finales primitives Feld immer nichtänderbar; ebenso ist dies ein
finales Feld des Bezugstyps, dessen deklarierter Typ eine finale
nichtänderbare
Klasse ist.
-
Zusätzlich zu
den Zugriffsmodifikatoren können
breiteres Scoping und Zugriffsbeschränkungen durch verschiedene
Sicherheitsmechanismen und hierarchische Klassenlademechanismen
durchgesetzt werden, die von der Java-Laufzeit angeboten werden, z.B. werden
Beschränkungen
des Hinzufügens
neuer Klassen zu einigen Paketen durch den Security Manager durchgesetzt.
Die Erörterung
von Scopingbeschränkungen, die
vom Java-Laufzeit-Sicherheitssystem zur Verfügung gestellt werden, geht über den
Rahmen dieser Patentanmeldung hinaus.
-
C++
enthält
einen Freund-Spezifikator, der paketübergreifenden Mitgliederzugriff
erlaubt. Im Unterschied zu C++ nutzt Java das Java-Laufzeit-Sicherheitssystem,
um den Zugriff auf Methoden und geschützte Java-Ressourcen zu beschränken. Java
stellt (auf Quellenebene) „Freundschaftsbeziehungen" zwischen Klassen über das
Konzept der verschachtelten Klassen und Schnittstellen zur Verfügung, welches
nichtstatische Klassen beinhaltet, die als innere Klassen bezeichnet
werden. Diese Klassen sind Teil des Vertrages oder der Realisierung
ihres Einschlusstyps und haben somit dieselbe Zugreifbarkeitsauswahl
wie andere Mitglieder. Auf der Ebene der JVM gibt es jedoch keine
solche Verschachtelungsbeziehungen. In der Praxis erzeugt der Compiler
künstliche
Felder und künstliche
Zugreifermethoden, um Zugriff zwischen Mitgliedern verschachtelter und
einschließender
Klassen zu gewähren.
was auf Quellenebene nach strengeren Zugreifbarkeitsregeln aussieht,
kann auf Objektcodeebene tatsächlich
ein schwächerer
Schutz sein.
-
Dennoch
ist der Schutz über
Zugriffsmodifikatoren begrenzt. Java-Code kann systemeigene Methoden
(Nicht-Java-Code) und Reflexion (Klassen/Methoden in java.lang.reflect)
verwenden, um Zugriff auf Klassenmitglieder zu erhalten, ohne sich
an die Java-Zugriffsmodifikatoren zu halten.
-
Sicherheitsmechanismen
werden im Allgemeinen verwendet, um Zugriffe einzuschränken, die
Java-Zugriffsregeln verletzen. Die vorliegende Analyse bearbeitet
weder systemeigenen Code, noch berücksichtigt sie dynamische Effekte,
die aus Reflexion resultieren. Es gibt andere Ansätze als
statische Analyse (z.B. Kommentare), die dafür verwendet werden könnten, systemeigenen
Code und Reflexionsverhalten zu erklären.
-
II. Änderbarkeitsanalyse
-
Um
die Änderbarkeit
einer Variable zu ermitteln, sollte man alle Methoden analysieren
können,
die ihren Zustand verändern
können.
Die hier vorgestellte Analyse widmet sich nicht dem Auffinden tatsächlicher Instanzierung
von Klassen. Somit würde
eine Variable als nicht änderbar
identifiziert, wenn gezeigt werden kann, dass es keine Methoden
gibt, die ihren Zustand verändern
können.
Die Erreichbarkeit aller Modifikatoren/Zugreifer einer bestimmten
Variable hängt
vom Analysebereich ab. Die beiden Extreme werden wie folgt unterschieden:
Ein
geschlossener Analysebereich, in dem sich alle symbolischen Bezüge auf analysierte
Klassen beziehen und alle möglichen
Ziele von Laufzeitaufrufen in analysierten Klassen definiert sind.
Ein
offener Analysebereich, in dem jede beliebige Gruppe von Klassen
analysiert wird.
-
Diese
Art der Analyse bezeichnet man auch als Komponentenanalyse.
-
In
der geschlossenen Bearbeitung würde
die Änderbarkeitsanalyse
die Gruppe der direkten Modifikatoren der Variablenzustände identifizieren.
Im Gegensatz dazu sollte eine offene Änderbarkeitsanalyse auch die
Situationen identifizieren, die Variablen möglichen Veränderungen durch Code außerhalb
des Analysebereichs aussetzen, und solche Variablen konservativ
als änderbar
klassifizieren. Die Begriffe der Variablenkapselung und ihr Aufbruch
werden, um die offene Analyse zu erleichtern, wie folgt definiert:
Eine
Variable ist eingekapselt, wenn alle Bezüge zu von ihr erreichbaren
Objekten durch Code innerhalb des Analysebereichs definiert werden.
Eine
Variablenkapselung ist durch einen Befehl in einer analysierten
Methode aufgebrochen, wenn dieser Befehl bewirkt, dass ein von der
variable erreichbares Objekt für
Code außerhalb
des Analysebereichs zugreifbar wird.
-
Man
beachte, dass die Aussetzung nichtänderbarer Objekte nicht Aufbruch
der Kapselung bedeutet, da nach ihrer Definition Kapselung nur von änderbarem
Zustand handelt.
-
A. Änderbarkeitsteilanalysen
-
Die Änderbarkeitsanalyse
teilt sich in zwei Hauptteile oder Teilanalysen auf, wie in 4 gezeigt
und im Folgenden beschrieben wird:
Die Zustandsveränderungsanalyse
wird verwendet, um mögliche
Veränderungen
des Zustands einer Variable durch Methoden in Klassen innerhalb
des Analysebereichs zu ermitteln.
-
Subanalysen
innerhalb der Zustandsveränderungsanalyse
entdecken:
-
• Wertveränderung
-
Veränderung
des Wertzustands der Variable durch Code innerhalb des Bereichs
(Schritt 410)
-
• Objektveränderung
-
Veränderung
des Zustandes des bezogenen Objekts durch Code innerhalb des Bereichs
(Schritt 420)
-
Die
Kapselungsanalyse wird verwendet, um mögliche Veränderungen des Zustands einer
Variable von außerhalb
des Analysebereichs zu ermitteln, d.h. durch Methoden, die in Klassen
definiert sind, die nicht Teil der analysierten Komponente sind.
Teilanalysen innerhalb der Kapselungsanalyse entdecken:
-
• Variablenzugreifbarkeit
-
Veränderung
des Wertzustands der Variable von außerhalb des Analysebereichs
(Schritt 430)
-
• Objektzugreifbarkeit
-
die
Variable ist auf ihren Initialisierungspunkt hin nicht eingekapselt
(Schritt 440)
-
• Aufbruch der variablenkapselung
(Schritt 450)
-
Jede
Teilanalyse behandelt ein Spektrum von Situationen, die Variablenänderbarkeit
bewirken, von denen einige nicht einfach ohne weiteres wahrgenommen
werden. Beispiel 1 veranschaulicht mehrere solche Situationen. Es
wird angenommen, dass der Analysebereich die Klasse Sample und die
Java-API-Klassen einschließt.
-
-
-
-
Im
Folgenden werden die Änderbarkeitsszenarien
in Beispiel 1 erklärt.
-
Zustandsveränderungsteilanalyse
-
• Wertveränderung:
-
Die
Methode resetData () stellt einen neuen Wert in privateData ein.
-
• Objektveränderung:
-
Beide
Methoden removeData () und addData (Object) verändern nicht den Wert von privateData,
aber verändern
das davon bezogene Vektorobjekt.
-
Kapselungssubanalyse
-
• Variablenzugreifbarkeit:
-
anObject
ist sowohl nichtfinal als auch nichtprivat, und daher kann sein
Wertzustand von außerhalb des
Analysebereichs verändert
werden.
-
• Objektzugreifbarkeit:
-
Die
von anObject bezogenen Objekte sind zugreifbar, da beide Felder
nichtprivat sind; der Bezug von anArray ist ein änderbares Array, und anObject
kann sich auf ein änderbares
Objekt beziehen. Die Zustände dieser
Objekte können
von außerhalb
des Analysebereichs verändert
werden.
-
privateData
ist auf Vollendung beider Konstruktoren hin nicht eingekapselt.
Im Fall von Sample () kann ein Bezug zu einem änderbaren Objekt, das Teil
des Zustands von privateData ist, durch Code außerhalb des Analysebereichs über anArray
erhalten werden. Dieser Bezug kann nachfolgend verwendet werden,
um den Zustand von privateData zu verändern. Im Fall von Sample (Object)
enthält
die Variable einen Bezug zu dem Parameter, der von dem Konstruktorenaufruf
definiert wurde, der sich nicht notwendigerweise in der analysierten
Komponente befindet. Dieses Alias kann später verwendet werden, um den
Zustand von privateData zu verändern.
-
• Aufbruch der Variablenkapselung:
-
addData
(Object) nimmt ein Objekt an, das Teil des Zustands von privateData
wird. Da diese Methode von außerhalb
des Analysebereichs aufgerufen werden kann und ihr Parameter ein änderbares
Objekt sein kann, kann die Kapselung von privateData aufgebrochen
werden. Ebenso kann exposeData (Object [ ]) von außerhalb
des Analysebereichs aufgerufen werden, und ein Teil des Zustands
von privateData wird Teil des Zustands des Parameters.
-
getData
() und exposeData (int) können
von außerhalb
des Analysebereichs aufgerufen werden. In beiden Methoden enthält der Zustand
des zurückgegebenen
Objekts Teile des Zustands von privateData.
-
isEqual
(Vector) leitet einen Teil des Zustands von privateData als Parameter
an einen virtuellen Methodenaufruf weiter. Die aufgerufene Methode
kann sich außerhalb
des Analysebereichs befinden.
-
exposeData
() bewirkt das gemeinsame Benutzen eines Zustands durch privateData
und einer zugreifbaren Variable anObject.
-
Als
Nächstes
wird ein höherer
Datenflussalgorithmus vorgestellt, um konservative statische Komponentenanalyse
durchzuführen.
Die Änderbarkeitsanalyse
berechnet Informationen über
das mögliche
Verhalten des Codes hinsichtlich der Variablen, die während der
Laufzeit erzeugt werden können,
und ihrer Laufzeittypen.
-
B. Höherer Algorithmus der Änderbarkeitsanalyse
-
Der
Analysebereich wird durch eine vorgegebene Gruppe von Klassendateien
identifiziert, die eine Java-Komponente bilden. Jede Klassendatei
entspricht einer analysierten Klasse oder Schnittstelle. Der Bereich ist
offen, also kann eine vorgegebene Klassendatei Bezüge zu Klassen
oder Schnittstellen enthalten, deren Klassendateien sich außerhalb
des Analysebereichs befinden.
-
Wann
immer die Analyse unerreichbare klassenübergreifende globale Informationen
erfordert, werden konservative Annahmen angewendet. Klassen außerhalb
des Analysebereichs werden als änderbar
angenommen. Klassen, die Klassen erweitern, die sich außerhalb
des Analysebereichs befinden, werden als änderbar angenommen, weil sie änderbare
Instanzvariablen erben können.
Wann immer eine analysierte Methode einen Methodenaufruf (invokestatic,
invokevirtual, invokespecial, invokeinterface) enthält, wendet
die Analyse in ähnlicher
Weise konservative Annahmen an, wenn sie entscheidet, dass es eine
Zielrealisierung geben könnte,
die sich außerhalb
des Analysebereichs befindet.
-
Wie
oben angegeben ist eine nichtabstrakte Klasse nichtänderbar,
wenn alle von ihr eingeführten nichtstatischen
Felder nichtänderbar
sind. Andererseits ist die Ermittlung der Kapselung einer Variable
(des Bezugstyps) abhängig
von der bekannten Gruppe nichtänderbarer
Klassen. Wenn z.B. die Klassenvariable, die einem öffentlichen
finalen statischen Feld entspricht, sich auf eine Instanz einer änderbaren
Klasse beziehen kann, würde
sie als nicht eingekapselt und daher änderbar angesehen werden. Da
andererseits jedes mögliche Objekt,
auf das sich diese Klassenvariable bezieht, nichtänderbar
ist, wird das Feld als nichtänderbar angesehen.
Somit sind Änderbarkeit/Nicht-Änderbarkeit
von nichtstatischen Feldern und von Klassen voneinander abhängig. Deswegen
erfordert die Analyse eine iterative Bearbeitung. Jede Iteration
bestimmt die Nicht-Änderbarkeit
nichtstatischer Felder (und dadurch die Nicht-Änderbarkeit von Klassen) basierend
auf einer Gruppe schon bestimmter nichtänderbarer Klassen, bis ein
festgelegter Punkt erreicht ist (z.B. bis die Klassifikationen vor
und nach einer Iteration dieselben bleiben; Einzelheiten siehe unten).
Wenn es Klassen gibt, deren Nicht-Änderbarkeit während des
Iterationsvorgangs nicht nachgewiesen wurde, werden diese konservativ
als änderbar
angesehen. Im Gegensatz dazu beeinflusst die Änderbarkeit/Nicht-Änderbarkeit
statischer Felder (Klassenvariablen) nicht die Änderbarkeit von Klassen und
wird berechnet, nachdem die Gruppen der änderbaren/nichtänderbaren
Klassen bekannt sind.
-
Jede
analysierte nichtabstrakte Klasse und jedes ihrer eingeführten Felder
werden während
des Ablaufs des Algorithmus als änderbar,
nichtänderbar
oder unentschieden klassifiziert. Intuitiv zeigt die unentschiedene
Klassifikation an, dass weitere Analyse (nachdem mehr Klassen als
entweder änderbar
oder nichtänderbar
klassifiziert worden sind) möglicherweise
die Klassifikation in änderbar
oder nichtänderbar
umändern
wird. Der Verwender der Analyse liefert als Eingabe in den Algorithmus
eine (möglicherweise
leere) Gruppe von Klassen und Feldern, die als änderbar klassifiziert sind,
und eine (möglicherweise
leere) Gruppe von Klassen und Feldern, die als nichtänderbar
klassifiziert sind.
-
Der
Algorithmus beginnt mit einer vorgegebenen Gruppe änderbarer
und nichtänderbarer
Klassen und Felder. Andere Klassen und Felder im Analysebereich
werden als unentschieden gekennzeichnet. Z.B. erfordert die (häufig verwendete)
Klasse java.lang.String komplexe Techniken zur Bytecodeanalyse und
auch die Analyse systemeigenen Codes, um ihre Nicht-Änderbarkeit korrekt nachzuweisen.
Wenn diese Klasse Teil des Analysebereichs ist, wird somit im Allgemeinen
erwartet, dass sie anfänglich
als nichtänderbar
klassifiziert wird, um für
andere Klassen genauere Ergebnisse zu erhalten. Wenn der Analysebereich
nicht alle Oberklassen einer analysierten Klasse enthält, kann
außerdem
der Verwender die vollständige
Liste nichtstatischer Felder zur Einführung durch diese Klasse bereitstellen.
Nach der Vollendung des Algorithmus ist jede Klasse und jedes statische
Feld entweder als änderbar
oder als nichtänderbar
klassifiziert.
-
1) Ermittlung der Änderbarkeit
eines bestimmten Feldes
-
In
der bevorzugten Ausführungsform
der vorliegenden Erfindung wird in dem Algorithmus die Routine TestField
verwendet, um die Änderbarkeit
eines vorgegebenen unentschiedenen Feldes zu ermitteln, basierend
auf einer Gruppe änderbarer,
nichtänderbarer
oder unentschiedener Klassen. Das eingegebene Feld wird durch seinen
Namen und die deklarierende Klasse gekennzeichnet. Für ein nichtstatisches
Feld wird auch die realisierende Klasse angegeben. Die Routine verwendet
die Informationen zur Klassenänderbarkeit
(die aus der Klassifikation der Klasse hervorgegangen sind), um
die Klassifikation des vorgegebenen Feldes auf änderbar oder nichtänderbar
zu setzen oder es unentschieden zu belassen. Es kann vorkommen,
dass ein Feld nicht als nichtänderbar klassifiziert
werden kann, es könnte
jedoch als nichtänderbar
klassifiziert werden, wenn mehr der zu dem Zeitpunkt als unentschieden
klassifizierte Klassen als nichtänderbar
umklassifiziert würden. Im
Fall unzureichender Klassenänderbarkeitsinformationen
bleibt die Klassifikation eines Feldes unentschieden. Wenn jede
nichtabstrakte Klasse entweder als änderbar oder nichtänderbar
klassifiziert ist (was der Fall ist, wenn TestField für statische
Felder aufgerufen wird), wird nach Vollendung von TestField das
Feld entweder als änderbar
oder nichtänderbar
klassifiziert.
-
Man
beachte, dass sich die Routine auf den diesem Feld entsprechenden
Initialisierungspunkt beziehen sollte, um die Änderbarkeit des vorgegebenen
Feldes unentschiedenen zu ermitteln. Wenn das Feld ein statisches
Feld ist, ist der Initialisierungspunkt auf Vollendung des enthaltenen
Klasseninitialisierers <clinit> festgelegt. Anderenfalls
ist der Initialisierungspunkt als Vollendung des entsprechenden
Instanzkonstruktors <init> definiert.
-
Das
Folgende umreißt
die Struktur der TestField-Routine:
- 1. Erhalten
einer Gruppe von Klassen, jede klassifiziert als änderbar,
nichtänderbar
oder unentschieden
- 2. Für
jede Variable, die Afield entspricht, Untersuchen auf Erfüllung der
folgenden Bedingungen:
A. Wertveränderung: Wertzustand kann durch
eine Methode verändert
werden, die nach dem Initialisierungspunkt aufgerufen werden kann
B.
Objektveränderung:
Zustand des bezogenen Objekts kann durch eine Methode verändert werden,
die nach dem Initialisierungspunkt aufgerufen werden kann
C.
Variablenzugreifbarkeit: Wertzustand kann von außerhalb des Analysebereichs
verändert
werden
D. Objektzugreifbarkeit: Variable kann nach ihrem Initialisierungspunkt
nicht eingekapselt werden
E. Aufbruch der Variablenkapselung:
Variablenkapselung kann durch eine Methode aufgebrochen werden, die
nach dem Initialisierungspunkt aufgerufen werden kann
- 3. Wenn keine der Bedingungen A bis E erfüllt ist, Klassifizieren des
Feldes als nichtänderbar
- 4. Ansonsten, wenn es momentan unzufriedenstellende Klassenänderbarkeitsinformationen
(wie oben definiert) gibt, Belassen der Klassifikation des Feldes
als unentschieden
- 5. Ansonsten, Klassifizieren des Feldes als änderbar
-
2) Bestimmen der Änderbarkeit
der Felder und Klassen aller Komponenten
-
Als
Nächstes
wird der iterative Hauptarbeitsschritt beschrieben, der die TestField-Routine
verwendet, um Änderbarkeit
von Klassen und Feldern nachzuweisen. Sie erhält eine anfängliche Klassifikation einiger
Felder und Klassen als änderbar
oder nichtänderbar
und klassifiziert vorübergehend
alle anderen Felder und Klassen als unentschieden.
- 1. /* Ermittle die Änderbarkeit
von Klassen und nichtstatischen Feldern */
- 2. Für
jede nichtabstrakte Klasse, die als unentschieden klassifiziert
ist,
/* ermittle die Änderbarkeit
von der Klasse eingeführter
nichtstatischer Felder */
- 3. Wenn die vollständige
Liste von der Klasse eingeführter
nichtstatischer Felder unbekannt ist,
/* könnte es ein von der Klasse
eingeführtes änderbares
nichtstatisches Feld geben */
- 4. Klassifiziere die Klasse als änderbar; gehe über zur
nächsten
Klasse
- 5. Wenn es ein von der Klasse eingeführtes nichtstatisches änderbares
Feld gibt
- 6. Klassifiziere die Klasse als änderbar; gehe über zur
nächsten
Klasse
- 7. Für
jedes von der Klasse eingeführte
nichtstatische unentschiedene Feld Afield TestField (Afield)
- 8. Wenn Afield als änderbar
klassifiziert wird
- 9. Klassifiziere die Klasse als änderbar; gehe über zur
nächsten
Klasse
/* alle von der Klasse eingeführten nichtstatischen Felder
sind entweder unentschieden oder nichtänderbar */
- 10. Wenn alle von der Klasse eingeführten nichtstatischen Felder
nichtänderbar
sind
- 11. Klassifiziere die Klasse als nichtänderbar
/* Ende von für */
- 12. Bis die Gruppe von Klassen, die als unentschieden klassifiziert
werden, in der laufenden Iteration nicht verkleinert worden ist
/*
ein festgelegter Punkt erreicht */
- 13. Für
jede als unentschieden klassifizierte Klasse
- 14. Klassifiziere die Klasse als änderbar
/* Bestimme die Änderbarkeit
statischer Felder */
- 15. Für
jede analysierte Klasse oder Schnittstelle
- 16. Für
jedes statische unentschiedene Feld Afield, das innerhalb der Klasse
deklariert wurde TestField (Afield)
-
III. Das Änderbarkeitsanalysator-Werkzeug
-
In
diesem Abschnitt wird das Änderbarkeitsanalysator-Werkzeug
beschrieben, das statische Änderbarkeitsanalyse
durchführt.
Das Werkzeug führt
eine offene Analyse an einer vorgegebenen Java-Komponente durch
und klassifiziert jedes statische Feld und jede Klasse in der Komponente
als änderbar
oder nichtänderbar.
Als Ausgabe erzeugt das Werkzeug eine Liste von Änderbarkeitsgründen für jene Klassen
und Felder in der Komponente, die als änderbar klassifiziert werden.
Insbesondere gibt das Werkzeug für
jedes statische Feld eine Liste von Bedingungen (A-E) an, wie in
TestField definiert, die für
dieses Feld nicht erfüllt
sind, zusammen mit zusätzlichen
Informationen. Eine Ausnahme sind die Bedingungen D und E; derselbe Änderbarkeitsgrund
wird angezeigt, wenn eine dieser Bedingungen nicht erfüllt ist.
Der Grund ist, dass der durchgeführte
Arbeitsschritt für
beide Bedingungen sehr ähnlich
ist.
-
A. TestField-Realisierungen
-
Eine
Hauptaufgabe des Werkzeugs ist es, auf sehr großen Komponenten zu laufen.
Die Ausführung ist
so gestaltet, dass eine besondere Betonung auf der Skalierbarkeit
liegt. Als ein Ergebnis wurden verschiedene TestField-Routinen entwickelt,
eine für
statische Felder und die andere für nichtstatische Felder. Die
beiden Ausführungen
von TestField unterscheiden sich in den Analysen, die sie anwenden,
um die im vorangehenden Abschnitt beschriebenen Bedingungen A bis
E zu untersuchen. Tabelle 1 beschreibt diese Unterschiede.
-
TABELLE
1 Variationen von TestField im Änderbarkeitsanalysator-Werkzeug
-
-
Aus
Gründen
der Zweckmäßigkeit
werden eine Reihe von Teilanalysen durchgeführt, von denen jede den gesamten
Code bearbeitet und die Informationen für jede analysierte Methode
herausfiltert und später
Informationen für
alle relevanten Felder zusammenträgt. Eine Teilanalyse wird aktiviert,
wenn die durch sie berechneten Daten zum ersten Mal benötigt werden;
diese Informationen werden während
folgender Aufrufe von TestField-Routinen wiederverwendet.
-
Die
TestField-Routine für
nichtstatische Felder analysiert das Feld nicht für jede vorgegebene
Ausführung
aufs Neue, wie in dem höheren
Algorithmus vorgeschlagen. Stattdessen errechnet die Routine eine
konservative Klassifikation des Feldes als änderbar oder nichtänderbar,
die für
alle Ausführungen
der Klasse gültig ist.
-
Die
Routine TestField für
statische Felder wendet einige komplexe Teilanalysen an, von denen
einige interprozedurale Iterationen erfordern.
-
B. Die Architektur des Änderbarkeitsanalysators
-
In
der bevorzugten Ausführungsform
der vorliegenden Erfindung verwendet die Realisierung des Änderbarkeitsanalysators
Kernbibliotheken, die als Teil von Toad ausgeführt werden, eine Post-Production-Umgebung,
die eine Symbiose dynamischer Informationen über eine laufende Anwendung
mit statischen Informationen erlaubt, die aus den Klassen erhalten
wurden, die sie umfassen. Das Toad-System kann von der IBM-Alphaworks-Website (http://www.alphaworks.ibm.com/tech/toad)
heruntergeladen werden. Die Toad-Umgebung ist als Schirmstruktur
für ein
Programmpaket aus Kernbibliotheken und Werkzeugen entwickelt worden,
die Java-Anwendungen überwachen,
verstehen und optimieren. Insbesondere die CFParse-Bibliothek erlaubt
dem Benutzer, Klassendateien sowohl zu lesen und zu schreiben, als
auch zu editieren. Die JAN-Bibliothek sammelt und beeinflusst statische
Informationen über
eine Java-Komponente
(z.B. Anwendung, Applet oder Servlet) durch Analysieren einer Gruppe
von Klassendateien und wirksames Aufbauen des Bezuges, der Hierarchie
und der Call-Graphen der Komponente.
-
Die
Architektur des Änderbarkeitsanalysators
besteht aus drei Ebenen, wie in 1 veranschaulicht ist:
Kernbibliotheken,
von denen jede eine bestimmte Abstraktion der Komponente zur Verfügung stellt.
Dienstprogrammmodule,
von denen jedes eine Analyse durchführt, die dafür vorgesehen
ist, in verschiedenen Zusammenhängen
verwendet zu werden.
Änderbarkeits-Teilanalysen,
von denen jede dazu dient, eine oder mehrere der TestField-Bedingungen
zu untersuchen.
-
1) Kernbibliotheken
-
CFParse
stellt Informationen zur Verfügung,
die die Struktur einer bestimmten Klassendatei widerspiegeln. Die
Datenflussanalyse erfordert eine höhere Abstraktion des Kontrollflusses,
entweder auf der intraprozeduralen Ebene (intraprozeduraler Control
Flow Graph) oder auf der interprozeduralen Ebene (Call Graph). Auf
dem Höhepunkt
dieser Abstraktionen werden zwei zusätzliche Kernbibliotheken realisiert,
von denen jede eine Maschine zur Datenflussanalyse ist.
Intraprozedurale
Maschine: | wird
verwendet, um iterativ die Wirkung eines Befehls auf Informationen
zu berechnen, die mit Standorten auf dem Methodenrahmen verbunden
sind (Operandenstack und Array lokaler Variablen). |
Interprozedurale
Maschine: | wird
verwendet, um die Wirkung einer Methode auf Informationen zu berechnen,
die mit den Variablen verbunden sind, die lebendig bleiben, nachdem
die Methode vollendet ist. |
-
Man
beachte, dass während
einer interprozeduralen Analyse die Wirkung einer aufgerufenen Methode,
die sich außerhalb
des Analysebereichs befinden kann, konservativ eingeschätzt werden
sollte. In der bevorzugten Ausführungsform
nimmt das Werkzeug an, dass jeder virtuelle Aufruf (invokevirtual
oder invokeinterface) eine mögliche
Zielrealisierung außerhalb
des Analysebereichs haben kann. Dies ist eine Hauptquelle der Konservativität der Analyse.
-
2) Dienstprogramme
-
Zusätzlich zu
den obigen Kernbibliotheken wird auch eine Gruppe von Dienstprogrammanalysen
eingeführt:
Typanalyse:
wird verwendet von beiden TestField-Routinen als Teil der Untersuchungen
auf die Bedingungen B,D,E. Für
jede analysierte Methode identifiziert die Typanalyse für jeden
Befehl und für
jeden Rahmenstandort die Gruppe möglicher Typen des Standorts.
Die Analyse unterscheidet zwischen den Fällen, in denen der genaue Laufzeittyp
des bezogenen Objekts bekannt ist, und den Fällen, in denen der Laufzeittyp
bis zur Kompatibilität
der Belegung bekannt ist. Die Analyse berücksichtigt auch die Scopingfragen.
Insbesondere, wenn ein Feld von außerhalb des Analysebereichs
verändert
werden kann, wird konservativ angenommen, dass sein Typ bekannt
ist bis zur Kompatibilität
der Belegung mit dem deklarierten Typ. Die Analyse verwendet die intraprozedurale
Maschine.
Erreichbarkeitsanalyse: wird nur von der Version
der TestField-Routine für
statische Felder zum Untersuchen der Bedingungen B,D,E als Basis
für andere
Teilanalysen verwendet. Um z.B. zu ermitteln, ob die Kapselung einer
Klassenvariable aufgebrochen ist, wird die Erreichbarkeitsanalyse
verwendet, um zu entscheiden, ob das Objekt, dessen Bezug gespeichert oder
zurückgegeben
wird, von einem statischen Feld erreichbar sein kann. Ebenso wird,
um zu ermitteln, ob der Zustand einer Klassenvariable durch einen
putfield-Befehl verändert
wird, die Erreichbarkeitsanalyse verwendet, um zu entscheiden, ob
die veränderte
Variable von einem statischen Feld erreichbar sein kann. Für jede analysierte
Methode identifiziert die Erreichbarkeitsanalyse für jeden
Befehl und für
jeden Rahmenstandort, der sich auf ein änderbares Objekt bezieht, die
Gruppe entweichender Objekte und Klassenvariablen, von denen dieses
Objekt erreichbar wird. Die Gruppe entweichender Objekte enthält Objekte,
die von den Methodenparametern und dem zurückgegebenen Objekt erreichbar
sind. Man beachte, dass Erreichbarkeitsbeziehungen nichtänderbarer
Objekte ignoriert werden. Die Änderbarkeit
wird basierend auf einer Liste nichtänderbarer Typen bestimmt, die
ein Parameter für
die Analyse ist.
Die Analyse verwendet sowohl die inter- als
auch die intraprozedurale Maschine.
-
3) Teilanalysen
-
Die
nächste
Ebene des Änderbarkeitsanalysators
(wie in 1 dargestellt) ist die Gruppe
der Teilanalysen:
Wertveränderung:
wird verwendet von beiden TestField-Routinen, um auf Bedingung A
zu untersuchen.
Für
jede analysierte Methode identifiziert die Wertveränderungsanalyse
die Gruppe von Feldern, deren entsprechende Instanzvariablen und
Klassenvariablen innerhalb dieser Methode eingestellt werden können. Drei Fälle werden
unterschieden:
Die analysierte Methode ist ein Klasseninitialisierer
(<clinit>): Ein Feld, das durch
einen Befehl putstatic beeinflusst wird, wird der Gruppe der veränderten
Felder hinzugefügt,
sofern das Feld nicht in der Klasse deklariert ist, die sich aktuell
in der Initialisierung befindet. Ein Feld, das durch einen Befehl
putfield beeinflusst wird, wird immer der Gruppe hinzugefügt.
Die
analysierte Methode ist ein Instanzkonstruktor (<init>):
Ein Feld, das durch einen Befehl putstatic beeinflusst wird, wird
immer der Gruppe veränderter
Felder hinzugefügt.
Ein Feld, das von einem Befehl putfield beeinflusst wird, wird hinzugefügt, sofern
nicht bewiesen werden kann, dass die entsprechende Variable zu dem Objekt
gehört,
das sich aktuell im Aufbau befindet.
Anderenfalls: Für jeden
Befehl putfield/putstatic wird das Feld, das von dem Befehl beeinflusst
wird, der Gruppe veränderter
Felder hinzugefügt.
Die
Analyse verwendet die intraprozedurale Maschine. Objektveränderung:
wird verwendet von der Version der TestField-Routine für statische
Felder, um auf Bedingung B zu untersuchen.
Für jede analysierte
Methode identifiziert die Zustandsveränderungsanalyse die Gruppe
der statischen Felder des Bezugstyps und Methodenparameter, deren
Zustand des bezogenen Objekts durch diese Methode verändert werden
kann. Für
jeden Befehl putfield- oder xastore befragt die Analyse die Erreichbarkeitsanalyse,
um zu ermitteln, ob das Objekt, in das gespeichert werden soll,
von einem statischen Feld oder einem Methodenparameter erreichbar
ist. Man beachte, dass die Analyse keine Ausnahme für Initialisierungsmethoden
(<init> oder <clinit>) macht; wenn z.B.
einem statischen Feld zuerst während
der entsprechenden Klasseninitialisierungsmethode ein Bezug zu einem
Objekt zugewiesen wird und der Zustand dieses Objekts im Folgenden
verändert
wird, würde
die Analyse dies als Zustandsveränderung
identifizieren.
Die Analyse verwendet die inter- und die intraprozedurale
Maschine und die Typ- und Erreichbarkeitsanalyse.
Variablenzugreifbarkeit:
wird verwendet von beiden Versionen der TestField-Routine, um auf
Bedingung C zu untersuchen.
Für jedes analysierte Feld ermittelt
die Variablenzugreifbarkeitsanalyse, ob der Wert der Variable von
außerhalb
des Analysebereichs verändert
werden kann. Die aktuelle Realisierung basiert nur auf den Zugriffsbeschränkungen,
die von der Sprache definiert sind. Daher wird ein Feld, das nichtprivat
und nichtfinal ist, als zugreifbar identifiziert. In anderen Ausführungsformen
kann diese Analyse verbessert werden, indem man Laufzeitzugriffsbeschränkungen
und zusätzliche
Scopinginformationen berücksichtigt.
Objektzugreifbarkeit
und Aufbruch der Kapselung: wird verwendet von der Version der TestField-Routine
für statische
Felder, um auf die Bedingungen D und E zu untersuchen.
Für jede analysierte
Methode identifiziert die Kapselungsanalyse die Gruppe der statischen
Felder des Bezugstyps und Methodenparameter, die nach Vollendung
der Methode nicht eingekapselt sein können. Die Definitionen der
Kapselung betrachten eine Variable als eingekapselt, sofern es kein
von der Variable erreichbares änderbares
Objekt gibt, das von außerhalb
des Analysebereichs zugreifbar ist. Die Realisierung in der bevorzugten
Ausführungsform
ist konservativer und betrachtet die Erzeugung irgendeines nichtlokalen
Bezugs zu einem änderbaren
Objekt, das von der Variable erreichbar ist, als Aufbruch der Kapselung
dieser Variable. Dies ist in der Schwierigkeit begründet, nichtlokale
Bezüge
zu verfolgen.
-
Eine nichtlokale Speicheroperation
-
(putfield/putstatic/aastore)
oder eine Rückgabe
verursacht den Aufbruch der Kapselung jeder Variable, die sich auf
das gespeicherte Objekt bezieht. Zuweisung durch ein putfield oder
putstatic eines nicht eingekapselten Objekts bricht die Kapselung
der Variable auf, in die der Bezug gespeichert ist.
-
In
der bevorzugten Ausführungsform
werden die Untersuchungen auf die Bedingungen D und E in einer einzelnen
Analyse verbunden, da die beiden Prozesse zu großen Teilen dieselben Informationen
erfordern.
-
Die
Analyse verwendet die inter- und intraprozedurale Maschine und die
Typ- und Erreichbarkeitsanalyse.
-
C. Ergebnisse
-
Die
Ergebnisse des Werkzeugs gemäß der bevorzugten
Ausführungsform
wurden bewertet, indem sie mit den Ergebnissen verglichen wurden,
die aus einem auf Zugriff basierenden Algorithmus, der unten beschrieben
wird, hergeleitet wurden.
-
Beide
Algorithmen beginnen mit der Klasse java.lang.String, die als nichtänderbar
klassifiziert wird. Der auf Zugriff basierende Algorithmus erfordert
keine Bearbeitung von Methodenrümpfen
und kann durch Verwendung des Java-Hauptreflexionsmechanismus ausgeführt werden.
Die TestComponent-Routine bleibt dieselbe; die Untersuchungen in
TestField auf die Bedingungen A bis E verlaufen wie folgt:
(A)
Wertveränderung: | Das
Feld ist nichtfinal |
(B)
Objektveränderung: | Der
deklarierte Typ des Feldes ist ein Arraytyp oder eine nichtfinale
Klasse oder eine änderbare
Klasse |
(C)
Variablenzugreifbarkeit: | Das
Feld ist nichtprivat oder nichtfinal |
(D)
Objektzugreifbarkeit: | Der
deklarierte Typ des Feldes ist ein Arraytyp oder eine nichtfinale
Klasse oder eine änderbare
Klasse |
(E)
Aufbruch der Variablenkapselung: | Der
deklarierte Typ des Feldes ist ein Arraytyp oder eine nichtfinale
Klasse oder eine änderbare
Klasse |
-
Die
Laufzeitbibliothek rt.jar aus dem Java 2 JDK Version 1.2 wird verwendet,
um den Nutzen dieser Verfahrensweise zu illustrieren. Diese Bibliothek
wurde ausgewählt,
da rt.jar ziemlich groß ist
(größer als
10,2 MB) und eine recht verschiedenartige Gruppe von Codierungsstilen
darstellt.
-
2 zeigt
die Verteilung änderbarer
und nichtänderbarer
statischer Felder für
die zwei Algorithmen.
-
Das
Schaubild zeigt eine Zunahme der Anzahl der als nichtänderbar
identifizierten Felder. Man beachte, dass die Identifizierung finaler
primitiver statischer Felder als nichtänderbar einfach ist und sie
in derselben Weise von jedem Werkzeug behandelt werden, das die
Verletzung von Java-zugriffsregeln
durch systemeigenen Code oder über
Reflexion ignoriert. Nichtfinale nichtprivate statische Felder würden in
jeder Analyse als änderbar
identifiziert, die Laufzeitzugreifbarkeit nicht berücksichtigt.
Deswegen resultiert die Verbesserung aus der Verringerung der Anzahl
anderer änderbarer
statischer Felder. In anderen Ausführungsformen kann das Werkzeug
so verbessert werden, dass die Größen beider Kategorien änderbarer
statischer Felder weiter verringert werden.
-
Zusätzlich zu
der Klassifizierung von Feldern als änderbar oder nichtänderbar
liefert der Änderbarkeitsanalysator
die Informationen über
die Gründe
der Änderbarkeit,
wie z.B. den Standort des möglicherweise verändernden
Codes. Diese Informationen können
verwendet werden, um den Benutzercode so zu verändern, dass bestimmte Felder
nichtänderbar
gemacht werden. Tabelle 2 zeigt die Anzahl von Feldern, die für jeden Änderbarkeitsgrund
in rt.jar angegeben wird.
-
-
-
Eine
der Beobachtungen in Tabelle 2 ist die große Anzahl an Feldern, für die Zustandsveränderungen und
Kapselungsaufbrüche
berichtet werden, und die hohe Korrelation zwischen dem Auftreten
dieser beiden Gründe.
Dies hängt
mit der sehr konservativen Verfahrensweise zusammen, die bei der
Bearbeitung virtueller Methoden angewendet wird. Um diese Über-Konservativität zu verringern,
können
Typanalyse und Paketzugriffsbeschränkungen angewendet werden.
Von diesen Verbesserungen wird erwartet, das sie die Anzahl der Felder,
für die
Zustandsveränderungen
und Kapselungsaufbrüche
berichtet werden, erheblich verringern. Die meisten der Felder,
die durch das Werkzeug als direkt veränderbar oder zugreifbar angegeben
werden (nichtfinale nichtprivate Felder, nichtprivate Felder änderbaren
Typs), sind tatsächlich
Felder des Standardbereichs in Paketen mit eingeschränktem Zugriff.
Es wird erwartet, dass die Anzahl dieser angegebenen Gründe fast auf
Null zurückgehen
wird, wenn Laufzeitzugriffsbeschränkungen berücksichtigt werden.
-
Die
Erfahrung zeigt, dass in vorhandenem Code die meisten der statischen
Felder in eine der beiden Kategorien fallen:
Kompilierungszeitkonstanten | (d.h.
final primitiv oder final java.lang.String) |
Änderbare
Felder | (unter
Ignorierung von Laufzeitzugriffsbeschränkungen, so dass nichtprivate
nichtfinale Felder als änderbar
angesehen werden) |
-
Es
gibt verhältnismäßig wenige
nichtänderbare
Felder, die keine Kompilierungszeitkonstanten sind. Das Werkzeug
lief auf einer großen
internen IBM-Struktur ab (ca. 2,4 MB zusammen mit Laufzeitbibliotheken, 4634
Klassen), die mehrere Bibliotheken, Applets und Servlets enthält. Diese
Struktur enthält
3553 statische Felder, von denen 2324 Kompilierungszeitkonstanten
sind. Von den verbleibenden 1229 Feldern sind 992 änderbar,
entweder weil sie nichtfinal und nichtprivat sind, oder weil ihr
Wertzustand verändert
wird. von den verbleibenden 237 Feldern wurden mehr als zwei Drittel
(160 Felder) durch unser Werkzeug als nichtänderbar identifiziert.
-
Entwickler
können
den erkannten Grund der Änderbarkeit,
den der Änderbarkeitsanalysator
liefert, verwenden, um den Code so zu verändern, dass das möglicherweise
gefährliche
gemeinsame Benutzen eines globalen Zustands vermieden wird. Dieses
Merkmal besitzt einzig dieses Werkzeug.
-
Eine
der Hauptanliegen während
der Entwicklung des Werkzeugs war seine Skalierbarkeit. Von besonderem
Interesse waren die Teilanalysen, die interprozedurale Iterationen
erfordern: Erreichbarkeitsanalyse, Objektzugreifbarkeits- und Kapselungsaufbruchsanalyse
und Zustandsveränderungsanalyse.
In der Praxis erwies sich das Werkzeug als ziemlich skalierbar.
Das Werkzeug lief auf einem Pentium III 500 mit 128 MB RAM unter
Verwendung der Sun JVM 1.3.Orcl-T. Die Analysezeit für die vollständige rt.jar
betrug etwa 20 Minuten. Obwohl die Analyse interprozedurale Bearbeitung
erfordert, ist sie in der Praxis von Natur aus wirksam und fast
linear in der Problemgröße. Dies
kann man in 3 sehen, die die Ergebnisse
der Analyse verschiedener Teile von rt.jar zeigt.
-
IV. Schlussfolgerung
-
Wie
bereits betont, ist die Analyse gemäß der bevorzugten Ausführungsform
der vorliegenden Erfindung dafür
gestaltet, in erster Linie offene Analyse zu unterstützen. Im
Allgemeinen Fall bringt dies mit sich, dass alle nichtprivaten Felder
und Methoden als zugreifbar von außerhalb des Analysebereichs
angesehen werden. In der Praxis gibt es jedoch bestimmte Umstände, unter
denen Zugreifbarkeit von Feldern und Methoden restriktiver werden
kann. Z.B. kann die Komponente als Java-Anwendung verstanden werden, von der
erwartet wird, dass sie als eine einzige Komponente in einer JVM
läuft.
In diesem Fall sollte keine benutzerdefinierte Methode (im Unterschied
zu JDK-Methoden) als zugreifbar von außerhalb der analysierten Komponente angesehen
werden, anders als die einzige „main"-Methode,
von der aus die Anwendung zu laufen beginnt. Solche Informationen
würden
die Größe des Call-Graphs
der Komponente merklich verringern. Als Folge würde die Menge der vom Änderbarkeitsanalysator
berichteten Änderbarkeitsgründe erheblich
verringert, und mehr Felder würden
als nichtänderbar
identifiziert. Allgemein würden
zusätzliche
Informationen über
jene Methoden, von denen aus die Komponente ihre Ausführung beginnen
kann, die Gruppe zugreifbarer Methoden verkleinern. Solche Informationen
können
dem Änderbarkeitsanalysator
als Metadaten zur Verfügung
gestellt werden.
-
Der
Laufzeitauflösungsprozess,
der die tatsächliche
Instanzvariable oder Klassenvariable, auf die zugegriffen wird,
bestimmt, unterliegt Laufzeitzugriffsbeschränkungen. Es ist der SecurityManager,
der für
das Beschränken
der Laufzeitzugreifbarkeit verantwortlich ist. Eine bestimmte Laufzeitzugriffsbeschränkung ergibt sich
aus der Paketversiegelung, d.h. einer bestimmten Kennzeichnung eines
Pakets, die besagt, dass alle Klassen in ihm von derselben JAR-Datei
kommen müssen. Ähnlich dem
oben erwähnten
Fall der Metadateien würden
Informationen über
versiegelte Pakete die Zugreifbarkeitsabstraktion von Feldern und
Methoden beschränken.
Es ist zu erwarten, dass die meisten der 541 nichtprivaten nichtfinalen
rt.jar-Felder, die aktuell als änderbar
bestimmt sind, als nichtzugreifbar von außerhalb der Komponente ermittelt
würden,
da alle außer zwei
Paketen von rt.jar als versiegelt angesehen werden. In einer anderen
Ausführungsform
des Änderbarkeitsanalysators
würde ein
artverwandtes Modul realisiert, das diese beiden Arten von Zugreifbarkeitsinformationen
verwenden würde.
-
Der Änderbarkeitsanalysator
gemäß der bevorzugten
Ausführungsform
der vorliegenden Erfindung ist von Natur aus konservativ. Eine Hauptquelle
für Über-Konservativität in der
derzeitigen Version ergibt sich aus der Tatsache, dass die Analyse
davon ausgeht, dass jeder virtuelle Aufruf auf eine Methode zurückgeführt werden
kann, die außerhalb
der analysierten Komponente definiert ist. Dies bedeutet, dass an
solche Aufrufe weitergeleitete Argumente als möglicherweise verändert angesehen
werden. In einer anderen Ausführungsform kann
der Call-Graph der Komponente in dem Sinn optimiert werden, dass
bestimmte virtuelle Aufruforte identifiziert werden, für die sich
alle möglichen
Zielrealisierungen innerhalb des Analysebereichs befinden. Zusätzlich können zur
besseren Aufrufauflösung
weiter, wie oben erläutert,
Informationen über
Zugriffsbeschränkungen
verwendet werden. Versiegelungsinformationen können verwendet werden, um versiegelte
Aufrufe zu identifizieren, d.h. Aufrufe, deren mögliche Ziele auf ein Paket
begrenzt sind und zur Analysezeit vollständig ermittelt werden können. Es
ist gezeigt worden, dass in etwa der Hälfte der versiegelten Pakete
in rt.jar 5-60% der
virtuellen Aufrufe nichtfinaler Methoden als versiegelt identifiziert
werden.
-
Obwohl
die bevorzugte Ausführungsform
der vorliegenden Erfindung im Zusammenhang des Auffindens von Änderbarkeit
zum Nutzen verbesserter Integrität,
Sicherheit und Skalierbarkeit vorgestellt wurde, gibt es andere
Verwendungen der Änderbarkeitsinformationen,
die die Tatsache relativieren, dass der Zustand bestimmter Felder/Objekte über längere Zeit
unveränderlich
ist. Aufgrund dessen sind Optimierungen möglich, um die Leistung eines
laufenden Systems zu verbessern, und die Untersuchung wird besser
lenkbar. Im Folgenden werden einige mögliche Verwendungen und Vorzüge der vorliegenden
Erfindung aufgelistet.
-
Speichern von Konstanten
im Festspeicher (ROM).
-
Um
die Skalierbarkeit und Leistung der Java-Laufzeit zu verbessern,
ist es oft wünschenswert,
Objekte, von denen man weiß,
dass sie Konstanten sind, im ROM zu speichern. Dies ist wünschenswert
für alle
Varianten von Java-High-End-Servern, bei denen JVMs schnell gestartet
werden sollen, um einen Arbeitsvorgang auszuführen und zu verlassen; eingebettete
Systeme, in denen RAM eine knappe Ressource ist; und Arbeitsflächen, bei
denen es wünschenswert
ist, eine Java- Anwendung
schnell mit minimalem Aufwand starten zu können. Der Netzeffekt ist, dass
Objekte in den Festspeicher gestellt werden, was Platz im Heap spart und
zusätzliche
Arbeit für
die Heap-Speicherbereinigung vermeidet.
-
Verteilter, gemeinsam
genutzter Speicher (Distributed Shared Memory – DSM).
-
Ein
wichtiger Faktor für
die Wirksamkeit von DSM ist die richtige Verteilung von Objekten
auf die Prozessoren. Das Identifizieren nichtänderbarer Objekte erlaubt die
Erzeugung von Duplikaten für
jeden der Prozessoren im DSM-Cluster. Dadurch wird es entbehrlich,
die Objekte zu beträchtlichen
Kosten von einem Prozessor zu einem anderen zu transportieren. Außerdem können Informationen über nichtänderbare
Objekte verwertet werden, um den Aufwand teurer Kohärenz und
Synchronisierung in nebenläufigen
und verteilten Umgebungen zu vermeiden.
-
Gleichzeitigkeit.
-
Die
Ausführung
gleichzeitig ablaufender Programme auf symmetrischen Mehrfachprozessoren
(Symmetrical Multi-Processors – SMP)
oder Systemen mit ungleichförmigem
Speicherzugriff (Non-Uniform Memory Access – NUMA) führt zu Sicherheits- und Lebendigkeitsbedenken.
Die Entwicklung von Gleichzeitigkeit erfordert das Vermeiden unsynchronisierter
Veränderungen
gemeinsam benutzter Zustände.
Solche Synchronisierung führt
zu einem Kostenaufwand. Dieser Aufwand kann für Objekte, die als nichtänderbar
bekannt sind, vermieden werden. Ein interessantes Beispiel einer
speziellen Verwendung der Identifizierung nichtänderbarer Objekte ist der generationale
Speicherbereiniger von Doligez und G.Gonthier, „Portable, unobtrusive garbage collection
for multiprocessor systems",
in Conference Record of the 21st Annual ACM Symposium on Principles
of Programming Languages, ACM SIGPLAN Notices, ACM Press, S.113
bis 123, 1994, und Doligez und X.Leroy, „A concurrent generational
garbage collector for a multi-threaded implementation of ML", Conference Record
of the 20th Annual ACM Symposium on Principles of Programming Languages,
ACM SIGPLAN Notices, ACM Press, S.113 bis 123, 1993. Dieser hochwirksame
Speicherbereiniger ordnet nur nichtänderbare Objekte einem lokalen
Subheap der jungen Generation zu. Die Schlüsselidee ist, dass nichtänderbare
Objekte vervielfältigt
werden können,
ohne die Programmsemantik zu beeinflussen. Der Algorithmus in der
bevorzugten Ausführungsform
der vorliegenden Erfindung kann verwendet werden, um nichtänderbare
Objekte in Java zu identifizieren, und daher zur Verwendung eines
solchen generationalen Speicherbereinigers motivieren.
-
Komponentenspezifikation.
-
Änderbarkeitsinformationen
sind nützlich
für die
Charakterisierung des Verhaltens einer Komponente. Diese Charakterisierung
verbessert die Schnittstellenspezifikation der Komponente und ist
nützlich
dafür,
die Wiederverwendbarkeit der Komponente zu erleichtern. Die üblichen
Vorher- und Nachher-Zustände,
die Eigenschaften an Argumenten festmachen, werden vermehrt durch
Informationen, die durch Folgerungen aus der Änderbarkeit des globalen Zustands
der Komponente gewonnen werden.
-
Untersuchungsreichweite.
-
Softwareuntersuchungen
verwenden Reichweitekriterien, um die Leistungsfähigkeit eines Testsatzes zu
ermitteln. Datenfluss-Untersuchungskriterien basieren üblicherweise
auf „def-use"-Informationen, wie
von A.S.Parrish und S.H.Zweben in „On the Relationships Among
the All-Uses, All-DU-Paths, and All-Edges Testing Criteria", IEEE Transactions
on Software Engineering, Band 21, Nr.12, Dezember 1995, gezeigt.
Es gibt zwei Hauptprobleme bei den üblichen def-use-Kriterien.
Eines tritt im Zusammenhang mit Real-Life-Anwendungen auf, bei denen die Größe der Testsätze als
Ergebnis des Verringerns von Informationen viel zu groß wird.
Das andere liegt beim Untersuchen von Bibliotheken, wo die Wirkung
von Methoden außerhalb
des Analysebereichs berücksichtigt
werden muss. Änderbarkeitsinformationen
können
verwertet werden, um ein Kriterium zu definieren, das schwächer als
die in der Literatur bekannten ist, aber skalierbarer und besser
auf Real-World-Anwendungen anwendbar ist. Im Unterschied zu den
existierenden Kriterien würde
das vorgeschlagene Änderbarkeitskriterium
auch Standorte abdecken, an denen eine Variable externer Veränderung
ausgesetzt wird, zusätzlich
zu Standorten, an denen herausgefunden wird, dass die Variable intern
verändert
wird.
-
Wertebereich.
-
Testdatensatzwerte
werden im Allgemeinen unter Verwendung von Standardgrenzwerten gebildet. Die
Domänengröße in wirklichen
Anwendungen ist riesig. In der Praxis begrenzt dies die Anwendbarkeit
des Untersuchungsmodells. Das Identifizieren konstanter Objekte
verringert das Problem der Domänengröße merklich
und erleichtert somit die wirksame Verwendung des Modells.
-
Definitionen
für Änderbarkeit
in Java sind niemals ausdrücklich
formuliert worden. Die bevorzugte Ausführungsform der vorliegenden
Erfindung präsentierte
Definitionen, die als Grundlage für weitere Forschungsarbeiten
auf diesem Gebiet dienen können
und einen innovativen Ansatz für
statische Analyse veranschaulichen, um änderbare und nichtänderbare
Variablen, Felder, Objekte und Klassen automatisch aufzufinden.
Einer der Hauptbeiträge,
die von der vorliegenden Erfindung erbracht werden, liegt darin,
dass sie mit einer offenen Analyse zurechtkommt, und daher jede
Java-Komponente im Analysebereich annehmen kann. Die Ergebnisse
des Änderbarkeitsanalysator-Werkzeugs
zeigen die Stärke
des Ansatzes und bestätigen
die Integrität
der Definitionen. Trotz der Tatsache, dass eine offene Analyse angewendet
worden ist, kategorisiert der Änderbarkeitsanalysator
erfolgreich sowohl Klassenvariablen als auch Instanzvariablen und
Klassen für
die Java-Laufzeit.
-
Eine
der zentralen Entscheidungen bei der Entwicklung, die die Realisierung
der bevorzugten Ausführungsform
vorantrieben, war es, grundlegende Kernbibliotheken wie CFParse
und JAN zu verwenden und allgemeine Maschinen für intraprozedurale und interprozedurale
Analysen einzuführen.
Der Code ist dafür
so ausgelegt, dass er skalierbar ist und in einen Rahmen der vielschichtigen
statischen Analyse passt, so dass Dienstprogramme und Teilanalysen
verwendet und darauf ausgedehnt werden können, sich mit anderen Eigenschaften
als Änderbarkeitscharakterisierung
zu befassen.
-
Statische
Analyse ist in einigen Fällen
beschränkt.
Deswegen werden in anderen Ausführungsformen für Eigenschaften,
die die Analyse nicht statisch auffinden können wird, intelligente Kommentare
erleichtert, um diese Fälle
in der Laufzeit aufzufinden. Dies kann erreicht werden durch Verwenden
der CFParse-Kernbibliothek, um Klassendateien zu analysieren, zu
editieren und zu kommentieren.
-
Zusätzlich werden
andere Ausführungsformen
der vorliegenden Erfindung die Ausweitung der Analyse leiten, wie
z.B. Intervall-Nicht-Änderbarkeitsanalyse,
um die Nicht- Änderbarkeit
von Variablen in bestimmten Intervallen zu bestimmen, z.B. während des
Aufrufs einer bestimmten Methode, und modulare Nicht-Änderbarkeitsanalyse,
die ein Zusammenschalten der Ergebnisse der Änderbarkeitsanalysen von Unterkomponenten
erlauben würde,
um eine Analyse der vollständigen
Komponente zu erhalten. Die Definition der Nicht-Änderbarkeit,
die von einem bestimmten Initialisierungspunkt aus beginnt, kann
verbessert werden, um träge
Initialisierung abzudecken, d.h. Fälle, in denen eine Variable
nur einmal eingestellt wird, aber nicht notwendigerweise während der
Klassen- oder Instanzinitialisierung.
-
Während die
Erfindung mit Bezug auf eine bestimmte bevorzugte Ausführungsform
gezeigt und beschrieben worden ist, wird der Fachmann verstehen,
dass verschiedene Veränderungen
in Form und Einzelheiten gemacht werden können, ohne von dem Umfang der
Erfindung abzuweichen, wie er durch die anliegenden Ansprüche definiert
ist.