feature: WASI preview1 support, per-context isolation & glue API
Implement full WASI snapshot_preview1 with 46 registered host functions, per-context hook/registry isolation, and a single-unit public API. WASI Preview1 (15 new handler units): - Tier 1: fd_write, fd_read, fd_close, fd_seek, fd_prestat_get, fd_prestat_dir_name, fd_fdstat_get, proc_exit, environ_*, args_* - Tier 2: clock_time_get, clock_res_get, random_get - Tier 3: 14 fd_* stubs, 10 path_* stubs (ENOSYS) - Tier 4: poll_oneoff, proc_raise, sched_yield, 4 sock_* stubs Per-context isolation: - Move WASIHooks (12 hook table) and HostFuncRegistry onto TWASMProcessContext — each context is fully independent - Dynamic registry: starts at 16 entries, doubles on demand, no cap - All 15 preview1 handlers read hooks from context, not globals Glue API (wasm.pas): - Single entry point: wasm_init, wasm_load, wasm_set_*, wasm_register_wasi_preview1, wasm_register_host_func, wasm_resolve_imports, wasm_start, wasm_prepare_start, wasm_tick, wasm_dump_state - WASURO.lpr simplified to use only the wasm unit Refactoring: - Rename leb128.pas -> wasm.types.leb128.pas (updates 40+ opcode units) - New wasm.types.wasi.pas with WASI errno/filetype constants - New wasm.types.context.pas fields for hooks + registry types - New wasm.vm.setup unit (find_start, count_func_imports) - New wasi.emu.hooks for emulator-side hook implementations - Add read_uint16, write_uint16 to wasm.types.heap Testing: - 10 new test units: hooks, registry, glue, stubs, clock, random, extensibility, call, types, vm.setup (1087 tests / 232 units) - E2E WAT test runner (wat.ps1) with 9 integration tests: hello, exit42, multiwrite, prestat, fdstat, environ, clock, clockres, random 121 files changed, 5391 insertions(+), 267 deletions(-)
This commit is contained in:
1 parent
378f999143
commit
302dcb66f0
121 files changed
+5394
-270
No files matched your search
@@ -1,66 +0,0 @@
|
||||
unit leb128;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
types;
|
||||
|
||||
function read_leb128_to_uint64(buf : puint8; buf_end : puint8; res : puint64) : uint8;
|
||||
function read_leb128_to_uint32(buf : puint8; buf_end : puint8; res : puint32) : uint8;
|
||||
|
||||
implementation
|
||||
|
||||
function read_leb128_to_uint64(buf: puint8; buf_end: puint8; res: puint64): uint8;
|
||||
var
|
||||
ptr : puint8;
|
||||
shift : uint32;
|
||||
localResult : uint64;
|
||||
currentByte : uint8;
|
||||
|
||||
begin
|
||||
ptr:= buf;
|
||||
shift:= 0;
|
||||
localResult:= 0;
|
||||
while (true) do begin
|
||||
if (ptr >= buf_end) then begin
|
||||
read_leb128_to_uint64:= 0;
|
||||
exit;
|
||||
end;
|
||||
currentByte:= ptr^;
|
||||
inc(ptr);
|
||||
localResult:= localResult or uint64(currentByte and $7F) SHL Shift;
|
||||
if ((currentByte AND $80) = 0) then break;
|
||||
Inc(Shift, 7);
|
||||
end;
|
||||
res^:= localResult;
|
||||
read_leb128_to_uint64:= uint8(ptr - buf);
|
||||
end;
|
||||
|
||||
function read_leb128_to_uint32(buf: puint8; buf_end: puint8; res: puint32): uint8;
|
||||
var
|
||||
ptr : puint8;
|
||||
shift : uint32;
|
||||
localResult : uint32;
|
||||
currentByte : uint8;
|
||||
|
||||
begin
|
||||
ptr:= buf;
|
||||
shift:= 0;
|
||||
localResult:= 0;
|
||||
while (true) do begin
|
||||
if (ptr >= buf_end) then begin
|
||||
read_leb128_to_uint32:= 0;
|
||||
exit;
|
||||
end;
|
||||
currentByte:= ptr^;
|
||||
inc(ptr);
|
||||
localResult:= localResult or uint64(currentByte and $7F) SHL Shift;
|
||||
if ((currentByte AND $80) = 0) then break;
|
||||
Inc(Shift, 7);
|
||||
end;
|
||||
res^:= localResult;
|
||||
read_leb128_to_uint32:= uint8(ptr - buf);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
{ Emulator-layer WASI OS hook implementations.
|
||||
This unit provides native hook callbacks for running WASI programs
|
||||
on a host OS (Windows/Linux). On bare-metal (Asuro), these would be
|
||||
replaced by kernel-level implementations.
|
||||
|
||||
Hooks are registered per-context via PWASMProcessContext.
|
||||
This unit has NO knowledge of WASM internals (no stacks, no linear memory). }
|
||||
unit wasi.emu.hooks;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
wasm.types.context;
|
||||
|
||||
procedure register_emu_hooks(ctx : PWASMProcessContext);
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
wasm.types.builtin,
|
||||
wasm.wasi.hooks,
|
||||
wasm.types.wasi;
|
||||
|
||||
{ fd_write: write buf to stdout (fd=1) or stderr (fd=2) }
|
||||
function emu_fd_write(fd: TWASMUInt32;
|
||||
buf: TWASMPUInt8;
|
||||
len: TWASMUInt32): TWASMUInt32;
|
||||
var
|
||||
i: TWASMUInt32;
|
||||
begin
|
||||
if (fd = 1) or (fd = 2) then begin
|
||||
for i := 0 to len - 1 do
|
||||
Write(TWASMChar(buf[i]));
|
||||
emu_fd_write := len;
|
||||
end else
|
||||
emu_fd_write := 0;
|
||||
end;
|
||||
|
||||
{ fd_read: stub — no stdin support in emu layer yet }
|
||||
function emu_fd_read(fd: TWASMUInt32;
|
||||
buf: TWASMPUInt8;
|
||||
len: TWASMUInt32): TWASMUInt32;
|
||||
begin
|
||||
emu_fd_read := 0;
|
||||
end;
|
||||
|
||||
{ fd_close: no-op for emu layer }
|
||||
function emu_fd_close(fd: TWASMUInt32): TWASMUInt32;
|
||||
begin
|
||||
emu_fd_close := 0; { ESUCCESS }
|
||||
end;
|
||||
|
||||
{ proc_exit: halt the program with the given exit code }
|
||||
procedure emu_proc_exit(code: TWASMUInt32);
|
||||
begin
|
||||
{ The glue layer already sets Running := false and ExitCode.
|
||||
On the emu layer, we also halt the host process. }
|
||||
halt(code);
|
||||
end;
|
||||
|
||||
{ clock_time_get: return current time in nanoseconds }
|
||||
function emu_clock_time_get(clock_id: TWASMUInt32;
|
||||
precision: TWASMUInt64;
|
||||
var time: TWASMUInt64): TWASMUInt32;
|
||||
begin
|
||||
{ Stub: return 0 nanoseconds. On a real OS we would query the system clock.
|
||||
Bare-metal would use RDTSC or PIT. }
|
||||
time := 0;
|
||||
emu_clock_time_get := WASI_ESUCCESS;
|
||||
end;
|
||||
|
||||
{ clock_res_get: return clock resolution in nanoseconds }
|
||||
function emu_clock_res_get(clock_id: TWASMUInt32;
|
||||
var resolution: TWASMUInt64): TWASMUInt32;
|
||||
begin
|
||||
{ Stub: report 1ms resolution }
|
||||
resolution := 1000000;
|
||||
emu_clock_res_get := WASI_ESUCCESS;
|
||||
end;
|
||||
|
||||
{ random_get: fill buffer with random bytes }
|
||||
function emu_random_get(buf: TWASMPUInt8;
|
||||
len: TWASMUInt32): TWASMUInt32;
|
||||
var
|
||||
i: TWASMUInt32;
|
||||
state: TWASMUInt32;
|
||||
begin
|
||||
{ Simple xorshift PRNG — not cryptographic, but deterministic
|
||||
and works without RTL. On bare-metal, use RDRAND. }
|
||||
state := 2463534242; { seed }
|
||||
for i := 0 to len - 1 do begin
|
||||
state := state xor (state shl 13);
|
||||
state := state xor (state shr 17);
|
||||
state := state xor (state shl 5);
|
||||
buf[i] := TWASMUInt8(state and $FF);
|
||||
end;
|
||||
emu_random_get := WASI_ESUCCESS;
|
||||
end;
|
||||
|
||||
procedure register_emu_hooks(ctx : PWASMProcessContext);
|
||||
begin
|
||||
wasm.wasi.hooks.init_hook_table(ctx);
|
||||
wasm.wasi.hooks.register_fd_write(ctx, @emu_fd_write);
|
||||
wasm.wasi.hooks.register_fd_read(ctx, @emu_fd_read);
|
||||
wasm.wasi.hooks.register_fd_close(ctx, @emu_fd_close);
|
||||
wasm.wasi.hooks.register_proc_exit(ctx, @emu_proc_exit);
|
||||
wasm.wasi.hooks.register_clock_time_get(ctx, @emu_clock_time_get);
|
||||
wasm.wasi.hooks.register_clock_res_get(ctx, @emu_clock_res_get);
|
||||
wasm.wasi.hooks.register_random_get(ctx, @emu_random_get);
|
||||
end;
|
||||
|
||||
end.
|
||||
+38
-28
@@ -2,14 +2,14 @@ program WASURO;
|
||||
|
||||
uses
|
||||
sysutils,
|
||||
|
||||
console,
|
||||
|
||||
wasm.types.builtin,
|
||||
wasm.types.context,
|
||||
wasm.types.stack,
|
||||
wasm.parser,
|
||||
wasm.vm
|
||||
wasm,
|
||||
|
||||
{ Emu-layer OS hooks }
|
||||
wasi.emu.hooks
|
||||
|
||||
{$IFDEF RUN_TESTS}
|
||||
, wasm.test
|
||||
, wasm.test.framework
|
||||
@@ -24,12 +24,15 @@ var
|
||||
|
||||
begin
|
||||
{$IFDEF RUN_TESTS}
|
||||
wasm.vm.init();
|
||||
wasm.wasm_init;
|
||||
wasm.test.run_all_tests;
|
||||
halt(wasm.test.framework.FailedTests);
|
||||
{$ELSE}
|
||||
{$IFDEF DEBUG_OUTPUT}
|
||||
console.writestringln('WASURO - WebAssembly Runtime in Object Pascal');
|
||||
wasm.vm.init();
|
||||
{$ENDIF}
|
||||
wasm.wasm_init;
|
||||
|
||||
if ParamCount > 0 then begin
|
||||
if not FileExists(ParamStr(1)) then begin
|
||||
console.writestringln('File not found.');
|
||||
@@ -41,28 +44,35 @@ begin
|
||||
GetMem(ModuleBuffer, ModuleSize);
|
||||
BlockRead(ModuleFile, ModuleBuffer^, ModuleSize);
|
||||
Close(ModuleFile);
|
||||
console.writestringln('Module loaded.');
|
||||
Context := wasm.parser.parse(ModuleBuffer, TWASMPUInt8(ModuleBuffer + ModuleSize));
|
||||
while wasm.vm.tick(Context) do ;
|
||||
console.writestringln('Execution finished.');
|
||||
console.writestringln('');
|
||||
console.writestringln('--- VM State ---');
|
||||
writeln('IP: ', Context^.ExecutionState.IP);
|
||||
writeln('Running: ', Context^.ExecutionState.Running);
|
||||
writeln('');
|
||||
writeln('Operand Stack (', Context^.ExecutionState.Operand_Stack^.Top, ' entries):');
|
||||
if Context^.ExecutionState.Operand_Stack^.Top > 0 then
|
||||
wasm.types.stack.walk(Context^.ExecutionState.Operand_Stack)
|
||||
else
|
||||
console.writestringln(' (empty)');
|
||||
writeln('');
|
||||
writeln('Control Stack (', Context^.ExecutionState.Control_Stack^.Top, ' entries):');
|
||||
if Context^.ExecutionState.Control_Stack^.Top > 0 then
|
||||
wasm.types.stack.walk(Context^.ExecutionState.Control_Stack)
|
||||
else
|
||||
console.writestringln(' (empty)');
|
||||
console.writestringln('----------------');
|
||||
{$IFDEF DEBUG_OUTPUT}
|
||||
write('Module loaded from file: ');
|
||||
writeln(ParamStr(1));
|
||||
{$ENDIF}
|
||||
|
||||
{ Load, configure, and run the WASM module }
|
||||
Context := wasm.wasm_load(ModuleBuffer, TWASMPUInt8(ModuleBuffer + ModuleSize));
|
||||
|
||||
if not Context^.ValidBinary then begin
|
||||
console.writestringln('Error: Invalid WASM binary.');
|
||||
halt(1);
|
||||
end;
|
||||
|
||||
{ Register emu-layer OS hooks and WASI preview1 into context }
|
||||
wasi.emu.hooks.register_emu_hooks(Context);
|
||||
wasm.wasm_register_wasi_preview1(Context);
|
||||
|
||||
{ Find _start and run to completion }
|
||||
if not wasm.wasm_start(Context) then begin
|
||||
console.writestringln('No _start export found.');
|
||||
halt(1);
|
||||
end;
|
||||
|
||||
{$IFDEF DEBUG_OUTPUT}
|
||||
wasm.wasm_dump_state(Context);
|
||||
{$ENDIF}
|
||||
|
||||
FreeMem(ModuleBuffer);
|
||||
halt(Context^.ExitCode);
|
||||
end else begin
|
||||
console.writestringln('Usage: WASURO <module.wasm>');
|
||||
halt(1);
|
||||
|
||||
@@ -3,7 +3,7 @@ unit wasm.parser.sections.codeSection;
|
||||
interface
|
||||
|
||||
uses
|
||||
wasm.types.builtin, lmemorymanager, console, leb128,
|
||||
wasm.types.builtin, lmemorymanager, console, wasm.types.leb128,
|
||||
wasm.types.enums, wasm.types.values, wasm.types.sections, wasm.types.context;
|
||||
|
||||
procedure handle(buffer: TWASMPUInt8; section_length: TWASMUInt32; ctx: PWASMProcessContext);
|
||||
|
||||
@@ -3,7 +3,7 @@ unit wasm.parser.sections.customSection;
|
||||
interface
|
||||
|
||||
uses
|
||||
wasm.types.builtin, lmemorymanager, console, leb128,
|
||||
wasm.types.builtin, lmemorymanager, console, wasm.types.leb128,
|
||||
wasm.types.context;
|
||||
|
||||
procedure handle(buffer: TWASMPUInt8; section_length: TWASMUInt32; ctx: PWASMProcessContext);
|
||||
|
||||
@@ -9,7 +9,7 @@ procedure handle(buffer: TWASMPUInt8; section_length: TWASMUInt32; ctx: PWASMPro
|
||||
|
||||
implementation
|
||||
|
||||
uses console, leb128, lmemorymanager, wasm.types.sections;
|
||||
uses console, wasm.types.leb128, lmemorymanager, wasm.types.sections;
|
||||
|
||||
{ DataCount section (ID 12): contains a single u32 count of data segments.
|
||||
Pre-allocates the DataSegments array so that bulk memory opcodes
|
||||
|
||||
@@ -3,7 +3,7 @@ unit wasm.parser.sections.dataSection;
|
||||
interface
|
||||
|
||||
uses
|
||||
wasm.types.builtin, lmemorymanager, console, leb128,
|
||||
wasm.types.builtin, lmemorymanager, console, wasm.types.leb128,
|
||||
wasm.types.sections, wasm.types.context, wasm.types.heap;
|
||||
|
||||
procedure handle(buffer: TWASMPUInt8; section_length: TWASMUInt32; ctx: PWASMProcessContext);
|
||||
|
||||
@@ -3,7 +3,7 @@ unit wasm.parser.sections.elementSection;
|
||||
interface
|
||||
|
||||
uses
|
||||
wasm.types.builtin, lmemorymanager, console, leb128,
|
||||
wasm.types.builtin, lmemorymanager, console, wasm.types.leb128,
|
||||
wasm.types.sections, wasm.types.context;
|
||||
|
||||
procedure handle(buffer: TWASMPUInt8; section_length: TWASMUInt32; ctx: PWASMProcessContext);
|
||||
|
||||
@@ -3,7 +3,7 @@ unit wasm.parser.sections.exportSection;
|
||||
interface
|
||||
|
||||
uses
|
||||
wasm.types.builtin, lmemorymanager, console, leb128,
|
||||
wasm.types.builtin, lmemorymanager, console, wasm.types.leb128,
|
||||
wasm.types.enums, wasm.types.sections, wasm.types.context;
|
||||
|
||||
procedure handle(buffer: TWASMPUInt8; section_length: TWASMUInt32; ctx: PWASMProcessContext);
|
||||
|
||||
@@ -3,7 +3,7 @@ unit wasm.parser.sections.functionSection;
|
||||
interface
|
||||
|
||||
uses
|
||||
wasm.types.builtin, lmemorymanager, console, leb128,
|
||||
wasm.types.builtin, lmemorymanager, console, wasm.types.leb128,
|
||||
wasm.types.sections, wasm.types.context;
|
||||
|
||||
procedure handle(buffer: TWASMPUInt8; section_length: TWASMUInt32; ctx: PWASMProcessContext);
|
||||
|
||||
@@ -3,7 +3,7 @@ unit wasm.parser.sections.globalSection;
|
||||
interface
|
||||
|
||||
uses
|
||||
wasm.types.builtin, lmemorymanager, console, leb128,
|
||||
wasm.types.builtin, lmemorymanager, console, wasm.types.leb128,
|
||||
wasm.types.enums, wasm.types.sections, wasm.types.context;
|
||||
|
||||
procedure handle(buffer: TWASMPUInt8; section_length: TWASMUInt32; ctx: PWASMProcessContext);
|
||||
|
||||
@@ -3,7 +3,7 @@ unit wasm.parser.sections.importSection;
|
||||
interface
|
||||
|
||||
uses
|
||||
wasm.types.builtin, lmemorymanager, console, leb128,
|
||||
wasm.types.builtin, lmemorymanager, console, wasm.types.leb128,
|
||||
wasm.types.enums, wasm.types.sections, wasm.types.context;
|
||||
|
||||
procedure handle(buffer: TWASMPUInt8; section_length: TWASMUInt32; ctx: PWASMProcessContext);
|
||||
|
||||
@@ -3,7 +3,7 @@ unit wasm.parser.sections.memorySection;
|
||||
interface
|
||||
|
||||
uses
|
||||
wasm.types.builtin, lmemorymanager, console, leb128,
|
||||
wasm.types.builtin, lmemorymanager, console, wasm.types.leb128,
|
||||
wasm.types.sections, wasm.types.context, wasm.types.heap;
|
||||
|
||||
procedure handle(buffer: TWASMPUInt8; section_length: TWASMUInt32; ctx: PWASMProcessContext);
|
||||
|
||||
@@ -3,7 +3,7 @@ unit wasm.parser.sections.startSection;
|
||||
interface
|
||||
|
||||
uses
|
||||
wasm.types.builtin, lmemorymanager, console, leb128,
|
||||
wasm.types.builtin, lmemorymanager, console, wasm.types.leb128,
|
||||
wasm.types.context;
|
||||
|
||||
procedure handle(buffer: TWASMPUInt8; section_length: TWASMUInt32; ctx: PWASMProcessContext);
|
||||
|
||||
@@ -3,7 +3,7 @@ unit wasm.parser.sections.tableSection;
|
||||
interface
|
||||
|
||||
uses
|
||||
wasm.types.builtin, lmemorymanager, console, leb128,
|
||||
wasm.types.builtin, lmemorymanager, console, wasm.types.leb128,
|
||||
wasm.types.sections, wasm.types.context;
|
||||
|
||||
procedure handle(buffer: TWASMPUInt8; section_length: TWASMUInt32; ctx: PWASMProcessContext);
|
||||
|
||||
@@ -3,7 +3,7 @@ unit wasm.parser.sections.typeSection;
|
||||
interface
|
||||
|
||||
uses
|
||||
wasm.types.builtin, lmemorymanager, console, leb128,
|
||||
wasm.types.builtin, lmemorymanager, console, wasm.types.leb128,
|
||||
wasm.types.enums, wasm.types.values, wasm.types.sections, wasm.types.context;
|
||||
|
||||
procedure handle(buffer: TWASMPUInt8; section_length: TWASMUInt32; ctx: PWASMProcessContext);
|
||||
|
||||
@@ -3,7 +3,7 @@ unit wasm.parser;
|
||||
interface
|
||||
|
||||
uses
|
||||
wasm.types.builtin, lmemorymanager, console, leb128,
|
||||
wasm.types.builtin, lmemorymanager, console, wasm.types.leb128,
|
||||
wasm.types.enums, wasm.types.sections, wasm.types.context, wasm.types.heap, wasm.types.stack,
|
||||
wasm.parser.sections, wasm.types.constants;
|
||||
|
||||
@@ -63,6 +63,30 @@ begin
|
||||
ctx^.ExecutionState.ElementSegments^.SegmentCount:= 0;
|
||||
ctx^.ExecutionState.ElementSegments^.Segments:= nil;
|
||||
|
||||
// Initialize WASI/import support
|
||||
ctx^.ExitCode := 0;
|
||||
ctx^.ResolvedImports.Count := 0;
|
||||
ctx^.ResolvedImports.Imports := nil;
|
||||
|
||||
// Initialize WASI hooks (all nil)
|
||||
ctx^.WASIHooks.OnFdWrite := nil;
|
||||
ctx^.WASIHooks.OnFdRead := nil;
|
||||
ctx^.WASIHooks.OnFdClose := nil;
|
||||
ctx^.WASIHooks.OnFdSeek := nil;
|
||||
ctx^.WASIHooks.OnProcExit := nil;
|
||||
ctx^.WASIHooks.OnClockTimeGet := nil;
|
||||
ctx^.WASIHooks.OnClockResGet := nil;
|
||||
ctx^.WASIHooks.OnRandomGet := nil;
|
||||
ctx^.WASIHooks.OnArgsSizesGet := nil;
|
||||
ctx^.WASIHooks.OnArgsGet := nil;
|
||||
ctx^.WASIHooks.OnEnvironSizesGet := nil;
|
||||
ctx^.WASIHooks.OnEnvironGet := nil;
|
||||
|
||||
// Initialize host function registry (empty, lazy-init on first register)
|
||||
ctx^.HostFuncRegistry.Count := 0;
|
||||
ctx^.HostFuncRegistry.Capacity := 0;
|
||||
ctx^.HostFuncRegistry.Entries := nil;
|
||||
|
||||
newContext:= ctx;
|
||||
end;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ procedure run;
|
||||
implementation
|
||||
|
||||
uses
|
||||
wasm.types.builtin, leb128, wasm.test.framework;
|
||||
wasm.types.builtin, wasm.types.leb128, wasm.test.framework;
|
||||
|
||||
procedure run;
|
||||
var
|
||||
@@ -17,7 +17,7 @@ var
|
||||
res64 : TWASMUInt64;
|
||||
bytesRead : TWASMUInt8;
|
||||
begin
|
||||
test_begin('leb128');
|
||||
test_begin('wasm.types.leb128');
|
||||
buf_end := @buf[10];
|
||||
|
||||
{ Test: single byte, value 0 }
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,137 @@
|
||||
unit wasm.test.wasi.clock;
|
||||
|
||||
interface
|
||||
|
||||
procedure run;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
wasm.types.builtin, lmemorymanager,
|
||||
wasm.types.enums, wasm.types.values, wasm.types.sections, wasm.types.context,
|
||||
wasm.types.stack, wasm.types.heap,
|
||||
wasm.types.wasi, wasm.wasi.hooks,
|
||||
wasm.wasi.preview1.clock,
|
||||
wasm.test.framework;
|
||||
|
||||
var
|
||||
last_clock_id : TWASMUInt32;
|
||||
last_precision : TWASMUInt64;
|
||||
|
||||
function mock_clock_time_get(clock_id: TWASMUInt32;
|
||||
precision: TWASMUInt64;
|
||||
var time: TWASMUInt64): TWASMUInt32;
|
||||
begin
|
||||
last_clock_id := clock_id;
|
||||
last_precision := precision;
|
||||
time := 1234567890000000000; { ~1.23 seconds in nanoseconds }
|
||||
mock_clock_time_get := WASI_ESUCCESS;
|
||||
end;
|
||||
|
||||
function mock_clock_res_get(clock_id: TWASMUInt32;
|
||||
var resolution: TWASMUInt64): TWASMUInt32;
|
||||
begin
|
||||
last_clock_id := clock_id;
|
||||
resolution := 1000000; { 1ms in nanoseconds }
|
||||
mock_clock_res_get := WASI_ESUCCESS;
|
||||
end;
|
||||
|
||||
procedure run;
|
||||
var
|
||||
code : array[0..3] of TWASMUInt8;
|
||||
ctx : PWASMProcessContext;
|
||||
os : PWASMStack;
|
||||
mem : PWasmHeap;
|
||||
errno: TWASMInt32;
|
||||
time_lo, time_hi : TWASMUInt32;
|
||||
begin
|
||||
test_begin('wasi.clock');
|
||||
|
||||
{ ------------------------------------------------------------------ }
|
||||
{ Test 1: clock_time_get with hook — returns time in linear memory }
|
||||
{ ------------------------------------------------------------------ }
|
||||
code[0] := $0B;
|
||||
ctx := make_test_context(@code[0], 1);
|
||||
wasm.wasi.hooks.register_clock_time_get(ctx, @mock_clock_time_get);
|
||||
os := ctx^.ExecutionState.Operand_Stack;
|
||||
mem := ctx^.ExecutionState.Memory;
|
||||
|
||||
{ clock_time_get(id=0 [realtime], precision=1000, time_ptr=400) }
|
||||
wasm.types.stack.pushi32(os, 0); { clock_id = REALTIME }
|
||||
wasm.types.stack.pushi64(os, 1000); { precision }
|
||||
wasm.types.stack.pushi32(os, 400); { time_ptr }
|
||||
|
||||
wasm.wasi.preview1.clock._WASI_clock_time_get(ctx);
|
||||
|
||||
errno := wasm.types.stack.popi32(os);
|
||||
assert_i32('clock_time_get returns ESUCCESS', errno, TWASMInt32(WASI_ESUCCESS));
|
||||
assert_u32('clock_time_get clock_id=0', last_clock_id, 0);
|
||||
assert_u64('clock_time_get precision=1000', last_precision, 1000);
|
||||
|
||||
{ Verify 64-bit time written to linear memory (little-endian) }
|
||||
{ 1234567890000000000 = 0x112210F4_768DB400 }
|
||||
wasm.types.heap.read_uint32(400, mem, @time_lo);
|
||||
wasm.types.heap.read_uint32(404, mem, @time_hi);
|
||||
assert_u32('clock_time_get time lo', time_lo, TWASMUInt32($768DB400));
|
||||
assert_u32('clock_time_get time hi', time_hi, TWASMUInt32($112210F4));
|
||||
|
||||
{ ------------------------------------------------------------------ }
|
||||
{ Test 2: clock_time_get without hook returns ENOSYS }
|
||||
{ ------------------------------------------------------------------ }
|
||||
code[0] := $0B;
|
||||
ctx := make_test_context(@code[0], 1);
|
||||
os := ctx^.ExecutionState.Operand_Stack;
|
||||
|
||||
wasm.types.stack.pushi32(os, 0); { clock_id }
|
||||
wasm.types.stack.pushi64(os, 0); { precision }
|
||||
wasm.types.stack.pushi32(os, 0); { time_ptr }
|
||||
|
||||
wasm.wasi.preview1.clock._WASI_clock_time_get(ctx);
|
||||
|
||||
errno := wasm.types.stack.popi32(os);
|
||||
assert_i32('clock_time_get no hook returns ENOSYS', errno, TWASMInt32(WASI_ENOSYS));
|
||||
|
||||
{ ------------------------------------------------------------------ }
|
||||
{ Test 3: clock_res_get with hook — returns resolution }
|
||||
{ ------------------------------------------------------------------ }
|
||||
code[0] := $0B;
|
||||
ctx := make_test_context(@code[0], 1);
|
||||
wasm.wasi.hooks.register_clock_res_get(ctx, @mock_clock_res_get);
|
||||
os := ctx^.ExecutionState.Operand_Stack;
|
||||
mem := ctx^.ExecutionState.Memory;
|
||||
|
||||
{ clock_res_get(id=1 [monotonic], resolution_ptr=500) }
|
||||
wasm.types.stack.pushi32(os, 1); { clock_id = MONOTONIC }
|
||||
wasm.types.stack.pushi32(os, 500); { resolution_ptr }
|
||||
|
||||
wasm.wasi.preview1.clock._WASI_clock_res_get(ctx);
|
||||
|
||||
errno := wasm.types.stack.popi32(os);
|
||||
assert_i32('clock_res_get returns ESUCCESS', errno, TWASMInt32(WASI_ESUCCESS));
|
||||
assert_u32('clock_res_get clock_id=1', last_clock_id, 1);
|
||||
|
||||
{ Verify 64-bit resolution written to linear memory }
|
||||
wasm.types.heap.read_uint32(500, mem, @time_lo);
|
||||
wasm.types.heap.read_uint32(504, mem, @time_hi);
|
||||
assert_u32('clock_res_get resolution lo', time_lo, 1000000);
|
||||
assert_u32('clock_res_get resolution hi', time_hi, 0);
|
||||
|
||||
{ ------------------------------------------------------------------ }
|
||||
{ Test 4: clock_res_get without hook returns ENOSYS }
|
||||
{ ------------------------------------------------------------------ }
|
||||
code[0] := $0B;
|
||||
ctx := make_test_context(@code[0], 1);
|
||||
os := ctx^.ExecutionState.Operand_Stack;
|
||||
|
||||
wasm.types.stack.pushi32(os, 0); { clock_id }
|
||||
wasm.types.stack.pushi32(os, 0); { resolution_ptr }
|
||||
|
||||
wasm.wasi.preview1.clock._WASI_clock_res_get(ctx);
|
||||
|
||||
errno := wasm.types.stack.popi32(os);
|
||||
assert_i32('clock_res_get no hook returns ENOSYS', errno, TWASMInt32(WASI_ENOSYS));
|
||||
|
||||
test_end;
|
||||
end;
|
||||
|
||||
end.
|
||||
Loaded 20 of 121 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user