Zum Inhalt

JDL — Empfehlung für ein Context-bound Resource Model und die Neuordnung von Speicher, Allokation und Storage

Status: Arbeitsnotiz / ernsthaft erwogene Designempfehlung
Geltung: Allgemeines Modell für contextgebundene, profilierte und scoped Ressourcen; darauf aufbauend Neuordnung der JDL-Speichersemantik, der Allokationskonfiguration und ihrer Einbettung in Context, Storage-Service, Engines und JadeME.
Nicht normativ: Diese Notiz beschreibt eine empfohlene Richtung. Sie ersetzt noch keine bestehende Spec.
Ziel: Zuerst eine allgemeine, wiederverwendbare Ressourcenarchitektur benennen; anschließend Storage als deren erste konkrete Ausprägung neu ordnen. Das Speichermodell soll orthogonaler, leichter erklärbar und für normalen JDL-Code deutlich weniger fehleranfällig werden, ohne die explizite Kontrolle über Speicher aufzugeben.


0. Kurzfassung

Die bisherige Speichersemantik von JDL vermischt mehrere unterschiedliche Fragen in einer gemeinsamen Memory(...)-Achse. Value, Rc beziehungsweise historisch Ref, Arena[A], Pool[P] und in älteren Notizen auch mapped stehen nebeneinander, obwohl sie nicht dieselbe Art von Entscheidung beschreiben.

Bei der Auflösung dieses Knotens zeigt sich ein allgemeineres Muster, das über Speicher hinausgeht:

Resource Role
    Was will der Code semantisch?
        ↓
Resource Profile
    Welche Policy soll dafür gelten?
        ↓
Resource Descriptor
    Welche geprüfte Semantik gilt konkret?
        ↓
Context Binding
    Welche Ausprägung gilt in dieser Ausführungsdomäne?
        ↓
Resource Instance
    Welche laufende Ressource wurde tatsächlich etabliert?
        ↓
Scopeable / ResourceEngine
    Wie wird ihr Lifecycle strukturiert und garantiert?

Storage ist eine besonders wichtige Spezialisierung dieses Musters, aber nicht dessen Definition. Dieselbe Form kann auch für Transaktionen, Locks, Dateien, Prozesse oder andere Ressourcen gelten, sofern diese tatsächlich einen relevanten Acquire-/Scope-/Release-Lifecycle besitzen.

Die Empfehlung schärft zugleich die bisherige Construction-/Cleanup-Achse: Create soll am Ursprungstyp eine geschlossene Menge zulässiger Konstruktionsübergänge beschreiben. Ein Create-Arm führt mit eigenen Inputs und eigener Row zu einem konkreten Created Type. Drop gehört anschließend diesem Created Type und nicht dem historischen Create-Pfad. Unterschiedliche Lifecycle-Pflichten werden damit typisiert statt als versteckte Creation-Provenance im Wert konserviert.

Eine Lifecycle-Meta-Ableitung kann diese Trennung automatisieren, ohne sie wieder zu vermischen: Sie liest die Create-Domain eines Ursprungstyps, sammelt die tatsächlich erreichbaren Created Types und prüft beziehungsweise leitet deren Drop-Semantik aus bereits materialisierten Facts ab. Sie koppelt also nicht jeden Create-Arm an einen Drop-Pfad. Mehrere Create-Arme, die denselben Created Type liefern, führen zu genau einer Lifecycle-Prüfung dieses Resultattyps. Wo die Drop-Semantik nicht eindeutig beweisbar ist, muss eine explizite Policy beziehungsweise ein Handler angegeben werden.

Die Empfehlung dieser Notiz lautet deshalb zweistufig:

JDL sollte ein allgemeines Context-bound Resource Model verwenden: Rollen benennen Absicht, Profile beschreiben Policy, Descriptoren tragen geprüfte Semantik, Contexts binden sie an eine Ausführungsdomäne und with strukturiert den Lifecycle einer konkreten Resource.

Für Speicher folgt daraus:

Der Typ soll nicht seinen Allocator wählen. Der Typ beschreibt nur die Garantien, die seine Werte benötigen. Der aktive Context stellt Storage-Rollen bereit und bindet diese an geprüfte MemoryRegion-Profile. JadeME führt die daraus folgende Speichermechanik aus.

Für normalen Anwendungscode gilt dabei:

Speicher ist in JDL explizit verwaltet, aber einzelne Allokationen sind normalerweise implizit. Der Benutzer verwaltet Speichergrenzen und Lebensdauern, nicht Speicherblöcke.

Ein Context darf mehrere Storage-Regionen konfigurieren, aber an jedem Ausführungspunkt gibt es genau eine aktive Default-Region für implizite dynamische Allokationen. Wer lokal davon abweichen will, tut dies ausdrücklich über den Storage-Service und with:

with Storage.region[Scratch] {
    ...
}

Scratch ist dabei keine Allokatorimplementierung, sondern eine semantische Storage-Rolle. Der aktive Context entscheidet, welches Storage-Profil diese Rolle erfüllt.

Damit kann derselbe Code unverändert unter unterschiedlichen Contexts laufen:

ProductionContext:
    Scratch -> FastScratchProfile

TestContext:
    Scratch -> CheckedScratchProfile

BenchmarkContext:
    Scratch -> InstrumentedScratchProfile

SandboxContext:
    Scratch -> RestrictedScratchProfile

Die eigentliche Region wird aus orthogonalen Bausteinen beschrieben:

MemoryRegion
├── Backing
├── Allocation
├── Reclamation
├── Lifetime
├── Budget
├── Accounting
└── Guarantees

Beispiele:

RequestRegion
├── Backing      = Mmap
├── Allocation   = Bump
├── Reclamation  = BulkReset
├── Lifetime     = Scope
├── Budget       = 32 MiB
└── Accounting   = Enabled
GeneralRegion
├── Backing      = Mmap
├── Allocation   = FreeList
├── Reclamation  = Individual
├── Lifetime     = Context
└── Accounting   = Basic

Rc und Weak gehören nicht in diese Allokationsachse. Sie beschreiben Ownership. Create beschreibt die geschlossene Menge zulässiger Konstruktionsübergänge eines Typs. Drop gehört dagegen zum jeweils erzeugten Typ und beschreibt dessen semantisches Cleanup am Lebensende. Share beschreibt Task-/Concurrency-Garantien. Diese Achsen sollen wieder sauber voneinander getrennt werden.


Teil I — Warum der heutige Zustand problematisch ist

1. Der aktuelle Knoten

Die aktuelle Dokumentation kennt beziehungsweise kannte unter anderem folgende Speicher- und Lebenszyklusbegriffe:

Memory(Value)
Memory(Ref) / Memory(Rc)
Memory(Arena[A])
Memory(Pool[P])
mapped
Own(Unique | Shared | Weak)
Share(Local | Send | Sync)
Create(Trivial | Factory[F])
Drop(Trivial | Custom)
Arena
Pool
StorageSpec
StorageEngine
ResourceEngine
JadeME
with Storage.arena[...]
HandleId
Generation Counter
Escape Analysis

Jeder einzelne Begriff hat eine nachvollziehbare Motivation. Das Problem ist ihre heutige Gruppierung.

Memory(Value) beschreibt hauptsächlich Repräsentation: inline statt indirekt.

Memory(Rc) beschreibt in der Praxis Repräsentation und Ownership-/Lifetime-Mechanik.

Memory(Arena[A]) beschreibt eine Regions- und Lebensdauerentscheidung und setzt meist eine bestimmte Allokationsstrategie voraus.

Memory(Pool[P]) beschreibt Wiederverwendung beziehungsweise einen speziellen Allocator.

mapped beschreibt eher die Herkunft beziehungsweise das Backing des Speichers.

Diese Dinge sind keine Varianten derselben ontologischen Frage. Sie wurden nur historisch unter demselben Begriff gesammelt.

Das erzeugt Scheinklarheit:

MemoryPolicy
    Value | Rc | Arena | Pool | Mapped

sieht einfach aus, zwingt aber Repräsentation, Ownership, Region, Allokator, Reclamation und Backing in ein gemeinsames Enum. Sobald weitere Strategien hinzukommen — mmap, Free-List, Slab, Buddy, TLSF, Shared Memory, File Mapping, Huge Pages — wird die Vermischung unhaltbar.

2. Die eigentliche Designfrage

JDL soll zwei Ziele gleichzeitig erfüllen:

  1. Der Entwickler soll wissen, dass Speicher eine endliche, verwaltete Ressource ist und bewusste Lebensdauerentscheidungen treffen können.
  2. Der normale Code soll nicht aussehen wie eine permanente Unterhaltung mit einem Allocator-Objekt.

Das Ziel ist ausdrücklich nicht:

GC-Sprache:
    "Vergiss Speicher vollständig."

aber ebenso wenig:

Low-Level-API:
    "Reiche durch jede dritte Funktion einen Allocator und entscheide an jeder Allocation-Site über dessen konkrete Mechanik."

Die gewünschte Mitte ist:

Der Entwickler verwaltet Speicherdomänen und deren Lebensdauer bewusst. Innerhalb einer gültigen Domäne darf normaler JDL-Code gewöhnlich allozieren, ohne jede einzelne Allokation mechanisch zu steuern.


Teil II — Warum Speicherverwaltung für ungeübte Entwickler so schnell zur Hölle wird

3. Das Problem ist selten die einzelne alloc()-Operation

Die meisten schwierigen Speicherfehler entstehen nicht, weil ein Entwickler nicht weiß, dass Speicher angefordert werden muss. Sie entstehen, weil mehrere zeitlich und räumlich getrennte Entscheidungen zusammenpassen müssen.

Typisches Muster:

Fehler wird hier verursacht
        ↓
        ↓ viele Funktionsaufrufe
        ↓ andere Datenstrukturen
        ↓ andere Tasks
        ↓ Cleanup / Reuse
        ↓
Symptom tritt hier auf

Der Entwickler untersucht anschließend einen Stack-Trace an der Stelle des Symptoms, obwohl die Ursache hunderte oder tausende Operationen früher entstanden ist.

Jade sollte möglichst viele solcher Fehler von späten Symptomen zu frühen Grenzverletzungen machen.

4. Typische Fehlerklasse: falsche Lebensdauer

Ein Wert wird in kurzlebigem Speicher erzeugt, aber eine Referenz darauf lebt länger.

Konzeptionell:

RequestArena
    └── UserView

Request endet
Arena reset

GlobalCache
    └── verweist weiterhin auf UserView

In klassischen manuellen Speichermodellen wird daraus schnell ein Use-After-Free oder ein scheinbar zufälliger Crash.

In einem regions- und contextorientierten Modell soll daraus stattdessen eine explizite Verletzung werden:

value cannot escape RequestRegion

oder beim kontrollierten Übergang:

Bridge transfer requires Copy / Transfer / Lease

5. Typische Fehlerklasse: falscher Allocator am falschen Ort

In allocator-zentrierten APIs muss häufig sichergestellt werden, dass Allocation und Deallocation über kompatible Mechanismen laufen.

Beispiele:

Allocator A allociert
Allocator B versucht freizugeben

oder:

Library erwartet langlebigen Allocator
Caller reicht temporären Scratch-Allocator

oder:

Objekt speichert intern Speicher aus einem Allocator,
der früher stirbt als das Objekt selbst

Solche Fehler sind besonders unangenehm, weil die API-Oberfläche syntaktisch korrekt aussehen kann.

Ein Context-/Region-Modell kann die Frage gröber und prüfbarer formulieren:

Diese Ausführungsdomäne besitzt Region R.
Diese Region besitzt Lifetime L.
Dieser Scope darf R verwenden.
Werte aus R dürfen nur unter definierten Crossing-Regeln entkommen.

6. Typische Fehlerklasse: Arena vergessen zurückzusetzen

Arenen sind einfach, solange ihre Lebensdauer wirklich klar begrenzt ist. Werden sie jedoch zu langlebig oder wird reset() vergessen, wächst der Speicherverbrauch ohne offensichtlichen einzelnen Leak.

Der Fehler ist semantisch:

"alles lebt bis reset"

und nicht mechanisch:

"JME hat vergessen zu free'en"

Deshalb sollte das Scope-Ende nicht der Disziplin eines Entwicklers überlassen werden, wenn es strukturell ausdrückbar ist.

with Storage.region[Scratch] {
    ...
}

ist dafür besser als:

val arena = Arena.open()
...
// hoffentlich auf allen Return-/Error-Pfaden arena.reset()/close()

7. Typische Fehlerklasse: Cleanup auf Fehlerpfaden

Manuelle Ressourcenverwaltung scheitert gern nicht im Happy Path, sondern bei:

early return
Throw
cancel
timeout
trap
partieller Initialisierung

Wenn Scope-Lifecycle über with und ResourceEngine zentral erzwungen wird, muss nicht jeder Storage-Provider seine eigene Variante von „hoffentlich wird Cleanup auf jedem Pfad ausgeführt“ implementieren.

Die zentrale Trennung lautet:

ResourceEngine
    versteht Scope-Lifecycle

Storage
    versteht MemoryRegions

JadeME
    versteht physische Speichermechanik

8. Typische Fehlerklasse: dangling Views und Slices

