From 0d6685abcdea858a3f55204b0721581d0351e5c9 Mon Sep 17 00:00:00 2001 From: hanez Date: Tue, 26 May 2026 04:43:11 +0200 Subject: [PATCH] Some internal optimzations and helpers. (0.41.14) --- CMakeLists.txt | 2 +- src/map.c | 36 ++++++++++++++++++++++++++++++++++++ src/value.h | 2 ++ src/vm.c | 23 +++++++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c0c319..b1f0d65 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(fun VERSION 0.41.13 LANGUAGES C) +project(fun VERSION 0.41.14 LANGUAGES C) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) diff --git a/src/map.c b/src/map.c index 772ea08..b85d399 100644 --- a/src/map.c +++ b/src/map.c @@ -99,6 +99,42 @@ int map_set(Value *vm, const char *key, Value v) { return 1; } +/** + * @brief Insert or replace a key in the map with a copy of the provided value. + * + * Unlike map_set(), this function does NOT take ownership of @p v. The value is + * copied according to copy_value() semantics, and the caller retains ownership + * of the original. + * + * @param vm Target Value of type VAL_MAP. + * @param key NUL-terminated key string (copied into the map). + * @param v Pointer to Value to copy from; not consumed. + * @return 1 on success, 0 on error (type mismatch, OOM, or NULL params). + */ +int map_set_copy(Value *vm, const char *key, const Value *v) { + if (!vm || vm->type != VAL_MAP || !vm->map || !key || !v) { + return 0; + } + /* make a defensive copy first so we can reuse map_set path semantics */ + Value cv = copy_value(v); + Map *m = (Map *)vm->map; + for (int i = 0; i < m->count; ++i) { + if (strcmp(m->keys[i], key) == 0) { + free_value(m->vals[i]); + m->vals[i] = cv; + return 1; + } + } + if (!map_ensure_cap(m, m->count + 1)) { + free_value(cv); + return 0; + } + m->keys[m->count] = strdup(key); + m->vals[m->count] = cv; + m->count++; + return 1; +} + /** * @brief Look up a key and copy the stored value into out. * diff --git a/src/value.h b/src/value.h index 0381801..8c614d9 100644 --- a/src/value.h +++ b/src/value.h @@ -118,6 +118,8 @@ Value array_concat(const Value *a, const Value *b); Value make_map_empty(void); /** Set key to v (takes ownership); returns 1 on success. */ int map_set(Value *m, const char *key, Value v); +/** Set key to a copy of v (caller retains ownership); returns 1 on success. */ +int map_set_copy(Value *m, const char *key, const Value *v); /** Lookup key; returns 1 and copies value to out on success. */ int map_get_copy(const Value *m, const char *key, Value *out); /** Test if key exists; returns 1/0. */ diff --git a/src/vm.c b/src/vm.c index a74e039..073feb0 100644 --- a/src/vm.c +++ b/src/vm.c @@ -546,6 +546,22 @@ void vm_debug_request_continue(VM *vm) { vm->debug_stop_requested = 0; } +/* --- Centralized operand stack safety helpers --- */ +/** Return current number of values on the stack. */ +static inline int vm_stack_count(const VM *vm) { return vm->sp + 1; } + +/** Return available space left on the stack (in Values). */ +static inline int vm_stack_space(const VM *vm) { return STACK_SIZE - (vm->sp + 1); } + +/** Ensure at least n values are available to pop; aborts on underflow. */ +static inline void vm_require_stack(VM *vm, int n) { + if (n < 0) n = 0; + if (vm_stack_count(vm) < n) { + fprintf(stderr, "Runtime error: stack underflow (need %d, have %d)\n", n, vm_stack_count(vm)); + exit(1); + } +} + /** * @brief Push a Value onto the VM operand stack. * @@ -868,6 +884,13 @@ void vm_run(VM *vm, Bytecode *entry) { Instruction inst = f->fn->instructions[f->ip++]; vm->instr_count++; /* count each executed instruction */ + /* Validate opcode defensively */ + if (!opcode_is_valid(inst.op)) { + fprintf(stderr, "Runtime error: invalid opcode %d at ip=%d\n", inst.op, f->ip - 1); + vm_raise_error(vm, "Invalid opcode"); + break; + } + if (vm->trace_enabled) { const char *opname = (inst.op >= 0 && inst.op < (int)(sizeof(opcode_names) / sizeof(opcode_names[0]))) ? opcode_names[inst.op]