Skip to content

Commit

Permalink
host_impl: make output streams configurable (#109)
Browse files Browse the repository at this point in the history
## Summary

Allow for configuring at run-time where to write the output and error
text for the VM host procedures. This is an internal change aimed at
making debugging and testing easier.

## Details

* require providing the output and error streams to use for
  `core.write` and `core.writeErr` via the `HostEnv` instance
* support passing an environment closure (`RootRef`) to the VM via the
  type-information `vmexec.run` overload
* rename `writeToFile` to `writeToStream` and have it take a `Stream`
  as the destination rather than a file

This also fixes `core.writeErr` always writing to the standard output
stream (`writeToFile` always used `stdout`, ignoring the file
argument).
  • Loading branch information
zerbina authored Jan 22, 2025
1 parent 9eab5f5 commit eb6b5a2
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 13 deletions.
25 changes: 16 additions & 9 deletions phy/host_impl.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import
system/ansi_c,
std/tables,
std/[
streams,
tables
],
vm/[
vmalloc,
vmenv
Expand All @@ -13,6 +16,10 @@ type
## Dynamic type of the object passed to the VM callbacks.
params*: seq[string]
## the program's command-line parameters
outStream*: Stream
## the output stream
errStream*: Stream
## the error output stream

proc c_fopen(name, mode: cstring): CFilePtr {.
importc: "fopen", header: "<stdio.h>".}
Expand Down Expand Up @@ -95,15 +102,15 @@ proc toString(x: openArray[char]): string =
result = newString(x.len)
copyMem(addr result[0], addr x[0], x.len)

proc writeToFile(env: var VmEnv, file: File, data: VirtualAddr, len: int
): CallbackResult =
proc writeToStream(env: var VmEnv, s: Stream, data: VirtualAddr, len: int
): CallbackResult =
if len < 0: trap() # guard against misuse

let chars = toHostChars(data, len.uint64)
try:
discard stdout.writeChars(toOpenArray(chars, 0, len - 1), 0, len)
s.writeData(chars, len)
result = CallbackResult(code: cecNothing)
except IOError:
except OSError, IOError:
# TODO: I/O errors need to be turned into either exceptions (very hard) or
# error codes that the callsite then turns into exception (or
# something else; easy). Trapping is a temporary solution to at
Expand Down Expand Up @@ -133,12 +140,12 @@ proc hostProcedures*(includeTest: bool): Table[string, VmCallback] =
hostProc "core.test":
CallbackResult(code: cecValue, value: args[0])

# TODO: the streams to write to need to be configurable from
# the outside, through the ref object passed to the callbacks
hostProc "core.write":
writeToFile(env, stdout, args[0].addrVal, args[1].intVal.int)
writeToStream(env, HostEnv(cl).outStream, args[0].addrVal,
args[1].intVal.int)
hostProc "core.writeErr":
writeToFile(env, stderr, args[0].addrVal, args[1].intVal.int)
writeToStream(env, HostEnv(cl).errStream, args[0].addrVal,
args[1].intVal.int)
# XXX: the current file API is meant to be temporary. It needs to be
# replaced with a handle-based one at some point
hostProc "core.fileSize":
Expand Down
4 changes: 3 additions & 1 deletion phy/phy.nim
Original file line number Diff line number Diff line change
Expand Up @@ -399,8 +399,10 @@ proc main(args: openArray[string]) =
let stack = hoSlice(mem.stackStart, mem.stackStart + mem.stackSize)

if source == langSource:
let cl = HostEnv(outStream: newFileStream(stdout),
errStream: newFileStream(stderr))
# we have type high-level type information
stdout.write run(env, stack, entry.unsafeGet, typ)
stdout.write run(env, stack, entry.unsafeGet, typ, cl)
else:
# program arguments are only supported for non-source-language
# programs at the moment
Expand Down
4 changes: 3 additions & 1 deletion phy/repl.nim
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,11 @@ proc process(ctx: var ModuleCtx, reporter: Reporter,
var env = initVm(mem.total, mem.total)
link(env, hostProcedures(includeTest = false), [module])

let cl = HostEnv(outStream: newFileStream(stdout),
errStream: newFileStream(stderr))
# eval and print:
echo run(env, hoSlice(mem.stackStart, mem.stackStart + mem.stackSize),
ProcIndex(entry), typ)
ProcIndex(entry), typ, cl)
else:
echo "Error: unexpected node: ", tree[NodeIndex(0)].kind

Expand Down
4 changes: 2 additions & 2 deletions phy/vmexec.nim
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ proc readMemConfig*(m: VmModule): Option[MemoryConfig] =
stackSize: uint(stackSize))

proc run*(env: var VmEnv, stack: HOslice[uint], prc: ProcIndex,
typ: SemType): string =
typ: SemType, cl: RootRef): string =
## Runs the nullary procedure with index `prc`, and returns the result
## rendered as a string. `typ` is the type of the resulting value.
var thread: VmThread
Expand All @@ -160,7 +160,7 @@ proc run*(env: var VmEnv, stack: HOslice[uint], prc: ProcIndex,
else:
thread = vm.initThread(env, prc, stack, @[])

let res = run(env, thread, nil)
let res = run(env, thread, cl)
env.dispose(thread)

case res.kind
Expand Down

0 comments on commit eb6b5a2

Please sign in to comment.