Views sind effizient, solange ihre Quelle lebt. Wird eine View über die Region hinaus getragen, entsteht dieselbe Lebensdauerproblematik wie bei Zeigern, nur hübscher verpackt.

Ein gutes Regionsmodell erlaubt klare Regeln:

View bleibt innerhalb Region
    -> zero-copy möglich

View verlässt Region
    -> Copy / Move / Fehler, abhängig von Typ und Boundary

Damit wird die Entscheidung an der Grenze sichtbar und nicht erst bei späterer Dereferenzierung.

9. Typische Fehlerklasse: Refcount-Zyklen

Rc verhindert keine logischen Zyklen:

A --Rc--> B
B --Rc--> A

Beide Refcounts bleiben größer als null.

Das ist kein Allokatorproblem und sollte auch nicht als solches behandelt werden.

Die dafür vorgesehene Lösung bleibt Weak:

Parent --Rc--> Child
Child --Weak--> Parent

Damit wird deutlich, warum Rc/Weak aus einer gemeinsamen MemoryPolicy herausgelöst werden sollten: Sie beschreiben Ownership-Beziehungen, nicht die Herkunft des Speichers.

Weak löst allerdings nicht jede Form logischen Leaks. Ein Cache, der nie Einträge verwirft, bleibt ein Leak, auch wenn alle Backreferences vorbildlich Weak sind.

10. Typische Fehlerklasse: unkontrollierter Ressourcenverbrauch

Ein Programm kann vollständig speichersicher sein und trotzdem den Prozess durch unbeschränktes Wachstum ruinieren.

Beispiele:

Plugin leak
Parser für bösartige Eingabe
unbegrenzter Cache
versehentlich riesige temporäre Struktur
Benchmark mit falscher Datenmenge

Hier hilft kein Generation Counter und kein Weak.

Was hilft, ist Memory Containment:

PluginContext
└── Storage
    └── PluginRegion
        budget = 256 MiB

Dann kann ein logischer Fehler weiterhin existieren, aber sein Schaden wird durch die Ausführungsdomäne begrenzt.

11. Typische Fehlerklasse: Context-Grenzen und fremde Handles

Wenn Speicherobjekte ohne kontrollierten Übergang zwischen Contexts wandern könnten, wäre Context-Isolation letztlich nur eine Konvention.

Jades bestehendes Handle-/Context-Modell bietet hier einen starken Ansatz:

Context A
    Handle auf Objekt in Region A

Context B
    darf dieses gewöhnliche Handle nicht einfach benutzen

Ein Übergang muss explizit erfolgen, etwa als:

Copy
Transfer
Lease
explizit geteiltes Bridge-Handle

Welche Formen davon normativ werden, ist noch offen. Entscheidend ist die Richtung: Eine Context-Grenze ist gleichzeitig eine natürliche Memory-Lifetime-Grenze.

12. Typische Fehlerklasse: Concurrency und Speichermechanik werden vermischt

Ein Allocator kann thread-safe oder thread-local sein. Ein Wert kann Send oder Sync sein. Eine Region kann an einen Task oder Context gebunden sein.

Das sind wieder unterschiedliche Fragen.

Share(Local | Send | Sync) sollte deshalb nicht Teil derselben Achse sein wie Bump, FreeList oder mmap.


Teil III — Das allgemeine Context-bound Resource Model

13. Der allgemeine Kern

Die Storage-Diskussion legt eine allgemeinere Architektur nahe, die nicht auf Speicher beschränkt werden sollte.

Der gemeinsame Ablauf lautet:

Service / fachliche API
        ↓
Resource Role
        ↓
Context Binding
        ↓
Resource Profile
        ↓
DescriptorEngine / fachliche Engine
        ↓
Resource Descriptor
        ↓
Resource Instance
        ↓
ResourceEngine / Scopeable
        ↓
enter → body → exit → mechanisches cleanup

Die Ebenen beantworten unterschiedliche Fragen:

Service
    Welche semantische Leistung verlangt der Code von seiner Umgebung?

Resource Role
    Welche Art von Ressource will der Code innerhalb dieser Leistung?

Resource Profile
    Welche Policy soll für diese Rolle gelten?

Resource Descriptor
    Welche geprüfte Semantik gilt nach Materialisierung tatsächlich?

Context Binding
    Welche konkrete Ausprägung dieser Rolle gilt in dieser Ausführungsdomäne?

Resource Instance
    Welche laufende Ressource wurde etabliert?

Scopeable / ResourceEngine
    Wie wird die Lebensdauer strukturiert und auf allen regulären Pfaden beendet?

Das Muster ist bewusst allgemeiner als Storage. Storage soll darauf aufbauen, nicht es definieren.

Die Schichten sind dabei kein Zwang, jede Resource maximal auszudekorieren. Wenn eine Domäne keine contextabhängige Rollenauswahl braucht, können Role und Profile zusammenfallen oder ganz entfallen. Wenn keine eigene DescriptorEngine nötig ist, kann ein Provider einen bereits geprüften Vertrag direkt liefern. Wenn eine Ressource nicht scoped ist, muss sie nicht Scopeable sein.

Der Zweck des Modells ist Trennung dort, wo unterschiedliche Fragen existieren — nicht das Erfinden leerer Abstraktionsschichten.

14. Ein Service ist nicht automatisch eine Resource

Ein Service in einer Effect Row bedeutet zunächst nur:

Diese Funktion benötigt eine semantische Leistung aus ihrer Umgebung.

Eine Resource ist enger:

Eine Resource ist eine laufende Instanz mit relevanter Besitz-, Lebensdauer-, Abschluss- oder Freigabesemantik.

Daher:

Service
    = Was brauche ich aus meiner Umgebung?

Resource
    = Was habe ich erhalten und muss lebenszyklisch korrekt behandeln?

Beispiel ohne explizite Resource:

// spekulativ
def report() ->{Logger} Unit {
    Logger.info("done")
}

Logger ist ein Service. Für den Aufrufer entsteht daraus nicht automatisch eine scoped Resource.

Dagegen:

// spekulativ
def update() ->{Database} Unit {
    with Database.transaction[ReadWrite] as tx {
        ...
    }
}

Hier gilt:

Database
    Service

ReadWrite
    Resource Role

Transaction[ReadWrite]
    Resource

Ein Service kann Ressourcen bereitstellen oder vermitteln. Nicht jeder Service ist selbst eine Resource und nicht jede Service-Operation erzeugt eine Resource.

Diese Trennung verhindert, dass jede Row-Abhängigkeit künstlich enter/exit/cleanup tragen muss.

15. Resource Role: Absicht statt Mechanik

Eine Resource Role benennt, was der aufrufende Code semantisch benötigt.

Beispiele:

Storage:
    Scratch
    Request
    Secure

Database:
    ReadOnly
    ReadWrite

Locking:
    CacheWrite
    MetadataRead

Filesystem:
    Temporary
    DurableOutput

Die Rolle beschreibt nicht, wie die Ressource technisch realisiert wird.

Scratch
    ≠ mmap + bump + bulk reset

ReadWrite
    ≠ PostgreSQL + Serializable + 5s timeout

Die konkrete Policy gehört ins Profil und die konkrete Auswahl in den Context.

16. Resource Profile: wiederverwendbare Policy

Ein Profil beschreibt die Policy einer Ressource unabhängig vom aufrufenden Code.

Konzeptionell:

Role
    semantische Bedeutung

Profile
    technische/fachliche Policy

Beispiel Transaktion:

ReadWrite
    ↓
SerializableWriteProfile
    isolation = Serializable
    timeout   = 5s
    retry     = Bounded(3)

Ein TestContext könnte dieselbe Rolle anders binden:

ReadWrite
    ↓
InMemoryWriteProfile

Der Anwendungscode bleibt:

with Database.transaction[ReadWrite] as tx {
    ...
}

Das ist dieselbe Entkopplung, die für Storage.region[Scratch] empfohlen wird.

17. Resource Descriptor: geprüfte Semantik

Ein Profil ist Beschreibung. Ein Descriptor ist die materialisierte, geprüfte Form.

ResourceProfile
    ↓ describe / validate
ResourceDescriptor

Der Descriptor muss alles enthalten, was spätere Phasen benötigen, ohne die CompilerDB erneut konsultieren zu müssen.

Er kann beispielsweise festhalten:

zulässige Operationen
Lifecycle-Semantik
Limits
Timeouts
Cleanup-/Reap-Vertrag
notwendige Capabilities
fachliche Policies
Kompatibilitätsbedingungen

Nicht jede Ressourcendomäne braucht zwingend eine eigene Engine. Wenn eine Domäne keine wiederverwendbare Ausführungssemantik besitzt, kann ein Service Provider ausreichen. Engines sind für Invarianten da, nicht als Belohnung dafür, dass ein Substantiv existiert.

18. Context Binding: Umgebung entscheidet über die konkrete Ausprägung

Der aufrufende Code soll context-agnostisch bleiben.

Beispiel:

ProductionContext:
    Database.ReadWrite -> SerializableWriteProfile

TestContext:
    Database.ReadWrite -> InMemoryWriteProfile

BenchmarkContext:
    Database.ReadWrite -> InstrumentedWriteProfile

Der Code bleibt unverändert:

with Database.transaction[ReadWrite] as tx {
    ...
}

Dasselbe Muster gilt später für Storage:

ProductionContext:
    Storage.Scratch -> FastScratchProfile

TestContext:
    Storage.Scratch -> CheckedScratchProfile

Dadurch sitzt die Deployment-/Umgebungsentscheidung dort, wo sie hingehört: im Context statt im Typ oder an jeder einzelnen Aufrufstelle.

19. Resource Instance: das laufende Ding

Der Descriptor ist kein Laufzeitzustand.

Erst beim tatsächlichen Erwerb entsteht eine Resource Instance:

TransactionDescriptor
    ↓ acquire
Transaction Instance

StorageDescriptor
    ↓ enter region
MemoryRegion Instance

FileDescriptorSpec
    ↓ open
File Instance

Die Resource Instance kann Handles, Leases oder andere lauf-lokale Identitäten enthalten. Diese gehören nicht in die kanonische Descriptor-Semantik.

20. Scopeable und ResourceEngine

with sollte nicht von Storage, Database, Locking oder Files jeweils neu erfunden werden. Gleichzeitig ist nicht jede Resource automatisch scoped. Ein langlebiger Context-eigener Pool oder ein persistentes Handle kann eine Resource sein, ohne als lokaler with-Scope aufzutreten. Scopeable beschreibt nur jene Ressourcen, deren Lifecycle lexikalisch oder strukturiert begrenzt werden soll.

Ebenso sollte daraus kein universeller Resource.open[...]-Service entstehen. Die fachliche API bleibt erhalten:

Storage.region[Scratch]
Database.transaction[ReadWrite]
Lock.acquire[CacheWrite]
FileSystem.file[Temporary]

Gemeinsam ist der Lifecycle, nicht die fachliche Bedeutung.

Der allgemeine Vertrag ist:

ResourceEngine
    besitzt die strukturierte Scope-Semantik

fachlicher Service / Engine
    besitzt die Domänensemantik

Resource Descriptor
    trägt den geprüften Lifecycle-Vertrag

Konzeptionell kann eine Resource Scopeable sein:

enter
    etabliert die Ressource vollständig

body
    benutzt die Ressource

exit
    beendet den fachlichen Scope abhängig vom Exit-Grund

cleanup / reap
    stellt mechanische Ressourceninvarianten garantiert wieder her

Die genaue JDL-Syntax ist offen. Entscheidend ist, dass cleanup nicht einfach ein beliebiger User-Callback sein sollte, wenn Jade auch bei beschädigten Fehlerpfaden mechanische Rückgewinnung garantieren will.

21. Fehlersemantik einer scoped Resource

Eine allgemeine Resource-Abstraktion muss den Fehlerfall genauso sauber tragen wie den Happy Path.

enter scheitert

enter
    ↓ Throw[OpenError]

Body wird nicht betreten.
exit wird nicht aufgerufen.

Falls enter intern bereits Teilressourcen beschafft hat, muss es deren mechanisches Cleanup selbst garantieren, bevor der Fehler sichtbar wird.

Eine Resource darf dem Body erst sichtbar werden, wenn ihre Scope-Invariante vollständig etabliert ist.

Body scheitert mit Throw[E]

enter
  ↓
body
  ↓ Throw[E]
exit(Failure[E])
  ↓
cleanup
  ↓
Throw[E] propagiert weiter

with garantiert damit das Ende der Ressourcenbeziehung, nicht den Erfolg der Arbeit.

exit scheitert

Bei erfolgreichem Body kann ein fachlich fallibles exit einen normalen Fehler erzeugen, etwa CommitError.

Schwieriger ist:

Body       -> DbError
Rollback   -> RollbackError

Ein Exit-Fehler darf einen bereits laufenden Primärfehler nicht stillschweigend überschreiben. Wie mehrere Fehler in JDL konkret repräsentiert werden, bleibt offen; die semantische Anforderung sollte jedoch feststehen.

Mechanisches cleanup scheitert

Mechanisches Cleanup sollte kein gewöhnlicher fachlicher Fehlerkanal sein.

