tom_d4rt
Executes Dart source at runtime without compilation, with bridging and a permission sandbox. The reference implementation of the D4rt runtime that the rest of the family tracks.
Overview
`tom_d4rt` runs Dart source at runtime by walking the analyzer's parse tree in two passes — declaration, then interpretation — and calling the target entry point. It supports full-script `execute()` and REPL-style `eval()`. Scripts run inside an isolated scope chain. Sensitive operations — `dart:io`, isolates, process and network access — are blocked by default, and the host grants fine-grained permissions through a `grant()` API. This is what lets AI-generated code execute safely. It is the stable reference all existing projects depend on. Its bridge API is kept in 1:1 lockstep with the analyzer-free successor line (`tom_d4rt_ast`), so generated bridges run against either interpreter unchanged.
What it enables
Enables dynamic AI code execution, scripting in tom_brain and tom_flow, sandboxed Dart evaluation.
Relationships
Standalone — no declared relationships.
tom_d4rt
A secure, sandboxed Dart interpreter written in Dart — the analyzer-based reference implementation of the D4rt runtime.
Overview
`tom_d4rt` executes Dart source code at runtime without compilation. It is built on top of the `analyzer` package, which provides a full parse tree (AST) for any Dart 3 source string. The interpreter walks that AST in two passes — a declaration pass that registers classes and mixins as placeholders, followed by an interpretation pass that evaluates imports, resolves members, and calls the target function.
Two execution modes are supported:
- **`execute()`** — full-script, two-pass execution. Parses the source, runs both the `DeclarationVisitor` (pass 1) and `InterpreterVisitor` (pass 2), then calls the named entry point. Each call gets a fresh module loader and global environment.
- **`eval()`** — REPL-style incremental evaluation. Reuses the `InterpreterVisitor` and `Environment` from the last `execute()` call, so previously defined variables and functions are still in scope.
Scripts run inside an isolated `Environment` scope chain. Sensitive operations — `dart:io`, `dart:isolate`, process execution, network access — are blocked by default. The host process grants fine-grained permissions via the `grant()` API.
`tom_d4rt` is the stable reference that all existing projects in this workspace depend on. Its public API (`D4rt`, `BridgedClass`, `BridgedEnumDefinition`, `D4`, and the permission classes) is kept in 1:1 lockstep with the analyzer-free successor line (`tom_d4rt_ast`) so that bridge code generated by `tom_d4rt_generator` works against either interpreter without modification.
Analyzer-free successor line
For production embeddings where pulling in the `analyzer` package is undesirable (e.g., mobile apps, size-constrained builds), a parallel line is maintained in the same repository:
tom_ast_model ← tom_d4rt_ast ← tom_ast_generator ← tom_d4rt_exec ← tom_dcli_exec
`tom_d4rt_ast` replaces the analyzer with a hand-rolled AST model and shares the same bridge API surface. Both lines are kept in sync; any fix merged into `tom_d4rt` is back-ported to `tom_d4rt_ast` and vice versa.
Installation
dependencies:
tom_d4rt: ^1.8.21
dart pub add tom_d4rt
Requires Dart SDK `^3.5.0`. The only runtime dependencies are `analyzer: ^8.0.0` and `pub_semver: ^2.2.0`.
Features
Language coverage
`tom_d4rt` passes all 20 areas of the Dart language overview test suite (1,680+ tests). Covered language constructs include:
| Area | Details |
|---|---|
| Classes | Declarations, constructors (factory, named, redirecting, `const`), inheritance, `super`, abstract/final/sealed/interface/base class modifiers |
| Mixins | `with`, mixin abstract method satisfaction, enum-with-mixin |
| Generics | Generic classes and functions, type bounds, variance, F-bounded polymorphism, `is` checks with parameterized types |
| Patterns | Destructuring, switch expressions and statements, logical-OR patterns, `when` guards, record patterns (named fields, shorthand `:name`) |
| Records | Positional and named fields (up to 9 positional fields returned as native Dart records; larger records return `InterpretedRecord`) |
| Async/await | `async`/`await`, `async*` generators, `sync*` generators, `await for`, `Future`, `Stream`, `StreamController`, `StreamTransformer`, `Timer`, `Completer` |
| Extensions | Instance and static extension members, extensions on bridged types, imported extensions |
| Extension types | Dart 3.3+ inline classes / extension types |
| Enums | Enhanced enums with members, enums with mixins, `.name`, `.index`, `.values`, `.byName` |
| Error handling | `try`/`catch`/`finally`, `rethrow`, custom exception classes |
| Operators | All arithmetic, comparison, bitwise, cascade (`..`, `?..`), spread, null-aware, type (`is`, `as`, `is!`) |
| `late` variables | Lazy initialization, `late final`, static and instance late fields, `LateInitializationError` |
| Control flow | `if`/`else`, `for`, `for-in`, `while`, `do-while`, `switch`, `break`/`continue` with labels |
| Collections | `List`, `Set`, `Map`, spread operator, collection-if / collection-for, `const` collections |
| Null safety | Full null-safety — nullable types, null-aware operators (`?.`, `??`, `??=`, `!`) |
| `const` | Const expressions, const constructors, const fields, const collections |
| Typedefs | Function and type typedefs |
| Annotations | Declaration annotations |
| Libraries | `import`/`export` with `show`/`hide`, relative imports, multi-file source maps, file-system imports |
| Isolates | `Isolate`, `SendPort`, `ReceivePort`, `Capability` (communication bridged; spawning interpreted closures across isolate boundaries is a known limitation) |
Standard library bridges
The following `dart:` libraries are bridged out of the box:
| Library | Key types |
|---|---|
| `dart:core` | `int`, `double`, `num`, `bool`, `String`, `StringBuffer`, `List`, `Map`, `Set`, `Iterable`, `Iterator`, `DateTime`, `Duration`, `RegExp`, `Uri`, `BigInt`, `Symbol`, `Runes`, `Enum`, `Function`, `Type`, `StackTrace`, `Error`, and all standard exceptions |
| `dart:async` | `Future`, `Stream`, `StreamController`, `StreamSubscription`, `StreamTransformer`, `Completer`, `Timer` |
| `dart:collection` | `HashMap`, `HashSet`, `LinkedHashMap`, `LinkedList`, `ListQueue`, `Queue`, `SplayTreeMap`, `UnmodifiableListView` |
| `dart:convert` | `jsonEncode`/`jsonDecode`, `base64Encode`/`base64Decode`, `utf8`, `ascii`, `latin1`, `LineSplitter`, `HtmlEscape`, codecs and converters |
| `dart:math` | `min`, `max`, `sqrt`, `pow`, `log`, `sin`, `cos`, `tan`, `Random`, constants (`pi`, `e`, `ln2`, …) |
| `dart:typed_data` | `Uint8List`, `Int16List`, `Float32List`, `ByteData`, `ByteBuffer`, `Endian` — eagerly registered so Flutter bridge code that uses `ByteData` without an explicit import works out of the box |
| `dart:io` | File, Directory, HttpClient, Socket, Process, IOSink, Stdin/Stdout — available only after `grant(FilesystemPermission.any)` / `grant(NetworkPermission.any)` |
| `dart:isolate` | Available only after `grant(IsolatePermission.any)` |
External `dart:` URIs not in the list above are checked for registered bridge content before raising an error, allowing embedding projects to supply bridges for `dart:ui` or other platform libraries.
Bridging native code
Any native Dart class, enum, top-level function, or variable can be exposed to interpreted scripts by registering it before execution. The `tom_d4rt_generator` package automates bridge creation from annotated source files.
Permission sandboxing
Scripts run in a deny-by-default sandbox. The host process grants and revokes permissions at any granularity.
Quick start
import 'package:tom_d4rt/tom_d4rt.dart';
void main() {
final d4rt = D4rt();
// Execute a script — calls main() by default
d4rt.execute(
source: '''
void main() {
print('Hello from D4rt!');
}
''',
);
// Call a named function with arguments
final result = d4rt.execute(
source: '''
String greet(String name, int age) {
return 'Hello \$name, you are \$age';
}
''',
name: 'greet',
positionalArgs: ['Alice', 30],
);
print(result); // Hello Alice, you are 30
}
Usage
Full-script execution
`execute()` always initializes a fresh `ModuleLoader` and `Environment`. Every call is independent unless you explicitly use `continuedExecute()` or `eval()`.
final d4rt = D4rt();
// With named and positional arguments
final result = d4rt.execute(
source: '''
String greet({required String name, int times = 1}) {
return List.generate(times, (_) => 'Hello \$name').join(', ');
}
''',
name: 'greet',
namedArgs: {'name': 'World', 'times': 3},
);
// 'Hello World, Hello World, Hello World'
// Multi-file execution via a source map
d4rt.execute(
source: '''
import 'package:my_app/utils.dart';
void main() {
print(formatDate(DateTime.now()));
}
''',
sources: {
'package:my_app/utils.dart': '''
String formatDate(DateTime d) => '\${d.year}-\${d.month}-\${d.day}';
''',
},
);
// File-system imports (requires filesystem permission)
d4rt.grant(FilesystemPermission.read);
d4rt.execute(
source: "import './lib/utils.dart'; void main() { helper(); }",
basePath: '/path/to/project',
allowFileSystemImports: true,
);
`continuedExecute()` reuses the existing environment from a prior `execute()` call, letting you add declarations without resetting state.
REPL-style evaluation
After calling `execute()` to establish a context, `eval()` evaluates expressions and statements incrementally in the same environment:
final d4rt = D4rt();
d4rt.execute(source: '''
var counter = 0;
void increment() { counter++; }
''');
d4rt.eval('increment()');
d4rt.eval('increment()');
print(d4rt.eval('counter')); // 2
// Define a new function in the same session
d4rt.eval('int doubled() => counter * 2;');
print(d4rt.eval('doubled()')); // 4
`eval()` first tries to parse the string as a top-level declaration. If that succeeds, it registers the declaration and returns `null`. Otherwise it wraps the string in `dynamic __eval__() { return <expr>; }` and executes it, returning the value.
To reset the session between REPL interactions without rebuilding the bridge registrations, call `resetScriptDeclarations()`.
Code introspection
`analyze()` parses source and returns an `IntrospectionResult` describing all top-level declarations without executing any function:
final result = d4rt.analyze(source: '''
class Person {
final String name;
final int age;
Person(this.name, this.age);
String greet() => "Hi, I'm \$name";
}
int add(int a, int b) => a + b;
final greeting = 'Hello';
''');
print(result.classes); // [ClassInfo(Person)]
print(result.functions); // [FunctionInfo(add)]
print(result.variables); // [VariableInfo(greeting)]
Registering bridged classes
A `BridgedClass` adapts a native Dart class for use inside interpreted scripts. Constructors, instance methods, instance getters/setters, static methods, and static getters/setters are all expressed as Dart closures with a standard adapter signature.
import 'package:tom_d4rt/tom_d4rt.dart';
class Counter {
int value;
Counter(this.value);
void increment() => value++;
void add(int n) => value += n;
}
void main() {
final d4rt = D4rt();
final counterBridge = BridgedClass(
nativeType: Counter,
name: 'Counter',
constructors: {
// Default constructor — named '' for the unnamed constructor
'': (visitor, positional, named) => Counter(positional[0] as int),
},
getters: {
'value': (visitor, target) => (target as Counter).value,
},
setters: {
'value': (visitor, target, v) => (target as Counter).value = v as int,
},
methods: {
'increment': (visitor, target, positional, named, typeArgs) {
(target as Counter).increment();
return null;
},
'add': (visitor, target, positional, named, typeArgs) {
(target as Counter).add(positional[0] as int);
return null;
},
},
);
d4rt.registerBridgedClass(counterBridge, 'package:my_app/counter.dart');
final result = d4rt.execute(source: '''
import 'package:my_app/counter.dart';
int main() {
final c = Counter(10);
c.increment();
c.add(5);
return c.value; // 16
}
''');
print(result); // 16
}
Additional registration methods on `D4rt`:
| Method | Purpose |
|---|---|
| `registerBridgedEnum(def, library)` | Expose a native `enum` to scripts |
| `registerBridgedExtension(def, library)` | Expose a Dart extension to scripts |
| `registertopLevelFunction(name, fn, library)` | Expose a top-level function |
| `registerGlobalVariable(name, value, library)` | Expose a top-level variable (eager) |
| `registerGlobalGetter(name, getter, library)` | Expose a top-level variable (lazy, evaluated on access) |
| `registerGlobalSetter(name, setter, library)` | Expose a top-level setter |
| `registerClassAlias(alias, target, library)` | Register a `typedef`-style class alias |
| `registerFunctionTypedef(name, library)` | Register a function typedef name for type resolution |
| `registerLibraryReExport(source, target, {show, hide})` | Mirror `export` directives so scripts that import a barrel get all re-exported symbols |
| `registerExtensions(packageName, body)` | Queue a post-registration callback for relaxers / proxy factories |
| `finalizeBridges()` | Run all queued extension callbacks (called automatically on first `execute`/`eval`) |
| `registerRelaxerFactory(baseTypeName, factory)` | Register a relaxer that coerces interpreted values into a native parameterized bridged type |
| `registerInterfaceProxy(bridgedTypeName, factory)` | Register a proxy so an interpreted instance can satisfy a bridged abstract interface |
| `registerGenericConstructor(className, ctorName, factory)` | Register a factory that builds a native generic bridged instance from interpreted args + type args |
| `warmup()` | Finalize bridges and JIT-warm the parser/interpreter with a throwaway build |
The three facades (`registerRelaxerFactory` / `registerInterfaceProxy` / `registerGenericConstructor`) are thin wrappers over the static `D4` registries, meant to be called from inside a `registerExtensions` body so they run once at finalize time, in package order, after the standard bridges are wired up. See the [User Guide → Extension Registration and Facades](doc/d4rt_user_guide.md#extension-registration-and-facades) for the full contract. Set `D4.usageLogEnabled = true` (or the env var `D4RT_LOG_RELAXER_USAGE`) to audit which relaxers/proxies are hit at runtime.
For large bridge surfaces, use `tom_d4rt_generator` to generate all adapter boilerplate from annotated native source. Duplicated registrations are safely deduplicated via `sourceUri` tracking.
Permission system
All sensitive operations are blocked by default. Grant permissions before executing code that needs them:
final d4rt = D4rt();
// Filesystem
d4rt.grant(FilesystemPermission.read); // read any path
d4rt.grant(FilesystemPermission.writePath('/tmp')); // write under /tmp only
d4rt.grant(FilesystemPermission.any); // read + write + execute, any path
// Network
d4rt.grant(NetworkPermission.connectTo('api.example.com'));
d4rt.grant(NetworkPermission.listenOn(8080));
d4rt.grant(NetworkPermission.any);
// Process execution
d4rt.grant(ProcessRunPermission.command('git'));
d4rt.grant(ProcessRunPermission.any);
// Isolates
d4rt.grant(IsolatePermission.spawn);
d4rt.grant(IsolatePermission.any);
// Dangerous (use with extreme caution)
d4rt.grant(DangerousPermission.codeEvaluation);
d4rt.grant(DangerousPermission.nativePlugins);
Permissions can be revoked at any time with `d4rt.revoke(permission)`. Check the current set with `d4rt.hasPermission(permission)` or `d4rt.checkPermission(operation)`.
The full permission class hierarchy:
| Class | Static constants | Factory constructors |
|---|---|---|
| `FilesystemPermission` | `.read`, `.write`, `.execute`, `.any` | `.readPath(p)`, `.writePath(p)`, `.executePath(p)`, `.path(p)` |
| `NetworkPermission` | `.connect`, `.listen`, `.bind`, `.any` | `.connectTo(host)`, `.connectToPort(host, port)`, `.listenOn(port)` |
| `ProcessRunPermission` | `.any` | `.command(cmd)`, `.commandWithArgs(cmd, args)` |
| `IsolatePermission` | `.spawn`, `.communicate`, `.any` | — |
| `DangerousPermission` | `.codeEvaluation`, `.nativePlugins`, `.any` | — |
D4 bridge helper
The `D4` class provides static utilities used in generated and hand-written bridge adapters:
- **Type coercion**: `D4.coerceList<T>(raw)`, `D4.coerceMap<K,V>(raw)`
- **Argument extraction**: `D4.getRequiredArg<T>(positional, index, name, className)`, `D4.getOptionalArg<T>(...)`, `D4.getNamedArg<T>(named, name, className)`
- **Target validation**: `D4.validateTarget<T>(target, className)` — ensures the receiver is of the expected native type before calling instance methods
- **Arity checking**: `D4.checkArity(positional, expected, methodName)`
- **Callback bridging**: `D4.callInterpreterCallback(visitor, fn, args)` — dispatches a call from a native callback into the interpreter
- **Active visitor**: `D4.withActiveVisitor(visitor, body)` — sets the thread-local active visitor used by interface-proxy factories
- **Generic wrappers**: `D4.registerGenericTypeWrapper<T>(factory)`, `D4.extractBridgedArg<T>(raw, visitor)` for covariant generic type resolution
Configuration inspection
`d4rt.getConfiguration()` returns a `D4rtConfiguration` snapshot listing all registered bridges, granted permissions, global variables and getters, and global functions. `d4rt.getEnvironmentState()` returns an `EnvironmentState` with the names of variables, bridged classes, and bridged enums currently live in the global scope.
`d4rt.validateRegistrations(source: ...)` runs a full parse and import pass in error-collection mode, returning a list of registration conflict messages without aborting on the first error.
Architecture
Two-pass execution
Source string
│
▼
analyzer.parseString() ──► CompilationUnit (AST)
│
├─ Pass 1: DeclarationVisitor
│ Registers InterpretedClass / InterpretedMixin placeholders
│ in the global Environment. No member resolution yet.
│
└─ Pass 2: InterpreterVisitor
1. ImportDirectives → ModuleLoader loads bridge libraries
2. EnumDeclarations → populate enum value tables
3. ClassDeclarations → populate constructors, methods, fields
(static field inits deferred to avoid forward-ref issues)
4. ExtensionDeclarations / ExtensionTypeDeclarations
5. FunctionDeclarations
6. TopLevelVariableDeclarations
7. Call named entry point
Environment
`Environment` is a lexical scope chain backed by a `Map<String, Object?>`. Each function call or block creates a child `Environment` that delegates lookups to its enclosing scope. The global environment holds stdlib bridges, user-registered bridges, and top-level script declarations. `InterpreterVisitor` carries a reference to the current `Environment` and updates it as it walks the AST.
Bridging system
A `BridgedClass` wraps a native Dart type. When the interpreter encounters `new MyNative()` or a method call on a `BridgedInstance`, it looks up the registered `BridgedClass` and invokes the corresponding adapter closure. The adapter receives an `InterpreterVisitor` (for re-entering the interpreter from callbacks), the native target object, and the evaluated argument lists.
`BridgedInstance` wraps a native value and carries a reference to its `BridgedClass`. It exposes `get`, `set`, and `call` dispatch and handles all Dart operators (`+`, `-`, `[]`, `[]=`, `==`, etc.).
`BridgedEnumDefinition` exposes native `Enum` values, including `.name`, `.index`, `.values`, and `.byName`, as well as custom methods and getters.
`BridgedExtensionDefinition` exposes extension methods so they are discoverable by `Environment.findExtensionMember`.
Module loader
`ModuleLoader` manages a map of package URI to source string. When an `import` directive is processed, it parses the target source, runs pass 1 and pass 2 in that module's own `Environment`, and merges the exported names (respecting `show`/`hide`) into the importing scope. Bridged libraries are registered into a per-module environment without requiring a source string. `registerLibraryReExport` lets bridge packages model `export` directives so transitive re-exports are automatically resolved.
Key types exposed from `package:tom_d4rt/tom_d4rt.dart`
| Type | Role |
|---|---|
| `D4rt` | Main interpreter class — entry point for all execution |
| `InterpreterVisitor` | AST visitor that drives interpretation; accessible via `d4rt.visitor` |
| `DeclarationVisitor` | Pass-1 AST visitor that seeds class placeholders |
| `Environment` | Lexical scope chain |
| `BridgedClass` | Adapter descriptor for a native class |
| `BridgedInstance` | Runtime wrapper around a native object |
| `BridgedEnumDefinition` | Adapter descriptor for a native enum |
| `BridgedExtensionDefinition` | Adapter descriptor for a native extension |
| `D4` | Static helpers for generated bridge adapters |
| `Permission` (and subclasses) | Sandbox permission objects |
| `IntrospectionResult` | Output of `d4rt.analyze()` |
| `ScriptExecutionResult` | Structured result from file-based script execution |
| `D4rtConfiguration` | Snapshot of all registered bridges and permissions |
| `RuntimeD4rtException` | Thrown for interpreter-level errors |
| `SourceCodeD4rtException` | Thrown for parse errors in the source |
| `LibraryVariable`, `LibraryGetter`, `LibrarySetter`, `LibraryFunction`, `LibraryClass`, `LibraryEnum`, `LibraryExtension` | Wrappers used when registering bridge elements under a library URI |
Ecosystem
tom_d4rt (this package)
│
├─ tom_d4rt_generator
│ Source-generator for BridgedClass / BridgedEnumDefinition boilerplate.
│ Reads @D4rtUserBridge annotations, emits bridge .dart files.
│ See: ../tom_d4rt_generator/doc/bridgegenerator_user_guide.md
│
└─ tom_d4rt_dcli (tom_dcli)
DCli-based CLI runner that uses tom_d4rt to execute *.dcli.dart scripts.
Analyzer-free parallel line (same bridge API, no analyzer dependency):
tom_ast_model ← tom_d4rt_ast ← tom_ast_generator ← tom_d4rt_exec ← tom_dcli_exec
Bridges generated by `tom_d4rt_generator` (or its AST-line counterpart `tom_ast_generator`) compile against this package's API. Both generators produce code that calls identical `registerBridgedClass` / `registerBridgedEnum` / `registerExtensions` / `finalizeBridges` sequences, so a bridge package works against either the `tom_d4rt` or `tom_d4rt_ast` interpreter without branching.
Documentation
- [User Guide](doc/d4rt_user_guide.md) — execution modes, configuration, multi-file scripts, extension registration & facades
- [Bridging Guide](doc/BRIDGING_GUIDE.md) — detailed coverage of every registration API
- [Advanced Bridging Guide](doc/advanced_bridging_user_guide.md) — D4 helper class, type coercion, argument extraction, interface proxies, generic constructors
- [Limitations](doc/d4rt_limitations.md) — **canonical** interpreter limitations reference. Every AST-variant, exec, and Flutter project links back to this file for shared interpreter limits and documents only its own deltas.
- [Bridge Generator User Guide](../tom_d4rt_generator/doc/bridgegenerator_user_guide.md) — automated bridge generation from annotated source
Status
Stable. Published at **1.8.21** on pub.dev.
The test suite covers 1,680+ tests (all passing; 2 known `Won't Fix` limitations for records with >9 positional fields and for spawning interpreted closures across isolate boundaries).
Repository: [github.com/al-the-bear/tom_d4rt — tom_d4rt](https://github.com/al-the-bear/tom_d4rt/tree/main/tom_d4rt)
License
MIT License Copyright (c) 2025 Moustapha Kodjo Amadou Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Extensions by Peter Nicolai Alexis Kyaw (find me on LinkedIn under Alexis Kyaw). This is a very extended version from the original.