Entwickler-Blog · Thomas Herchenröder · 20.08.20

Delphi to Java - Ein Sourcecode Übersetzer

Kann man alten Code automatisch in eine aktuelle Programmiersprache übersetzen? Unser Innovation Engineer Thomas Herchenröder berichtet im Blog über seine Erfahrungen mit dem Delphi to Java Sourcecode Übersetzer.

Motivation

Wenn die eigene Code Base groß ist und verschiedene Technologien umfasst, die über die Zeit zusammengekommen sind, erwächst der Wunsch, diese zu vereinheitlichen und so Komplexität zu reduzieren. Entwickler:innen müssen dann nicht mehr mit mehreren Tools und Sprachen umgehen, deren Eigenarten kennen und Code-Veränderungen durch Neuentwicklung und technologischen Fortschritt in verschiedene Welten abbilden. Weiterbildung und Ressourcenplanung vereinfachen sich, Kapazitäten werden frei um sich mit technologischen Innovationen zu beschäftigen - die Welt bleibt ja nicht stehen. Nicht zuletzt deshalb beschäftigen wir uns bei MACH mit der Ablösung unseres Delphi Codes.

Delphi ist eine objekt-orientierte Programmiersprache, die auf Pascal aufbaut. Sie war besonders populär zu den Zeiten, als man native, grafische Applikationen für die Windows Plattform entwickelte. Delphi tat sich da hervor durch schnelle Entwicklungszyklen, gute Datenbankanbindung und die Erzeugung performanter, ausführbarer Programme.

Mittlerweile hat sich aber das Web zur prädominanten Ablaufplattform entwickelt, grafische Oberflächen laufen im Browser und die vorherrschende Implementierungssprache für Geschäftslogik ist Java. So entstand die Idee, Delphi Source Code nach Java zu übersetzen.

Durchführung

Zunächst mussten wir dafür Delphi Sourcedateien – die ja, wie jeder Source Code, einfache Textdateien sind – in ein Format überführen, das die eigentliche Übersetzung unterstützt. Dieses Format bildet in der Form einer Baumstruktur die Elemente der Delphi Datei wie Anweisungen, Schleifen, Variablen, Daten und so weiter ab. (Man spricht davon als dem "Parse Tree" oder dem "Syntaxbaum" der Sourcedatei.) Für diesen Vorgang, genannt Parsing, gibt es viele Tools. Wir entschieden uns für das Parsing Framework ANTLR. Den durch ANTLR entstandenen Syntaxbaum analysierten wir auf vielfälltige Weise (strukturelle Analyse, Scope Analyse, Type Inference), um damit das Zielmodell für den Java Code zu erstellen.

Zur Erstellung dieses Zielmodells bedienten wir uns einer Bibliothek aus dem Eclipse Umfeld, dem Eclipse Modelling Framework (EMF). Dieses erlaubt den sukzessiven Aufbau von Programmstrukturen wie Klassen, deren Attributen und Methoden. Nachdem dieses Zielmodell vollständig erstellt war, konnten wir es mithilfe einer weiteren Bibliothek, JavaPoet, in textuellen Java Code umwandeln, der in eine Zieldatei geschrieben wird. Damit ist der Übersetzungsvorgang im Prinzip abgeschlossen.

Architektur

Herausforderungen

Die größten Herausforderungen bestanden in der geeigneten Abbildung von Delphi Sprachelementen in Java Konstrukte, die die gleiche Bedeutung haben, also bei der Ausführung den gleichen Effekt erzielen (Erhalt der "operational semantics").

In vielen Fällen ähneln sich Delphi und Java so stark, daß eine direkte Übersetzung möglich war, zum Beispiel bei Zuweisungen, Vergleichen, Verzweigungen und Schleifen. Auch ist das grundlegende Konzept einer Klasse und deren Vererbung bei beiden Sprachen identisch. Es gibt aber auch signifikante Unterschiede. Delphi kennt dynamische Objekt Scopes durch das "with" Statement, das es so auch in JavaScript gibt, in Java aber unbekannt ist. Bei Funktions- oder Methodenaufrufen gibt es die Möglichkeit von Call-by-Reference Semantik (durch "var" Parameter), was Java nicht unterstützt. Es gibt eine Reihe nativer Datentypen wie Arrays oder Subranges, die es so in der Java Sprache nicht gibt oder die sich anders verhalten. Darüber hinaus erlaubt Delphi viele Sprachkonstrukte außerhalb von Klassen, was in Java nicht erlaubt ist.

So haben wir zum Beispiel die Call-by-Reference Semantik abgebildet, indem wir Code erzeugen, der den ursprünglichen Parameter in ein Wrapper Objekt packt und stattdessen im Aufruf übergibt. In die aufgerufene Methode fügten wir nun Code ein, der den eingepackten Wert dort auspackt, darauf seine ursprüngliche Berechnung und Modifikation ausführt und den – möglicherweise geänderten – Wert wieder in das Wrapper Objekt zurückstellt. Da Objektparameter in Java zwischen Aufrufer und aufgerufener Methode geteilt werden, kann der Aufrufer daraufhin – wieder mit zusätzlichem Code – den Wert auspacken und der ursprünglichen Variablen zuweisen. Somit war der Effekt erreicht, der auch im Delphi Code nach dem Methodenaufruf erzielt worden wäre. Danach geht es mit dem ursprünglichen Code nach dem Aufruf weiter.

Ist der Source Code einmal übersetzt, wartet eine weitere Herausforderung in der Integration dieses Codes in einer Zielumgebung. Kein Source Code existiert in Isolation, und so muss sich auch der übersetzte Code in eine Umgebung mit eigenen Schnittstellen, Bibliotheken und Idiomen einfügen. Eine große Hilfe hierbei war die Anwendung des Adapter Patterns, womit wir übersetzte Klassen und APIs auf entsprechende Konstrukte der Zielumgebung angepasst haben. Tests und Qualitätssicherung des portierten Codes sind weitere Themen, die Beachtung verlangen.

Ausblick

Wir haben es geschafft und eine komplexere Backend-Funktionalität (Leistungsverrechnung) von Delphi nach Java portiert, die vollständig lauffähig ist und die gleichen Ergebnisse liefert wie die Delphi-Anwendung. Als nächstes müssen wir das Tool in weiteren Einsätzen erproben und weiter härten, um die Möglichkeit zu haben, effizient die Portierung und Ablösung produktiven Codes zu unterstützen. Darüber hinaus sind durch die gewonnenen Erkenntnisse und Methoden zukünftig auch komplexe Refactorings in Java denkbar, um uns dort von Altlasten zu befreien und auf neue Technologien und Verfahren umzustellen.

MACH Karriere

Komm ins Team

und mach mit uns die öffentliche Verwaltung digital
Du willst deine Superkräfte für eine gute Sache mobilisieren? Dann finde bei uns deine Aufgabe!
Entwickler-Blog
#Fachwissen #Methode

REST Services in Clojure

Thomas Herchenröder, Innovation Engineer im MACH Innovation Hub, erklärt im Blog wie er in einem hoch dynamischen, verteilten Entwicklungsprojekt unter engen Zeitvorgaben eine solide REST API implementierte und dokumentierte.