Wenn die Runtime ihre elementaren Ressourceninvarianten nicht wiederherstellen kann, liegt eher eine gebrochene Runtime-Invariante vor:

cleanup impossible
    -> Trap / Runtime invariant failure

Sonst würde aus einem Fehlerpfad schnell eine Kaskade aus Body-, Exit- und Cleanup-Fehlern, bei der niemand mehr weiß, welcher Fehler eigentlich die Ursache war.

Trap im Body

Ein Trap ist nicht dasselbe wie Throw[E].

Auf einem Trap-Pfad sollte Jade nicht beliebig weitere User-Semantik ausführen müssen. Die ResourceEngine beziehungsweise Ring 0 muss jedoch mechanische Rückgewinnung ermöglichen können, solange die betreffende Runtime-Domäne noch fortsetzbar ist. Ein Prozessabbruch, Hostverlust oder SIGKILL kann naturgemäß keine in-process Cleanup-Garantie erfüllen.

Damit ergibt sich die Trennung:

Throw
    -> geordneter fachlicher Exit
    -> mechanisches Cleanup
    -> Propagation

Trap
    -> keine beliebige User-Semantik voraussetzen
    -> mechanisches Reaping
    -> Trap propagiert

22. Statische Übergangsgarantien

Der allgemeine Resource-/Context-Ansatz erlaubt einen stärkeren Anspruch als bloß „wir prüfen ein paar Typen“.

Der Zielpfad lautet:

describe
    ↓
validate
    ↓
materialize / admit
    ↓
execute

Für vollständig statisch bekannte Programme fällt ein großer Teil davon in die Übersetzung. Für nachgeladene Plugins oder andere dynamische Bestandteile wandert die Prüfung an die späteste legitime Grenze.

Der sinnvolle Anspruch lautet nicht:

Wenn es kompiliert, kann zur Laufzeit nichts mehr schiefgehen.

Netzwerk, Speicherknappheit, externe Systeme und fachlich fallible Operationen bleiben dynamisch.

Der stärkere und realistischere Satz ist:

Was Jade zur Ausführung zulässt, soll nicht mehr an einer bereits statisch beziehungsweise bei Admission beschreibbaren strukturellen Unzulässigkeit scheitern.

Beispiele:

Role fehlt im Context
Profile ist inkompatibel
Capability fehlt
Lifetime passt nicht
Bridge kann Transferform nicht tragen
Storage Guarantee wird nicht erfüllt
Resource Scope ist strukturell ungültig

Diese Fehler sollen vor der eigentlichen Ausführung an der jeweiligen Materialisierungs-/Admission-Grenze erscheinen.

Die Runtime führt dann geprüfte Übergänge aus, statt erst dort zu entscheiden, ob sie semantisch überhaupt erlaubt sind.

23. Beispiele derselben Architektur

Storage

with Storage.region[Scratch] {
    ...
}
Storage             Service
Scratch             Resource Role
FastScratchProfile  Resource Profile
StorageDescriptor   Resource Descriptor
MemoryRegion        Resource Instance
ResourceEngine      Scope Lifecycle
StorageEngine/JME   Storage-Semantik und Mechanik

Database

Konzeptionell:

tag TransactionMode : enum =
    | ReadOnly
    | ReadWrite

type Database : service {
    def transaction[mode: TransactionMode](self)
        ->{Throw[TransactionOpenError]}
        Transaction[mode]
}

Die Resource selbst bleibt ein normaler Created Type mit Lifecycle-Vertrag:

// spekulativ
type Transaction[
    phantom mode: TransactionMode
] : struct {
    handle: TransactionHandle
}
    :> Drop(Handler[DatabaseTransactionCleanup])

Database.transaction[mode] ist hier der fachliche Konstruktionsweg, der den Created Type Transaction[mode] liefert. Falls die allgemeine Create-Domain für solche Service-Ressourcen verwendet wird, würde die Service-Operation einen entsprechenden Create-Arm realisieren; der spätere Lifecycle hängt trotzdem am erzeugten Transaction[mode], nicht an der historischen Erzeugungsoperation.

Benutzung:

with Database.transaction[ReadWrite] as tx {
    ...
}

Lock

with Lock.acquire[CacheWrite] {
    ...
}

CacheWrite benennt die Rolle; das Profil kann Fairness, Timeout oder Reentrancy festlegen.

File

with FileSystem.file[Temporary] as file {
    ...
}

Temporary kann im Context auf ein Profil mit privaten Rechten, bestimmtem Temp-Bereich und delete-on-scope-exit gebunden werden.

Diese Beispiele sollen nicht beweisen, dass bereits eine vollständige universelle Resource-Abstraktion normiert ist. Sie zeigen vielmehr einen Test: Wenn mehrere unabhängige Domänen ohne Sondergymnastik dieselbe Form tragen, ist das ein starkes Indiz, dass die Abstraktion tatsächlich allgemein ist.


Teil IV — Storage als erste Spezialisierung des allgemeinen Resource-Modells

24. Leitprinzip für Storage

Storage übernimmt das allgemeine Resource-Muster und ergänzt die domänenspezifischen Speicherachsen. Die empfohlene Schichtung lautet:

Typ
    beschreibt notwendige Garantien
        ↓
Code
    benutzt semantische Storage-Rollen
        ↓
Context
    bindet Rollen an Storage-Profile
        ↓
StorageEngine
    materialisiert / orchestriert MemoryRegions
        ↓
ResourceEngine
    erzwingt Scope-Lifecycle
        ↓
JadeME
    führt die physische Speichermechanik aus

Daraus folgt eine wichtige Vereinfachung:

Normale JDL-Werte benötigen keine sichtbare Allokatorwahl. Explizite Storage-Semantik erscheint dort, wo der Entwickler bewusst eine andere Lebensdauer, Region oder Ressourcenpolicy verlangt.


Teil V — Die orthogonalen Achsen

25. Representation: Wie wird ein Wert dargestellt?

Beispiele:

Inline
Handle

Diese Entscheidung sollte größtenteils aus Typstruktur, Größe und benötigten Garantien ableitbar sein.

Sie ist kein guter Kandidat für dieselbe öffentliche Policy wie Arena- oder Pool-Auswahl.

Ein Descriptor-Fact kann weiterhin festhalten:

representation = Inline

oder:

representation = Handle

aber normaler JDL-Code sollte selten gezwungen sein, dies selbst festzulegen.

26. Ownership: Wer hält den Wert am Leben?

Beispiele:

Unique
Rc[T]
Weak[T]

Ownership beschreibt keine Speicherregion.

Rc[T] bedeutet nicht „benutze den Rc-Allocator“. Es bedeutet, dass mehrere logische Besitzer die Lebensdauer eines Wertes gemeinsam kontrollieren.

Weak[T] ist die nicht-besitzende Gegenrichtung für Ownership-Graphen.

27. Sharing: Wo darf ein Wert verwendet werden?

Beispiele:

Local
Send
Sync

Das ist eine Concurrency-/Task-Garantie, keine Allocation Policy.

28. Construction: Create beschreibt eine geschlossene Konstruktionsdomäne

Create gehört weiterhin nicht in die Speicherachse. Die bisherige Lesart Create(Factory[F]) ist jedoch wahrscheinlich zu eng: Sie beantwortet primär die Frage, wer konstruieren darf, obwohl für den Compiler die interessantere Semantik lautet, welche zulässigen Konstruktionsübergänge es überhaupt gibt und welcher Typ daraus entsteht.

Empfohlen wird deshalb, Create am konstruierbaren Ursprungstyp als geschlossene Konstruktionsdomäne zu modellieren. Eine Tag-/Enum-Domain bietet dafür eine natürliche Form:

// spekulativ
tag ConnectionCreate : enum =
    | Pooled
    | Direct
    | Replica
    | Borrowed

type Connection : struct {
    ...
}
    :> Create(ConnectionCreate)

Die Enum-Arme benennen nicht bloß Factory-Namen. Jeder Arm beschreibt konzeptionell einen Konstruktionsübergang mit eigenen Eingaben, eigener Row und einem konkreten Resultattyp:

ConnectionCreate.Pooled(...)
    ->{ConnectionPool}
    PooledConnection

ConnectionCreate.Direct(...)
    ->{Network}
    OwnedConnection

ConnectionCreate.Replica(...)
    ->{ReplicaService}
    OwnedConnection

ConnectionCreate.Borrowed(...)
    ->{}
    BorrowedConnection

Die genaue Syntax ist offen. Entscheidend ist die Zuständigkeit:

Create am Ursprungstyp
    = welche geschlossenen Erzeugungswege existieren?
      welche Eingaben und Services benötigen sie?
      welcher Created Type entsteht?

Damit wird Create zu einer kleinen, exhaustiv prüfbaren Konstruktionsalgebra statt zu einer offenen Sammlung verstreuter Factory-Funktionen. IDE, LSP, CompilerDB und :why können die vollständige Create-Domain eines Typs direkt anzeigen.

Mehrere Create-Arme dürfen denselben Resultattyp erzeugen. Direct und Replica können beispielsweise beide einen OwnedConnection liefern. Umgekehrt muss ein Unterschied, der nach erfolgreicher Konstruktion für Ownership oder Cleanup relevant bleibt, im Created Type sichtbar werden und nicht als versteckte Creation-Provenance im Wert weiterleben.

Damit wird ausdrücklich vermieden:

Connection {
    createdBy: Pooled | Direct | Replica
}

Drop:
    switch createdBy { ... }

Wenn der Erzeugungsweg einen anderen Lifecycle erzeugt, soll der Resultattyp diesen Unterschied tragen.

Create allokiert dabei weiterhin nichts und wählt keine MemoryRegion. Die Konstruktionssemantik und die Storage-Semantik bleiben orthogonal.

Ebenso wenig legt ein Create-Arm selbst seinen späteren Drop-Pfad fest. Seine Verantwortung endet mit dem erfolgreichen Übergang in einen Created Type. Ab dort gilt ausschließlich dessen materialisierte Lifecycle-Semantik. Das verhindert, dass Construction-Provenance nur deshalb in den Laufzeitwert eingebaut werden muss, damit später ein Cleanup-Pfad ausgewählt werden kann.

29. Cleanup: Drop gehört zum Created Type

Drop wird nicht an einen Create-Arm gekoppelt. Es gehört zum Typ des fertig erzeugten Wertes.

Konzeptionell:

// spekulativ
type PooledConnection : struct {
    ...
}
    :> Drop(Handler[ConnectionPool])

type OwnedConnection : struct {
    ...
}
    :> Drop(Handler[ConnectionCleanup])

type BorrowedConnection : struct {
    ...
}
    :> Drop(Trivial)

Damit gilt:

Create Arm
    -> erzeugt einen Created Type

Created Type
    -> trägt Ownership, Share, Drop und weitere Lifecycle-Garantien

Es gibt deshalb keine 1:1-Regel zwischen Create und Drop. Mehrere Create-Arme können denselben Created Type und damit dieselbe Drop-Semantik liefern. Unterschiedliche Drop-Semantik ist dagegen ein starkes Indiz dafür, dass unterschiedliche Created Types vorliegen sollten.

Die gewünschte Trennung lautet:

vor erfolgreicher Konstruktion
    Create Domain / Create Arm / Inputs / Row

nach erfolgreicher Konstruktion
    Created Type / Ownership / Share / Drop / weitere Invarianten

Drop bleibt semantisches Lifecycle-/Resource-Cleanup und ist nicht synonym mit physischem free.

Beispiel:

Drop(Handler[ConnectionCleanup])
    -> Socket schließen
    -> Protokollzustand sauber beenden

Drop(Handler[SecureCleanup])
    -> Schlüssel zeroen
    -> native Ressource freigeben

JME reclaim
    -> Speicher mechanisch zurückgewinnen

Diese Trennung ist besonders wichtig bei Trap-/Unwind-Semantik und bei Bulk-Reclamation: Der Lifecycle des Created Type bleibt semantisch, die mechanische Speicherrückgewinnung bleibt Aufgabe von Region/JadeME.

29.1 Lifecycle-Meta: Create und Drop automatisch zusammenführen, ohne sie zu koppeln

Die Trennung zwischen Create-Domain und Created Type eröffnet eine sinnvolle Meta-Ableitung. Ihr Zweck ist nicht, zu jedem Create-Arm automatisch einen korrespondierenden Drop-Arm zu erzeugen. Genau das würde die eben eingeführte Zuständigkeitsgrenze wieder aufheben.

Die Meta-Ableitung arbeitet stattdessen auf dem resultierenden Construction-/Lifecycle-Graphen:

Origin Type
    ↓ Create Domain
Create Arms
    ↓
Created Types
    ↓
Lifecycle Meta
    ├── vorhandenes Drop prüfen
    ├── eindeutiges Drop ableiten
    └── sonst explizite Policy verlangen

Konzeptionell könnte sie als CompilerDB-Ableitung erscheinen:

// spekulativ; Name und konkrete Fassade sind offen
def deriveLifecycle[T: TypeSubject](subject: T) ->Meta{} Unit

Für Connection würde sie zunächst die geschlossene Create-Domain lesen:

