Document

Werbung
Übersetzung objektorientierter Sprachen
André Christ
Münster, 5. Januar 2007
Seminar Übersetzung von künstlichen Sprachen
Gliederung
1. Objektorientierte Konzepte
2. Übersetzung
1. Klassen und Methoden
2. Vererbung
3. Parametrisierung
3. Zusammenfassung & Fazit
1 / 30
Objektorientierte Programmierung
Dr. Alan Kay: „Objektorientierte Programmierung“
Austausch von Nachrichten zwischen Objekten
Objekte
Zustand: Instanzvariablen
Verhalten: Methoden
Identität: Bei Erzeugung (Instanziierung) festgelegt (new)
Nachrichten
Anfrage, eine Operation auf einem Objekt auszuführen
Zur Laufzeit wird „passende“ Methode ausgewählt und ausgeführt
Syntax z.B.: obj.m() oder obj->m()
1. Objektorientierte Konzepte
2 / 30
Klassen und Methoden
Klasse
Beschreibt Menge von Objekten gleicher Struktur (Methoden,
Instanzvariablen)
Klassendefinition: Instanzvariablen und Methoden
Führt neuen Datentyp ein
Methoden
Mögliche Operationen eines Objekts
Vergleichbar mit Funktionen (Prozeduren) aus imperativen Sprachen
Aber: Können auf Instanzvariablen ihres Objekts zugreifen (this)
public class IntStack {
private int size;
private int[] data;
public int pop() {
return this.data[this.size – 1];
this.size--;
}
// Instanzvariable
// Instanzvariable
// Methode
}
1. Objektorientierte Konzepte
3 / 30
Vererbung
Vererbung
B erbt von A: Alle Instanzvariablen und Methoden
einer Superklasse A in der Unterklasse B enthalten
Spezialisierung
Unterklasse kann Instanzvariablen und Methoden
hinzufügen
Implementierung geerbter Methoden änderbar (Signatur
gleich  Invariante Spezialisierung)
Figur
Flaeche()
Kreis
Flaeche()
Radius()
Einfachvererbung (1 Superklasse, V-Baum)
Mehrfachvererbung (n Superklassen, V-Graph)
Teiltypregel
a,b Variablen der Klassen A, B
Zuweisung a = b gültig, falls A und B identisch oder
B Unterklasse (auch indirekt) von A
Zugriff nur über in A definierte Schnittstelle (A-Sicht
auf B)
1. Objektorientierte Konzepte
4 / 30
Polymorphie
Polymorphie („Vielgestaltigkeit“)
Variablen, Datenobjekte sowie Argument- und Rückgabewerte
können mehr als einen Datentypen annehmen
Prinzip nicht auf objektorientierte Sprachen beschränkt
Polymorphie insbesondere auch bei funktionalen Sprachen
Klassifikation
Subklassen-Polymorphie
Parametrische-Polymorphie
Überladen
Coercion
Universelle-Polymorphie
Ad hoc-Polymorphie
Nach: Strachey / Cardelli, Wegner
1. Objektorientierte Konzepte
5 / 30
Subklassen-Polymorphie
public class Kreis extends Figur {…}
Figur
Flaeche()
Kreis k = new Kreis();
k.Flaeche();
// Kreis::Flaeche()
Figur f = new Kreis();
f.Flaeche();
// Superklassenkontext
// Kreis::Flaeche()
Kreis
Flaeche()
Anwendungsfall: Plugins / Frameworks
Methoden-Auswahlregel
Ein Objekt einer Unterklasse B von A kann im Superklassenkontext
von A verwendet werden
Methode m wird in Unterklasse überschrieben
Methode m muss auch dann ausgeführt werden, wenn das Objekt B
in Variable vom Typ A vorliegt (A-Sicht auf B)
Erst zur Laufzeit bestimmbar, welche Instanz die Verarbeitung
einer Nachricht übernimmt
1. Objektorientierte Konzepte
6 / 30
Parametrische-Polymorphie
Motivation: Gleiche Funktionalität für mehrere Datentypen
notwendig (insb. Datenbehältern)
Bisher (Unsicherer Cast / Typüberprüfung)
Stack oldStack = new Stack();
oldStack.push(new Integer(2));
String top = (String) oldStack.pop();
if (oldStack.pop() instanceof String)
// Unsafe Downcast
// instanceof type check
Generische Klasse mit formalem Parameter T
public class Stack<T> {
public void push(T element) {…}
public T pop() {…}
}
Instanziierung mit aktuellem Parameter String:
Stack<String> stringStack = new Stack<String>();
stringStack.push("Hello World");
String top = stringStack.pop();
1. Objektorientierte Konzepte
7 / 30
Gliederung
1. Objektorientierte Konzepte
2. Übersetzung
1. Klassen und Methoden
2. Vererbung
3. Parametrisierung
3. Zusammenfassung & Fazit
8 / 30
Übersetzung
Aufspaltung des Übersetzungprozesses
Abstrakte Maschine: Zwischencode, an Quellsprache angepasst
Reale Maschine: Maschinencode, durch Prozessorarchitektur
bestimmt (Weit verbreitet: CISC und RISC)
Abstrakte Maschine für objektorientierte Sprache
Programmspeicher (Zwischencode)
Befehlsinterpreter (Ausführung des Zwischencodes)
Stack (Methodeninkarnationen in Frames)
Heap (u.a. Instanzen von Klassen)
Virtuelle Maschine für objektorientierte Sprache
Ausführungsumgebung moderner obj. Sprachen C# / Java
Ausprägung einer abstrakten Maschine
Ausführung des Zwischencodes zur Laufzeit des Programms
2. Übersetzung
9 / 30
Abstrakte Maschine
Klassen- 5
deskriptor
Klassendeskriptor
Befehlszähler
...
Framepointer
Methodenrumpf 6
Methodenrumpf 0
Objekt
Instvar 0
Objekt
3
Instvar 1
...
Objekt
Instvar n
...
Register
Hilfspeicher
...
Main()
Virtuelle
4
Methodentabelle
Befehlsinterpreter
Heap
Frame
Programmspeicher
2
Object 1
Frame
Object 2
1
...
Object n
Stack
Verweis (Zeiger)
Befehlszähler: Zeigt auf abzuarbeitenden Befehl in Methodenrumpf
Framepointer: Verweist auf den Frame (lokale Variablen einer Methode) einer
Methodeninkarnation (passend zu Befehlszähler)
In Anlehung an: Bauer, Höllerer 1998
2. Übersetzung
10 / 30
Klassendeskriptor
Methodentabelle: Indizierte Datenstruktur mit Methodenselektoren (Namen)
Methodenselektor verweist auf entsprechenden Methodenrumpf im Methodenarray
Detaillierung Klassendeskriptor:
ClassFile {
// Referenz auf Superklasse
u2 super_class;
Superklasse
Methodentabelle
# Methoden
# Instanzvariablen
Methodenarray
Klassendeskriptor
Auszug aus Java-class Datei:
MethodenSelektor 1
MethodenSelektor 2
// Anzahl Instanzvariablen
u2 fields_count;
Methodenrumpf 1
Methodenrumpf 2
// Anzahl der Methoden
u2 methods_count;
...
...
Methodenrumpf n
MethodenSelektor n
Methodenarray
Methodentabelle
// Name u. Typ d. Instanzvar.
field_info fields[field_count];
// Methodentabelle
method_info methods[methods_count];
[…]
}
In Anlehung an: Bauer, Höllerer 1998
2. Übersetzung // Klassen und Methoden
11 / 30
Übersetzung von Methoden (1)
Methodenrumpf im Wesentlichen wie Funktions- oder
Prozedurrümpfe imperativer Sprachen
Variablen, Schleifen, Verzweigungen
...
Objektorientierte Sprachkonstrukte (in Methoden)
Senden einer Nachricht:
Object.message() (auch O->m())
Zugriff auf Instanzvariable: Object.variable
this
Selbstreferenz:
this.message()
/
Zugriff auf Superklasse:
super.message() /
2. Übersetzung // Klassen und Methoden
(auch O->v)
(auch self)
this.variable
super
(auch parent)
super.variable
12 / 30
Übersetzung von Methoden (2)
Realisierung der Selbstreferenz this
Methode m einer Klasse K
Übersetzt als Funktion:
Nachricht m an Objekt o vom Typ K:
Umgewandelt in Funktionsaufruf:
<ret> m(<args>)
<ret> Km(K this, <args>)
<ret> o.m(<args>)
<ret> Km(o, <args>)
Abbildung auf Konzept von Funktionen / Prozeduren imp. Sprachen
Methodennamen -> Funktionsnamen
Problem: Globaler Namensraum von Funktionen
Funktionen müssen sich in ihrem Namen unterscheiden
Codierungsschema: _ZN#<Klasse>#<Methode>E<Typ>* (GNU G++ 3.0)
Stack::push(int element)
Stack::push(float element)
Stack::push(float comp, float imag)
2. Übersetzung // Klassen und Methoden
_ZN5Stack4pushEi
_ZN5Stack4pushEf
_ZN5Stack4pushEff
13 / 30
Methodenaufrufe
Statisches Binden (imp. Prozedur- und Funktionsaufruf)
Funktionsaufruf wird zur Übersetzungszeit der Definition der
Funktion zugeordnet
Nach Typüberprüfung von Argument- und Rückgabewerten legt
Übersetzer relative Speicheradresse fest
Dynamische Bindungsregel
„Überschreibt eine Klasse B eine Methode ihrer Superklasse A und
wird eine Nachricht m an ein Objekt geschickt, dessen
Klassenzugehörigkeit zur Übersetzungszeit nicht bekannt ist, so
muss die Methodenimplementierung zur Laufzeit an das Objekt
gebunden werden.“
Bauer, Höllerer 1998
2. Übersetzung // Vererbung
14 / 30
ur
Dynamisches Binden mittels vtable
Virtuelle Methodentabelle (vtable)
In C++ auch virtuelle Funktionstabelle
Sog. virtuelle Methoden in Unterklassen überschreibbar
Einträge in der vtable verweisen auf Methodenimplementationen
Sichten: Offsets in der vtable (siehe geschweifte Klammern)
vtable
f : Figur
0:Figur::Flaeche()
vtable
0:Kreis::Flaeche()
1:Kreis::Radius()
virtual Flaeche()
k : Kreis
r : Rechteck
Flaeche()
Flaeche()
Radius()
Ecken()
q : Quadrat
Kante()
vtable
0:Rechteck::Flaeche()
Figur
Rechteck
1:Rechteck::Ecken()
vtable
0:Rechteck::Flaeche()
1:Rechteck::Ecken()
Figur
Rechteck
Quadrat
2:Quadrat::Kante()
2. Übersetzung // Vererbung
15 / 30
Realisierung vtable in C++
Instanziierung
Quadrat q = new Quadrat();
Objekt erhält Zeiger auf vtable seiner
Klasse
f : Figur
virtual Flaeche()
r : Rechteck
Methodenaufruf
q->Flaeche();
In der vtable wird Adresse der
Funktionsimplementation
nachgeschlagen
Effiziente Implementation durch
Funktionszeiger in C:
Flaeche()
Ecken()
q : Quadrat
Kante()
vtable
0:Rechteck::Flaeche()
1:Rechteck::Ecken()
vtable
0:Rechteck::Flaeche()
1:Rechteck::Ecken()
2:Quadrat::Kante()
Standardisierte Indezierung der vtable
Umwandlung der Methodenaufrufe:
q->Flaeche()  (*(q->vtable[0]))()
2. Übersetzung // Vererbung
16 / 30
Realisierung vtable in C++
Subklassen-Polymorphie
Quadrat q = new Quadrat();
Rechteck f = (Rechteck) q;
f->Flaeche();
Sicht über vtable des Objekts vom Typ Quadrat
q : Quadrat
Kante()
vtable
0:Rechteck::Flaeche()
1:Rechteck::Ecken()
Figur
Rechteck
Quadrat
2:Quadrat::Kante()
2. Übersetzung // Vererbung
17 / 30
Mehrfachvererbung
Diamant Problem (Auszug)
Wiederholte Beerbung: Figur und Linie
erhalten Methoden und Instanzvariablen
die sie an Rechteck weitervererben
Uneindeutigkeit wegen doppelter
Methodennamen und Instanzvariablen
(Skalieren())
Lösungsansatz: Echte
Mehrfachvererbung vermeiden
Mehrfachvererbung nur mit Superklassen
ohne Implementierungsteil (Java, C#)
z.B. Java Interfaces:
public class Rechteck
implements Figur, Linie {
[...]
}
2. Übersetzung // Vererbung
g : GUIObjekt
int farbtiefe
virtual Zeichne()
f : Figur
l : Linie
virtual Flaeche()
Int[][] Koordinaten
virtual Skalieren()
Zeichne()
virtual Skalieren()
r : Rechteck
Flaeche()
Parallelpfad
Realisierung
18 / 30
Übersetzung von Parametrisierung
Ursprung in funktionaler Sprache ML
Viel Gesprächsstoff bzgl. Umsetzung von Parametrisierung
in objektorientierten Sprachen
In C++, Java und C# nachträglich hinzugefügt
“Correction these early oversights in C++ was a long and painful
process, creating years of havoc as compilers never quite supported
the same language, books never quite gave accurate information,
trainers never quite taught the right stuff, and programmers never
quite knew what to think“
(Betrand Mayer 1998, Entwickler der Programmiersprache Eiffel)
Unterschiedliche Strategien
C++ Templates
Java Generics (ab J2SE 5.0)
C# Generische Klassen (ab .NET 2.0)
2. Übersetzung // Parametrisierung
19 / 30
Parametrisierung in C++ (1)
Generische Klassen: C++ Templates
Übersetzer expandiert Templates anhand aktueller Parameter
Für jeden aktuellen Parameter eigene Klasse
Daher: Kopierende (auch heterogene) Übersetzung
Stack<int>
void push(int item)
Stack<T>
int pop()
void push(T item)
T pop()
Stack<float>
void push(float item)
float pop()
2. Übersetzung // Parametrisierung
20 / 30
Parametrisierung in C++ (2)
Umsetzung der Methodenaufrufe durch C++ Compiler
Stack<int> intStackA;
Stack<int> intStackB;
intStackA.push(1);
intStackB.push(2);
Stack<float> floatStack;
floatStack.push(1);
floatStack.pop();
2. Übersetzung // Parametrisierung
_ZN5StackIiE4pushEi
_ZN5StackIfE4pushEf
_ZN5StackIfE3popEv
21 / 30
Parametrisierung in C++ (3)
Strategie
Dem Übersetzungsprozess vorgeschaltete Expansion
Vgl. mit Makro-Expansion durch Präprozessor in C
Bewertung
 Performanz Laufzeit: Parametrisierung bringt keinen Overhead mit
sich – da Abbildung auf bekannte Sprachkonstrukte
 Keine Integration in den Sprachkern – generische Klassen sind nicht
Bestandteil des Typsystems
 Programmgröße wächst stark an (Redundanter Code)
2. Übersetzung // Parametrisierung
22 / 30
Parametrisierung in Java (1)
Java Generics
Hervorgegangen aus Pizza-Projekt (später GJ-Projekt)
Anforderung: Auf unveränderter JVM lauffähig
Seit J2SE 5.0 offizieller Bestandteil
Erasure
Java-Compiler überprüft Typen (aktuelle Parameter)
Formale Parameter werden durch ihren Bound ersetzt und
Typkonvertierungen eingefügt
Ergebnis: Raw Type, frei von generischen Instuktionen
Auch: Homogene Übersetzung
Bound (Obere Grenze):
Implizit Object:
Explizit Number:
2. Übersetzung // Parametrisierung
class Stack<T> {...}
class Stack<T extends Number> {...}
23 / 30
Parametrisierung in Java (2)
Beispiel Erasure Stack<T> mit Bound Object:
Generische Klasse:
Raw Type (nach Erasure):
public class Stack<T> {
public void push(T element){…}
public T pop() {…}
}
public class Stack {
public void push(Object element){…}
public Object pop() {…}
}
Stack<String> st = new Stack<String>();
st.push("Hello World");
Stack st = new Stack();
st.push("Hello World");
String top = st.pop();
String top = (String) st.pop();
2. Übersetzung // Parametrisierung
24 / 30
Parametrisierung in Java (3)
Probleme (u.a.)
Stack<String> strStack = new Stack<String>();
strStack.push("Test");
Object tmp = strStack;
Stack<Integer> intStack = (Stack<Integer>) tmp;
[...]
Integer intVal = intStack.pop();
// Unchecked cast
// without type
// CastException
// later in Code
Bewertung
 Keine Anpassungen an JVM nötig (Prämisse an Pizza-Projekt)
 Overhead durch Typkonvertierungen
 Generische Typen existieren zur Laufzeit nicht mehr
stack instanceof Stack<Integer> nicht möglich
 Primitive Typen (int, float) können keine aktuellen Parameter sein, da
kein gemeinsamer Bound existiert
2. Übersetzung // Parametrisierung
25 / 30
Parametrisierung in C# (1)
Kompatibilität generischer Klasseninstanzen
Datenstrukturen und Algorithmen der aktuellen Parameter identisch
Referenztypen zueinander kompatibel (32-bit Pointer)
Primitiven Datentypen untereinander und zu Referenzzypen inkompatibel
Kopie der virtuellen Methodentabelle für jede generische Klasseninstanz
Kompatible Klasseninstanzen verweisen auf gemeinsamen Code
s1: Stack<string>
s2: Stack<string>
s3 : Stack<object>
s4 : Stack<int>
vtable Pointer
vtable Pointer
vtable Pointer
vtable Pointer
Instvar 1
Instvar 1
Instvar 1
Instvar 1
...
...
...
...
Instvar n
Instvar n
Instvar n
Instvar n
push()
push()
push()
pop()
Code push()
pop()
pop()
Code push()
...
Code pop()
...
...
Code pop()
object
int
string
vtable Stack<string>
2. Übersetzung // Parametrisierung
vtable Stack<object>
vtable Stack<int>
26 / 30
Parametrisierung in C# (2)
Strategie
Kombination der Vorteile der heterogenen Übersetzung (C++) und
der homogenen Übersetzung (Java)
Bauer, Höllerer: Echte generische Übersetzung
Bewertung
 Völlständige Integration generischer Typen in den Sprachkern
 Typüberprüfungen auch zur Laufzeit möglich
 Wenig Overhead zur Laufzeit da Typkonvertierungen nicht nötig
 Zusätzliche Verwaltung von vtables (Aber: Effiziente Implementation
mit vtable Dictionaries möglich)
2. Übersetzung // Parametrisierung
27 / 30
Gliederung
1. Objektorientierte Konzepte
2. Übersetzung
1. Klassen und Methoden
2. Vererbung
3. Parametrisierung
3. Zusammenfassung & Fazit
28 / 30
Zusammenfassung & Fazit
Grundlagen objektorientierter Sprachen
Klassen und Objekte
Methoden und Nachrichten
Polymorphie
Übersetzung
Abstrakte Maschine
Klassen und Methoden  Gemeinsamkeiten mit imp. Prozeduren
Vererbung  echte Mehrfachvererbung wird meist vermieden
Strategien zur Realisierung von Parametrisierung
Hintergrund: Diskussionen um C++ Templates und Java Generic
Tieferes Verständnis für das objektorientierte Paradigma & für die
Realisierung in konkreten objektorientierten Sprachen
3. Zusammenfassung & Fazit
29 / 30
Vielen Dank
für Eure
Aufmerksamkeit!
Seminar Übersetzung von künstlichen Sprachen
Realisierung Mehrfachvererbung
g : GUIObjekt
int farbtiefe
GUIObjekt
virtual Zeichne()
Figur
GUIObjekt
Figur
Linie
Linie
Rechteck
f : Figur
l : Linie
virtual Flaeche()
Int[][] Koordinaten
virtual Skalieren()
Zeichne()
Rechteck
b.1) Mehrfache Instantiierung (von GUIObjekt)
virtual Skalieren()
GUIObjekt
Figur
k : Kreis
r : Rechteck
Flaeche()
Flaeche()
Zeichne()
a) Vererbungsgraph (Diamant)
Figur
Linie
Rechteck
Linie
Rechteck
b.2) Einfache Instantiierung (von GUIObjekt)
Zurück
2. Übersetzung // Vererbung
31 / 30
Mehrfachvererbung
Beispiel:
Figur* f = new Rechteck();
f->Zeichne();
// Linie::Zeichne() auf
g : GUIObjekt
int farbtiefe
virtual Zeichne()
Nachricht im Pfad:
GUIObjekt <- Figur <- Rechteck
Aufruf im Parallel-Pfad:
GUIObjekt <- Linie <- Rechteck
f : Figur
l : Linie
virtual Flaeche()
Int[][] Koordinaten
virtual Skalieren()
Zeichne()
virtual Skalieren()
Folgt Regeln der Polymorphie
Aber: Programmierer hätte erwarten
können, dass GUIObjekt::Zeichne()
aufgerufen wird (falls Pfad nicht offen
liegt – Teamarbeit, Bibliothek)
r : Rechteck
Flaeche()
Zurück
2. Übersetzung // Vererbung
32 / 30
Herunterladen