Table Of ContentFernUnivesität in Hagen
Fachbereich Mathematik und Informatik
Lehrstuhl Programmiersysteme
Abschlussarbeit im Studiengang Bachelor of Science im Fach Informatik
Ein EclipsePlugin zum Aufspüren toter
Codefragmente auf Basis von
Sichtbarkeitsconstraints
Autor: Anastasia Pasenkova
Betreuer: Prof. Dr. Friedrich Steimann, Dipl.Inf. Andreas Thies
Inhaltsverzeichnis
1 Einleitung und Motivation....................................................................................................1
1.1 Aufbau der Arbeit.........................................................................................................1
2 Das Problem toten Codes.....................................................................................................2
2.1 Arten von totem Code...................................................................................................2
2.2 Auswirkungen von totem Code.....................................................................................3
3 Sichtbarkeitsconstraints als Grundlage für die Erkennung toten Codes................................8
3.1 Das Constraintsystem ..................................................................................................8
3.1.1 ConstraintGrundlagen..........................................................................................9
3.1.2 Sichtbarkeitsconstraints.......................................................................................10
3.1.3 Constraints des CDCDPlugin.............................................................................14
3.1.4 Implementierung des ConstraintSystems...........................................................16
4 Aufbau und Funktionsweise des CDCDPlugins................................................................17
4.1 Anforderungen an den CDCD.....................................................................................17
4.2 Entfernen toten Codes als Refactoring........................................................................20
4.3 Implementierung des Plugins.....................................................................................20
4.3.1 Paketstruktur.......................................................................................................20
4.3.2 Programmablauf.................................................................................................21
4.3.2.1 Start der Suche............................................................................................21
4.3.2.2 Die Klasse org.intoj.cdcd.CDCDetector.....................................................22
4.3.2.3 Aufbau und Lösung des Constraintsystems................................................23
4.3.2.4 Analyse der Lösung....................................................................................26
4.3.2.5 Darstellung der Ergebnisse.........................................................................29
4.3.3 Entfernen toten Codes........................................................................................33
4.3.3.1 QuickFixes..................................................................................................33
4.3.3.2 Refactoring in Eclipse................................................................................36
4.3.3.3 CDCDRefactorings...................................................................................37
4.3.3.4 Entfernen aller toten Deklarationselemente................................................45
4.3.4 Theoretische Aspekte ........................................................................................45
4.3.4.1 Eindeutigkeit der optimalen Lösung ..........................................................45
4.3.4.2 Notwendigkeit der Änderung aller Sichtbarkeiten......................................47
5 Analyse des CDCDPlugins................................................................................................49
5.1 Anwedungsszenarien und Ergebnisse.........................................................................49
5.2 Verifikation der Funktionsweise des CDCDPlugin....................................................51
5.3 Qualitative Analyse der Ergebnisse............................................................................52
5.3.1 Projekt codec......................................................................................................52
5.3.2 Projekt DeadCodeDetector.................................................................................53
5.3.3 Projekt jaxen.......................................................................................................53
5.3.4 Projekt jbook......................................................................................................54
5.3.5 Projekt Jester......................................................................................................54
5.3.6 Projekt JHotDraw...............................................................................................55
5.3.7 Projekt schemalizer............................................................................................55
5.4 Laufzeitverhalten........................................................................................................56
5.5 Bewertung der Ergebnisse..........................................................................................56
6 Existierende Ansätze zum Aufspüren toten Codes in JavaProgrammen............................58
6.1 Analyse von JavaBytecode........................................................................................58
6.1.1 Aufbau eines Aufrufgraphen...............................................................................58
6.1.2 Aufbau eines Abhängigkeitsgraphen...................................................................61
6.1.3 Analyse des abstrakten Syntaxbaums..................................................................61
6.1.4 Weitere Ansätze .................................................................................................62
6.2 Analyse von JavaSourcecode.....................................................................................63
6.2.1 Eliminierung toten Codes als CompilerOptimierung.........................................63
6.2.2 Dead Code Detektoren als Hilfswerkzeuge für Softwareentwickler...................64
6.3 Einschränkungen statischer Programmanalyse...........................................................65
7 Zusammenfassung und Ausblick........................................................................................66
7.1 Ausblick......................................................................................................................67
7.1.1 APIElemente......................................................................................................67
7.1.2 TestKlassen........................................................................................................67
7.1.3 Unbenutzte InterfaceMethoden..........................................................................68
7.1.4 Reflection............................................................................................................68
Abbildungsverzeichnis..........................................................................................................69
Listingverzeichnis..................................................................................................................69
Tabellenverzeichnis...............................................................................................................70
Literaturverzeichnis...............................................................................................................71
Anhang A Bedienungsanleitung für das CDCD Plugin.......................................................73
Anhang B Inhaltsverzeichnis der beiliegenden CD..............................................................80
1 Einleitung und Motivation
Die vorliegende Arbeit befasst sich mit dem Problem toten Codes in JavaProgrammen,
speziell mit deklarierten Typen, Methoden und Attributen, die in einem Programm an keiner
Stelle verwendet werden. Die Existenz solcher Programmelemente kann verschiedene
Ursachen haben. Meist handelt es sich um Programmteile, deren Funktionalität im Lauf der
Programmentwicklung überflüssig geworden ist, weil sie von anderen Teilen übernommen
wurde oder weil sie ursprünglich nur zu Testzwecken vorhanden war. In [Tip02] wird auch
die Modularisierung von Programmen als mögliche Ursache genannt: wenn Komponenten
einer Anwendung unabhängig voneinander von verschiedenen Entwicklern entworfen und
implementiert werden, kann die von anderen Komponenten genutzte Funktionalität nicht
immer genau vorhergesehen werden, was die Existenz überflüssiger Methoden in der
Endkomponente nach sich ziehen kann.
Moderne IDEs wie Eclipse [EclURL] warnen zwar, wenn private Codeelemente nicht
verwendet werden, die Erkennung unbenutzter Elemente anderer Sichtbarkeit bleibt jedoch
dem Entwickler überlassen. Der in dieser Arbeit vorgestellte Ansatz zum Aufspüren solcher
Codeelemente basiert ebenso auf statischer Analyse von JavaSourcecode, bei der die
zulässige Sichtbarkeit jedes Programmelementes durch Constraints bestimmt wird. Tote
Elemente sind demnach diejenigen, für die durch kein Constraint eine minimale Sichtbarkeit
gefordert wird. Praktisch umgesetzt wurde das SichtbarkeitsConstraintModell in [Ste09]. In
der vorliegenden Arbeit wurde auf dieser Grundlage ein Plugin für Eclipse entwickelt, das
unter Benutzung der SichtbarkeitConstraintFunktionalität tote Codeelemente eines Java
Projektes identifiziert, die einen Zugriffsmodifikator besitzen können – eine Bedingung, die
aus dem Umstand resultiert, dass in Java die Sichtbarkeit von Elementen durch
Zugriffsmodifikatoren, bzw. deren Abwesenheit, festgelegt wird. Dieses Plugin mit dem
Namen Constraint based Dead Code Detector, kurz CDCD, verfolgt das Ziel, Entwickler
durch die Möglichkeit automatischer Erkennung unbenutzter Funktionalität bei der Wartung
des Programmcodes zu unterstützen und bietet die Möglichkeiten, tote Codeelemente
auszukommentieren oder zu löschen.
1.1 Aufbau der Arbeit
Das anschließende Kapitel 2 verschafft einen Überblick über das Problem toten Codes, in
Kapitel 3 werden die Sichtbarkeitsconstraints beschrieben, auf deren Grundlage das
Aufspüren toter Codeelemente im CDCDPlugin erfolgt. Die Implementierungsdetails, sowie
die Funktionsweise des CDCDPlugins werden in Kapitel 4 erläutert. Kapitel 5 befasst sich
1
mit der Leistungsanalyse des CDCDPlugins bei Anwendung auf verschiedenen Java
Projekten. In Kapitel 6 werden die bereits existierenden Ansätze, toten Code in einem
Programm ausfindig zu machen, beschrieben und ein Vergleich mit dem CDCD angestellt.
Kapitel 7 schließt die Arbeit mit einer Zusammenfassung und dem Ausblick ab.
2 Das Problem toten Codes
2.1 Arten von totem Code
In der Literatur findet sich keine einheitliche Definition des Begriffs „toter Code“, es ist
dabei aber immer von Code die Rede, der im Programm nicht benutzt wird und dessen
Entfernung keine Auswirkungen auf die Programmfunktion hat, wohl aber zur Reduzierung
der Größe und besserem Laufzeitverhaltens des Programms, sowie zur Verbesserung der
Übersichtlichkeit und Verständlichkeit des Programmcodes, beitragen kann.
Toter Code kann unerreichbaren Code mit einschließen, d.h. Code, der nie ausgeführt werden
kann, da kein Pfad im Kontrollfluss zu ihm führt [Deb00], vgl. Listing 1:
public void m() {
int i = 1;
if (i > 0) {
// ...
} else {
// wird nie erreicht
}
}
Listing 1: Unerreichbarer Code
Innerhalb von Methoden werden Deklarationen nicht referenzierter lokaler Variablen als toter
Code bezeichnet. Auch Anweisungen, die zwar ausgeführt werden, aber keinen Einfluss auf
die Programmfunktion haben oder Berechnungen, deren Ergebnisse nicht weiter im
Programm verwendet werden, können als toter Code betrachtet werden [Deb00]. Diese Arten
von totem Code, dargestellt in Listing 2, werden bereits durch den Java Compiler [JavURL]
erkannt und bei der Übersetzung des Source in Bytecode verworfen.
2
public void m() {
int i;
// Unbenutzte lokale Variable
int j = 0;
// Unnötige Berechnung, n wird nicht mehr verwendet
int n = 0;
for (i=0; i< 10; i++){
n++;
}
// Überflüssige Anweisung i = 1+1;
i = 1+1;
i = 0;
}
Listing 2: Toter Code in Methoden
Manche Compiler, z.B. der Eclipse Compiler [EclURL], können auch unbenutzten Code auf
Klassenebene, wie private Attribute, Methoden und innere Klassen, erkennen. Das in dieser
Arbeit vorgestellte CDCDPlugin erweitert Eclipse um die Möglichkeit, tote
Deklarationselemente mit beliebigen Zugriffsmodifikatoren aufzuspüren. Im Einzelnen
handelt es sich dabei um Klassen, Interfaces, EnumTypen, Methoden (inklusive
Konstruktoren), sowie Attribute und EnumKonstanten, die im umschließenden Projekt an
keiner Stelle benutzt werden, was so viel bedeutet, dass sie weder referenziert werden, noch
dass deren Existenz nötig ist, um die syntaktische und semantische Korrektheit des
Programms zu gewährleisten.
2.2 Auswirkungen von totem Code
In erster Linie wirkt sich die Existenz von totem Code in einem Programm negativ auf die
Wartbarkeit und Verständlichkeit des Sourcecode aus. Wie die Beispiele aus Abschnitt 2.1
zeigen, kann die Funktion einer Methode mit totem und unerreichbaren Code nicht so
einfach nachvollzogen werden, was die Fehleranalyse erschwert.
Eine potentielle Fehlerquelle, was die Semantik des Programms angeht, ist in Listing 3
dargestellt, das einen Ausschnitt aus der Klasse NullHandle des Projekts1 JHotDraw2 zeigt.
1 Die Versionen aller Projekte, auf die im Folgenden verwiesen wird, sind in Tabelle 1 (Kapitel 5.1)
aufgeführt.
2 http://www.jhotdraw.org/
3
public class NullHandle extends LocatorHandle {
/**
* The handle's locator.
*/
protected Locator fLocator;
public NullHandle(Figure owner, Locator locator) {
super(owner, locator);
}
// ...
Listing 3: Unbenutztes Attribut als potentielle Fehlerquelle
Hier wird ein Attribut fLocator deklariert, obwohl in der Superklasse ein gleichnamiges
Attribute existiert, das als private deklariert ist. Das neue Attribut wird nirgendwo initialisiert
und an keiner Stelle im Programm benutzt, daher sollte es entfernt werden – Zugriffe von
neuen Subklassen aus könnten zu unerwarteten Ergebnissen, im schlimmsten Fall einer
NullPointerException, führen.
Das Projekt JHotDraw enthält weitere Beispiele von totem Code, der zur Fehleranfälligkeit
eines Programms beiträgt und zudem Entwicklern die Einarbeitung in das Projekt erschwert.
So finden sich in der Klasse CommandMenu folgende zwei Methoden:
public synchronized void remove(Command command) {
throw new JHotDrawRuntimeException("not implemented");
}
public synchronized void remove(MenuItem item) {
throw new JHotDrawRuntimeException("not implemented");
}
Listing 4: Unbenutzte Methoden der Klasse CommandMenu
Hier scheint es sich um einen Fall zu handeln, in dem eine Funktionalität geplant war, jedoch
später aufgegeben wurde, die angelegten Methoden aber erhalten geblieben sind.
Ein ähnlicher Fall findet sich in der Klasse PaletteButton: hier ist eine Methode value()
deklariert, die lediglich null zurückgibt und nirgendwo benutzt wird. Womöglich war in der
Klasse die Möglichkeit vorgesehen, einen bestimmten Wert zu speichern, der mittels dieser
Methode erfragt werden kann, ist aber nicht weiter verfolgt worden.
Im Projekt schemalizer3 ist ein weiteres Beispiel zu finden: in der Klasse
SimpleTypeInferrer hat die Methode buildValidatorSubElements() einen leeren Rumpf.
Auch die Umstrukturierung von Code kann zur Existenz toter Codeelemente führen: die
Klasse StandardDrawingView des Projekts JHotDraw enthält die Methoden
keyTyped(KeyEvent e) und keyReleased(KeyEvent e), beide mit leerem Rumpf. Dem
3 http://schemalizer.sourceforge.net/
4
Namen der Methoden nach handelt es sich um Methoden des Interfaces
java.awt.event.KeyListener, das die Klasse wohl einst implementiert hat. In der
vorliegenden Version enthält StandardDrawingView eine innere Klasse, die das
KeyListenerInterface implementiert. Dem Anschein nach wurde beim Löschen des
implementsStatements das Entfernen dieser beiden Methoden vergessen. Weitere Beispiele
für toten Code, der aus der Ersetzung einer Funktionalität durch eine andere resultiert, finden
sich in den Listings 5 und 6:
/**
* Handles a change of the current selection. Updates all
* menu items that are selection sensitive.
* @see DrawingEditor
*/
public void figureSelectionChanged(DrawingView view) {
setupAttributes();
}
public void viewSelectionChanged(DrawingView oldView,
DrawingView newView) {
}
Listing 5: Obsolete Methode der Klasse DrawApplet
Hier sind zwei ListenerMethoden deklariert, von denen die zweite einen leeren Rumpf hat
und nirgendwo benutzt wird, jedoch immer noch im Programmcode vorhanden ist.
/**
* Open a new window for this application containing the
* passed in drawing,or a new drawing if the passed
* in drawing is null.
*/
public void newWindow(Drawing initialDrawing) {
DrawApplication window = createApplication();
if (initialDrawing == null) {
window.open();
} else {
window.open(window.createDrawingView(initialDrawing));
}
}
public final void newWindow() {
newWindow(createDrawing());
}
Listing 6: Obsolete Methode der Klasse DrawApplication
Die Methode newWindow() hat zwar keinen leeren Rumpf, wie in den vorherigen Beispielen,
wurde vom CDCDPlugin aber als unbenutzt eingestuft. Die Existenz der überladenen
Methode, sowie die Abwesenheit eines JavadocKommentars lässt vermuten, dass
newWindow() im Lauf der Entwicklung ersetzt, jedoch nicht gelöscht wurde.
5
Ähnliche Beispiele finden sich auch in anderen Projekten – Listing 7 zeigt einen Ausschnitt
aus der Klasse State des Projekts jbook4. Die laut CDCD toten Attribute in den Zeilen 2 bis 7
werden nach dem Einführen des Attributs props (Zeile 8) nicht mehr benötigt, da der
Zustand nun in den Properties und nicht in den Attributen gespeichert wird. Die Attribute
wurden jedoch nicht aus dem Programm entfernt und es wird, da sie als paketlokal deklariert
sind, keine Warnmeldung von Eclipse über unbenutzte Attribute gemeldet.
In Abb. 1 ist das unbenutzte Interface XMLFileChangedListener des Projekts schemalizer
1:public class State {
2: Color colorBackground = Color.black;
3: Color colorText = Color.yellow;
4: int iX = -1;
5: int iY = -1;
6: int iWidth = -1;
7: int iHeight = -1;
8: Properties props = new Properties();
9: // ...
10: public void setWidth(int iWidth) {
11: setIntProperty("width", iWidth);
12: }
13:
14: public int getWidth() {
15: return getIntProperty("width", -1);
16: }
17: // ...
18:}
Listing 7: Obsolete Attribute im Projekt jbook
zu sehen. Im Package Explorer View von Eclipse ist das Interface XMLFileClosedListener
markiert, das vermutlich als Ersatz des XMLFileChangedListenerInterfaces eingeführt
wurde, da nur das Ereignis „Schließe XMLDatei“ sich für das Programm als relevant
herausgestellt hat.
4 http://jbook.sourceforge.net/
6
Abbildung 1: Unbenutztes Interface im Projekt schemalizer
All diese Beispiele zeigen, dass toter Code die Fehleranfälligkeit eines Programms erhöhen
kann, wenn in der Weiterentwicklung des Programms von Komponenten Gebrauch gemacht
wird, die keine sinnvolle Funktion mehr haben, es aber keine direkten Hinweise im
Sourcecode auf diesen Umstand gibt.
Unbenutzter Code hat auch Auswirkungen auf die Größe des Programms, vor allem, wenn es
sich um Code von externen Bibliotheken handelt, von denen nur ein geringer Teil im
Programm verwendet wird. Die Entfernung unbenutzter Bibliotheksfunktionalität trägt dazu
bei, dass die Größe von im Internet übertragbaren Programmen, wie Java Applets, und damit
deren Übertragungszeit minimiert wird, indem nur so viel Code wie nötig mit übertragen
wird [Tip03]. Auch bei eingebetteten Systemen spielt die Programmgröße eine wichtige
Rolle, da hier Ressourcen wie Rechenleistung, sowie zur Verfügung stehender Arbeits und
Datenspeicher im Vergleich zu herkömmlichen PCs stark eingeschränkt sind [Tip99] .
7
Description:Lehrstuhl Programmiersysteme. Abschlussarbeit im Studiengang Bachelor of Science im Fach Informatik. Ein EclipsePlugin zum Aufspüren toter.