Connection.Create
├── Pooled(...)  -> PooledConnection
├── Direct(...)  -> OwnedConnection
├── Replica(...) -> OwnedConnection
└── Borrowed(...) -> BorrowedConnection

Danach wird nicht pro Arm, sondern pro eindeutigem Created Type gearbeitet:

reachable Created Types:
    PooledConnection
    OwnedConnection
    BorrowedConnection

Direct und Replica führen daher nicht zu zwei Drop-Ableitungen. Beide enden im selben OwnedConnection und teilen dessen Lifecycle-Vertrag.

29.2 Drei mögliche Ergebnisse der Ableitung

Für jeden erreichbaren Created Type gibt es drei sinnvolle Fälle.

1. Trivial. Der Typ besitzt keine nicht-trivialen Lifecycle-Pflichten.

BorrowedConnection
    -> Drop(Trivial)

2. Eindeutig strukturell ableitbar. Die Lifecycle-Semantik folgt vollständig aus bereits materialisierten Facts der enthaltenen Ressourcen beziehungsweise Felder.

// spekulativ
type OwnedConnection : struct {
    socket: OwnedSocket
}

Wenn für OwnedSocket eindeutig feststeht, dass sein reguläres Lebensende Close verlangt, kann die Lifecycle-Meta den Drop von OwnedConnection strukturell daraus ableiten. Entsprechendes gilt etwa für:

// spekulativ
type PooledConnection : struct {
    lease: ConnectionLease
}

wenn ConnectionLease eindeutig beschreibt, dass die Lease an den Pool zurückgegeben werden muss.

3. Mehrdeutig. Aus den vorhandenen Facts folgen mehrere semantisch mögliche Cleanup-Wege. Dann darf Meta nicht raten.

// spekulativ
type NativeThing : struct {
    handle: RawHandle
}

Aus einem bloßen RawHandle folgt nicht eindeutig, ob er geschlossen, zurückgegeben, übertragen oder bewusst nicht besessen wird. In diesem Fall ist eine explizite Lifecycle-Policy erforderlich:

// spekulativ
type NativeThing : struct {
    handle: RawHandle
}
    :> Drop(Handler[NativeThingCleanup])

Die Leitregel lautet deshalb:

Lifecycle-Meta darf Drop nur dort automatisch ableiten, wo die Semantik eindeutig aus bereits materialisierten Facts folgt. Mehrdeutigkeit verlangt eine explizite Entscheidung.

29.3 Die CompilerDB-Fassade bleibt die konstruktive Grenze

Die Lifecycle-Meta benötigt keinen privilegierten Sonderpfad im Compiler. Sie arbeitet wie andere Meta-Ableitungen ausschließlich über die CompilerDB-Fassade. Was diese Fassade nicht ausdrücken oder abfragen kann, kann die Meta-Ableitung weder konstruieren noch als Fact behaupten.

Konzeptionell:

CompilerDB-Fassade
    ↓
Create-Domain lesen
    ↓
Created Types sammeln
    ↓
Lifecycle-Facts lesen
    ↓
Drop ableiten oder explizite Policy verlangen
    ↓
zuständige Materialisierung / Validierung

Damit bleibt dieselbe Architekturgrenze erhalten wie im übrigen Meta-System:

Meta
    konstruiert und leitet ab

zuständige Engine / Materializer
    validiert

CompilerDB
    hält kanonische Semantik und Provenance

Die Herkunft der Drop-Semantik bleibt dadurch erklärbar. :why kann beispielsweise zeigen:

Why does OwnedConnection close its socket?

OwnedConnection
  -> field socket: OwnedSocket
  -> OwnedSocket lifecycle requires Close
  -> structural lifecycle derivation
  -> Drop fact for OwnedConnection

Diese Provenance ist besonders wichtig, weil automatisch erzeugte Lifecycle-Semantik sonst leicht zu unsichtbarer Compiler-Magie würde.

29.4 Lifecycle-Meta ist breiter als Drop-Codegenerierung

Langfristig sollte die Ableitung nicht als Generator für Destruktorfunktionen verstanden werden. Sie prüft einen Zustandsübergang und dessen resultierende Invarianten:

Source Type
    ↓ Create Arm
Created Type
    ├── Ownership
    ├── Share
    ├── Drop
    ├── Scopeability
    ├── Transferability
    └── Storage Requirements

Drop ist nur eine besonders offensichtliche Endbedingung des erzeugten Zustands. Ob und wie weit diese Meta-Ableitung später auch andere Lifecycle-Facts ableiten darf, bleibt offen. Für v0.1/v0.2 reicht die engere Regel: Create-Domain erfassen, Created Types deduplizieren, deren Drop-Semantik prüfen beziehungsweise bei Eindeutigkeit ableiten.

30. Storage Region: In welcher Speicherdomäne lebt dynamischer Speicher?

Das ist die Ebene, auf der Arena, Pool, Scratch, langlebiger Heap, Shared Mapping und ähnliche Mechanismen neu eingeordnet werden sollten.

Die oberste Abstraktion ist MemoryRegion.


Teil VI — MemoryRegion als allgemeine Speicherdomäne

31. Vorschlag für die Struktur

MemoryRegion
├── Backing
├── Allocation
├── Reclamation
├── Lifetime
├── Budget
├── Accounting
└── Guarantees

Backing

Frage:

Woher kommt der Speicher überhaupt?

Beispiele:

System
Mmap
FileMapped
SharedMemory
External
HugePages

Allocation

Frage:

Wie wird Speicher innerhalb der Region aufgeteilt?

Beispiele:

Bump
FreeList
Slab
Buddy
FixedSlot
TLSF
General

Reclamation

Frage:

Wie wird belegter Speicher wieder verfügbar?

Beispiele:

Individual
BulkReset
ReturnToPool
RegionDestroy
ExternalOwner

Lifetime

Frage:

Wie lange existiert die Region beziehungsweise ihre logische Bindung?

Beispiele:

Scope
Task
Request
Context
Persistent
Explicit

Budget

Beispiele:

hard limit
soft limit
reserve
growth policy

Accounting

Beispiele:

Disabled
Basic
Detailed
PerType
PerCallSite

Guarantees

Beispiele:

alignment >= N
stableAddress
zeroOnRelease
movable / nonMovable
threadLocal
threadSafe
wirePinned

Nicht jede dieser Eigenschaften muss in v0.1 existieren. Wichtig ist die kategoriale Trennung.

32. Warum mmap und FreeList nicht nebeneinander stehen sollten

mmap und FreeList werden häufig beide umgangssprachlich als „Allokationsstrategie“ bezeichnet, beantworten aber unterschiedliche Fragen.

mmap
    -> Backing / Beschaffung virtueller Speicherseiten

FreeList
    -> Allocation/Reclamation innerhalb bereits vorhandenen Speichers

Ebenso:

Arena
    -> Region-/Lifetime-Muster, typischerweise Bump + BulkReset

Pool
    -> Allocation-/Wiederverwendungsmuster, häufig FixedSlot/Slab + ReturnToPool

Rc
    -> Ownership

Die frühere gemeinsame MemoryPolicy versteckt diese Unterschiede.

33. Beispiele für zusammengesetzte Regionen

Request Region

RequestRegion
├── Backing      = Mmap
├── Allocation   = Bump
├── Reclamation  = BulkReset
├── Lifetime     = Scope / Request
├── Budget       = 32 MiB
└── Accounting   = Basic

General Region

GeneralRegion
├── Backing      = Mmap
├── Allocation   = FreeList
├── Reclamation  = Individual
├── Lifetime     = Context
├── Budget       = 1 GiB
└── Accounting   = Basic

Connection Region

ConnectionRegion
├── Backing      = System
├── Allocation   = FixedSlot
├── Reclamation  = ReturnToPool
├── Lifetime     = Context
├── Capacity     = 4096
└── Accounting   = Basic

Secure Region

SecureRegion
├── Backing      = Mmap
├── Allocation   = FreeList
├── Reclamation  = Individual
├── Lifetime     = Context
├── Budget       = 64 MiB
└── Guarantees
    ├── zeroOnRelease
    └── stableAddress

Teil VII — Storage-Rollen und Context-Bindung

34. StorageRole als semantische Auswahl

Anwendungscode sollte nicht auf konkrete Allocator- oder Regionstypen gekoppelt sein.

Empfohlen ist eine geschlossene nominale Rolle, etwa:

// Syntax spekulativ
tag StorageRole : enum =
    | Default
    | Scratch
    | Request
    | Secure
    | Cache

Der Tag sagt nur:

Welche Art Speicher brauche ich semantisch?

Er sagt nicht:

Scratch = mmap + bump + bulk reset + 64 MiB

Diese Mechanik gehört in ein Profil und dessen Context-Bindung.

35. StorageProfile als beschreibbare Konfiguration

Konzeptionell:

// spekulativ
type StorageProfile : blueprint {
    backing:     BackingSpec
    allocation:  AllocationSpec
    reclamation: ReclamationSpec
    lifetime:    LifetimeSpec
    budget:      MemoryBudget
    accounting:  AccountingSpec
    guarantees:  StorageGuarantees
}

Beispiel:

// spekulativ
type FastScratchProfile = StorageProfile & {
    backing:     Mmap
    allocation:  Bump
    reclamation: BulkReset
    lifetime:    Scope
    budget:      64_MiB
}

Ob StorageProfile tatsächlich ein Blueprint ist, hängt davon ab, ob die StorageEngine daraus einen Descriptor materialisieren muss. Das ist derzeit eine plausible und konsistente Richtung, aber noch nicht normiert.

36. Context bindet Rolle an Profil

WebContext
└── Storage
    ├── Default -> GeneralProfile
    ├── Scratch -> FastScratchProfile
    ├── Request -> RequestProfile
    └── Secure  -> SecureProfile

Damit bleibt eine Library context-agnostisch.

Sie sagt nur:

with Storage.region[Scratch] {
    ...
}

und muss nicht wissen, wie Scratch in Produktion, Tests oder Benchmarks realisiert wird.

37. Gleicher Code, andere Contexts

ProductionContext
    Scratch -> FastScratchProfile

TestContext
    Scratch -> CheckedScratchProfile

BenchmarkContext
    Scratch -> InstrumentedScratchProfile

SandboxContext
    Scratch -> RestrictedScratchProfile

Der Anwendungscode bleibt identisch.

Das ist eine konsequente Anwendung des Context-Prinzips:

Definition
    context-agnostisch

Descriptor
    geprüfte Semantik

Context
    Verdrahtung

Runtime
    Ausführung

38. Alias und globale Projektkonfiguration

Ein Projekt kann zusätzlich Aliase oder Profile verwenden, um eine Default-Konfiguration zentral auszutauschen.

Empfohlen ist jedoch, die semantische Rolle und die technische Konfiguration getrennt zu halten:

Scratch
    = Rolle

FastScratchProfile
    = Mechanik

Ein Alias sollte daher eher ein Profil aliasen oder eine projektweite Binding-Vorgabe beeinflussen, statt den Tag selbst mit mechanischen Details zu überladen.

Der Grund ist einfach: Derselbe Scratch-Code soll unter unterschiedlichen Contexts unterschiedliche Realisierungen erhalten können.


Teil VIII — Eine aktive Default-Region, mehrere konfigurierte Regionen

39. Präzisierung

Ein Context soll nicht auf genau eine einzige konfigurierte Speicherstrategie beschränkt sein.

Er kann mehrere Regionen beziehungsweise Rollen anbieten.

Aber:

Zu jedem Ausführungspunkt gibt es genau eine aktive Default-Region für implizite dynamische Allokationen.

Beispiel:

WebContext
├── Default -> GeneralRegion
├── Request -> RequestRegion
├── Scratch -> ScratchRegion
└── Secure  -> SecureRegion

Normaler Code:

def parse(input: str) ->{} Ast {
    val tokens = tokenize(input)
    val ast    = buildAst(tokens)
    ast
}

Dynamische Allokationen benutzen implizit die aktive Default-Region.

Dadurch muss nicht jede allokierende Funktion ->{Storage} deklarieren.

40. Warum normale Allocation kein Storage-Effect sein sollte

Wenn jede String-Vergrößerung oder Array-Allokation Storage in die Row ziehen würde, wäre praktisch jede nichttriviale Funktion mit einem Storage-Effect versehen.

Das wäre semantisch wenig hilfreich.

Daher:

implizite normale Allokation
    -> aktive Default-Region
    -> kein expliziter Storage-Service-Use

explizite Storage-Kontrolle
    -> Storage-Service
    -> Storage erscheint in der Effect Row

Der Effekt drückt damit nicht aus:

Diese Funktion braucht irgendwo Bytes.

sondern:

Diese Funktion verlangt bewusste Kontrolle über die Storage-Umgebung.


Teil IX — Storage als Service

41. Rolle des Services

Storage sollte die JDL-seitige semantische Fassade für explizite Storage-Kontrolle sein.

Er ist nicht selbst der Allocator.

Konzeptionell:

Storage Service
    "Ich möchte Region mit Rolle Scratch benutzen."

StorageEngine
    "Welche geprüfte Region erfüllt Scratch und wie wird sie orchestriert?"

ResourceEngine
    "Wie wird der Scope sicher betreten und verlassen?"

JadeME
    "Wie werden die Bytes tatsächlich bereitgestellt und zurückgewonnen?"

