Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET |...

23
Ruby-Compiler für .NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für .NET | 12. Februar 2009

Transcript of Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET |...

Page 1: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Ruby-Compiler für .NET

Tilman GieseHolger JustMurat KnechtMark Liebetrau

Ruby-Compiler für .NET | 12. Februar 2009

Page 2: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Übersicht

Ruby-Compiler für .NET | 12. Februar 2009

Page 3: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Ruby

■ Dynamische Sprachen

■ Die Sprache Ruby

Ruby-Compiler für .NET | 12. Februar 2009

Page 4: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Dynamische vs. Statische Sprachen

■ dynamische Semantik: Typüberprüfung und Namensauflösung werden erst zur Laufzeit durchgeführt

■ Laufzeitumgebung für alle dynamische Aspekte der Sprache

■ enge Verzahnung von Laufzeitumgebung und Klassenbibliothek

■ Für Ruby (aber auch andere Sprachen) gilt insbesondere:

□ Klassendefinitionen sind nicht abgeschlossen, sondern zu jedem Zeitpunkt an jeder Stelle des Programms änderbar

□ eval: dynamische Auswertung beliebigen Codes innerhalb eines beliebigen Kontextes

Ruby-Compiler für .NET | 12. Februar 2009

Page 5: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Die Sprache Ruby

■ Namensauflösung für Methoden und für Konstanten (= Modul- oder Klassennamen) erfolgt nach unterschiedlichen Prinzipien

□ Methoden: Vererbungs- und Mixin-Hierarchie

□ Konstanten: Hierarchie der lexikalischen Scopes

■ Dynamische Scopes: Erst zur Laufzeit stehen Scopes fest

class A; class B; end; end # innerer Scope: [A, A::B]class A::B; end # innerer Scope: [A::B]

class A; class << self; end; end # innerer Scope: [A, #<Class:A>]

def m(x, y); String; endclass m(42, 9)::A; class << self; end; end # innerer Scope: [#<Class:String::A>]

Ruby-Compiler für .NET | 12. Februar 2009

Page 6: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Ausschnitt: Klassenhierarchie

RubyObject

RubyBasicObject

RubyModule

RubyClass

RubyNumeric RubyString

RubyInteger

RubyFixnum

Ruby-Compiler für .NET | 12. Februar 2009

Page 7: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Ruby-Konzepte .NET-Konzepte (1)

■ Methodenaufruf

■ Beispiel

Ruby: m(5, 6) # implizit: self.m(5, 6)

class RubyObject { public RubyObject Invoke(String method, RubyObject[] args, System.Ruby.Block blk);}

C#: scope.Self().Invoke(″m″, new RubyObject[] { RubyFixnum.Get(5), RubyFixnum.Get(6) }, null);

Ruby-Compiler für .NET | 12. Februar 2009

Page 8: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Ruby-Konzepte .NET-Konzepte (2)

■ Methodendefinitionen

Ruby: class A; def my(a, b); end; end

C#: namespace __ruby { class __methods_A_0 {

[Ruby.Method("my")] public static RubyObject __my(RubyObject self, RubyObject[] args, Ruby.Block blk);

public static Ruby.Scope scope__my_0; } }

C#: scope.DefineMethod("my", typeof(__ruby.__methods_A_0).GetMethod("__my_0"));

Ruby-Compiler für .NET | 12. Februar 2009

Page 9: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Absurdes

■ Klassendefinition gibt etwas zurück ... und zwar nicht die Klasse

■ Lokale Variable verhalten sich ... anders

■ Modul- und Klassendefinitionen

class A; 5 + 5; end.to_f #=> 10.0

def m; 42; endputs m #=> 42 (Methodenaufruf)

if 1 == 2 then m = 6; endputs m #=> nil (lokale Variable)

ary = [ { ′4′ => Object } ]class ary.first[′4′]::MyClass; end

Ruby-Compiler für .NET | 12. Februar 2009

Page 10: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Klassenbibliothek

■ rudimentäre Klassenbibliothek selbst implementiert

■ einfach um neue Methoden und Klassen erweiterbar

