959 lines
37 KiB
C
959 lines
37 KiB
C
#include "spl_syscall.h"
|
|
#include "include/core_map.h"
|
|
#include "include/core_vec.h"
|
|
#include "spl_mcode.h"
|
|
#include "spl_vm.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
/* VM 偏移 -> 宿主地址;size=0 表示不做边界检查。 */
|
|
#define VM_X(_vm, off) ((void *)spl_vm_xlate((_vm), (spl_vm_val_t)(off), 0))
|
|
#define VM_XN(_vm, off, n) ((void *)spl_vm_xlate((_vm), (spl_vm_val_t)(off), (usize)(n)))
|
|
#define VM_ASP(_vm) spl_vm_asp(_vm)
|
|
|
|
#define CHECK_NARGS(fname, expected) \
|
|
do { \
|
|
if (nargs != (expected)) { \
|
|
fprintf(stderr, fname ": expected " #expected " args, got %d\n", nargs); \
|
|
return -1; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define SYSCALL_0(name, ret_expr) \
|
|
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
|
(void)_vm; \
|
|
CHECK_NARGS(#name, 0); \
|
|
(void)args; \
|
|
return (spl_vm_val_t)(uintptr_t)(ret_expr); \
|
|
}
|
|
|
|
#define SYSCALL_1(name, t1, ret_expr) \
|
|
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
|
(void)_vm; \
|
|
CHECK_NARGS(#name, 1); \
|
|
t1 a1 = (t1)(uintptr_t)args[0]; \
|
|
return (spl_vm_val_t)(uintptr_t)(ret_expr); \
|
|
}
|
|
|
|
#define SYSCALL_2(name, t1, t2, ret_expr) \
|
|
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
|
(void)_vm; \
|
|
CHECK_NARGS(#name, 2); \
|
|
t1 a1 = (t1)(uintptr_t)args[0]; \
|
|
t2 a2 = (t2)(uintptr_t)args[1]; \
|
|
return (spl_vm_val_t)(uintptr_t)(ret_expr); \
|
|
}
|
|
|
|
#define SYSCALL_3(name, t1, t2, t3, ret_expr) \
|
|
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
|
(void)_vm; \
|
|
CHECK_NARGS(#name, 3); \
|
|
t1 a1 = (t1)(uintptr_t)args[0]; \
|
|
t2 a2 = (t2)(uintptr_t)args[1]; \
|
|
t3 a3 = (t3)(uintptr_t)args[2]; \
|
|
return (spl_vm_val_t)(uintptr_t)(ret_expr); \
|
|
}
|
|
|
|
#define SYSCALL_4(name, t1, t2, t3, t4, ret_expr) \
|
|
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
|
(void)_vm; \
|
|
CHECK_NARGS(#name, 4); \
|
|
t1 a1 = (t1)(uintptr_t)args[0]; \
|
|
t2 a2 = (t2)(uintptr_t)args[1]; \
|
|
t3 a3 = (t3)(uintptr_t)args[2]; \
|
|
t4 a4 = (t4)(uintptr_t)args[3]; \
|
|
return (spl_vm_val_t)(uintptr_t)(ret_expr); \
|
|
}
|
|
|
|
#define ADDRCHECK(vm, ptr, size, ctx) \
|
|
do { \
|
|
uintptr_t _acp = (uintptr_t)(ptr); \
|
|
usize _acs = (usize)(size); \
|
|
if (!spl_vm_addr_valid(_acp, _acs)) { \
|
|
fprintf(stderr, "%s: invalid address %p size=%zu (ip=%zu fp=%zu sp=%zu)\n", (ctx), \
|
|
(void *)_acp, _acs, (vm)->ip, (vm)->fp, (vm)->sp); \
|
|
spl_vm_backtrace((vm), (vm)->fp); \
|
|
(vm)->exit_code = 2; \
|
|
return 0; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define SYSCALL_DUMP(vm) \
|
|
do { \
|
|
fprintf(stderr, " --- syscall dump (sp=%zu fp=%zu cp=%zu ip=%zu) ---\n", (vm)->sp, \
|
|
(vm)->fp, (vm)->cp, (vm)->ip); \
|
|
spl_vm_backtrace((vm), (vm)->fp); \
|
|
spl_vm_stackdump((vm), (vm)->sp); \
|
|
} while (0)
|
|
|
|
#define SYSCALL_FAIL(vm, ...) \
|
|
do { \
|
|
fprintf(stderr, "syscall error: "); \
|
|
fprintf(stderr, __VA_ARGS__); \
|
|
fprintf(stderr, "\n"); \
|
|
SYSCALL_DUMP(vm); \
|
|
(vm)->exit_code = 1; \
|
|
return 0; \
|
|
} while (0)
|
|
|
|
/* =================================================================
|
|
* 从共享核心 spl_rt_core.h 派生的 syscall 包装
|
|
* ================================================================= */
|
|
|
|
#include "spl_rt_core.h"
|
|
|
|
#define SPL_WRAP1(name, fn, t1) \
|
|
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
|
(void)_vm; \
|
|
CHECK_NARGS(#name, 1); \
|
|
return (spl_vm_val_t)(uintptr_t)fn((t1)(uintptr_t)args[0]); \
|
|
}
|
|
|
|
#define SPL_WRAP1_V(name, fn, t1) \
|
|
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
|
(void)_vm; \
|
|
CHECK_NARGS(#name, 1); \
|
|
fn((t1)(uintptr_t)args[0]); \
|
|
return 0; \
|
|
}
|
|
|
|
#define SPL_WRAP2(name, fn, t1, t2) \
|
|
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
|
(void)_vm; \
|
|
CHECK_NARGS(#name, 2); \
|
|
return (spl_vm_val_t)(uintptr_t)fn((t1)(uintptr_t)args[0], (t2)(uintptr_t)args[1]); \
|
|
}
|
|
|
|
#define SPL_WRAP3(name, fn, t1, t2, t3) \
|
|
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
|
(void)_vm; \
|
|
CHECK_NARGS(#name, 3); \
|
|
return (spl_vm_val_t)(uintptr_t)fn((t1)(uintptr_t)args[0], (t2)(uintptr_t)args[1], \
|
|
(t3)(uintptr_t)args[2]); \
|
|
}
|
|
|
|
#define SPL_WRAP4(name, fn, t1, t2, t3, t4) \
|
|
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
|
(void)_vm; \
|
|
CHECK_NARGS(#name, 4); \
|
|
return (spl_vm_val_t)(uintptr_t)fn((t1)(uintptr_t)args[0], (t2)(uintptr_t)args[1], \
|
|
(t3)(uintptr_t)args[2], (t4)(uintptr_t)args[3]); \
|
|
}
|
|
|
|
/* =================================================================
|
|
* OS syscalls
|
|
* ================================================================= */
|
|
|
|
SYSCALL_1(vm_exit, int, (exit(a1), (spl_vm_val_t)0))
|
|
SYSCALL_1(vm_putchar, int, putchar(a1))
|
|
SYSCALL_0(vm_getchar, getchar())
|
|
SYSCALL_1(vm_putint, int, (fprintf(stdout, "%d", a1), (spl_vm_val_t)0))
|
|
|
|
/* FILE* 存于 VM 空间,返回其偏移(不透明句柄)。 */
|
|
static spl_vm_val_t vm_file_handle(spl_vm_t *vm, FILE *f) {
|
|
spl_vm_val_t off;
|
|
FILE **slot;
|
|
if (!f)
|
|
return 0;
|
|
off = (spl_vm_val_t)(uintptr_t)spl_alloc_ex(VM_ASP(vm), sizeof(FILE *));
|
|
if (off == 0)
|
|
return 0;
|
|
slot = (FILE **)VM_XN(vm, off, sizeof(FILE *));
|
|
if (!slot)
|
|
return 0;
|
|
*slot = f;
|
|
return off;
|
|
}
|
|
|
|
static FILE *vm_file_get(spl_vm_t *vm, spl_vm_val_t off) {
|
|
FILE **slot = (FILE **)VM_XN(vm, off, sizeof(FILE *));
|
|
return slot ? *slot : NULL;
|
|
}
|
|
|
|
static spl_vm_val_t vm_putstr(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
const char *s;
|
|
CHECK_NARGS("vm_putstr", 1);
|
|
s = (const char *)VM_X(_vm, args[0]);
|
|
if (s)
|
|
fputs(s, stdout);
|
|
return 0;
|
|
}
|
|
|
|
static spl_vm_val_t vm_fopen(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
const char *path, *mode;
|
|
CHECK_NARGS("vm_fopen", 2);
|
|
path = (const char *)VM_X(_vm, args[0]);
|
|
mode = (const char *)VM_X(_vm, args[1]);
|
|
return vm_file_handle(_vm, fopen(path, mode));
|
|
}
|
|
|
|
static spl_vm_val_t vm_fclose(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
FILE *f;
|
|
CHECK_NARGS("vm_fclose", 1);
|
|
f = vm_file_get(_vm, args[0]);
|
|
return f ? (spl_vm_val_t)fclose(f) : 0;
|
|
}
|
|
|
|
static spl_vm_val_t vm_fread(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
void *buf;
|
|
FILE *f;
|
|
CHECK_NARGS("vm_fread", 4);
|
|
buf = VM_X(_vm, args[0]);
|
|
f = vm_file_get(_vm, args[3]);
|
|
if (!buf || !f)
|
|
return 0;
|
|
return (spl_vm_val_t)fread(buf, (size_t)args[1], (size_t)args[2], f);
|
|
}
|
|
|
|
static spl_vm_val_t vm_fwrite(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
const void *buf;
|
|
FILE *f;
|
|
CHECK_NARGS("vm_fwrite", 4);
|
|
buf = VM_X(_vm, args[0]);
|
|
f = vm_file_get(_vm, args[3]);
|
|
if (!buf || !f)
|
|
return 0;
|
|
return (spl_vm_val_t)fwrite(buf, (size_t)args[1], (size_t)args[2], f);
|
|
}
|
|
|
|
static spl_vm_val_t vm_fsize(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
FILE *f;
|
|
long cur, sz;
|
|
CHECK_NARGS("vm_fsize", 1);
|
|
f = vm_file_get(_vm, args[0]);
|
|
if (!f)
|
|
return 0;
|
|
cur = ftell(f);
|
|
fseek(f, 0, SEEK_END);
|
|
sz = ftell(f);
|
|
fseek(f, cur, SEEK_SET);
|
|
return (spl_vm_val_t)(uintptr_t)(sz < 0 ? 0 : sz);
|
|
}
|
|
|
|
static spl_vm_val_t vm_std_handle(spl_vm_t *_vm, int which) {
|
|
if (_vm->stdio_off[which] == 0) {
|
|
FILE *f = which == 0 ? stdin : (which == 1 ? stdout : stderr);
|
|
_vm->stdio_off[which] = vm_file_handle(_vm, f);
|
|
}
|
|
return _vm->stdio_off[which];
|
|
}
|
|
|
|
static spl_vm_val_t vm_stdin(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
(void)args;
|
|
CHECK_NARGS("vm_stdin", 0);
|
|
return vm_std_handle(_vm, 0);
|
|
}
|
|
static spl_vm_val_t vm_stdout(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
(void)args;
|
|
CHECK_NARGS("vm_stdout", 0);
|
|
return vm_std_handle(_vm, 1);
|
|
}
|
|
static spl_vm_val_t vm_stderr(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
(void)args;
|
|
CHECK_NARGS("vm_stderr", 0);
|
|
return vm_std_handle(_vm, 2);
|
|
}
|
|
|
|
static spl_vm_val_t vm_read_file(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_read_file", 1);
|
|
const char *path = (const char *)VM_X(_vm, args[0]);
|
|
FILE *f;
|
|
long sz;
|
|
spl_vm_val_t off;
|
|
char *buf;
|
|
if (path == nullptr) {
|
|
fprintf(stderr, "filepath can't be null");
|
|
return 0;
|
|
}
|
|
f = fopen(path, "rb");
|
|
if (!f) {
|
|
fprintf(stderr, "filepath %s can't be open", path);
|
|
return 1;
|
|
}
|
|
fseek(f, 0, SEEK_END);
|
|
sz = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
off = (spl_vm_val_t)(uintptr_t)spl_alloc_ex(VM_ASP(_vm), (uintptr_t)sz + 1);
|
|
if (off == 0) {
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
buf = (char *)VM_XN(_vm, off, (uintptr_t)sz + 1);
|
|
if (!buf) {
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
{
|
|
size_t nread = fread(buf, 1, (size_t)sz, f);
|
|
buf[nread] = '\0';
|
|
}
|
|
fclose(f);
|
|
return off;
|
|
}
|
|
|
|
/* =================================================================
|
|
* mem syscalls —— 使用 spl_rt_core.h 中的实现
|
|
* ================================================================= */
|
|
|
|
static spl_vm_val_t spl_sys_vm_alloc(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
uintptr_t sz;
|
|
spl_vm_val_t p;
|
|
CHECK_NARGS("vm_alloc", 1);
|
|
sz = (uintptr_t)args[0];
|
|
if (sz == 0)
|
|
SYSCALL_FAIL(_vm, "vm_alloc: zero size");
|
|
if (sz > ((uintptr_t)1 << 32))
|
|
SYSCALL_FAIL(_vm, "vm_alloc: size too large (%zu)", (size_t)sz);
|
|
p = (spl_vm_val_t)(uintptr_t)spl_alloc_ex(VM_ASP(_vm), sz);
|
|
if (p == 0)
|
|
SYSCALL_FAIL(_vm, "vm_alloc: alloc(%zu) failed", (size_t)sz);
|
|
return p;
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_free(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_free", 1);
|
|
spl_free_ex(VM_ASP(_vm), (void *)(uintptr_t)args[0]);
|
|
return 0;
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_realloc(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
uintptr_t sz;
|
|
spl_vm_val_t np;
|
|
CHECK_NARGS("vm_realloc", 2);
|
|
sz = (uintptr_t)args[1];
|
|
if (args[0] == 0)
|
|
SYSCALL_FAIL(_vm, "vm_realloc: NULL ptr");
|
|
if (sz == 0)
|
|
SYSCALL_FAIL(_vm, "vm_realloc: zero size");
|
|
np = (spl_vm_val_t)(uintptr_t)spl_realloc_ex(VM_ASP(_vm), (void *)(uintptr_t)args[0], sz);
|
|
if (np == 0)
|
|
SYSCALL_FAIL(_vm, "vm_realloc: realloc failed");
|
|
return np;
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_memcpy(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
void *dst, *src;
|
|
uintptr_t n;
|
|
CHECK_NARGS("vm_memcpy", 3);
|
|
n = (uintptr_t)args[2];
|
|
if (n == 0)
|
|
return args[0];
|
|
dst = VM_X(_vm, args[0]);
|
|
src = VM_X(_vm, args[1]);
|
|
if (!dst || !src)
|
|
SYSCALL_FAIL(_vm, "vm_memcpy: invalid address");
|
|
vm_memcpy(dst, src, n);
|
|
return args[0];
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_memmove(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
void *dst, *src;
|
|
uintptr_t n;
|
|
CHECK_NARGS("vm_memmove", 3);
|
|
n = (uintptr_t)args[2];
|
|
if (n == 0)
|
|
return args[0];
|
|
dst = VM_X(_vm, args[0]);
|
|
src = VM_X(_vm, args[1]);
|
|
if (!dst || !src)
|
|
SYSCALL_FAIL(_vm, "vm_memmove: invalid address");
|
|
vm_memmove(dst, src, n);
|
|
return args[0];
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_memcmp(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
const void *a, *b;
|
|
uintptr_t n;
|
|
CHECK_NARGS("vm_memcmp", 3);
|
|
a = VM_X(_vm, args[0]);
|
|
b = VM_X(_vm, args[1]);
|
|
n = (uintptr_t)args[2];
|
|
if (n && (!a || !b))
|
|
SYSCALL_FAIL(_vm, "vm_memcmp: invalid address");
|
|
return (spl_vm_val_t)(uintptr_t)vm_memcmp(a, b, n);
|
|
}
|
|
|
|
/* =================================================================
|
|
* Vec syscalls(参数按 VM 偏移解释)
|
|
* ================================================================= */
|
|
|
|
#define VM_VEC_BYTES ((uintptr_t)sizeof(spl_vec_t))
|
|
#define VM_MAP_BYTES ((uintptr_t)sizeof(spl_map_t))
|
|
|
|
static spl_vm_val_t spl_sys_vm_vec_valid(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_vec_valid", 1);
|
|
return (spl_vm_val_t)(uintptr_t)spl_vec_valid_ex(VM_XN(_vm, args[0], VM_VEC_BYTES));
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_vec_push_raw(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_vec_push_raw", 2);
|
|
return (spl_vm_val_t)(uintptr_t)spl_vec_push_raw_ex(
|
|
VM_ASP(_vm), VM_XN(_vm, args[0], VM_VEC_BYTES), VM_X(_vm, args[1]));
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_vec_push(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_vec_push", 2);
|
|
return (spl_vm_val_t)(uintptr_t)spl_vec_push_ex(
|
|
VM_ASP(_vm), VM_XN(_vm, args[0], VM_VEC_BYTES), VM_X(_vm, args[1]));
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_vec_get(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_vec_get", 3);
|
|
return (spl_vm_val_t)(uintptr_t)spl_vec_get_ex(
|
|
VM_ASP(_vm), VM_XN(_vm, args[0], VM_VEC_BYTES), (uintptr_t)args[1],
|
|
(void **)VM_XN(_vm, args[2], sizeof(void *)));
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_vec_allocate(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_vec_allocate", 4);
|
|
return (spl_vm_val_t)(uintptr_t)spl_vec_allocate_out_ex(
|
|
VM_ASP(_vm), VM_XN(_vm, args[0], VM_VEC_BYTES), (uintptr_t)args[1], (uintptr_t)args[2],
|
|
(uintptr_t)args[3]);
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_vec_reserve_raw(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_vec_reserve_raw", 2);
|
|
return (spl_vm_val_t)(uintptr_t)spl_vec_reserve_raw_void_ex(
|
|
VM_ASP(_vm), VM_XN(_vm, args[0], VM_VEC_BYTES), (uintptr_t)args[1]);
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_vec_append(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_vec_append", 3);
|
|
return (spl_vm_val_t)(uintptr_t)spl_vec_append_ex(
|
|
VM_ASP(_vm), VM_XN(_vm, args[0], VM_VEC_BYTES), VM_X(_vm, args[1]), (uintptr_t)args[2]);
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_vec_set(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_vec_set", 3);
|
|
return (spl_vm_val_t)(uintptr_t)spl_vec_set_ex(
|
|
VM_ASP(_vm), VM_XN(_vm, args[0], VM_VEC_BYTES), (uintptr_t)args[1], VM_X(_vm, args[2]));
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_vec_pop(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_vec_pop", 2);
|
|
return (spl_vm_val_t)(uintptr_t)spl_vec_pop_ex(VM_ASP(_vm),
|
|
VM_XN(_vm, args[0], VM_VEC_BYTES),
|
|
VM_X(_vm, args[1]));
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_vec_clone(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_vec_clone", 2);
|
|
return (spl_vm_val_t)(uintptr_t)spl_vec_clone_ex(
|
|
VM_ASP(_vm), VM_XN(_vm, args[0], VM_VEC_BYTES), VM_XN(_vm, args[1], VM_VEC_BYTES));
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_vec_drop(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_vec_drop", 1);
|
|
return (spl_vm_val_t)(uintptr_t)spl_vec_drop_ex(VM_ASP(_vm),
|
|
VM_XN(_vm, args[0], VM_VEC_BYTES));
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_bytes_eq(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_bytes_eq", 4);
|
|
return (spl_vm_val_t)(uintptr_t)spl_bytes_eq_ex(VM_ASP(_vm), VM_X(_vm, args[0]),
|
|
(uintptr_t)args[1], VM_X(_vm, args[2]),
|
|
(uintptr_t)args[3]);
|
|
}
|
|
|
|
/* =================================================================
|
|
* Map syscalls
|
|
* ================================================================= */
|
|
|
|
static spl_vm_val_t spl_sys_vm_map_valid(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_map_valid", 1);
|
|
return (spl_vm_val_t)(uintptr_t)spl_map_valid_ex(VM_XN(_vm, args[0], VM_MAP_BYTES));
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_map_reserve(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_map_reserve", 2);
|
|
return (spl_vm_val_t)(uintptr_t)spl_map_reserve_void_ex(
|
|
VM_ASP(_vm), VM_XN(_vm, args[0], VM_MAP_BYTES), (uintptr_t)args[1]);
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_map_put(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_map_put", 3);
|
|
return (spl_vm_val_t)(uintptr_t)spl_map_put_ex(
|
|
VM_ASP(_vm), VM_XN(_vm, args[0], VM_MAP_BYTES), VM_X(_vm, args[1]), VM_X(_vm, args[2]));
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_map_get(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_map_get", 3);
|
|
return (spl_vm_val_t)(uintptr_t)spl_map_get_ex(
|
|
VM_ASP(_vm), VM_XN(_vm, args[0], VM_MAP_BYTES), VM_X(_vm, args[1]), VM_X(_vm, args[2]));
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_map_del(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_map_del", 2);
|
|
return (spl_vm_val_t)(uintptr_t)spl_map_del_ex(
|
|
VM_ASP(_vm), VM_XN(_vm, args[0], VM_MAP_BYTES), VM_X(_vm, args[1]));
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_map_clear(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_map_clear", 1);
|
|
return (spl_vm_val_t)(uintptr_t)spl_map_clear_ex(VM_ASP(_vm),
|
|
VM_XN(_vm, args[0], VM_MAP_BYTES));
|
|
}
|
|
|
|
static spl_vm_val_t spl_sys_vm_map_drop(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_map_drop", 1);
|
|
return (spl_vm_val_t)(uintptr_t)spl_map_drop_ex(VM_ASP(_vm),
|
|
VM_XN(_vm, args[0], VM_MAP_BYTES));
|
|
}
|
|
|
|
/* =================================================================
|
|
* printf / timestamp
|
|
* ================================================================= */
|
|
|
|
static spl_vm_val_t vm_printf(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
if (nargs <= 0)
|
|
return 0;
|
|
|
|
const spl_vm_val_t *fmt_slice =
|
|
(const spl_vm_val_t *)VM_XN(_vm, args[0], 2 * sizeof(spl_vm_val_t));
|
|
const char *fmt;
|
|
usize fmt_len;
|
|
if (!fmt_slice)
|
|
return 0;
|
|
fmt = (const char *)VM_X(_vm, fmt_slice[0]);
|
|
fmt_len = (usize)fmt_slice[1];
|
|
if (!fmt && fmt_len)
|
|
return 0;
|
|
|
|
if (nargs <= 1) {
|
|
fwrite(fmt, 1, fmt_len, stdout);
|
|
return (spl_vm_val_t)nargs;
|
|
}
|
|
|
|
typedef VEC(char) string_builder_t;
|
|
string_builder_t buffer;
|
|
vec_init(buffer);
|
|
|
|
int arg_idx = 1;
|
|
char tmp_buf[32];
|
|
|
|
for (usize i = 0; i < fmt_len; ++i) {
|
|
if (fmt[i] != '%') {
|
|
vec_push(buffer, fmt[i]);
|
|
continue;
|
|
}
|
|
if (++i >= fmt_len)
|
|
break;
|
|
if (arg_idx >= nargs)
|
|
break;
|
|
|
|
switch (fmt[i]) {
|
|
case 'd': {
|
|
memset(tmp_buf, 0, sizeof(tmp_buf));
|
|
snprintf(tmp_buf, sizeof(tmp_buf), "%zd", (isize)args[arg_idx]);
|
|
for (usize j = 0; tmp_buf[j] != '\0'; ++j)
|
|
vec_push(buffer, tmp_buf[j]);
|
|
arg_idx++;
|
|
break;
|
|
}
|
|
case 'c':
|
|
vec_push(buffer, (char)args[arg_idx]);
|
|
arg_idx++;
|
|
break;
|
|
case 's': {
|
|
const spl_vm_val_t *s =
|
|
(const spl_vm_val_t *)VM_XN(_vm, args[arg_idx], 2 * sizeof(spl_vm_val_t));
|
|
const char *p = s ? (const char *)VM_X(_vm, s[0]) : NULL;
|
|
usize l = s ? (usize)s[1] : 0;
|
|
if (p)
|
|
for (usize j = 0; j < l; ++j)
|
|
vec_push(buffer, p[j]);
|
|
arg_idx++;
|
|
break;
|
|
}
|
|
default:
|
|
vec_push(buffer, '%');
|
|
vec_push(buffer, fmt[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (vec_size(buffer) > 0)
|
|
fwrite(buffer.data, 1, vec_size(buffer), stdout);
|
|
vec_free(buffer);
|
|
return (spl_vm_val_t)nargs;
|
|
}
|
|
|
|
static spl_vm_val_t vm_timestamp(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_timestamp", 1);
|
|
spl_vm_val_t arg = args[0];
|
|
struct timespec ts;
|
|
int has_time = (timespec_get(&ts, TIME_UTC) == TIME_UTC);
|
|
switch (arg) {
|
|
case 0:
|
|
return has_time ? 1 : 0;
|
|
case 1:
|
|
return has_time ? (spl_vm_val_t)ts.tv_sec : 0;
|
|
case 2:
|
|
return has_time ? (spl_vm_val_t)ts.tv_nsec : 0;
|
|
case 3:
|
|
if (!_vm)
|
|
return 0;
|
|
return (spl_vm_val_t)_vm->steps;
|
|
case 5:
|
|
if (!_vm)
|
|
return 0;
|
|
return (spl_vm_val_t)_vm->ncalls;
|
|
case 4:
|
|
return has_time ? (spl_vm_val_t)((unsigned long long)ts.tv_sec * 1000000000ull +
|
|
(unsigned long long)ts.tv_nsec)
|
|
: 0;
|
|
default:
|
|
fprintf(stderr, "vm_timestamp: invalid arg %zu\n", (size_t)arg);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* =================================================================
|
|
* nested-VM syscalls
|
|
* ================================================================= */
|
|
|
|
/* 通用双向桥(配置层策略,非 VM 平台 API):
|
|
* 在子 VM 上安装「未解析 native 处理器」,把子 VM 的未解析调用按名转发到
|
|
* 宿主(调用者)VM 中由 `entry` 指定的函数。宿主函数收到:
|
|
* (ctx, name_off, name_len, nested_vm, arg_base, nargs)
|
|
* 平台/C 侧**纯转发**:不复制实参、不限元数、不了解类型;聚合读写由宿主用
|
|
* vm_stack_get / vm_mem_read / vm_mem_alloc / vm_mem_write 自行完成。
|
|
* `entry` 由宿主显式给出,平台不预设任何函数名。 */
|
|
typedef struct {
|
|
spl_vm_t *host;
|
|
void *ctx;
|
|
char entry[128];
|
|
} spl_bridge_t;
|
|
|
|
static spl_vm_val_t spl_bridge_forward(spl_vm_t *nested, const spl_vm_call_t *call, void *user) {
|
|
spl_bridge_t *b = (spl_bridge_t *)user;
|
|
spl_vm_val_t name_off;
|
|
spl_vm_val_t args[6];
|
|
spl_vm_call_t back;
|
|
usize n;
|
|
usize arg_base;
|
|
if (!b || !b->host || !call || !call->name || b->entry[0] == '\0')
|
|
return 0;
|
|
n = strlen(call->name);
|
|
/* 名拷进宿主 VM 内存,宿主按偏移读取。 */
|
|
name_off = spl_vm_mem_alloc(b->host, n + 1);
|
|
if (!name_off)
|
|
return 0;
|
|
if (spl_vm_mem_write(b->host, name_off, call->name, n + 1) != 0)
|
|
return 0;
|
|
arg_base = spl_vm_call_arg_base(nested, call);
|
|
if (arg_base == (usize)-1)
|
|
return 0;
|
|
args[0] = (spl_vm_val_t)(uintptr_t)b->ctx;
|
|
args[1] = name_off;
|
|
args[2] = (spl_vm_val_t)n;
|
|
args[3] = (spl_vm_val_t)(uintptr_t)nested;
|
|
args[4] = (spl_vm_val_t)arg_base;
|
|
args[5] = (spl_vm_val_t)call->nargs;
|
|
back.name = b->entry;
|
|
back.args = args;
|
|
back.nargs = 6;
|
|
back.ret = 0;
|
|
if (spl_vm_invoke(b->host, &back) != 0)
|
|
return 0;
|
|
return back.ret;
|
|
}
|
|
|
|
static spl_vm_val_t vm_new(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_new", 0);
|
|
(void)args;
|
|
(void)_vm;
|
|
spl_vm_t *vm = (spl_vm_t *)malloc(sizeof(spl_vm_t));
|
|
if (!vm)
|
|
return 0;
|
|
spl_vm_init(vm);
|
|
return (spl_vm_val_t)(uintptr_t)vm;
|
|
}
|
|
|
|
/* vm_set_foreign(vm, entry, ctx):宿主显式指定处理器函数名。 */
|
|
static spl_vm_val_t sc_vm_set_foreign(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
spl_vm_t *vm;
|
|
spl_bridge_t *b;
|
|
const spl_vm_val_t *entry;
|
|
const char *eptr;
|
|
usize elen;
|
|
CHECK_NARGS("vm_set_foreign", 3);
|
|
vm = (spl_vm_t *)(uintptr_t)args[0];
|
|
if (!vm || !_vm)
|
|
return -1;
|
|
entry = (const spl_vm_val_t *)VM_XN(_vm, args[1], 2 * sizeof(spl_vm_val_t));
|
|
if (!entry)
|
|
return -1;
|
|
eptr = (const char *)VM_X(_vm, entry[0]);
|
|
elen = (usize)entry[1];
|
|
if (!eptr || elen == 0 || elen >= sizeof(((spl_bridge_t *)0)->entry))
|
|
return -1;
|
|
b = (spl_bridge_t *)malloc(sizeof(*b));
|
|
if (!b)
|
|
return -1;
|
|
b->host = _vm;
|
|
b->ctx = (void *)(uintptr_t)args[2];
|
|
memcpy(b->entry, eptr, elen);
|
|
b->entry[elen] = '\0';
|
|
spl_vm_set_foreign(vm, spl_bridge_forward, b);
|
|
return 0;
|
|
}
|
|
|
|
static spl_vm_val_t vm_drop(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
(void)_vm;
|
|
CHECK_NARGS("vm_drop", 1);
|
|
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
|
if (!vm)
|
|
return 0;
|
|
if (vm->foreign == spl_bridge_forward && vm->foreign_ctx)
|
|
free(vm->foreign_ctx);
|
|
if (vm->prog) {
|
|
spl_prog_drop(vm->prog);
|
|
free(vm->prog);
|
|
vm->prog = NULL;
|
|
}
|
|
spl_vm_drop(vm);
|
|
free(vm);
|
|
return 0;
|
|
}
|
|
|
|
static spl_vm_val_t vm_load(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_load", 2);
|
|
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
|
const char *path = (const char *)VM_X(_vm, args[1]);
|
|
if (!vm || !path)
|
|
return -1;
|
|
|
|
spl_prog_t *prog = (spl_prog_t *)malloc(sizeof(spl_prog_t));
|
|
if (!prog)
|
|
return -1;
|
|
if (spl_prog_load_from_file(path, prog) != 0) {
|
|
free(prog);
|
|
return -1;
|
|
}
|
|
spl_syscall_register(prog);
|
|
if (spl_vm_load_prog(vm, prog) != 0) {
|
|
spl_prog_drop(prog);
|
|
free(prog);
|
|
return -1;
|
|
}
|
|
return (spl_vm_val_t)(uintptr_t)prog;
|
|
}
|
|
|
|
static spl_vm_val_t vm_load_from_buffer(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_load_from_buffer", 3);
|
|
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
|
const char *buf = (const char *)VM_XN(_vm, args[1], (usize)args[2]);
|
|
usize len = (usize)args[2];
|
|
spl_prog_t *prog;
|
|
if (!vm || !buf)
|
|
return -1;
|
|
prog = (spl_prog_t *)malloc(sizeof(spl_prog_t));
|
|
if (!prog)
|
|
return -1;
|
|
if (spl_prog_load_from_buffer(buf, len, prog) != 0) {
|
|
free(prog);
|
|
return -1;
|
|
}
|
|
/* 不自动注入任何 native/syscall:解析策略由使用者配置。 */
|
|
if (spl_vm_load_prog(vm, prog) != 0) {
|
|
spl_prog_drop(prog);
|
|
free(prog);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static spl_vm_val_t vm_push(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
(void)_vm;
|
|
CHECK_NARGS("vm_push", 2);
|
|
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
|
spl_vm_val_t val = args[1];
|
|
if (!vm)
|
|
return -1;
|
|
if (vm->sp >= vm->config.max_stack_depth) {
|
|
fprintf(stderr, "vm_push: stack overflow\n");
|
|
return -1;
|
|
}
|
|
vm->stacks.data[vm->sp++] = val;
|
|
return 0;
|
|
}
|
|
|
|
static spl_vm_val_t vm_call(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
CHECK_NARGS("vm_call", 3);
|
|
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
|
const char *name = (const char *)VM_X(_vm, args[1]);
|
|
spl_vm_val_t call_nargs = args[2];
|
|
if (!vm || !name)
|
|
return -1;
|
|
|
|
if (spl_vm_call_entry(vm, name, (int)call_nargs) != 0) {
|
|
fprintf(stderr, "vm_call: function '%s' not found or bad ABI\n", name);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static spl_vm_val_t vm_run(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
(void)_vm;
|
|
CHECK_NARGS("vm_run", 1);
|
|
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
|
if (!vm)
|
|
return -1;
|
|
spl_vm_run_until(vm, 0);
|
|
return (spl_vm_val_t)vm->ret_val;
|
|
}
|
|
|
|
/* ---- 栈 / 结果 / 内存 访问(不暴露宿主指针) ---- */
|
|
|
|
static spl_vm_val_t sc_vm_ret_val(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
(void)_vm;
|
|
CHECK_NARGS("vm_ret_val", 1);
|
|
return spl_vm_ret_val((spl_vm_t *)(uintptr_t)args[0]);
|
|
}
|
|
|
|
static spl_vm_val_t sc_vm_sp(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
(void)_vm;
|
|
CHECK_NARGS("vm_sp", 1);
|
|
return (spl_vm_val_t)spl_vm_sp((spl_vm_t *)(uintptr_t)args[0]);
|
|
}
|
|
|
|
static spl_vm_val_t sc_vm_fp(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
(void)_vm;
|
|
CHECK_NARGS("vm_fp", 1);
|
|
return (spl_vm_val_t)spl_vm_fp((spl_vm_t *)(uintptr_t)args[0]);
|
|
}
|
|
|
|
static spl_vm_val_t sc_vm_stack_get(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
(void)_vm;
|
|
CHECK_NARGS("vm_stack_get", 2);
|
|
return spl_vm_stack_get((spl_vm_t *)(uintptr_t)args[0], (usize)args[1]);
|
|
}
|
|
|
|
static spl_vm_val_t sc_vm_stack_set(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
(void)_vm;
|
|
CHECK_NARGS("vm_stack_set", 3);
|
|
return (spl_vm_val_t)spl_vm_stack_set((spl_vm_t *)(uintptr_t)args[0], (usize)args[1], args[2]);
|
|
}
|
|
|
|
static spl_vm_val_t sc_vm_mem_alloc(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
(void)_vm;
|
|
CHECK_NARGS("vm_mem_alloc", 2);
|
|
return spl_vm_mem_alloc((spl_vm_t *)(uintptr_t)args[0], (usize)args[1]);
|
|
}
|
|
|
|
static spl_vm_val_t sc_vm_mem_read(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
spl_vm_t *vm;
|
|
void *dst;
|
|
usize n;
|
|
CHECK_NARGS("vm_mem_read", 4);
|
|
vm = (spl_vm_t *)(uintptr_t)args[0];
|
|
n = (usize)args[3];
|
|
dst = VM_XN(_vm, args[2], n);
|
|
if ((!dst) && n)
|
|
return -1;
|
|
return (spl_vm_val_t)spl_vm_mem_read(vm, (spl_vm_val_t)args[1], dst, n);
|
|
}
|
|
|
|
static spl_vm_val_t sc_vm_mem_write(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
spl_vm_t *vm;
|
|
void *src;
|
|
usize n;
|
|
CHECK_NARGS("vm_mem_write", 4);
|
|
vm = (spl_vm_t *)(uintptr_t)args[0];
|
|
n = (usize)args[3];
|
|
src = VM_XN(_vm, args[2], n);
|
|
if ((!src) && n)
|
|
return -1;
|
|
return (spl_vm_val_t)spl_vm_mem_write(vm, (spl_vm_val_t)args[1], src, n);
|
|
}
|
|
|
|
/* =================================================================
|
|
* Registration
|
|
* ================================================================= */
|
|
|
|
void spl_syscall_register(spl_prog_t *prog) {
|
|
static spl_vm_native_t table[] = {
|
|
{"vm_exit", 0, vm_exit},
|
|
{"vm_putchar", 0, vm_putchar},
|
|
{"vm_getchar", 0, vm_getchar},
|
|
{"vm_putint", 0, vm_putint},
|
|
{"vm_putstr", 0, vm_putstr},
|
|
{"vm_fopen", 0, vm_fopen},
|
|
{"vm_fclose", 0, vm_fclose},
|
|
{"vm_fread", 0, vm_fread},
|
|
{"vm_fwrite", 0, vm_fwrite},
|
|
{"vm_fsize", 0, vm_fsize},
|
|
{"vm_stdin", 0, vm_stdin},
|
|
{"vm_stdout", 0, vm_stdout},
|
|
{"vm_stderr", 0, vm_stderr},
|
|
{"vm_read_file", 0, vm_read_file},
|
|
|
|
{"vm_alloc", 0, spl_sys_vm_alloc},
|
|
{"vm_free", 0, spl_sys_vm_free},
|
|
{"vm_realloc", 0, spl_sys_vm_realloc},
|
|
{"vm_memcpy", 0, spl_sys_vm_memcpy},
|
|
{"vm_memmove", 0, spl_sys_vm_memmove},
|
|
{"vm_memcmp", 0, spl_sys_vm_memcmp},
|
|
{"vm_printf", 0, vm_printf},
|
|
|
|
{"vm_vec_valid", 0, spl_sys_vm_vec_valid},
|
|
{"vm_vec_push_raw", 0, spl_sys_vm_vec_push_raw},
|
|
{"vm_vec_push", 0, spl_sys_vm_vec_push},
|
|
{"vm_vec_get", 0, spl_sys_vm_vec_get},
|
|
{"vm_vec_allocate", 0, spl_sys_vm_vec_allocate},
|
|
{"vm_vec_reserve_raw", 0, spl_sys_vm_vec_reserve_raw},
|
|
{"vm_vec_append", 0, spl_sys_vm_vec_append},
|
|
{"vm_vec_set", 0, spl_sys_vm_vec_set},
|
|
{"vm_vec_pop", 0, spl_sys_vm_vec_pop},
|
|
{"vm_vec_clone", 0, spl_sys_vm_vec_clone},
|
|
{"vm_vec_drop", 0, spl_sys_vm_vec_drop},
|
|
{"vm_bytes_eq", 0, spl_sys_vm_bytes_eq},
|
|
|
|
{"vm_map_valid", 0, spl_sys_vm_map_valid},
|
|
{"vm_map_reserve", 0, spl_sys_vm_map_reserve},
|
|
{"vm_map_put", 0, spl_sys_vm_map_put},
|
|
{"vm_map_get", 0, spl_sys_vm_map_get},
|
|
{"vm_map_del", 0, spl_sys_vm_map_del},
|
|
{"vm_map_clear", 0, spl_sys_vm_map_clear},
|
|
{"vm_map_drop", 0, spl_sys_vm_map_drop},
|
|
|
|
{"vm_timestamp", 0, vm_timestamp},
|
|
|
|
{"vm_new", 0, vm_new},
|
|
{"vm_set_foreign", 0, sc_vm_set_foreign},
|
|
{"vm_drop", 0, vm_drop},
|
|
{"vm_load", 0, vm_load},
|
|
{"vm_load_from_buffer", 0, vm_load_from_buffer},
|
|
{"vm_push", 0, vm_push},
|
|
{"vm_call", 0, vm_call},
|
|
{"vm_run", 0, vm_run},
|
|
|
|
{"vm_ret_val", 0, sc_vm_ret_val},
|
|
{"vm_sp", 0, sc_vm_sp},
|
|
{"vm_fp", 0, sc_vm_fp},
|
|
{"vm_stack_get", 0, sc_vm_stack_get},
|
|
{"vm_stack_set", 0, sc_vm_stack_set},
|
|
{"vm_mem_alloc", 0, sc_vm_mem_alloc},
|
|
{"vm_mem_read", 0, sc_vm_mem_read},
|
|
{"vm_mem_write", 0, sc_vm_mem_write},
|
|
};
|
|
int n = (int)(sizeof(table) / sizeof(table[0]));
|
|
if (!prog)
|
|
return;
|
|
vec_for(prog->natives, i) {
|
|
spl_vm_native_t *nat = &vec_at(prog->natives, i);
|
|
if (!nat->name || nat->impl_fn)
|
|
continue;
|
|
for (int j = 0; j < n; j++) {
|
|
if (strcmp(nat->name, table[j].name) == 0) {
|
|
nat->impl_fn = table[j].impl_fn;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|