42. Storage in der Effect Row

Beispiel:

// spekulativ
def optimize(data: Data) ->{Storage} Result {
    with Storage.region[Scratch] {
        ...
    }
}

Der Context bindet den Service wie jeden anderen Service:

->{Storage}
     ↓
Effect-/Handler-Semantik
     ↓
Context Binding
     ↓
Storage Provider

Der EffectEngine führt Storage nicht mechanisch aus. Er ist nur für die Service-/Handler-Semantik zuständig.


Teil X — with, Scopeable und ResourceEngine

43. with ist die lokale Escape Hatch

Der normale Code benutzt die Default-Region.

Wer bewusst eine andere Storage-Domäne braucht, schreibt:

with Storage.region[Scratch] {
    ...
}

Das ist die sichtbare Stelle, an der der Entwickler Verantwortung übernimmt.

Innerhalb des Blocks bleiben die einzelnen Allokationen normal und implizit.

44. Gewünschte Semantik

vor Scope:
    activeRegion = Context.Default

enter:
    StorageRole.Scratch auflösen
    passende Region aktivieren/binden

Body:
    implizite dynamische Allokationen -> ScratchRegion

exit:
    vorherige aktive Region wiederherstellen
    Scope-Regeln prüfen
    Reclamation-/Lifetime-Policy anwenden

45. Storage verwendet das allgemeine Scopeable-Modell

Storage soll keine eigene with-Semantik besitzen. Es übernimmt den in Teil III beschriebenen Resource-Lifecycle.

Für eine Storage-Region bedeutet das konkret:

enter
    StorageRole auflösen
    Region etablieren / aktivieren
    vorherige Default-Region sichern

body
    implizite Allokationen benutzen die neue aktive Region

exit
    Escape-/Lifetime-Regeln abschließen
    vorherige Default-Region wiederherstellen
    fachliche Region-Policy anwenden

mechanisches cleanup / reap
    reservierte Ressourcen gemäß Descriptor sicher zurückgewinnen

Damit bleibt die Trennung bestehen:

ResourceEngine
    universelle Scope- und Fehlersemantik

StorageEngine
    MemoryRegion-spezifische Semantik

JadeME / Ring 0
    mechanische Speicheroperationen

Andere Ressourcen können denselben Scope-Mechanismus verwenden, ohne Storage nachzuahmen:

with Storage.region[Scratch] {
    with Database.transaction[ReadWrite] as tx {
        with Lock.acquire[CacheWrite] {
            ...
        }
    }
}

46. exit und mechanisches cleanup bleiben bei Storage verschieden

Für eine Arena-artige Region:

exit
    -> logischer Scope endet
    -> Escape-Regeln werden abgeschlossen
    -> BulkReset möglich

mechanisches cleanup / reap
    -> reservierte Pages tatsächlich zurückgeben

Eine Region kann nach exit logisch leer sein, aber reservierten Speicher für spätere Wiederverwendung behalten.

Diese Trennung folgt dem allgemeinen Fehler- und Lifecycle-Modell aus §21: fachliches Scope-Ende darf von mechanischer Rückgewinnung unterschieden werden.


Teil XI — StorageEngine, ResourceEngine und JadeME

47. StorageEngine

Die bisherige Beschreibung „Arenen, Pools, Rc, Allokationspolicies“ ist nach dieser Empfehlung zu grob und vermischt Ownership mit Storage.

Empfohlen:

StorageEngine materialisiert und orchestriert MemoryRegions, löst Storage-Rollen auf Regionsbindungen auf und vermittelt deren Allocation-, Reclamation-, Budget-, Accounting- und Guarantee-Semantik gegenüber der JadeME.

Rc gehört nicht zu ihrer Kernzuständigkeit.

48. ResourceEngine

Empfohlen:

ResourceEngine erzwingt die allgemeine Scope-Lifecycle-Semantik von with/Scopeable, einschließlich Enter, Exit und garantiertem Cleanup gemäß dem jeweiligen Ressourcenvertrag.

49. JadeME

JadeME behält die mechanische Speicherhoheit.

Sie übernimmt unter anderem:

Backing anfordern/freigeben
Allocation-Mechanik
Handle-Tabelle
Generation Counter
Bounds / Typprüfung
Region Accounting
Budget Enforcement
Reclamation
gegebenenfalls Refcount-Mechanik

Der Context bekommt nicht „die JME“. Er bekommt geprüfte Storage-Policies beziehungsweise Regionsbindungen.


Teil XII — Context-Grenzen als Memory-Grenzen

50. Context und Region gehören zusammen

Contexts bieten eine natürliche Lebensdauer- und Autoritätsgrenze für Speicher.

Konzeptionell:

WebContext
└── RequestContext
    └── RequestRegion

Wenn der RequestContext endet, darf seine Region nicht beliebig als lebende Ressource außerhalb fortbestehen, sofern keine ausdrückliche Transfersemantik dies erlaubt.

51. Cross-Context-Transfer

Ein gewöhnlicher Handle aus Context A darf nicht automatisch in Context B gültig sein.

Mögliche Transferformen bleiben:

Copy
Lease
Transfer
Shared Bridge Handle

Diese Formen müssen später sauber spezifiziert werden.

Der entscheidende Vorteil ist bereits jetzt klar:

Ein Wert, der einen Context verlässt, überquert eine explizite Boundary. Genau dort kann Jade Lifetime-, Ownership- und Transferregeln prüfen.

Dadurch wird eine klassische Fehlerklasse strukturell erschwert:

Pointer/Handle auf tote Request-Daten landet unbemerkt in langlebigem globalem Zustand.

52. Memory Containment

Context-Limits und Regions-Budgets erlauben zusätzlich Schadensbegrenzung:

PluginContext
└── PluginStorage
    budget = 256 MiB

oder:

SandboxEnvelope
└── ParserContext
    └── Scratch
        budget = 32 MiB

Damit löst Jade nicht jeden logischen Leak, begrenzt aber dessen Reichweite.


Teil XIII — Was mit den bisherigen Refinements geschehen sollte

53. Memory(...) muss vollständig auditiert werden

Die heutige MemoryPolicy ist der Hauptkandidat für eine Neuordnung.

Historisch:

Memory(Value)
Memory(Ref/Rc)
Memory(Arena[A])
Memory(Pool[P])
Memory(mapped)

Empfehlung:

Value

Nicht als öffentliche Allokationsstrategie behandeln.

Stattdessen größtenteils als abgeleitete Representation:

Inline / Handle

Rc

Aus der Storage-/Allocation-Achse herauslösen.

Als Ownership-Konstrukt behandeln:

Rc[T]
Weak[T]

Arena[A]

Nicht mehr als Typ-Memory-Policy verstehen.

Als Region-/Profilmuster neu modellieren:

Backing + Bump + BulkReset + Scope Lifetime

Pool[P]

Nicht mehr als Typ-Memory-Policy verstehen.

Als Regions-/Allocation-/Reclamation-Profil:

FixedSlot/Slab + ReturnToPool + Context/Explicit Lifetime

mapped

Nicht als gleichrangige MemoryPolicy führen.

Eher als Backing beziehungsweise spezielle Region-Guarantee behandeln.

54. Own, Share, Create, Drop

Diese Konzepte bleiben grundsätzlich sinnvoll, müssen aber sauber von Allocation getrennt und untereinander präziser geschnitten werden.

Own
    Ownership / Move / Shared-Beziehung des fertigen Wertes

Share
    Task-/Concurrency-Garantien des fertigen Wertes

Create
    geschlossene Konstruktionsdomäne am Ursprungstyp
    Create Arm -> Inputs + Row + Created Type

Drop
    semantisches Cleanup des Created Type

Die empfohlene Zuständigkeitsgrenze lautet:

Create gehört an den Typ, dessen zulässige Erzeugungswege beschrieben werden. Drop gehört an den jeweils daraus entstehenden Created Type.

Damit muss ein Wert seine Erzeugungsgeschichte nicht als versteckten Runtime-State mitschleppen. Wenn mehrere Create-Arme denselben Lifecycle erzeugen, dürfen sie denselben Created Type zurückgeben. Wenn sich der Lifecycle unterscheidet, soll der Unterschied im Resultattyp sichtbar sein.

Ob Own langfristig als Refinement, als Wrapper-Typen oder als Kombination daraus bestehen bleibt, ist separat zu entscheiden. Ebenso ist noch offen, wie Create-Arme syntaktisch implementiert beziehungsweise an Handler/Provider gebunden werden; die semantische Zuständigkeit sollte davon unabhängig feststehen.

55. Typen sollten nur echte Storage-Anforderungen tragen

Bestimmte Speicheranforderungen sind tatsächlich Eigenschaften eines Typs oder einer Verwendung.

Beispiele:

stableAddress
alignment >= 64
zeroOnRelease
nonMovable

Diese Anforderungen sollten nicht den konkreten Allocator bestimmen.

Stattdessen:

Type Requirements
        ↓
Region Guarantees
        ↓
compatible?

Damit bleibt die Implementierung austauschbar.

Ein kryptographischer Schlüssel muss vielleicht zeroOnRelease verlangen. Er muss nicht wissen, ob diese Garantie durch mmap, Locked Pages oder einen speziellen Secure Allocator realisiert wird.


Teil XIV — Was der Benutzer davon merkt

56. Weniger Tipparbeit ohne versteckte Magie

Das Ziel ist nicht, Speicherverwaltung zu verstecken.

Das Ziel ist, die richtige Granularität der Explizitheit zu wählen.

Der Benutzer entscheidet bewusst:

Welche Speicherdomäne brauche ich?
Welche Lebensdauer soll sie haben?
Wann betrete ich eine besondere Region?
Wann verlasse ich sie?
Welche Rolle soll der Context bereitstellen?

Der Benutzer entscheidet normalerweise nicht:

Welcher FreeList-Knoten wird genommen?
Welcher mmap-Block bedient diese 48 Bytes?
Welche einzelne Allocation muss wie freigegeben werden?
Welcher Allocator-Parameter muss durch fünf Funktionssignaturen gereicht werden?

Das ergibt den gewünschten Charakter:

Manuelle Speicherverwaltung, die sich nicht permanent wie manuelle Speicherverwaltung anfühlt.

57. Normalfall

def loadUsers() ->{} [User] {
    ...
}

Keine sichtbare Allocator-API.

Implizite Allokationen verwenden die aktive Default-Region des Contexts.

58. Bewusste Abweichung

def compileLargeThing(input: Input) ->{Storage} Output {
    with Storage.region[Scratch] {
        ...
    }
}

Hier ist sichtbar, dass die Funktion bewusst eine besondere Speicherdomäne verlangt.

59. Library-Code bleibt context-agnostisch

Eine Library kann Scratch anfordern, ohne Produktion, Test, Benchmark oder Sandbox zu kennen.

Das verhindert eine enge Kopplung an Deployment- oder Hostentscheidungen.


Teil XV — Debugging und Tooling

60. Fehler sollen an der Boundary erklärt werden

Statt:

invalid pointer
segmentation fault
out of memory

soll Jade möglichst Informationen besitzen wie:

allocation failed

Context:
    PluginContext

Storage role:
    Scratch

Profile:
    PluginScratchProfile

Region:
    PluginScratch#17

Budget:
    64 MiB

Used:
    63.8 MiB

Requested:
    384 KiB

oder:

value cannot escape RequestRegion

created in:
    RequestContext / StorageRole.Request

attempted escape to:
    ApplicationContext

allowed transfer forms:
    Copy

61. Mission Control

Das Modell ist für Mission Control besonders geeignet:

Context
  → StorageRole
    → StorageProfile
      → MemoryRegion
        → Backing
        → Allocator
        → Reclamation
        → Lifetime
        → Budget
        → Accounting

Daraus ergeben sich sinnvolle Probes und Metriken:

reserved
committed
used
peak
allocation count
reclamation count
fragmentation
per-type use
per-callsite use
budget pressure

Ein BenchmarkContext könnte dieselben Regionen mit zusätzlichem Accounting binden, ohne den Anwendungscode zu verändern.


Teil XVI — Optimierungen

62. Escape Analysis bleibt wichtig, aber ihre Rolle ändert sich

Die heutige Escape-Analyse betrachtet unter anderem NoEscape, Argument-Escape und globale Escapes und darf Refcount-Operationen elidieren.

Nach dieser Empfehlung sollte sie nicht eigenmächtig die semantische Storage-Rolle wechseln.

Sie kann aber innerhalb der garantierten Semantik optimieren:

Retain/Release elidieren
Copy vermeiden
Views beibehalten
Region Crossing früh erkennen
Allokationsgrößen/Layouts optimieren

Ob später automatische Region-Promotion beziehungsweise -Demotion erlaubt wird, sollte separat entschieden werden. Für den Anfang ist eine konservative Regel vorzuziehen:

Optimierungen dürfen die beobachtbare Lifetime-, Boundary- und Storage-Garantie nicht verändern.

63. Context-spezifische Optimierung

Weil Storage-Profile im Context gebunden werden, kann dasselbe Programm in verschiedenen Umgebungen unterschiedlich optimiert werden:

Production
    FastScratch

