Status: Archiv/Informativ Prioritaet bei Konflikt: Es gelten immer
00bis08. Hinweis: Diese Datei ist historischer oder ergaenzender Kontext und nicht normativer Kern.
Jade Design Constitution v1.2¶
Status: Entwurf, grundlegend Zielgruppe: Mitwirkende, fortgeschrittene Nutzer und interessierte Leser (einschließlich Nicht-Experten) Umfang: Dieses Dokument definiert die nicht verhandelbaren architekturellen Prinzipien der Jade VM, JDL und ihres Laufzeit-Ökosystems.
0. Zweck dieses Dokuments¶
Jade ist ein langfristiges Projekt. Im Laufe der Zeit wird sich Code ändern, Menschen werden hinzustoßen und gehen, und Ideen werden sich weiterentwickeln. Diese Constitution existiert, um:
- die grundlegenden Designentscheidungen festzuhalten, die konsistent bleiben müssen,
- zu erklären, warum diese Entscheidungen existieren, in einer Sprache, die auch jenseits von Compiler-Experten verständlich ist,
- eine Referenz bereitzustellen, gegen die neue Features und Refaktorierungen geprüft werden können.
Wenn eine zukünftige Designentscheidung dieser Constitution widerspricht, gibt es nur zwei legitime Optionen:
- Das Design so ändern, dass es zur Constitution passt, oder
- Die Constitution explizit ändern, mit einer klaren Begründung.
1. Identität und Umfang von Jade¶
1.1 Jade ist VM-first, keine generische Compiler-Infrastruktur¶
Jades primäre Rolle ist:
Eine sprachzentrierte virtuelle Maschinenplattform für JDL, mit einem eigenen typisierten Mid-Level IR und einer Pass-Pipeline.
In der Praxis bedeutet das:
- Jade ist in erster Linie dafür ausgelegt, JDL-Programme effizient und sicher auf der Jade VM auszuführen.
- Die VM verfügt über:
- einen eigenen Befehlssatz,
- eine eigene Speicher-Engine (für Allokation und Lebensdauer),
- ein eigenes Typ- und Effektsystem.
Jade ist nicht initial dafür gedacht:
- "ein offenes IR für jede mögliche Sprache" zu sein, oder
- "ein universelles Backend für jede CPU-Architektur" zu sein.
Es kann sich später zu einer allgemeineren Plattform entwickeln, aber das ist nicht der Ausgangspunkt.
1.2 Internes IR zuerst, externes IR später (vielleicht)¶
Jade verwendet eine interne Intermediate Representation (IR):
- IR ist eine strukturierte, typisierte Beschreibung eines Programms, die vom Compiler und Optimierer verwendet wird.
- Sie liegt zwischen "JDL-Quellcode" und "VM-Bytecode / D-Code / anderen Zielen".
Diese Constitution legt fest:
- Zunächst ist IR ein Implementierungsdetail.
- Compiler und VM dürfen das IR-Format und die Struktur nach Bedarf ändern.
- Nur wenn Jade später mehrere Frontends (mehrere Sprachen) oder mehrere Backends (mehrere Ziele) unterstützt, kann IR stabilisiert und als öffentliche Schnittstelle behandelt werden.
Dies verhindert ein zu frühes Over-Engineering eines "ewigen IR".
1.3 Zielplattform: POSIX-kompatible Betriebssysteme¶
Jade zielt auf POSIX-kompatible Betriebssysteme als unterstützte Plattformumgebung ab. Dies ist eine bewusste Umfangsentscheidung — keine temporäre Einschränkung. Es wird keine Windows- oder Nicht-POSIX-Kompatibilitätsschicht präventiv entworfen. Sollte Nicht-POSIX-Unterstützung in Zukunft notwendig werden, muss diese Entscheidung explizit überprüft und die Constitution entsprechend geändert werden.
2. Kein undefiniertes Verhalten im Jade IR¶
2.1 Was "undefiniertes Verhalten" bedeutet (und warum wir es vermeiden)¶
In vielen Low-Level-Systemen (insbesondere C und LLVM) bedeutet "undefiniertes Verhalten" (UB):
"Wenn du X tust, gibt die Sprache oder das System keine Garantien. Alles kann passieren."
Das ist für Optimierungen praktisch, aber gefährlich:
- Dasselbe Programm kann sich je nach Compiler-Version, Optimierungsstufe oder kleinen Änderungen unterschiedlich verhalten,
- Bugs können extrem schwer zu diagnostizieren sein.
Jade lehnt UB in diesem Sinne explizit ab.
2.2 Gültiges IR vs. ungültiges IR¶
Jedes Jade-Programm, das die IR-Stufe erreicht, muss sich in einem von zwei Zuständen befinden:
-
Gültiges IR
-
Alle Strukturregeln sind erfüllt (z.B. Sprünge gehen zu gültigen Blöcken, Register sind im Bereich).
- Alle Typregeln sind erfüllt (z.B. man addiert keinen String zu einem Handle).
- Alle Policies und Effekte sind konsistent (z.B. bewegt man kein
local-Objekt in einen anderen Thread). -
Ergebnis: Das Verhalten des Programms ist vollständig definiert.
- Es können zur Laufzeit noch Fehler auftreten (siehe Traps unten),
- aber diese Fehler sind Teil der definierten Semantik.
-
Ungültiges IR
-
IR verletzt Invarianten: Typen stimmen nicht überein, Sprünge sind falsch, Handles werden falsch verwendet, etc.
- Ergebnis: Der Verifier lehnt es ab. Das Programm wird nicht ausgeführt.
Es gibt keinen Zwischenzustand wie "irgendwie gültig, aber undefiniert".
2.3 Traps: definierte Laufzeitfehler¶
Viele reale Programme können zur Laufzeit scheitern: Division durch Null, Indexierung außerhalb eines Arrays, Verwendung eines geschlossenen Handles usw.
In Jade:
- Diese Situationen werden als Traps modelliert.
- Ein Trap ist:
- eine explizite IR- oder Instruktion,
- die bei Ausführung einen kontrollierten, wohl-definierten Fehler verursacht.
Beispiel (informell):
- IR-Äquivalent von:
x / y - wenn
y == 0, springt die Ausführung zu einemTrapDivByZero-Knoten / Instruktion, - die VM meldet einen definierten Fehler (mit Typ, Nachricht, möglicherweise Stack-Trace),
- kein "undefiniertes Verhalten".
Dies hält die Semantik vollständig definiert, erlaubt aber dennoch Laufzeitfehler.
3. Instruktionsarchitektur: Core vs. Extended¶
Die Jade VM führt keine beliebigen High-Level-Operationen direkt aus. Stattdessen verwendet sie ein geschichtetes Instruktionsdesign.
3.1 Core-Instruktionen: das Ausführungsmodell¶
Der Execution Core ist:
Eine bewusst kleine, stabile Menge von Instruktionen, die die VM direkt ausführen kann.
Eigenschaften:
- Jedes Programm, das auf der VM läuft, wird schließlich ausschließlich in Core-Instruktionen ausgedrückt.
- Der Core ist bewusst minimal:
- jede Core-Instruktion muss ihre Existenz rechtfertigen,
- eine Core-Instruktion später zu entfernen ist aufwändig und wird abgeraten.
Beispiele für wahrscheinliche Core-Level-Verantwortlichkeiten (konzeptionell):
- grundlegende arithmetische und logische Operationen,
- Wertevergleiche,
- Laden und Speichern aus Registern, Stack und strukturierten Werten,
- Funktionsaufruf und Rückgabe,
- Verzweigung und Sprung,
- Allokation / Freigabe von Handles unter wohl-definierten Policies.
Die genauen Instruktionsnamen und -formate sind in der VM-Spezifikation definiert, nicht hier. Diese Constitution schreibt nur die Trennung zwischen Core und Extended vor.
3.2 Extended-Instruktionen: Sugar und High-Level-Konstrukte¶
Der Extended Layer enthält reichhaltigere Instruktionen, die nützlich sind für:
- Frontends (einfachere Codegenerierung),
- frühe Optimierungspässe (einfachere Argumentation),
- Lesbarkeit in internen Tools.
Beispiele (konzeptionell, nicht endgültige Syntax):
TailCallanstatt einer Kombination ausCall+Return,NewStructanstatt einer expliziten "allokieren, dann jedes Feld setzen"-Sequenz,Closure, das Upvalues in einer Operation erfasst,- High-Level-Schleifen oder Pattern-Matches, die später in einfachere Kontrollflüsse expandieren.
Pflicht-Lowering:
- Bevor Code verifiziert und an die VM übergeben werden kann, müssen alle Extended-Instruktionen in Core-Instruktionen transformiert werden.
- Dies geschieht durch einen oder mehrere Lowering-Pässe.
- Nach dem Lowering verbleiben nur noch Core-Instruktionen.
3.3 Plugins können den Extended Layer erweitern, nicht den Core¶
Jade unterstützt Plugins und Erweiterungen in höheren Ringen (Ring 2). Um die VM-Integrität zu wahren:
- Plugins dürfen:
- neue Extended-Instruktionsformen definieren,
-
Pässe definieren, die ihre benutzerdefinierten Extended-Instruktionen in Core-Instruktionen absenken (oder in einfachere Extended-Instruktionen, die schließlich abgesenkt werden).
-
Plugins dürfen nicht:
- Core-Instruktionen modifizieren oder hinzufügen,
- die Bedeutung (Semantik) bestehender Core-Instruktionen verändern.
Dies hält das Ausführungsmodell der VM stabil und macht Verifikation und Tooling praktikabel.
4. Pass-System und IR-Invarianten¶
4.1 Was ist ein Pass?¶
In Jade ist ein Pass eine Transformation oder Analyse, die auf IR (oder auf der linearen Instruktionsform) operiert und eine modifizierte Version oder zusätzliche Daten zurückgibt.
Beispiele:
- Ein Pass, der toten Code entfernt.
- Ein Pass, der High-Level-Kontrollfluss in einfachere Basisblöcke transformiert.
- Ein Pass, der konkrete Aufrufkonventionen oder Stack-Layouts wählt (in einer späteren Phase).
- Ein Pass, der nur die Liveness analysiert, ohne IR zu verändern.
Das Pass-System ist die Engine, die:
- IR vom Frontend entgegennimmt,
- eine Sequenz von Pässen anwendet (gruppiert in Phasen),
- IR oder Instruktionen ausgibt, die bereit für Lowering und Ausführung sind.
4.2 IR-Invarianten: was "wohlgeformtes IR" bedeutet¶
Eine Invariante ist eine Eigenschaft, die das IR entweder erfüllt oder nicht. Das Pass-System verwendet Invarianten, um zu beschreiben, wann ein Pass ausgeführt werden darf und was er danach garantiert.
Einige wichtige Invarianten (initialer Satz):
-
SSAFormJeder Wert wird genau einmal zugewiesen, und Verwendungen beziehen sich auf eine eindeutige Definition (Static Single Assignment Form). -
DominatorsValidDer Kontrollflussgraph hat einen gültigen Dominator-Baum. Das ist für viele Optimierungen wichtig. -
TypesResolvedAlle Typinformationen sind vollständig bekannt; es gibt keine "unbekannten" oder "später zu inferierenden" Typen. -
PoliciesConsistentRefinements und Policies (z.B. Ownership, Share-Modus, Speicherstrategie) sind konsistent und widersprechen sich nicht. -
EffectsNormalizedEffekte (wie I/O, Zustandsänderungen, Task-Spawning) sind in einer normalisierten Darstellung, die Pässe verstehen. -
NoDeadBlocksEs gibt keine Basisblöcke, die vom Einstiegspunkt aus unerreichbar sind.
Diese Liste kann wachsen, aber mit einem kleinen, klaren Satz zu beginnen ist besser.
4.3 Pass-Contracts: Vorbedingungen, Nachbedingungen, Invalidierungen¶
Jeder Pass muss deklarieren:
-
Vorbedingungen: Welche Invarianten vor der Ausführung des Passes gelten müssen. Beispiel: Ein bestimmter Optimierungspass benötigt möglicherweise
SSAFormundTypesResolved. -
Nachbedingungen: Welche Invarianten der Pass danach garantiert. Beispiel: Ein Pass könnte
NoDeadBlocksgarantieren. -
Invalidierungen: Welche Invarianten der Pass brechen kann. Beispiel: Ein Pass, der den Kontrollfluss ändert, kann
DominatorsValidinvalidieren.
Informell sieht ein Pass in Jade so aus:
Pass X:
erfordert: [SSAForm, TypesResolved]
garantiert: [SSAForm, TypesResolved, NoDeadBlocks]
kann invalidieren: [DominatorsValid]
Das macht Pipelines vorhersagbar und debuggbar.
5. Verifikation: der Verifier als Gatekeeper¶
5.1 Rolle des Verifiers¶
Der Verifier ist eine Komponente, die prüft, ob IR oder Instruktionssequenzen alle strukturellen und semantischen Regeln einhalten, die für eine sichere Ausführung erforderlich sind.
Er beantwortet die Frage:
"Ist dieses Programm sicher und wohlgeformt genug, dass die VM es ausführen darf?"
Wenn die Antwort "nein" ist, wird das Programm nicht ausgeführt.
5.2 Wann Verifikation stattfindet¶
Um Sicherheit und Performance in Einklang zu bringen, läuft die Verifikation an definierten Pipeline-Grenzen, nicht nach jeder mikroskopischen Transformation.
Die Constitution verlangt Verifikation mindestens:
- Nach dem Frontend
-
um sicherzustellen, dass das initiale IR strukturell korrekt und typkorrekt ist.
-
Nach großen Pass-Gruppen / Phasengrenzen
-
zum Beispiel nach "High-Level-Optimierung" und vor "Lowering".
-
Vor der Ausgabe oder dem Laden von ausführbarem Code
- z.B. bevor VM-Bytecode/Wordcode produziert wird, der ausgeführt oder in Artefakten gespeichert werden kann.
Implementierungen können in Debug-Builds zusätzliche Verifikation durchführen.
5.3 Performance-Überlegungen¶
Der Verifier sollte generell in linearer oder quasi-linearer Zeit bezüglich der Programmgröße laufen.
- Tiefe oder quadratische Algorithmen werden auf dem Hot Path abgeraten.
- Aufwändige Prüfungen können:
- nur für Debug-Builds sein,
- oder hinter expliziten Flags liegen.
Der Schlüssel ist: Verifikation muss zuverlässig und schnell genug sein, um systematisch eingesetzt werden zu können.
6. Speichermodell: Handles, Policies und Nebenläufigkeit¶
Speicher und Nebenläufigkeit sind häufige Quellen subtiler Bugs. Jade begegnet ihnen durch die Verwendung von Handles, Policies und einem klaren Nebenläufigkeitsmodell.
6.1 Handles statt roher Pointer¶
In vielen Systemen manipuliert Code rohe Speicheradressen (Pointer). Das ist mächtig, aber gefährlich:
- man kann Offsets falsch berechnen,
- freigegebenen Speicher verwenden,
- denselben Speicher auf unsichere Weise aliasieren.
Jade vermeidet das durch die Verwendung von Handles:
- Ein Handle ist ein opaker Bezeichner, der auf einen Wert oder ein Objekt verweist, das von der Jade Memory Engine (JME) verwaltet wird.
- Code kann keine Pointer-Arithmetik auf Handles ausführen.
- Operationen auf Handles laufen durch JME, das Regeln über folgendes durchsetzt:
- Lebensdauer,
- Ownership,
- Teilen zwischen Tasks,
- spezielle Policies (z.B. Arena-Allokation, FFI-Speicher).
Intern weiß JME, wie Handles auf tatsächlichen Speicher abgebildet werden, aber das ist vor Benutzercode, Plugins und sogar der VM verborgen.
6.2 Typen, Layouts und Felder¶
Handles sind an Typen gebunden:
- Ein Handle ist mit einer
TypeIdassoziiert, die beschreibt: - wie der Wert aussieht (Layout),
- welche Operationen darauf gültig sind,
- wie er zerstört oder bereinigt werden soll.
Auf strukturierte Werte (wie Records/Structs) wird zugegriffen durch:
- Feld-Bezeichner oder Feld-Offsets, nicht durch manuelle Pointer-Arithmetik.
Beispiel:
- Um
person.namezu laden, macht IR etwas wie: - "Lade Feld
namevon Handleh_personunter Verwendung des Layouts, das durchTypeId(person)beschrieben wird."
Das hält Speicherzugriffe eingeschränkt, explizit und typisiert.
6.3 Share-Policies: local, send, sync¶
Jade-Programme können in nebenläufigen Szenarien laufen (mehrere Tasks / Threads). Um zu kontrollieren, wie Daten geteilt werden, hat jedes Handle (oder jeder Typ) eine Share-Policy wie:
-
share: local -
Der Wert ist auf einen einzigen Task/Thread beschränkt.
- Kein nebenläufiger Zugriff ist erlaubt oder erwartet.
- Compiler und Optimierer können davon ausgehen, dass kein anderer Thread diesen Wert sieht.
-
Das erlaubt aggressive Neuanordnung von Operationen auf diesem Wert innerhalb desselben Tasks.
-
share: send -
Der Wert kann zwischen Tasks verschoben werden, wird aber nicht gleichzeitig geteilt.
- Ownership wird von einem Task auf einen anderen übertragen.
- Es gibt keinen gültigen Zustand, in dem zwei Tasks gleichzeitig aktiven Zugriff auf dieselbe Instanz haben.
-
Typisch für "Ownership-Transfer"-Muster (z.B. eine Nachricht mit exklusiven Daten an einen anderen Worker senden).
-
share: sync -
Der Wert kann gleichzeitig von mehreren Tasks geteilt werden.
- Der Zugriff kann synchronisiert sein (z.B. über Locks, Atomics, Channels).
- Für solche Werte garantiert Jade sequentielle Konsistenz:
- alle Threads beobachten Operationen in einer Reihenfolge, die mit einer globalen Ordnung von Lese- und Schreiboperationen konsistent ist.
- Optimierungen dürfen Operationen nicht über Synchronisierungs- oder Effektgrenzen hinweg neu anordnen.
Indem Share-Policies explizit gemacht werden, kann Jade:
- unsichere Sharing-Muster verhindern,
- klare Erwartungen darüber geben, worauf nebenläufiger Code sich verlassen kann.
6.4 Happens-Before und Effekte¶
"Happens-Before" ist ein Standardkonzept in der Nebenläufigkeit:
- Wenn A happens-before B gilt, dann ist garantiert, dass B die Seiteneffekte von A sieht.
In Jade:
- Happens-Before-Beziehungen entstehen durch:
- Task-Erzeugung und -Joining,
- Kommunikationsprimitive (Send/Receive),
- Synchronisierungsoperationen,
- und Effekt-Sequenzierungsregeln (siehe nächster Abschnitt).
Optimierungen müssen diese Beziehungen wahren:
- sie können Operationen innerhalb von Segmenten neu anordnen, von denen bekannt ist, dass sie unabhängig und rein sind,
- sie dürfen nicht über Grenzen hinweg neu anordnen, wo Effekte oder Synchronisierung eine Happens-Before-Kante definieren.
7. Effekte als First-Class Citizens¶
7.1 Was sind Effekte?¶
Ein Effekt ist alles, was eine Funktion oder Instruktion über die Rückgabe eines reinen Wertes hinaus tut, zum Beispiel:
- in eine Datei schreiben,
- eine globale Variable modifizieren,
- eine Nachricht an einen anderen Task senden,
- Speicher allokieren oder freigeben,
- loggen.
In Jade:
- sind Effekte keine nachträglichen Gedanken oder bloße Annotationen,
- sie sind Teil des Typsystems und des IR.
7.2 Warum Effekte für Optimierungen wichtig sind¶
Optimierungen sind am einfachsten für reinen Code (keine Effekte), weil Operationen frei neu angeordnet oder zusammengeführt werden können, solange der endgültige Wert derselbe ist.
Für Code mit Effekten:
- Reihenfolge und Vorhandensein von Operationen sind wichtig (z.B. kann man
print()-Aufrufe nicht beliebig neu anordnen), - manche Effekte können nicht dupliziert oder entfernt werden, ohne das Programmverhalten zu ändern.
Indem Effekte explizit modelliert werden:
- kann Jade aggressive Optimierungen innerhalb reiner Regionen erlauben,
- und strengere Regeln dort durchsetzen, wo Effekte involviert sind.
7.3 Effektgrenzen als harte Constraints¶
Für jede IR-Instruktion, die Effekte trägt, wissen Compiler und Pässe:
- welche Effekte sie hat,
- wie sie sich zu anderen Effekten verhalten (z.B. können sie kommutieren, müssen sie geordnet bleiben).
Die Constitution legt fest:
- Effektgrenzen sind harte Constraints:
- Ein Pass darf effektbehaftete Operationen nicht willkürlich neu anordnen oder eliminieren, es sei denn, er kann Äquivalenz beweisen.
- In vielen Fällen gilt die einfache Regel:
- "Effektbehaftete Operationen bleiben geordnet, reine Operationen können um sie herum verschoben werden."
Das hält Optimierungen korrekt und vorhersagbar.
8. Debug-Semantik: Debug als Referenz¶
8.1 Debug-Repräsentation¶
Jade unterstützt eine Debug-Repräsentation von Programmen, typischerweise:
- eine strukturierte Sequenz von Instruktionen (oder IR), in der:
- jede Instruktion eine
SourceLochat (Datei, Zeile, Spalte usw.), - optionale Links zu Variablen, Scopes und Inline-Calls.
Diese Debug-Repräsentation ist ausgelegt für:
- Debugger,
- IDEs und Language Server,
- Profiler und Visualisierer.
8.2 Debug vs. Release: Beobachtungsäquivalenz¶
Debug-Builds sind oft:
- weniger optimiert,
- leichter zu inspizieren,
- langsamer.
Sie müssen jedoch aus der Sicht des Programms "dasselbe tun" wie Release-Builds.
In Jade:
Debug-Ausführung ist die Referenzsemantik. Release-Ausführung muss beobachtungsäquivalent sein.
"Beobachtungsäquivalent" bedeutet:
- gleiche Rückgabewerte,
- gleiches extern sichtbares Verhalten (innerhalb der Abstraktionsebene der Tests), einschließlich:
- gleiche persistente Zustandsänderungen,
- gleiche gesendete Nachrichten,
- Logs, die mit der beabsichtigten Semantik konsistent sind (in der Praxis können Tests auf Teilmengen bestehen).
Wenn Debug und Release sich in einer Weise unterscheiden, die ein Benutzer unter der definierten Semantik beobachten kann, ist das ein Compiler/VM-Bug, kein "nur ein Seiteneffekt der Optimierung".
8.3 Testpflichten¶
Um das praktikabel zu machen, muss das Jade-Projekt automatisierte Tests pflegen, die:
- ein gegebenes Programm im Debug-Modus ausführen,
- dasselbe Programm im Release-Modus ausführen,
- ihre beobachtbaren Ergebnisse vergleichen.
Jede Abweichung wird als Bug im Compiler, der VM oder den Pässen behandelt.
9. Backends: einfache Übersetzer, keine zweiten Compiler¶
9.1 Die Rolle von Backends¶
Jade kann folgendes als Ziel haben:
- eigenen VM-Bytecode/Wordcode (primär),
- andere Umgebungen wie D-Code (AOT-Deployment),
- möglicherweise weitere Ziele in der Zukunft.
Diese Ziele werden als Backends implementiert: Komponenten, die IR oder Instruktionssequenzen entgegennehmen und Code für eine bestimmte Umgebung ausgeben.
9.2 Backends müssen einfach bleiben¶
Die Constitution verlangt:
- Backends sind einfache, deterministische Übersetzer:
- sie bilden IR/Core-Instruktionen auf äquivalente Konstrukte im Ziel ab,
- sie erfinden kein neues beobachtbares Verhalten,
-
sie implementieren keine ausgefeilten semantischen Optimierungen.
-
Alle "Intelligenz" (komplexe Optimierungen, Transformationen, Analysen) gehört in:
- die Jade IR-Pässe,
- nicht ins Backend.
Das hat mehrere Vorteile:
- Verhalten ist leichter zu verstehen und zu debuggen,
- mehrere Backends können dieselbe Optimierungspipeline teilen,
- Backends können ersetzt oder erweitert werden, ohne komplexe Logik zu duplizieren.
Das D-Backend zum Beispiel sollte klaren, unkomplizierten D-Code generieren, der das IR widerspiegelt, und sich auf den D-Compiler für Low-Level-Optimierungen wie Register-Allokation oder Instruktionsauswahl verlassen.
10. Ringe und Subsystem-Isolation¶
Jade verwendet eine Ring-Architektur, um Verantwortlichkeiten zu trennen und kritische Komponenten vor versehentlichem Missbrauch zu schützen.
10.1 Ring 0 – JME (Jade Memory Engine)¶
Ring 0 enthält die Jade Memory Engine (JME). Es ist der vertrauenswürdigste und am stärksten eingeschränkte Teil des Systems.
Eigenschaften:
- Verwaltet:
- Allokation,
- Deallokation,
- Handle-Lebensdauern,
- Speicher-Policies,
- interne Metadaten.
- Tut nicht:
- Plugins laden,
- direkt mit fremdem Code kommunizieren (FFI),
- von höherstufigen Subsystemen wie VM oder Type Engine abhängen.
Ring 0 ist bewusst klein und eigenständig. Er bildet das Fundament der Sicherheit für alle Speicheroperationen.
10.2 Ring 1 – VM und Type Engine¶
Ring 1 enthält:
- die Jade VM (Ausführungs-Engine),
- die Type Engine (Typinformationen, Typprüfung, Typ-Reflexion).
Ring 1:
- verwendet JME nur über seine wohl-definierte API,
- hat keinen direkten Zugriff auf rohen Speicher,
- ist verantwortlich für:
- Interpretieren oder Ausführen von Instruktionen,
- Behandlung von Aufrufen, Kontrollfluss, Tasks,
- Verwaltung von Typinformationen und Sicherstellung von Typsicherheit auf VM-Ebene.
10.3 Ring 2 – Plugins und Userland¶
Ring 2 enthält:
- Benutzercode,
- Plugins,
- Services, die auf VM und Type Engine aufbauen.
Ring 2-Komponenten:
- dürfen die VM oder JME nicht umgehen,
- interagieren nur über:
- offizielle APIs,
- Effekt/Handle-Mechanismen,
- deklarierte Interfaces.
Sie dürfen neue Abstraktionen, Protokolle und Extended-Instruktionen definieren, aber immer innerhalb der Grenzen, die von den unteren Ringen definiert werden.
10.4 Keine "Abkürzungen" durch die Ringe¶
Aus Performance- oder Bequemlichkeitsgründen kann es verlockend sein:
- einem Plugin direkten Zugriff auf JME zu geben,
- Speicher-Layouts ohne die APIs zu manipulieren,
- Typprüfungen zu umgehen.
Die Constitution verbietet solche Abkürzungen:
- Jede Änderung, die es Ring 2 erlaubt, direkt in Ring 0 zu greifen, verletzt dieses Design.
- Wenn ein echter Bedarf entsteht, ist die korrekte Reaktion:
- die öffentliche API sorgfältig zu erweitern,
- oder die Constitution explizit mit einer klaren Begründung zu überarbeiten.
11. Beziehung zu LLVM und ähnlichen Systemen¶
Jade lernt von bestehenden Systemen (LLVM, JVM, CLR, Lua usw.), ist aber kein Klon von einem davon.
11.1 Was Jade übernimmt¶
Von LLVM und ähnlichen Infrastrukturen übernimmt Jade:
- die Idee eines klaren, dokumentierten IR mit präziser Semantik,
- eine pass-basierte Pipeline für Transformationen und Analysen,
- die Trennung zwischen:
- Frontend(s),
- Mid-Level IR,
- Backend(s).
Von der JVM und anderen VMs übernimmt Jade:
- das Konzept, Code vor der Ausführung zu verifizieren,
- eine Bytecode/Wordcode-Ebene, die die VM direkt ausführt,
- die Idee, dass Debugging und Tooling First-Class-Concerns sind.
11.2 Was Jade explizit ablehnt¶
Jade lehnt explizit ab:
- Undefiniertes Verhalten als Designwerkzeug Kein "wenn du das tust, kann alles passieren." Fehler sind entweder:
- Compile-Time (abgelehntes IR), oder
-
Laufzeit-Traps mit definierter Semantik.
-
Pointer-zentrisches, schwach spezifiziertes Speichermodell Jade verwendet Handles, Policies und explizite Sharing-Regeln.
-
Debug-Info als nachträglich angehängtes Metadatensystem Debug-Informationen sind in die Repräsentation integriert und werden durch Transformationen hindurch bewahrt.
-
Monolithische, architekturspezifische Backends mit verborgener Semantik Backends sind dünne Übersetzungsschichten; Semantik lebt in Jade IR und VM.
Das Ziel ist, Jade verständlich, testbar und erweiterbar zu halten, ohne die Komplexität und das historische Gepäck älterer Systeme zu erben.
12. Versionierung und Änderungen¶
Diese Constitution ist versioniert:
- Aktuelle Version: v1.2
Im Laufe der Zeit:
- Wenn die Erfahrung zeigt, dass eine Regel zu streng, zu schwach oder schlicht falsch ist, kann sie geändert werden.
- Solche Änderungen müssen:
- als neue Version aufgezeichnet werden (v1.3, v1.4, …),
- von einer kurzen Erklärung begleitet werden, was sich geändert hat und warum.
Vorgeschlagene Struktur für zukünftige Änderungen:
Version 1.3 – Beispiel
- Artikel 6 geändert: Speichermodell erlaubt jetzt eine schwächere Ordnung für share: sync.
- Grund: Performance-Messungen auf realen Workloads, mit formalem Beweis, dass Sicherheit gewahrt bleibt.
Ingenieure und Mitwirkende sollten immer prüfen:
- Respektiert eine vorgeschlagene Änderung die aktuelle Constitution?
- Falls nicht, ist sie wichtig genug, um eine Änderung der Constitution zu rechtfertigen?
- Falls ja, ist die Änderung klar dokumentiert?
Das hält Jades Entwicklung kontrolliert und nachvollziehbar.
Änderungsprotokoll¶
Version 1.2 - Abschnitt 1.3 hinzugefügt: Zielplattform auf POSIX-kompatible Betriebssysteme festgelegt. - Gesamtes Dokument ins Deutsche übersetzt.
13. Glossar¶
Dieser Abschnitt erklärt Schlüsselbegriffe, die in der Constitution verwendet werden, in einfacher Sprache.
Intermediate Representation (IR)¶
Eine strukturierte, maschinenlesbare Form eines Programms, die zwischen Quellcode und ausführbarem Code liegt. IR ist einfacher zu analysieren und zu transformieren als roher Quellcode oder roher Maschinencode.
Pass¶
Eine Transformation oder Analyse, die IR als Eingabe nimmt und entweder:
- modifiziertes IR zurückgibt, oder
- Informationen über das IR produziert (ohne es zu ändern).
Mehrere Pässe werden zu einer Pipeline verkettet.
Invariante¶
Eine Eigenschaft, die immer gelten muss, sobald sie für das IR deklariert wurde. Beispiele: "jeder Wert ist definiert, bevor er verwendet wird", "es gibt keine unerreichbaren Blöcke."
Trap¶
Eine spezielle Operation, die einen definierten Laufzeitfehler darstellt (z.B. Division durch Null). Wenn erreicht, stoppt sie die normale Ausführung und löst einen kontrollierten Fehler aus.
Handle¶
Ein opaker Bezeichner für einen Wert, der von der Jade Memory Engine verwaltet wird. Code sieht nicht die Speicheradresse, nur das Handle.
Policy¶
Metadaten auf Typen oder Werten, die nicht-funktionale Regeln beschreiben, wie:
- wie der Wert geteilt werden kann (local, send, sync),
- wie er allokiert und freigegeben wird,
- ob er FFI-Grenzen überschreiten darf.
Effekt¶
Alles, was ein Programm über die Berechnung eines reinen Wertes hinaus tut. Beispiele: I/O, Logging, Zustandsänderungen, Task-Spawning.
Beobachtungsäquivalenz¶
Zwei Ausführungen sind beobachtungsäquivalent, wenn sie von außen gleich aussehen:
- gleiche Ausgaben,
- gleiche persistente Zustandsänderungen (innerhalb des Modells),
- gleiches extern sichtbares Verhalten.
Debug- und Release-Builds desselben Programms müssen beobachtungsäquivalent sein.
Ring¶
Eine konzeptionelle Schutzebene. Niedrigere Ringe sind vertrauenswürdiger und stärker eingeschränkt:
- Ring 0: Kern-Speicher-Engine (JME).
- Ring 1: VM und Type Engine.
- Ring 2: Plugins und Userland-Code.
Höhere Ringe dürfen von niedrigeren abhängen, aber nicht umgekehrt.
Ende der Jade Design Constitution v1.2