/* * call-seq: * fix == other*/[Ruby.Method("==")]public RubyObject eq(RubyObject arg) {

if (this == arg) return RubyObject.__true; if (arg is RubyFixnum) return RubyObject.__false;

// some stuff omitted return RubyObject.__false;}

Ruby-Compiler für .NET | 12. Februar 2009

Page 11: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Plattform-Unabhängigkeit (1)

Ruby-Compiler für .NET | 12. Februar 2009

“An assembly is one of the fundamental programmingunits in Phoenix.” [PDOC]

Page 12: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Plattform-Unabhängigkeit (2)

■ OpCode

□ Phx.Common vs. Phx.Targets.Architectures.Msil

□ High-level vs. Low-level

□ „ In general it's best to use HIR opcodes if possible; you should only need to resort to the LIR opcodes for the ldfld/ldsfld cases that came up in the other thread.” [PF1]

□ Wir benutzen MSIL für:

◊ ldsfld, ldstr, ldtoken, newobj

□ Plattformunabhängigkeit von Phoenix?

Ruby-Compiler für .NET | 12. Februar 2009

Page 13: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

API-Unarten

■ Hinzufügen einer Anweisung

□ Append(), Insert(), InsertAfter(), InsertBefore(), InsertBranchAndLabelAfter()

■ Zugriff auf Zieloperand

□ DestinationOperand: = DestinationOperand1

□ DestinationOperand2 …

□ DestinationOperands: Unterstützt Iteratorkonzept (foreach)

□ DestinationOperandList

◊ Nicht etwa eine Liste

◊ Erstes Element der Kette, nächstes mit Operand.Next

Ruby-Compiler für .NET | 12. Februar 2009

Page 14: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Ausführung einer Ruby EXE

■ main(): [RubyCoreLibrary]Runtime::Run()

■ Laufzeitumgebung initialisieren

□ Typsystem (Object, Class, …)

□ Wurzel der Scope-Hierarchie

□ Eingebaute Funktionalität

◊ Kernel Modul (puts, require)

◊ File Klasse

■ „Main() Methode“ von Ruby suchen und rufen

□ C# Reflection: __main.Run()

■ RubyExceptions abfangen und ausgeben

Ruby-Compiler für .NET | 12. Februar 2009

Page 15: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Instruktionen (1)

■ Ganz allgemein

□ Ziel := Operator ( Operanden )

■ Operator := Instruction + OpCode

■ Instruction: Technische Aufteilung der Operatoren

□ ValueInstruction: Evaluation Stack / Register

□ CallInstruction: Funktionsruf, d.h. Callstack

□ DataInstruction: ?

□ …

Ruby-Compiler für .NET | 12. Februar 2009

Page 16: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Operanden

■ Verschiedene Typen

□ Für SourceOperand und DestinationOperand

□ VariableOperand, MemoryOperand, ImmediateOperand, …

■ Optimierung?

□ Beispiel: ldstr „Kuchen“ -> call Console.WriteLine

□ VariableOperand + TemporaryVariable -> 2 lokale Variablen

□ VariableOperand + LocalVariable -> immerhin nur 1

Ruby-Compiler für .NET | 12. Februar 2009

Page 17: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Ein Resümee

■ Motivation für Phoenix

□ HIR bietet Unabhängigkeit zu Zielplattform

□ Backend austauschbar

■ Abstraktions-Level sind nicht sauber getrennt

□ Konsequenz ist nicht nur uneinheitliches Coding

■ Die versprochene Plattform-Unabhängigkeit fehlt.

Ruby-Compiler für .NET | 12. Februar 2009

Page 18: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Quellen

■ [PDOC] Phoenix Documentation, Microsoft Phoenix SDK June 2008

■ [PF1] „Error load float32“, Phoenix Forum, http://social.msdn.microsoft.com/Forums/en-US/phoenix/thread/b72a2f6d-c3bf-488b-b27a-6cc14b9006aa/ (zuletzt abgerufen am 11.02.09)

Ruby-Compiler für .NET | 12. Februar 2009

Page 19: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Dynamic Language Runtime

■ Erlaubt die Implementierung von dynamischen Sprachen auf Basis der .NET CLR

■ „pluggable language backends“

□ unterschiedliche dynamische Sprachen wie Python oder Ruby können einfach in ein .NET-Programm „geladen“ werden

□ Durch gemeinsames Typsystem ist Interaktion zwischen den Sprachen sowie der Host-Applikation möglich

■ Mögliche Anwendungsfälle

□ Verwendung von Bibliotheken dynamischer Sprachen in .NET

□ Scripting von Applikationen

Page 20: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Features der DLR

■ Generische Features aller dynamischen Sprachen werden von der DLR selbst implementiert

□ Gemeinsames Typsystem

□ Namensauflösung von Methoden und Variablen

□ Schleifen, Verzweigungen, …

□ Ausnahmebehandlung

□ Basisoperatoren

□ Methodenaufrufe

□ Scopes

□ Codeblöcke (für Funktionen, Methoden, Lambdas, …)

■ Implementierte Sprachen parsen ihren Code und erzeugen einen AST aus DLR-Expressions

Page 21: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Implementierung

■ Unabhängig von der Eingabesprache…

… wird (naiv) der gleiche AST generiert

MethodInfo writeline = typeof(Console).getMethod("WriteLine", Type.EmptyTypes);

Statements.Add( Expression.Call( writeline, Expression.Constant("Hello World") ));

puts "Hello World" # Ruby

print "Hello World" # Python

Page 22: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Einbettung in den rubyc

■ Neuer Einstiegspunkt für den DLR Host: RubyLanguageContext

□ Beschreibt Spracheigenschaften

□ Diverse Einstiegspunkte für die DLR

1. Aufrufen des Parsers und Generieren des generischen ASTs

□ Unverändert zu „normalem“ Compiler

2. Ablaufen des AST und Generieren des DLR-AST

□ Der Ablaufstruktur des Compilers wird gefolgt

□ Es werden Objekte, Scopes, Module und Klassen aus der Ruby-Standardbibliothek erzeugt und verwendet

Page 23: Ruby-Compiler für.NET Tilman Giese Holger Just Murat Knecht Mark Liebetrau Ruby-Compiler für.NET | 12. Februar 2009.

Fazit DLR

■ Vorhandene Infrastruktur des Compilers und der Standardbibliothek wird weiterverwendet

□ Interoperabilität mit anderen DLR-Sprachen ist dadurch eingeschränkt

□ „Echte“ DLR-Implementierung müsste Vieles erneut implementieren

■ Insgesamt wesentlich weniger Implementierungsaufwand als bei Phoenix

DEMOHello World!