Debug
    CheckedScratch + poisoning

Benchmark
    InstrumentedScratch

Test
    TinyBudgetScratch

Das ist kein Quellcode-Fork, sondern eine andere geprüfte Ausführungsumgebung.


Teil XVII — Vorteile der Empfehlung

64. Klare Orthogonalität

Die größte Verbesserung ist konzeptuell:

Representation
Ownership
Sharing
Construction
Cleanup
Storage Region
Allocation
Reclamation
Lifetime

werden nicht mehr künstlich in eine gemeinsame MemoryPolicy gedrückt.

65. Weniger lokale Konfiguration

Speicherentscheidungen, die für eine gesamte Ausführungsdomäne gelten, werden einmal im Context konfiguriert statt an vielen Typen und Allocation-Sites wiederholt.

66. Context-agnostischer Anwendungscode

Eine Library arbeitet mit semantischen Rollen statt konkreten Allokatoren.

Das verbessert Wiederverwendbarkeit und Testbarkeit.

67. Explizite Kontrolle bleibt erhalten

with Storage.region[...] ist eine sichtbare, lokale Entscheidung.

JDL wird dadurch nicht zu einer Sprache, in der Speicher einfach „irgendwie passiert“.

68. Weniger gefährliche Escape-Situationen

Contexts und Bridges bilden natürliche Stellen, an denen Lifetime- und Transferregeln geprüft werden können.

69. Memory Containment

Budgets und Context-Limits reduzieren den Schaden aus logischen Leaks oder pathologischen Eingaben.

70. Besseres Tooling

Die klare Kette

Role -> Profile -> Descriptor -> Region -> JME

liefert bessere Diagnosen, :why, Mission-Control-Ansichten und Benchmarks.

71. Erweiterbarkeit

Neue Allocator- oder Backing-Mechanismen können ergänzt werden, ohne neue Sprachkonstrukte einzuführen.

Beispielsweise:

TLSF
Buddy
Slab
HugePages
NUMA-local
SharedMemory

solange sie dieselben Descriptor-Verträge erfüllen.

72. Weniger Framework-/API-Lärm

Kein permanentes allocator-Argument durch normale Business-Logik.

Kein eigener Arena-Scope-Mechanismus neben with.

Kein Pool-Sonderfall im Typ-System, wenn er als Storage-Profil ausdrückbar ist.


Teil XVIII — Nachteile und Risiken

73. Die aktive Region ist impliziter Zustand

Das Modell hat bewusst eine implizite Komponente:

active default region

Das ist bequem, kann aber verwirrend werden, wenn Code nicht weiß, unter welcher Region er ausgeführt wird.

Gegenmaßnahmen:

Descriptor-/Context-Introspektion
LSP-Hints
Mission Control
klare Scope-Grenzen
keine willkürliche Runtime-Mutation

Die aktive Region darf nicht zu einem versteckten globalen Allocator werden. Sie muss Teil der geprüften Context-/Scope-Semantik bleiben.

74. Escape-Prüfung wird wichtiger

Je stärker Regionen die Lebensdauer strukturieren, desto wichtiger wird die Frage:

Darf dieser Wert diese Region verlassen?

Das verlangt saubere Regeln für:

Copy
Move
Rc
Weak
Slices / Views
Handles
Bridge Transfer
Return Values
Closures
Tasks

Der Gewinn ist groß, aber die Compilerarbeit verschwindet nicht.

75. Profile können kombinatorisch wachsen

Wenn jede Kleinigkeit ein eigenes StorageProfile bekommt, entsteht dieselbe Konfigurationshölle nur eine Ebene höher.

Daher sollte gelten:

Profile sind wiederverwendbare technische Konfigurationen. Rollen sind wenige semantische Namen. Contexts binden beides gezielt zusammen.

76. Zu viel implizite Optimierung wäre gefährlich

Wenn die Runtime spontan andere Regionen oder Allokatoren auswählt, können Performance und Lifetime schwer vorhersehbar werden.

Die erste Version sollte daher konservativ bleiben.

77. Komplexe Policy-Kompatibilität

Nicht jede Kombination ist gültig:

Bump + Individual Free
FileMapped + Moving Compaction
zeroOnRelease + bestimmte External Backings
stableAddress + moving allocator

Die StorageEngine beziehungsweise der Materializer braucht eine klare Kompatibilitätsprüfung.

78. Allocation Failure bleibt eine echte Sprachdesignfrage

Die Region kann ihr Budget erreichen. mmap kann scheitern. Ein Pool kann voll sein.

Offen ist, welche Fehlerklasse daraus entsteht.

Wenn jede implizite Allocation Throw[OutOfMemory] erzeugt, verseucht dies praktisch alle Effect Rows.

Wenn jede Allocation stattdessen trappt, verliert der Benutzer bei absichtlich begrenzten Regionen Kontrolle.

Eine mögliche Trennung:

normale implizite Default-Allocation
    -> definierter Resource Trap bei harter Erschöpfung

explizit fallible / begrenzte Region
    -> AllocationError als normaler Fehler

Das ist noch zu entscheiden.


Teil XIX — Offene Fragen

79. Allgemeine offene Fragen des Resource-Modells

Bevor das Muster normativ wird, müssen einige Fragen unabhängig von Storage beantwortet werden.

Ist ResourceRole immer ein Tag?

Tags sind für geschlossene nominale Rollen wie Scratch, ReadWrite oder Temporary sehr attraktiv. Es sollte aber nicht vorschnell normiert werden, dass jede Resource-Domäne dieselbe Deklarationsform braucht.

Entscheidend ist die Semantik:

Role
    nominale Auswahl / Absicht

nicht das konkrete Syntaxkonstrukt.

Muss jedes ResourceProfile ein Blueprint sein?

Wenn ein Profil von einer DescriptorEngine materialisiert und durch Refinements erweitert wird, passt blueprint sehr gut zum bestehenden Jade-Modell.

Für triviale Ressourcen könnte dies unnötig sein. Zu entscheiden ist, ob ResourceProfile eine allgemeine semantische Rolle von Typen ist oder ob jede Fachdomäne ihre eigenen Profiltypen definiert.

Gibt es einen allgemeinen ResourceDescriptor?

Wahrscheinlich sollte es keinen riesigen universellen Descriptor mit Feldern für jede Ressource geben.

Sinnvoller erscheint:

allgemeine Lifecycle-Facts
    +
fachspezifischer Descriptor

Beispielsweise braucht eine Transaction Isolation und Commit-Policy, eine MemoryRegion dagegen Backing und Allocation. Gemeinsam sind nur die Lifecycle-/Scope-Invarianten.

Wie sieht Scopeable exakt aus?

Offen sind unter anderem:

enter-Signatur
ExitReason / ScopeExit
assoziierte Resource-Typen
fallible exit
Cancellation
ob enter das Resource-Objekt selbst oder eine Lease liefert

Die Syntax sollte erst nach der Semantik festgelegt werden.

Wie werden mehrere Fehler erhalten?

Der schwierige Fall ist:

Body      -> Fehler A
exit      -> Fehler B

Fehler B darf A nicht stillschweigend überschreiben. Zu entscheiden ist, ob JDL dafür eine allgemeine Fehlerkomposition, einen ScopeFailure oder eine andere Provenance-/Suppressed-Error-Form erhält.

Welche Cleanup-Garantie ist wirklich universal?

Zu definieren ist, welche mechanischen Invarianten die ResourceEngine beziehungsweise Ring 0 ohne beliebigen User-Code wiederherstellen können muss.

Insbesondere:

Throw
Cancellation
Trap
Context-Abbruch
Prozessabbruch
Hostverlust

haben unterschiedliche Wiederherstellungsmöglichkeiten.

Wie verhalten sich Resources an Context-Grenzen?

Nicht jede Resource darf einen Context verlassen. Für manche kommen Copy, Transfer oder Lease in Frage; andere müssen strikt contextgebunden bleiben.

Das Resource-Modell sollte hier mit der bestehenden Bridge-/Handle-Semantik zusammenlaufen, nicht einen zweiten Transfermechanismus erfinden.

Wie wird eine Role an einen Service gebunden?

Storage.Scratch und Database.ReadWrite gehören unterschiedlichen fachlichen Domänen an. Zu entscheiden ist, ob ContextDescriptoren dafür eine gemeinsame generische Binding-Struktur besitzen oder jede Service-/Engine-Domäne ihre eigenen Descriptorfelder materialisiert.

Braucht jede Resource-Domäne eine Engine?

Nein als Designziel; aber die genaue Schwelle sollte dokumentiert werden.

Faustregel:

Eine eigene Engine ist nur gerechtfertigt, wenn wiederverwendbare Ausführungsinvarianten orchestriert werden müssen, die nicht bloß Provider-Logik sind.

Wie interagiert with mit Cancellation und Scheduler?

Cancellation ist weder normaler Return noch zwingend Throw. Für scoped Resources muss festgelegt werden, ob und wann exit noch fachlich ausgeführt wird und welche mechanischen Cleanup-Garantien gelten.

Wie werden nicht-scoped Resources modelliert?

Pools, langlebige Verbindungen, Daemons oder Context-eigene Ressourcen können einen Lifecycle besitzen, ohne in einem lokalen with zu leben. Das allgemeine Resource-Modell darf Resource und Scopeable daher nicht synonym machen.


80. Was gilt exakt als JME-Allokation?

Ist jede indirekte Wertrepräsentation eine Allocation?

Wie werden Reallocations dynamischer Collections behandelt?

Wie werden Inline-Werte und temporäre Compiler-/VM-Frames abgegrenzt?

81. Hat jede dynamische JME-Allokation genau eine MemoryRegion?

Empfehlung: ja.

Das würde Accounting, Lifetime und Containment erheblich vereinfachen.

Es muss aber geprüft werden, ob bestimmte globale JME-Strukturen oder internierte Runtime-Daten eine Sonderdomäne brauchen.

82. Wie wird die aktive Default-Region gespeichert?

Mögliche Orte:

Context Runtime State
Task State
Frame/Scope State
StorageEngine State

Sie muss billig erreichbar sein, ohne jeden Allocation-Hot-Path über RuntimeBus zu schicken.

83. Wie funktioniert with Storage.region[...] mechanisch?

Die ältere Arena-Command-Skizze verwendet Push/PopReset und RuntimeBus.

Für das allgemeine Regionsmodell sollte getrennt werden:

Control Path
    Region enter/exit/create/destroy

Hot Path
    tatsächliche Allocation

Der Hot Path sollte direkte privilegierte JME-Mechanik benutzen können, nachdem die Region einmal autorisiert und gebunden wurde.

84. Wie werden Region-Crossings behandelt?

Für verschiedene Wertformen werden unterschiedliche Regeln nötig:

Value
    Copy trivial

Copyable handle-backed value
    Copy in Zielregion möglich

Unique
    Transfer möglich?

Rc
    darf Rc über Context-/Region-Grenzen geteilt werden?

View/Slice
    Copy oder Verbot

non-Copyable resource
    Lease / Transfer / Fehler

85. Was bedeutet Rc[T] regionsübergreifend?

Wenn der referenzierte Wert in einer kurzlebigen Region liegt, darf ein langlebiger Rc[T] darauf zeigen?

Wahrscheinlich nur, wenn die Region-Lifetime die Ownership-Lifetime tragen kann oder eine Promotion/Copy stattfindet.

Das muss formalisiert werden.

86. Wie interagieren Created Types mit nicht-trivialem Drop und Bulk Reclamation?

Eine Bump-/BulkReset-Region kann Speicher billig verwerfen. Ein Created Type mit Drop(Handler[H]) kann aber semantisches Cleanup verlangen, das vor der mechanischen Rückgewinnung ausgeführt werden muss.

Mögliche Regeln:

Created Types mit nicht-trivialem Drop in bestimmten BulkRegions verboten

oder:

Region führt Drop-Liste für betroffene Created Types

oder:

nur bestimmte Drop-Klassen / Cleanup-Verträge zulässig

Hier muss Performance gegen semantische Korrektheit abgewogen werden. Wichtig ist, dass diese Entscheidung am Created Type und dessen Drop-Vertrag hängt, nicht am Create-Arm, über den der Wert ursprünglich entstanden ist.

86.1 Wie weit darf Lifecycle-Meta automatisch ableiten?

Die Grundregel ist klar: nur bei Eindeutigkeit. Offen bleibt jedoch, welche Facts für diesen Beweis zulässig sind und wie weit strukturelle Ableitung reichen darf.

Zu klären sind insbesondere:

Darf Drop rekursiv aus Feldern abgeleitet werden?
Wie werden rekursive Typen behandelt?
Welche Resource-/Ownership-Facts gelten als hinreichend eindeutig?
Darf ein Provider zusätzliche Lifecycle-Facts materialisieren?
Wie werden widersprüchliche explizite und abgeleitete Facts diagnostiziert?
Wann gilt ein Raw-/Native-Handle als prinzipiell nicht ableitbar?
Wie werden Ableitung und Provenance in CompilerDB gecacht/invalidiert?

Die sichere Default-Position sollte konservativ sein: nicht beweisbar bedeutet nicht ableiten. Ein zusätzlicher expliziter Handler ist lästig; ein falsch automatisch erzeugter Cleanup-Pfad ist dagegen ein Runtime-Schaden mit akademischer Begründung.

