Virtual Machine
The virtual machine (VM) executes the instructions produced by the Devlish compiler. It holds program values, follows branches and loops, and asks the surrounding application to perform operations such as reading a file. That surrounding application is called the host.
This separation lets the same bytecode be used in native and browser environments while each host controls the external operations it supports. This page explains execution state, host effects, and limits; read the Architecture Overview for how compilation and execution fit together.
Execution Model
Section titled “Execution Model”The VM keeps the values and bookkeeping needed to execute a program one instruction at a time. These internal structures explain how it follows branches, recovers from errors, and reports results.
The VM maintains:
- Constant pool: Immutable values referenced by index
- Registers: Named temporary storage for intermediate values
- Context: Input variables and program state (a JSON object)
- Program counter: Current instruction index
- Try stack: Nested error recovery frames
- Events: Execution trace for debugging and observability
devlish run program.dvlc.json --input '{"customer_tier": "priority"}'HostEffects Trait
Section titled “HostEffects Trait”All side effects are delegated to the host through a trait interface:
HostEffects: emit_event(event) - Execution tracing write_file(request) - File output read_file(request) - File input (JSON, CSV, text) http_request(method, url, body, headers) - HTTP calls call_service(request) - External service integration respond(value) - Structured output file_copy(src, dst) - Filesystem copy file_move(src, dst) - Filesystem move file_mkdir(path) - Create directory file_delete(path) - Remove file/directory file_exists(path) - Check existence file_stat(path) - Get file metadata file_list(path) - List directory file_glob(pattern, dir) - Glob search resolve_credential(key) - Secure credential accessThe native CLI implements all effects. The WASM host implements only safe effects (emit_event, respond). Unimplemented effects return an error string.
Instruction Limit
Section titled “Instruction Limit”The VM enforces a maximum instruction count to prevent infinite loops:
Default: 10,000,000 instructionsWhen the limit is reached, execution stops with an error. The limit is configurable:
// In devlish-runtimeconst tool = await loadTool({ bytecode: compiled, instructionLimit: 5000000});Event Emission
Section titled “Event Emission”The VM emits structured events during execution for tracing and debugging:
| Event | Fired When |
|---|---|
run_started |
Execution begins |
variable_assigned |
A variable receives a value |
effect_requested |
A host effect is about to execute |
effect_completed |
A host effect finished |
output_emitted |
Print or Show produces output |
run_finished |
Execution completes successfully |
run_failed |
Execution terminates with an error |
Events can be suppressed for production use or consumed by debugging tools.
Try/Catch Frames
Section titled “Try/Catch Frames”The VM uses a frame stack for error recovery. When a TRY_PUSH instruction executes, it registers a handler address:
TRY_PUSH handler=15 ; push frame pointing to instruction 15... risky operations ...TRY_POP ; pop frame on success, skip to after handler; instruction 15: ; handler runs if error occurredWhen an error occurs inside a try block, the VM:
- Pops the try frame
- Stores the error message in
last_error - Jumps to the handler address
- Continues execution in the Otherwise block
Try frames nest. An error propagates to the nearest enclosing frame.
Checkpoint Handling
Section titled “Checkpoint Handling”When the VM encounters a CHECKPOINT instruction, it pauses execution and returns the checkpoint data to the caller:
{ "success": true, "checkpoint": { "message": "Review before continuing", "context": {"amount": 50000} }}The program can be resumed later with new input merged into context.
Permission Enforcement
Section titled “Permission Enforcement”If the bytecode package includes a manifest with permissions, the VM checks every effect against the declared list before dispatching to the host. A mismatch produces a “Permission denied” error that can be caught by Try blocks.
Result Structure
Section titled “Result Structure”On completion, the VM returns:
{ "success": true, "context": {"all": "variables"}, "results": {"validations": "..."}, "events": [{"type": "run_started"}, "..."]}Failed runs include the error message and any events emitted before failure.