87. Welche Storage-Garantien gehören wirklich auf den Typ?

Kandidaten:

stableAddress
alignment
zeroOnRelease
nonMovable

Es sollte vermieden werden, wieder technische Implementierungsdetails als Typmetadaten einzuführen.

88. Sind StorageRole-Tags global, namespace-lokal oder projektspezifisch?

Scratch, Request, Secure sind naheliegende Stdlib-Rollen.

Anwendungen und Libraries brauchen vermutlich eigene Rollen.

Die Namens- und Konfliktregeln müssen zum bestehenden Namespace-/Tag-Modell passen.

89. Wie funktioniert ein fehlendes Role-Binding?

Wenn Code verlangt:

Storage.region[Scratch]

aber der Context Scratch nicht bindet, sollte das möglichst früh scheitern:

Compilezeit
Ladezeit
Context-Materialisierung

abhängig davon, wann der Context bekannt ist.

90. Darf ein Context dieselbe Rolle mehrfach binden?

Wahrscheinlich nein, sofern kein expliziter Auswahlmechanismus existiert.

Eine Rolle sollte innerhalb eines materialisierten Contexts eindeutig aufgelöst werden.

91. Wie stark darf Storage Meta sein?

Wenn StorageProfile ein Blueprint ist und die StorageEngine Descriptoren erzeugt, kann Meta Profile ableiten und komponieren.

Das passt zum allgemeinen Jade-Modell, muss aber gegen die CompilerDB-Fassade und die Materialisierungsinvarianten spezifiziert werden.

92. Wie werden benutzerdefinierte Allocator-Bausteine zugelassen?

Mögliche Ebenen:

nur Stdlib/Ring 1
Plugins
gewöhnliches JDL mit Engine/Provider-Vertrag
native Provider über Jade Value Bridge

Nicht jeder benutzerdefinierte Allocator sollte automatisch Trusted-Core-Rechte bekommen.

93. Welche Teile sind compile-time, load-time und runtime?

Beispielsweise:

StorageProfile
    meist compile-time materialisierbar

Budget aus Deployment-Konfiguration
    eventuell load-time

Region-Instanz
    runtime

aktive Region
    runtime scope state

Die Descriptor-Grenzen müssen diese Staffelung sauber tragen.

94. Wie verhält sich Storage zu Envelopes?

Ein Envelope darf die Storage-Autorität des Targets nur erhalten oder einschränken.

Beispiele:

SandboxEnvelope
    ersetzt DefaultStorage durch RestrictedStorage
    reduziert Budget
    entfernt Secure/Network-backed roles

Ein Envelope kann eigene Storage-Rollen besitzen, ohne sie dem Target automatisch zu exponieren.

Das folgt derselben Authority-Monotonie wie bei anderen Context-Eigenschaften.


Teil XX — Empfohlene Vereinfachung der bisherigen Architektur

95. Was verschwinden oder zurückgestuft werden könnte

Die Empfehlung würde mehrere heutige Sonderfälle vereinfachen:

Memory(Arena[A])
    -> StorageRole + StorageProfile + Region

Memory(Pool[P])
    -> StorageProfile / Region

Memory(mapped)
    -> Backing

Memory(Rc)
    -> Ownership / Representation

Arena-spezifisches with
    -> allgemeines with + Storage.region[Role]

Damit sinkt die Zahl der Dinge, die TypeEngine und normaler Anwendungscode als spezielle Memory-Arten kennen müssen.

96. Was bestehen bleibt

JadeME Handle-Safety
Generation Counter
Context Isolation
Bridge Transfer
Rc / Weak Ownership
Share / Send / Sync
Create Domain / Create Arms
Drop Lifecycle des Created Type
ResourceEngine
Effect Rows
Storage Service

Die Empfehlung verwirft diese Mechanismen nicht. Sie ordnet sie neu.

97. StorageEngine neu zuschneiden

Alt, grob:

Arenen, Pools, Rc, Allokationspolicies

Empfohlen:

MemoryRegion descriptors
Storage role resolution
Region lifecycle/orchestration
Backing/allocation/reclamation contracts
Budgets/accounting/guarantees
JME binding

Ownership (Rc, Weak) wird herausgelöst.


Teil XXI — Migrations- und Audit-Liste

98. Dokumente, die bei Annahme überprüft werden müssten

Mindestens:

00-refinements
jade-design-decisions
02-typsystem
08-intrinsics-vm-api
10-runtime-handles-und-memory-safety
jade-commands-with-arena
sketch-escape-analysis-refcount-elision
spec-context-model
Engine-Beschreibungen / Foundation Engine Set
JadeValue/Register-Layout
JME-/Handle-Konsolidierungen

Die wichtigste Arbeit ist nicht mechanisches Umbenennen, sondern das Entfernen falscher Querabhängigkeiten.

99. Besonders kritisch zu auditieren

MemoryPolicy

Welche Teile sind Representation, Ownership, Region oder Backing?

Rc / historisches Ref

Ist Rc Typ-/Ownership-Semantik oder JME-Repräsentation? Wahrscheinlich beides intern, aber nicht dieselbe öffentliche Achse.

Arena / Pool

Von Type-Policy zu StorageProfile/MemoryRegion verschieben.

Escape Analysis

Alte Invariante „Allokationsort bleibt identisch“ auf das neue Regionsmodell übertragen.

Commands with Arena

Von Arena-Sonderfall auf generischen Region-Control-Path heben.

StorageEngine

Ownership entfernen, Region-Semantik schärfen.

Create / Drop

Create(Factory[F]) und Drop(Custom) gegen die neue Zuständigkeitsgrenze auditieren:

Create
    -> geschlossene Create-Domain am Ursprungstyp
    -> Create Arm beschreibt Inputs, Row und Created Type

Drop
    -> Eigenschaft des Created Type
    -> Trivial oder expliziter Handler/Vertrag

Creation-Provenance darf nicht nur deshalb als Runtime-State konserviert werden, damit später der richtige Drop-Pfad gefunden wird. Unterschiedliche Lifecycle-Pflichten sollen durch unterschiedliche Created Types ausgedrückt werden.

Zusätzlich sollte eine Lifecycle-Meta-Ableitung vorgesehen werden, die die Create-Domain liest, die erreichbaren Created Types dedupliziert und deren Drop-Semantik prüft beziehungsweise nur bei eindeutiger Herleitung automatisch materialisiert. Die heutige Factory[F]-/Custom-Form enthält dafür weder die geschlossene Transition-Domain noch genügend Provenance.


Teil XXII — Gegenüberstellung alt gegen empfohlen

100. Alter mentaler Pfad

Typ
  :> Memory(Arena[X])
  :> Own(...)
  :> Share(...)
  :> Drop(...)
  :> Create(...)

Compiler/JME versucht daraus Storage, Konstruktion und Lifecycle gleichzeitig abzuleiten

Problem: Der Typ wird zum Sammelort von Entscheidungen, die eigentlich aus Umgebung, Lebensdauer und konkreter Verwendung stammen.

101. Empfohlener mentaler Pfad

Ursprungstyp
    -> echte Typgarantien
    -> geschlossene Create-Domain

Create Arm
    -> Inputs + Row + Created Type

Created Type
    -> Ownership / Share / Drop / weitere Lifecycle-Garantien

Code
    -> StorageRole bei bewusster Abweichung

Context
    -> Role -> Profile

StorageProfile
    -> Backing + Allocation + Reclamation + Lifetime + ...

StorageEngine
    -> geprüfter Descriptor / Region Binding

JadeME
    -> Bytes + Handles + Enforcement

Damit bekommt jede Ebene genau die Entscheidungen, die zu ihr gehören.


Teil XXIII — Vorläufige Designregeln

Die folgenden Regeln sind als Empfehlung zu verstehen, nicht als bereits normative Spec.

Allgemeines Resource-Modell:

  1. Ein Service ist eine semantische Umgebungsanforderung, nicht automatisch eine Resource.
  2. Eine Resource ist eine laufende Instanz mit relevantem Lifecycle.
  3. Resource Roles benennen Absicht, nicht Mechanik.
  4. Resource Profiles beschreiben wiederverwendbare Policy.
  5. Contexts binden Roles an konkrete Profiles beziehungsweise Descriptoren.
  6. with verwendet einen allgemeinen, geprüften Resource-Lifecycle statt domänenspezifischer Scope-Sondermechanismen.
  7. enter darf fachlich scheitern; der Body wird dann nicht betreten.
  8. Ein Throw im Body führt zu geordnetem exit, anschließendem mechanischem Cleanup und danach Propagation.
  9. Ein Exit-Fehler darf einen bereits laufenden Primärfehler nicht stillschweigend vernichten.
  10. Mechanisches Cleanup/Reaping ist Runtime-Invariante und kein gewöhnlicher fachlicher Fehlerkanal.
  11. Strukturell beschreibbare Unzulässigkeiten sollen vor Ausführung bei Materialisierung/Admission scheitern.
  12. Create beschreibt am Ursprungstyp eine geschlossene Menge zulässiger Konstruktionsübergänge.
  13. Ein Create-Arm beschreibt mindestens seine Eingaben, seine erforderliche Row und den daraus entstehenden Created Type.
  14. Drop gehört zum Created Type, nicht zum Create-Arm. Mehrere Create-Arme dürfen denselben Created Type und damit dieselbe Drop-Semantik liefern.
  15. Unterschiedliche Lifecycle-/Drop-Pflichten sollen durch unterschiedliche Created Types ausgedrückt werden, nicht durch versteckte Creation-Provenance im Wert.
  16. Lifecycle-Meta arbeitet auf eindeutigen Created Types, nicht auf einer 1:1-Paarung von Create-Armen und Drop-Pfaden.
  17. Automatische Drop-Ableitung ist nur zulässig, wenn sie eindeutig aus bereits materialisierter Semantik folgt; Mehrdeutigkeit verlangt eine explizite Policy beziehungsweise einen Handler.
  18. Automatisch abgeleitete Lifecycle-Semantik muss Provenance tragen und über die normale CompilerDB-Fassade sowie die zuständige Materialisierung/Validierung laufen.

Storage-spezifische Regeln:

  1. Jede dynamische JME-Allokation gehört genau einer MemoryRegion.
  2. Ein Context darf mehrere Storage-Rollen binden.
  3. Zu jedem Ausführungspunkt existiert genau eine aktive Default-Region für implizite Allokationen.
  4. Normale implizite Allokationen erzeugen keinen Storage-Eintrag in der Effect Row.
  5. Explizite Auswahl oder Manipulation einer Storage-Region erfolgt über den Storage-Service und ist in der Row sichtbar.
  6. with Storage.region[R] ist die normale lokale Form zum temporären Wechsel der aktiven Region.
  7. StorageRole benennt Bedeutung, nicht Mechanik.
  8. Der Context bindet StorageRole an ein StorageProfile beziehungsweise einen materialisierten StorageDescriptor.
  9. StorageProfile zerlegt Speichermechanik mindestens in Backing, Allocation, Reclamation und Lifetime.
  10. Budget, Accounting und Guarantees sind orthogonale Region-Eigenschaften.
  11. Rc und Weak gehören zur Ownership-Semantik, nicht zur Storage-Strategie.
  12. Create, Share und Drop bleiben von Allocation/Storage getrennte Achsen; Create sitzt am Ursprungstyp, Share und Drop gelten für den fertigen beziehungsweise Created Type.
  13. Typen dürfen notwendige Storage-Garantien formulieren, aber keine konkrete Deployment-Allocator-Mechanik erzwingen, sofern diese nicht tatsächlich Teil der Typsemantik ist.
  14. Context-Grenzen sind zugleich Memory-Boundaries; gewöhnliche Handles überschreiten sie nicht unkontrolliert.
  15. Cross-Context-Werte benötigen eine explizite Transferform.
  16. ResourceEngine besitzt die allgemeine Scope-Semantik; Storage implementiert keinen zweiten with-Mechanismus.
  17. StorageEngine orchestriert Regionen; JadeME besitzt die mechanische Speicherhoheit.
  18. Der Allocation-Hot-Path darf nach erfolgter Autorisierung nicht unnötig über RuntimeBus gehen.
  19. Debugging- und Toolingdaten sollen Role, Profile, Region, Budget und Lifetime nachvollziehbar machen.
  20. Optimierungen dürfen die beobachtbare Storage-/Lifetime-Semantik nicht verändern.

Teil XXIV — Abwägung

102. Warum diese Richtung attraktiv ist

Sie passt zu mehreren bereits etablierten Jade-Prinzipien gleichzeitig:

Context statt globaler Umgebung
Descriptor statt versteckter Runtime-Magie
Service Row statt Ambient Service Locator
Engines für Ausführungssemantik
JadeME für mechanische Speicherhoheit
with für deterministischen Scope
Tags für nominale Bedeutung ohne Runtime-Layout

Vor allem reduziert sie die Zahl lokaler Entscheidungen, ohne Kontrolle zu entfernen.

Sie ist deshalb keine „höhere Abstraktion um weniger sehen zu müssen“, sondern eine Verschiebung der Entscheidung an den Ort, an dem sie semantisch hingehört.

103. Was sie nicht löst

Sie verhindert nicht automatisch:

Rc-Zyklen
logisch unbeschränkte Caches
schlechte Datenstrukturen
falsche Weak-Beziehungen
pathologische Algorithmen
FFI-Bugs außerhalb der sicheren Grenze

Sie kann diese Fehler aber besser begrenzen und diagnostizieren.

104. Was sie voraussichtlich wirklich eliminiert oder stark reduziert

Besonders die lästigen Klassen:

falsche Region am falschen Ort
vergessenes Arena-Reset
inkompatible Allocator-Lebensdauer
versteckte temporäre Speicher-Escapes
fehlendes Cleanup auf normalen Scope-Pfaden
unbeabsichtigte langlebige temporäre Allokationen
unkontrollierte Context-übergreifende Handles
fehlende Speicherbudgets pro Domäne
Allocator-Konfiguration über die Codebase verteilt

Das sind genau die Fehler, bei denen ein Entwickler sonst lange Stack-Traces analysiert, obwohl der Fehler an einer völlig anderen Stelle entstanden ist.


Teil XXV — Empfehlung

105. Empfehlung für die weitere Planung

Diese Richtung sollte ernsthaft weiterverfolgt werden.

Nicht empfohlen ist, sofort Syntax festzulegen.

Zuerst sollte das semantische Modell stabilisiert werden:

1. Allgemeines Resource-Modell festziehen: Service, Role, Profile, Descriptor, Instance, Scopeable.
2. Fehlersemantik von `with` festlegen: enter, body, exit, mechanisches cleanup/reap, Trap-Pfad.
3. Context-Binding für Resource Roles und Profiles normieren.
4. MemoryPolicy inventarisieren und jede Variante einer echten Achse zuordnen.
5. `Create`/`Drop` neu schneiden: geschlossene Create-Domain am Ursprungstyp, Drop-Vertrag am Created Type; alte `Factory[F]`-/`Custom`-Formen auditieren.
6. Lifecycle-Meta definieren: Create-Domain lesen, Created Types deduplizieren, explizite Drop-Facts prüfen und nur eindeutig beweisbare Drop-Semantik automatisch ableiten; Provenance verpflichtend erhalten.
7. MemoryRegion formal definieren.
8. StorageRole -> StorageProfile -> StorageDescriptor -> Region festlegen.
9. Default-Region-Semantik festlegen.
10. `with Storage.region[...]` und Scope-Crossing definieren.
11. Rc/Weak/Ownership aus Storage herauslösen.
12. StorageEngine und ResourceEngine neu abgrenzen.
13. Cross-Region- und Cross-Context-Regeln definieren.
14. Allocation-Failure entscheiden.
15. Das allgemeine Resource-Muster an mindestens Transaction, Lock und File gegenprüfen.
16. Erst danach Syntax und Stdlib-Profile normieren.

Der wahrscheinlich stärkste Leitsatz für die weitere Arbeit lautet:

Der Benutzer verwaltet in JDL Speichergrenzen und Lebensdauern bewusst; die konkrete Einzelallokation bleibt innerhalb einer geprüften Speicherdomäne gewöhnlich implizit.

Und die allgemeine architektonische Kurzform:

Code benennt Rollen. Profile beschreiben Policy. Descriptoren tragen geprüfte Semantik. Contexts binden konkrete Ausprägungen. ResourceEngine strukturiert den Lifecycle. Fachliche Engines beziehungsweise Provider führen die Domänensemantik aus. Ring 0 erledigt die Mechanik.

Für Storage konkret:

Typen formulieren Garantien. Code benennt Storage-Rollen. Contexts binden Rollen an Profile. StorageEngine materialisiert Regionen. JadeME verwaltet die Bytes.


Glossar

Context-bound Resource Model
Empfohlenes allgemeines Muster, bei dem Resource Roles semantische Absicht benennen, Profiles Policy beschreiben, Descriptoren geprüfte Semantik tragen, Contexts konkrete Ausprägungen binden und ResourceEngine den strukturierten Lifecycle einer laufenden Resource garantiert.

Service
Semantische Umgebungsanforderung in einer Effect Row. Ein Service kann Ressourcen bereitstellen oder vermitteln, ist aber nicht automatisch selbst eine Resource.

Resource
Laufende Instanz mit relevanter Besitz-, Lebensdauer-, Abschluss- oder Freigabesemantik, etwa eine MemoryRegion, Transaction, Lock-Lease oder geöffnete Datei.

Resource Role
Nominale semantische Auswahl für eine Ressource, etwa Scratch, ReadWrite, CacheWrite oder Temporary. Die Rolle benennt Absicht, nicht konkrete Mechanik.

Resource Profile
Deklarative Policy für eine Resource Role. Ein Context kann dieselbe Rolle an unterschiedliche Profile binden.

Resource Descriptor
Materialisierte und geprüfte Form eines Resource Profiles beziehungsweise Ressourcenvertrags. Enthält die Semantik, die spätere Phasen ohne erneuten CompilerDB-Zugriff benötigen.

Resource Instance
Konkrete laufende Ressource, die aus einem geprüften Descriptor beziehungsweise Provider hervorgeht. Darf lauf-lokale Handles oder Leases enthalten.

Scopeable
Allgemeiner Lifecycle-Vertrag für Ressourcen, die über with strukturiert werden können. Fachliches enter/exit wird von mechanischem Cleanup beziehungsweise Reaping unterschieden.

ResourceEngine
Foundation Engine für allgemeine with-/Scope-/Fehlerpfad-Semantik. Sie kennt nicht die fachliche Bedeutung jeder Ressource, sondern erzwingt deren geprüften Lifecycle-Vertrag.

Admission / Materialisierung
Prüfgrenze, an der eine beschriebene Resource-/Context-Konfiguration gültig werden muss, bevor sie ausgeführt werden darf. Ziel ist, strukturell beschreibbare Unzulässigkeiten vor der eigentlichen Ausführung zu eliminieren.

Create Domain
Geschlossene, am Ursprungstyp definierte Menge zulässiger Konstruktionsformen. Die Domain sollte nominal und exhaustiv prüfbar sein, beispielsweise über eine Tag-/Enum-Menge.

Create Arm
Ein einzelner Konstruktionsübergang innerhalb einer Create Domain. Beschreibt konzeptionell Eingaben, erforderliche Services/Rows und den daraus entstehenden Created Type.

Created Type
Der konkrete Resultattyp nach erfolgreicher Konstruktion. Er trägt die für den fertigen Wert relevanten Garantien wie Ownership, Sharing und insbesondere Drop. Mehrere Create-Arme dürfen denselben Created Type liefern.

Drop
Semantische Cleanup-Verpflichtung eines Created Type am regulären Lebensende. Drop ist nicht der Gegenpart eines einzelnen Create-Arms und nicht synonym mit mechanischem Speicher-Reclaim.

Lifecycle-Meta
Meta-Ableitung über dem Construction-/Lifecycle-Graphen. Sie liest die Create-Domain eines Ursprungstyps, sammelt die eindeutigen Created Types und prüft beziehungsweise leitet deren Lifecycle-/Drop-Semantik aus bereits materialisierten Facts ab. Sie paart nicht Create-Arme mit Drops und darf bei Mehrdeutigkeit keine Semantik erfinden.

Structural Drop Derivation
Automatische Ableitung eines Drop-Facts für einen Created Type aus eindeutigem Lifecycle-Verhalten seiner enthaltenen Ressourcen beziehungsweise Felder. Ist die Ableitung nicht eindeutig, ist eine explizite Policy oder ein Handler erforderlich.

Allocation
Bereitstellung eines Speicherbereichs für einen konkreten Laufzeitwert innerhalb einer Region.

Backing
Quelle beziehungsweise unterliegende Beschaffung des Speichers, etwa System-Speicher, mmap, File Mapping oder Shared Memory.

Allocation Strategy
Mechanismus, mit dem vorhandener Regionsspeicher in konkrete Allokationen aufgeteilt wird, etwa Bump, FreeList oder Slab.

Reclamation
Mechanismus, mit dem belegter Speicher wieder verfügbar wird, etwa Individual Free, Bulk Reset oder Return to Pool.

Lifetime
Regel, wie lange eine MemoryRegion beziehungsweise ihre logische Bindung existiert.

MemoryRegion
Geprüfte Speicher- und Lebensdauerdomäne mit Backing, Allocation, Reclamation, Lifetime, Budget, Accounting und Garantien.

StorageRole
Nominale semantische Bezeichnung dafür, wofür Speicher benötigt wird, etwa Scratch, Request oder Secure. Die Rolle beschreibt nicht die konkrete Mechanik.

StorageProfile
Deklarative Konfiguration, welche Speichermechanik und Garantien eine Region besitzen soll.

StorageDescriptor
Materialisierte, geprüfte Form eines StorageProfiles beziehungsweise einer Regionskonfiguration.

Default-Region
Die an einem Ausführungspunkt aktive Region, die normale implizite dynamische Allokationen bedient.

Storage Service
JDL-seitige Service-Fassade für bewusste, explizite Storage-Kontrolle. Nur diese bewusste Kontrolle soll Storage in die Effect Row bringen.

StorageEngine
Engine für Materialisierung und Orchestrierung von MemoryRegions sowie für Role-/Profile-/Descriptor-Auflösung. Sie besitzt nicht die physische Speicherhoheit.

JadeME
Privilegiertes Jade-Memory-Subsystem. Führt mechanische Speicheroperationen aus, verwaltet Handles, Generation Counter, Region Accounting und Enforcement.

Memory Containment
Begrenzung des Schadens eines Speicher- oder Ownership-Fehlers durch Context- und Region-Grenzen, Budgets und kontrollierte Übergänge. Es ist nicht dasselbe wie vollständige Leak-Vermeidung.

Ownership
Semantik darüber, wer einen Wert am Leben hält und wie Besitz geteilt oder übertragen wird. Rc und Weak gehören hierher.

Representation
Physische beziehungsweise VM-seitige Darstellung eines Wertes, beispielsweise inline oder über Handle. Diese sollte weitgehend abgeleitet werden.

Storage Guarantee
Anforderung oder Zusage wie Alignment, stabile Adresse, Zero-on-Release oder Non-Movable. Eine Garantie sagt nicht, mit welcher konkreten Allocator-Implementierung sie erreicht wird.


Quellenbasis und Bezug zum heutigen Stand

Diese Notiz baut auf dem derzeitigen Jade/JDL-Stand auf, insbesondere auf:

  • JDL — Context-Modell: Context als descriptor-materialisierte Ausführungsgrenze; StorageSpec; Storage-Policy im Context, operative Speicherhoheit bei JadeME.
  • Refinements: heutige compilerinterne MemoryPolicy mit Value, Ref, Arena[A], Pool[P] sowie getrennte Own, Share, Create, Drop; diese Notiz empfiehlt für Create/Drop zusätzlich einen neuen Zuständigkeitsschnitt mit Create-Domain am Ursprungstyp und Drop am Created Type.
  • Runtime Handles & Memory Safety: HandleId + Generation Counter, Trennung von Speicherkorruption und logischen Leaks, Arena-/Pool-/Mapped-Policies als historische Speicherstrategien.
  • Commands with Arena: bestehende with Storage.arena[...]-Semantik, Push/Pop/Reset und Arena-Crossing.
  • Escape Analysis & Refcount Elision: EscapeInfo, Refcount-Elision und die heutige Forderung, beobachtbare Allokationssemantik bei Optimierungen nicht zu verändern.
  • Jade Design Decisions: Rc/Weak, Arena- und Pool-Motive sowie der bisherige composability-orientierte Memory-Ansatz.

Die Empfehlung dieser Notiz ist bewusst eine Neuordnung dieser bestehenden Bausteine. Sie behauptet nicht, dass der vorgeschlagene Schnitt bereits normativ wäre.

Neu hinzu kommt die übergeordnete Empfehlung eines Context-bound Resource Model: Services bleiben semantische Umgebungsanforderungen; Ressourcen sind konkrete laufende Instanzen mit Lifecycle; Rollen benennen Absicht; Profile beschreiben Policy; Descriptoren tragen geprüfte Semantik; Contexts binden konkrete Ausprägungen; ResourceEngine strukturiert with und Fehlerpfade. Storage wird als erste ausführlich ausgearbeitete Spezialisierung dieses allgemeineren Musters verstanden.

Für Construction/Lifecycle ergänzt die Notiz außerdem eine Lifecycle-Meta-Ableitung: Create bleibt geschlossene Transition-Domain am Ursprungstyp; die Meta-Ableitung dedupliziert die daraus erreichbaren Created Types und prüft beziehungsweise materialisiert deren Drop nur bei eindeutiger Herleitung. Nicht eindeutige Fälle bleiben explizit.

Online-Referenzen:

  • https://specs.iowling.at/spec-context-model/
  • https://specs.iowling.at/00-refinements/
  • https://specs.iowling.at/10-runtime-handles-und-memory-safety/
  • https://specs.iowling.at/jade-commands-with-arena/
  • https://specs.iowling.at/sketch-escape-analysis-refcount-elision/
  • https://specs.iowling.at/jade-design-decisions/