diff --git a/runtime/include/hemlock_runtime.h b/runtime/include/hemlock_runtime.h index 83bcc083..670a36d8 100644 --- a/runtime/include/hemlock_runtime.h +++ b/runtime/include/hemlock_runtime.h @@ -493,6 +493,7 @@ typedef struct HmlExceptionContext { HmlExceptionContext* hml_exception_push(void); void hml_exception_pop(void); __attribute__((noreturn)) void hml_throw(HmlValue exception_value); +__attribute__((noreturn)) void hml_throw_take(HmlValue exception_value); HmlValue hml_exception_get_value(void); // Runtime error helper - throws catchable exception with formatted message diff --git a/runtime/src/builtins_async.c b/runtime/src/builtins_async.c index 4f76779d..0fe52e9d 100644 --- a/runtime/src/builtins_async.c +++ b/runtime/src/builtins_async.c @@ -403,7 +403,7 @@ HmlValue hml_join(HmlValue task_val) { // Re-throw task exception in the joining thread (parity with interpreter) if (had_exception) { hml_release(&result); - hml_throw(exception); + hml_throw_take(exception); } return result; diff --git a/runtime/src/builtins_core.c b/runtime/src/builtins_core.c index a7fcfa7c..76adb376 100644 --- a/runtime/src/builtins_core.c +++ b/runtime/src/builtins_core.c @@ -169,6 +169,7 @@ HmlValue hml_get_args(void) { for (int i = 0; i < g_argc; i++) { HmlValue str = hml_val_string(g_argv[i]); hml_array_push(arr, str); + hml_release(&str); // array_push retained its own reference } return arr; @@ -821,13 +822,11 @@ HmlValue hml_parse_string_to_type(HmlValue val, HmlValueType target_type) { void hml_assert(HmlValue condition, HmlValue message) { if (!hml_to_bool(condition)) { // Throw catchable exception (match interpreter behavior) - HmlValue exception_msg; if (message.type == HML_VAL_STRING && message.as.as_string) { - exception_msg = message; + hml_throw(message); // borrowed argument } else { - exception_msg = hml_val_string("assertion failed"); + hml_throw_take(hml_val_string("assertion failed")); } - hml_throw(exception_msg); } } diff --git a/runtime/src/builtins_func.c b/runtime/src/builtins_func.c index 0954f8a2..313f3484 100644 --- a/runtime/src/builtins_func.c +++ b/runtime/src/builtins_func.c @@ -30,20 +30,26 @@ void hml_exception_pop(void) { } } -void hml_throw(HmlValue exception_value) { +void hml_throw_take(HmlValue exception_value) { if (!g_exception_stack || !g_exception_stack->is_active) { // Uncaught exception fprintf(stderr, "Uncaught exception: "); print_value_to(stderr, exception_value); fprintf(stderr, "\n"); + hml_release(&exception_value); exit(1); } + hml_release(&g_exception_stack->exception_value); g_exception_stack->exception_value = exception_value; - hml_retain(&g_exception_stack->exception_value); longjmp(g_exception_stack->exception_buf, 1); } +void hml_throw(HmlValue exception_value) { + hml_retain(&exception_value); + hml_throw_take(exception_value); +} + HmlValue hml_exception_get_value(void) { if (g_exception_stack) { HmlValue v = g_exception_stack->exception_value; @@ -62,7 +68,7 @@ void hml_runtime_error(const char *format, ...) { va_end(args); HmlValue error_msg = hml_val_string(buffer); - hml_throw(error_msg); + hml_throw_take(error_msg); } // ========== DEFER SUPPORT ========== diff --git a/runtime/src/builtins_io.c b/runtime/src/builtins_io.c index 9d8a20a3..0b202cbe 100644 --- a/runtime/src/builtins_io.c +++ b/runtime/src/builtins_io.c @@ -39,7 +39,7 @@ HmlValue hml_read_line(void) { HmlValue hml_open(HmlValue path, HmlValue mode) { if (path.type != HML_VAL_STRING) { - hml_throw(hml_val_string("open() expects string path")); + hml_throw_take(hml_val_string("open() expects string path")); } const char *path_str = path.as.as_string->data; @@ -66,7 +66,7 @@ HmlValue hml_open(HmlValue path, HmlValue mode) { char err_buf[512]; snprintf(err_buf, sizeof(err_buf), "Failed to open '%s' with mode '%s': %s", path_str, mode_str, strerror(errno)); - hml_throw(hml_val_string(err_buf)); + hml_throw_take(hml_val_string(err_buf)); } HmlFileHandle *fh = malloc(sizeof(HmlFileHandle)); @@ -99,7 +99,7 @@ static int hml_mode_to_open_flags(const char *mode) { // open_fd(path, mode?) -> i32: open a file and return its raw POSIX fd. HmlValue hml_open_fd(HmlValue path, HmlValue mode) { if (path.type != HML_VAL_STRING) { - hml_throw(hml_val_string("open_fd() expects string path")); + hml_throw_take(hml_val_string("open_fd() expects string path")); } const char *path_str = path.as.as_string->data; @@ -113,7 +113,7 @@ HmlValue hml_open_fd(HmlValue path, HmlValue mode) { char err_buf[256]; snprintf(err_buf, sizeof(err_buf), "open_fd() unsupported mode '%s' (use r, w, a, r+, w+, a+)", mode_str); - hml_throw(hml_val_string(err_buf)); + hml_throw_take(hml_val_string(err_buf)); } int is_write = (strchr(mode_str, 'w') != NULL || strchr(mode_str, 'a') != NULL || @@ -131,7 +131,7 @@ HmlValue hml_open_fd(HmlValue path, HmlValue mode) { char err_buf[512]; snprintf(err_buf, sizeof(err_buf), "Failed to open '%s' with mode '%s': %s", path_str, mode_str, strerror(errno)); - hml_throw(hml_val_string(err_buf)); + hml_throw_take(hml_val_string(err_buf)); } return hml_val_i32(fd); @@ -141,7 +141,7 @@ HmlValue hml_open_fd(HmlValue path, HmlValue mode) { // file retains ownership of the fd; closing the file closes the fd. HmlValue hml_fileno(HmlValue file) { if (file.type != HML_VAL_FILE || !file.as.as_file) { - hml_throw(hml_val_string("fileno() expects file object")); + hml_throw_take(hml_val_string("fileno() expects file object")); } HmlFileHandle *fh = file.as.as_file; @@ -149,14 +149,14 @@ HmlValue hml_fileno(HmlValue file) { char err_buf[512]; snprintf(err_buf, sizeof(err_buf), "fileno() called on closed file '%s'", fh->path ? fh->path : ""); - hml_throw(hml_val_string(err_buf)); + hml_throw_take(hml_val_string(err_buf)); } int fd = fileno((FILE*)fh->fp); if (fd < 0) { char err_buf[256]; snprintf(err_buf, sizeof(err_buf), "fileno() failed: %s", strerror(errno)); - hml_throw(hml_val_string(err_buf)); + hml_throw_take(hml_val_string(err_buf)); } return hml_val_i32(fd); @@ -164,14 +164,14 @@ HmlValue hml_fileno(HmlValue file) { HmlValue hml_file_read(HmlValue file, HmlValue size) { if (file.type != HML_VAL_FILE) { - hml_throw(hml_val_string("read() expects file object")); + hml_throw_take(hml_val_string("read() expects file object")); } HmlFileHandle *fh = file.as.as_file; if (fh->closed) { char _err_buf[512]; snprintf(_err_buf, sizeof(_err_buf), "Cannot read from closed file '%s'", fh->path); - hml_throw(hml_val_string(_err_buf)); + hml_throw_take(hml_val_string(_err_buf)); } int32_t read_size = 0; @@ -195,14 +195,14 @@ HmlValue hml_file_read(HmlValue file, HmlValue size) { HmlValue hml_file_read_all(HmlValue file) { if (file.type != HML_VAL_FILE) { - hml_throw(hml_val_string("read() expects file object")); + hml_throw_take(hml_val_string("read() expects file object")); } HmlFileHandle *fh = file.as.as_file; if (fh->closed) { char _err_buf[512]; snprintf(_err_buf, sizeof(_err_buf), "Cannot read from closed file '%s'", fh->path); - hml_throw(hml_val_string(_err_buf)); + hml_throw_take(hml_val_string(_err_buf)); } FILE *fp = (FILE*)fh->fp; @@ -223,7 +223,7 @@ HmlValue hml_file_read_all(HmlValue file) { char *buffer = malloc(size + 1); if (!buffer) { - hml_throw(hml_val_string("Memory allocation failed")); + hml_throw_take(hml_val_string("Memory allocation failed")); } size_t bytes_read = fread(buffer, 1, size, fp); buffer[bytes_read] = '\0'; @@ -237,7 +237,7 @@ HmlValue hml_file_read_all(HmlValue file) { size_t total_read = 0; char *buffer = malloc(capacity); if (!buffer) { - hml_throw(hml_val_string("Memory allocation failed")); + hml_throw_take(hml_val_string("Memory allocation failed")); } while (1) { @@ -247,7 +247,7 @@ HmlValue hml_file_read_all(HmlValue file) { char *new_buffer = realloc(buffer, capacity); if (!new_buffer) { free(buffer); - hml_throw(hml_val_string("Memory allocation failed")); + hml_throw_take(hml_val_string("Memory allocation failed")); } buffer = new_buffer; } @@ -261,7 +261,7 @@ HmlValue hml_file_read_all(HmlValue file) { free(buffer); char _err_buf[512]; snprintf(_err_buf, sizeof(_err_buf), "Read error on file '%s'", fh->path); - hml_throw(hml_val_string(_err_buf)); + hml_throw_take(hml_val_string(_err_buf)); } break; // EOF reached } @@ -277,14 +277,14 @@ HmlValue hml_file_read_all(HmlValue file) { HmlValue hml_file_write(HmlValue file, HmlValue data) { if (file.type != HML_VAL_FILE) { - hml_throw(hml_val_string("write() expects file object")); + hml_throw_take(hml_val_string("write() expects file object")); } HmlFileHandle *fh = file.as.as_file; if (fh->closed) { char _err_buf[512]; snprintf(_err_buf, sizeof(_err_buf), "Cannot write to closed file '%s'", fh->path); - hml_throw(hml_val_string(_err_buf)); + hml_throw_take(hml_val_string(_err_buf)); } const char *str = ""; @@ -298,14 +298,14 @@ HmlValue hml_file_write(HmlValue file, HmlValue data) { HmlValue hml_file_seek(HmlValue file, HmlValue position) { if (file.type != HML_VAL_FILE) { - hml_throw(hml_val_string("seek() expects file object")); + hml_throw_take(hml_val_string("seek() expects file object")); } HmlFileHandle *fh = file.as.as_file; if (fh->closed) { char _err_buf[512]; snprintf(_err_buf, sizeof(_err_buf), "Cannot seek in closed file '%s'", fh->path); - hml_throw(hml_val_string(_err_buf)); + hml_throw_take(hml_val_string(_err_buf)); } long pos = 0; @@ -321,14 +321,14 @@ HmlValue hml_file_seek(HmlValue file, HmlValue position) { HmlValue hml_file_tell(HmlValue file) { if (file.type != HML_VAL_FILE) { - hml_throw(hml_val_string("tell() expects file object")); + hml_throw_take(hml_val_string("tell() expects file object")); } HmlFileHandle *fh = file.as.as_file; if (fh->closed) { char _err_buf[512]; snprintf(_err_buf, sizeof(_err_buf), "Cannot tell position in closed file '%s'", fh->path); - hml_throw(hml_val_string(_err_buf)); + hml_throw_take(hml_val_string(_err_buf)); } return hml_val_i32((int32_t)ftell((FILE*)fh->fp)); @@ -348,14 +348,14 @@ void hml_file_close(HmlValue file) { HmlValue hml_file_read_bytes(HmlValue file, HmlValue size) { if (file.type != HML_VAL_FILE) { - hml_throw(hml_val_string("read_bytes() expects file object")); + hml_throw_take(hml_val_string("read_bytes() expects file object")); } HmlFileHandle *fh = file.as.as_file; if (fh->closed) { char _err_buf[512]; snprintf(_err_buf, sizeof(_err_buf), "Cannot read from closed file '%s'", fh->path); - hml_throw(hml_val_string(_err_buf)); + hml_throw_take(hml_val_string(_err_buf)); } int32_t read_size = 0; @@ -364,7 +364,7 @@ HmlValue hml_file_read_bytes(HmlValue file, HmlValue size) { } else if (size.type == HML_VAL_I64) { read_size = (int32_t)size.as.as_i64; } else { - hml_throw(hml_val_string("read_bytes() expects integer size argument")); + hml_throw_take(hml_val_string("read_bytes() expects integer size argument")); } if (read_size <= 0) { @@ -381,7 +381,7 @@ HmlValue hml_file_read_bytes(HmlValue file, HmlValue size) { void *data = malloc(read_size); if (!data) { - hml_throw(hml_val_string("Memory allocation failed in read_bytes()")); + hml_throw_take(hml_val_string("Memory allocation failed in read_bytes()")); } size_t bytes_read = fread(data, 1, read_size, (FILE*)fh->fp); @@ -390,7 +390,7 @@ HmlValue hml_file_read_bytes(HmlValue file, HmlValue size) { free(data); char _err_buf[512]; snprintf(_err_buf, sizeof(_err_buf), "Read error on file '%s': %s", fh->path, strerror(errno)); - hml_throw(hml_val_string(_err_buf)); + hml_throw_take(hml_val_string(_err_buf)); } HmlBuffer *buf = calloc(1, sizeof(HmlBuffer)); @@ -406,25 +406,25 @@ HmlValue hml_file_read_bytes(HmlValue file, HmlValue size) { HmlValue hml_file_write_bytes(HmlValue file, HmlValue data) { if (file.type != HML_VAL_FILE) { - hml_throw(hml_val_string("write_bytes() expects file object")); + hml_throw_take(hml_val_string("write_bytes() expects file object")); } HmlFileHandle *fh = file.as.as_file; if (fh->closed) { char _err_buf[512]; snprintf(_err_buf, sizeof(_err_buf), "Cannot write to closed file '%s'", fh->path); - hml_throw(hml_val_string(_err_buf)); + hml_throw_take(hml_val_string(_err_buf)); } // Check if file is writable if (fh->mode[0] == 'r' && strchr(fh->mode, '+') == NULL) { char _err_buf[512]; snprintf(_err_buf, sizeof(_err_buf), "Cannot write to file '%s' opened in read-only mode", fh->path); - hml_throw(hml_val_string(_err_buf)); + hml_throw_take(hml_val_string(_err_buf)); } if (data.type != HML_VAL_BUFFER || !data.as.as_buffer) { - hml_throw(hml_val_string("write_bytes() expects buffer argument")); + hml_throw_take(hml_val_string("write_bytes() expects buffer argument")); } HmlBuffer *buf = data.as.as_buffer; @@ -433,7 +433,7 @@ HmlValue hml_file_write_bytes(HmlValue file, HmlValue data) { if (ferror((FILE*)fh->fp)) { char _err_buf[512]; snprintf(_err_buf, sizeof(_err_buf), "Write error on file '%s': %s", fh->path, strerror(errno)); - hml_throw(hml_val_string(_err_buf)); + hml_throw_take(hml_val_string(_err_buf)); } return hml_val_i32((int32_t)written); @@ -964,7 +964,7 @@ HmlValue hml_is_dir(HmlValue path) { HmlValue hml_file_stat(HmlValue path) { if (path.type != HML_VAL_STRING || !path.as.as_string) { - hml_throw(hml_val_string("file_stat() requires a string path")); + hml_throw_take(hml_val_string("file_stat() requires a string path")); } struct stat st; @@ -972,7 +972,7 @@ HmlValue hml_file_stat(HmlValue path) { char err_buf[512]; snprintf(err_buf, sizeof(err_buf), "Failed to stat '%s': %s", path.as.as_string->data, strerror(errno)); - hml_throw(hml_val_string(err_buf)); + hml_throw_take(hml_val_string(err_buf)); } HmlValue obj = hml_val_object(); diff --git a/src/backends/compiler/codegen_stmt.c b/src/backends/compiler/codegen_stmt.c index 4116ee6f..f62711d7 100644 --- a/src/backends/compiler/codegen_stmt.c +++ b/src/backends/compiler/codegen_stmt.c @@ -1288,15 +1288,38 @@ void codegen_stmt(CodegenContext *ctx, Stmt *stmt) { codegen_writeln(ctx, "%s:;", finally_label); } - codegen_stmt(ctx, stmt->as.try_stmt.finally_block); - - // Re-throw saved exception if try threw and there was no catch if (!has_catch) { + // If the try body threw, run finally under a temporary handler so + // a finally-thrown exception can release and replace the saved one. codegen_writeln(ctx, "if (_had_exception) {"); codegen_indent_inc(ctx); - codegen_writeln(ctx, "hml_throw(_saved_exception);"); + char *finally_ex_var = codegen_temp(ctx); + char *finally_value_var = codegen_temp(ctx); + codegen_writeln(ctx, "HmlExceptionContext *%s = hml_exception_push();", finally_ex_var); + codegen_writeln(ctx, "if (setjmp(%s->exception_buf) == 0) {", finally_ex_var); + codegen_indent_inc(ctx); + codegen_stmt(ctx, stmt->as.try_stmt.finally_block); + codegen_writeln(ctx, "hml_exception_pop();"); + codegen_writeln(ctx, "hml_throw_take(_saved_exception);"); + codegen_indent_dec(ctx); + codegen_writeln(ctx, "} else {"); + codegen_indent_inc(ctx); + codegen_writeln(ctx, "HmlValue %s = hml_exception_get_value();", finally_value_var); + codegen_writeln(ctx, "hml_exception_pop();"); + codegen_writeln(ctx, "hml_release(&_saved_exception);"); + codegen_writeln(ctx, "hml_throw_take(%s);", finally_value_var); codegen_indent_dec(ctx); codegen_writeln(ctx, "}"); + free(finally_ex_var); + free(finally_value_var); + codegen_indent_dec(ctx); + codegen_writeln(ctx, "} else {"); + codegen_indent_inc(ctx); + codegen_stmt(ctx, stmt->as.try_stmt.finally_block); + codegen_indent_dec(ctx); + codegen_writeln(ctx, "}"); + } else { + codegen_stmt(ctx, stmt->as.try_stmt.finally_block); } // Check if we should return (from a return statement in the try block) @@ -1333,7 +1356,7 @@ void codegen_stmt(CodegenContext *ctx, Stmt *stmt) { if (ctx->defer_stack) { codegen_defer_execute_all(ctx); } - codegen_writeln(ctx, "hml_throw(%s);", value); + codegen_writeln(ctx, "hml_throw_take(%s);", value); free(value); break; } diff --git a/src/backends/interpreter/builtins/concurrency.c b/src/backends/interpreter/builtins/concurrency.c index 9e49edd2..5bc7f3cb 100644 --- a/src/backends/interpreter/builtins/concurrency.c +++ b/src/backends/interpreter/builtins/concurrency.c @@ -73,8 +73,7 @@ static void* task_thread_wrapper(void* arg) { // Memory allocation failed - log error and set exception state // The result will be lost, but the caller will see an exception // Set exception directly on the task's context (we're in the task thread) - task->ctx->exception_state.exception_value = val_string("Memory allocation failed for task result"); - task->ctx->exception_state.is_throwing = 1; + exception_set_value(task->ctx, val_string("Memory allocation failed for task result")); } task->state = TASK_COMPLETED; pthread_mutex_unlock((pthread_mutex_t*)task->task_mutex); diff --git a/src/backends/interpreter/builtins/debugging.c b/src/backends/interpreter/builtins/debugging.c index 4881f0bc..b8673e61 100644 --- a/src/backends/interpreter/builtins/debugging.c +++ b/src/backends/interpreter/builtins/debugging.c @@ -192,10 +192,11 @@ Value builtin_assert(Value *args, int num_args, ExecutionContext *ctx) { exception_msg = val_string("assertion failed"); } - // Retain the exception value so it survives past environment cleanups during unwinding - value_retain(exception_msg); - ctx->exception_state.exception_value = exception_msg; - ctx->exception_state.is_throwing = 1; + // Retain borrowed arguments before transferring exception ownership. + if (num_args == 2) { + VALUE_RETAIN(exception_msg); + } + exception_set_value(ctx, exception_msg); } return val_null(); diff --git a/src/backends/interpreter/builtins/directories.c b/src/backends/interpreter/builtins/directories.c index df44e472..1c4001ad 100644 --- a/src/backends/interpreter/builtins/directories.c +++ b/src/backends/interpreter/builtins/directories.c @@ -40,8 +40,7 @@ Value builtin_make_dir(Value *args, int num_args, ExecutionContext *ctx) { char error_msg[512]; snprintf(error_msg, sizeof(error_msg), "Failed to create directory '%s': %s", cpath, strerror(errno)); free(cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -80,8 +79,7 @@ Value builtin_remove_dir(Value *args, int num_args, ExecutionContext *ctx) { char error_msg[512]; snprintf(error_msg, sizeof(error_msg), "Failed to remove directory '%s': %s", cpath, strerror(errno)); free(cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -121,8 +119,7 @@ Value builtin_list_dir(Value *args, int num_args, ExecutionContext *ctx) { char error_msg[512]; snprintf(error_msg, sizeof(error_msg), "Failed to open directory '%s': %s", cpath, strerror(errno)); free(cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -153,8 +150,7 @@ Value builtin_cwd(Value *args, int num_args, ExecutionContext *ctx) { char buffer[PATH_MAX]; if (getcwd(buffer, sizeof(buffer)) == NULL) { - ctx->exception_state.exception_value = val_string(strerror(errno)); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(strerror(errno))); return val_null(); } @@ -185,8 +181,7 @@ Value builtin_chdir(Value *args, int num_args, ExecutionContext *ctx) { char error_msg[512]; snprintf(error_msg, sizeof(error_msg), "Failed to change directory to '%s': %s", cpath, strerror(errno)); free(cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -219,8 +214,7 @@ Value builtin_absolute_path(Value *args, int num_args, ExecutionContext *ctx) { char error_msg[512]; snprintf(error_msg, sizeof(error_msg), "Failed to resolve path '%s': %s", cpath, strerror(errno)); free(cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } diff --git a/src/backends/interpreter/builtins/env.c b/src/backends/interpreter/builtins/env.c index 4866e495..7ca9570d 100644 --- a/src/backends/interpreter/builtins/env.c +++ b/src/backends/interpreter/builtins/env.c @@ -208,8 +208,7 @@ Value builtin_exec(Value *args, int num_args, ExecutionContext *ctx) { snprintf(error_msg, sizeof(error_msg), "exec() pipe creation failed: %s", strerror(errno)); for (int i = 0; i <= arr->length; i++) free(argv[i]); free(argv); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -221,8 +220,7 @@ Value builtin_exec(Value *args, int num_args, ExecutionContext *ctx) { free(argv); close(stdout_pipe[0]); close(stdout_pipe[1]); close(stderr_pipe[0]); close(stderr_pipe[1]); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -459,8 +457,7 @@ Value builtin_exec(Value *args, int num_args, ExecutionContext *ctx) { char error_msg[512]; snprintf(error_msg, sizeof(error_msg), "Failed to execute command '%s': %s", ccmd, strerror(errno)); free(ccmd); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -622,8 +619,7 @@ Value builtin_exec_argv(Value *args, int num_args, ExecutionContext *ctx) { snprintf(error_msg, sizeof(error_msg), "exec_argv() pipe creation failed: %s", strerror(errno)); for (int i = 0; i < arr->length; i++) free(argv[i]); free(argv); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -635,8 +631,7 @@ Value builtin_exec_argv(Value *args, int num_args, ExecutionContext *ctx) { free(argv); close(stdout_pipe[0]); close(stdout_pipe[1]); close(stderr_pipe[0]); close(stderr_pipe[1]); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -913,8 +908,7 @@ Value builtin_kill(Value *args, int num_args, ExecutionContext *ctx) { if (kill(pid, sig) != 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "kill(%d, %d) failed: %s", pid, sig, strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -939,8 +933,7 @@ Value builtin_fork(Value *args, int num_args, ExecutionContext *ctx) { if (pid < 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "fork() failed: %s", strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -959,8 +952,7 @@ Value builtin_wait(Value *args, int num_args, ExecutionContext *ctx) { if (pid < 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "wait() failed: %s", strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -1013,8 +1005,7 @@ Value builtin_waitpid(Value *args, int num_args, ExecutionContext *ctx) { if (result_pid < 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "waitpid(%d, %d) failed: %s", pid, options, strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -1251,8 +1242,7 @@ Value builtin_posix_spawn(Value *args, int num_args, ExecutionContext *ctx) { HML_SPAWN_CLEANUP(); char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "spawn() addchdir failed: %s", strerror(chdir_rc)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } #else @@ -1306,8 +1296,7 @@ Value builtin_posix_spawn(Value *args, int num_args, ExecutionContext *ctx) { if (rc != 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "spawn() failed: %s", strerror(rc)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -1366,8 +1355,7 @@ Value builtin_pipe(Value *args, int num_args, ExecutionContext *ctx) { if (pipe(fds) != 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "pipe() failed: %s", strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -1398,8 +1386,7 @@ Value builtin_close_fd(Value *args, int num_args, ExecutionContext *ctx) { if (close(fd) != 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "close_fd(%d) failed: %s", fd, strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); } return val_null(); } @@ -1432,8 +1419,7 @@ Value builtin_read_fd(Value *args, int num_args, ExecutionContext *ctx) { } char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "read_fd(%d) failed: %s", fd, strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -1466,8 +1452,7 @@ Value builtin_write_fd(Value *args, int num_args, ExecutionContext *ctx) { if (written < 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "write_fd(%d) failed: %s", fd, strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -1493,8 +1478,7 @@ Value builtin_dup2(Value *args, int num_args, ExecutionContext *ctx) { if (result < 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "dup2(%d, %d) failed: %s", old_fd, new_fd, strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } diff --git a/src/backends/interpreter/builtins/filesystem.c b/src/backends/interpreter/builtins/filesystem.c index 9d81c034..96547ed7 100644 --- a/src/backends/interpreter/builtins/filesystem.c +++ b/src/backends/interpreter/builtins/filesystem.c @@ -63,8 +63,7 @@ Value builtin_read_file(Value *args, int num_args, ExecutionContext *ctx) { snprintf(error_msg, sizeof(error_msg), "Failed to open '%s': %s", cpath, strerror(errno)); } free(cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -74,8 +73,7 @@ Value builtin_read_file(Value *args, int num_args, ExecutionContext *ctx) { snprintf(error_msg, sizeof(error_msg), "Failed to open '%s': %s", cpath, strerror(errno)); close(fd); free(cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -154,8 +152,7 @@ Value builtin_write_file(Value *args, int num_args, ExecutionContext *ctx) { snprintf(error_msg, sizeof(error_msg), "Failed to open '%s': %s", cpath, strerror(errno)); } free(cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -166,8 +163,7 @@ Value builtin_write_file(Value *args, int num_args, ExecutionContext *ctx) { snprintf(error_msg, sizeof(error_msg), "Failed to open '%s': %s", cpath, strerror(errno)); close(fd); free(cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -228,8 +224,7 @@ Value builtin_append_file(Value *args, int num_args, ExecutionContext *ctx) { snprintf(error_msg, sizeof(error_msg), "Failed to open '%s': %s", cpath, strerror(errno)); } free(cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -239,8 +234,7 @@ Value builtin_append_file(Value *args, int num_args, ExecutionContext *ctx) { snprintf(error_msg, sizeof(error_msg), "Failed to open '%s': %s", cpath, strerror(errno)); close(fd); free(cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -274,8 +268,7 @@ Value builtin_remove_file(Value *args, int num_args, ExecutionContext *ctx) { char error_msg[512]; snprintf(error_msg, sizeof(error_msg), "Failed to remove file '%s': %s", cpath, strerror(errno)); free(cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -319,8 +312,7 @@ Value builtin_rename(Value *args, int num_args, ExecutionContext *ctx) { snprintf(error_msg, sizeof(error_msg), "Failed to rename '%s' to '%s': %s", old_cpath, new_cpath, strerror(errno)); free(old_cpath); free(new_cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -371,8 +363,7 @@ Value builtin_copy_file(Value *args, int num_args, ExecutionContext *ctx) { } free(src_cpath); free(dest_cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -383,8 +374,7 @@ Value builtin_copy_file(Value *args, int num_args, ExecutionContext *ctx) { close(src_fd); free(src_cpath); free(dest_cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -400,8 +390,7 @@ Value builtin_copy_file(Value *args, int num_args, ExecutionContext *ctx) { fclose(src_fp); free(src_cpath); free(dest_cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -413,8 +402,7 @@ Value builtin_copy_file(Value *args, int num_args, ExecutionContext *ctx) { close(dest_fd); free(src_cpath); free(dest_cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -429,8 +417,7 @@ Value builtin_copy_file(Value *args, int num_args, ExecutionContext *ctx) { fclose(dest_fp); free(src_cpath); free(dest_cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } } @@ -442,8 +429,7 @@ Value builtin_copy_file(Value *args, int num_args, ExecutionContext *ctx) { fclose(dest_fp); free(src_cpath); free(dest_cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -539,8 +525,7 @@ Value builtin_file_stat(Value *args, int num_args, ExecutionContext *ctx) { char error_msg[512]; snprintf(error_msg, sizeof(error_msg), "Failed to stat '%s': %s", cpath, strerror(errno)); free(cpath); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -633,9 +618,7 @@ Value builtin_open_fd(Value *args, int num_args, ExecutionContext *ctx) { char error_msg[512]; snprintf(error_msg, sizeof(error_msg), "Failed to open '%s' with mode '%s': %s", path, mode, strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -666,9 +649,7 @@ Value builtin_fileno(Value *args, int num_args, ExecutionContext *ctx) { if (fd < 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "fileno() failed: %s", strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } diff --git a/src/backends/interpreter/builtins/net.c b/src/backends/interpreter/builtins/net.c index 629693be..bc76922e 100644 --- a/src/backends/interpreter/builtins/net.c +++ b/src/backends/interpreter/builtins/net.c @@ -26,9 +26,7 @@ static Value throw_runtime_error(ExecutionContext *ctx, const char *format, ...) vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); - ctx->exception_state.exception_value = val_string(buffer); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(buffer)); return val_null(); } @@ -929,23 +927,17 @@ static int get_fd_from_value(Value val) { // Wait for I/O events on multiple file descriptors Value builtin_poll(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 2) { - ctx->exception_state.exception_value = val_string("poll() expects 2 arguments (fds, timeout_ms)"); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string("poll() expects 2 arguments (fds, timeout_ms)")); return val_null(); } if (args[0].type != VAL_ARRAY) { - ctx->exception_state.exception_value = val_string("poll() first argument must be array"); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string("poll() first argument must be array")); return val_null(); } if (!is_integer(args[1])) { - ctx->exception_state.exception_value = val_string("poll() second argument must be integer (timeout_ms)"); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string("poll() second argument must be integer (timeout_ms)")); return val_null(); } @@ -960,9 +952,7 @@ Value builtin_poll(Value *args, int num_args, ExecutionContext *ctx) { // Build pollfd array struct pollfd *pfds = malloc(sizeof(struct pollfd) * fds_arr->length); if (!pfds) { - ctx->exception_state.exception_value = val_string("poll() memory allocation failed"); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string("poll() memory allocation failed")); return val_null(); } @@ -970,9 +960,7 @@ Value builtin_poll(Value *args, int num_args, ExecutionContext *ctx) { Value *original_fds = malloc(sizeof(Value) * fds_arr->length); if (!original_fds) { free(pfds); - ctx->exception_state.exception_value = val_string("poll() memory allocation failed"); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string("poll() memory allocation failed")); return val_null(); } @@ -982,9 +970,7 @@ Value builtin_poll(Value *args, int num_args, ExecutionContext *ctx) { if (item.type != VAL_OBJECT) { free(pfds); free(original_fds); - ctx->exception_state.exception_value = val_string("poll() array elements must be objects with 'fd' and 'events'"); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string("poll() array elements must be objects with 'fd' and 'events'")); return val_null(); } @@ -1005,18 +991,14 @@ Value builtin_poll(Value *args, int num_args, ExecutionContext *ctx) { if (fd < 0) { free(pfds); free(original_fds); - ctx->exception_state.exception_value = val_string("poll() fd must be a socket or file"); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string("poll() fd must be a socket or file")); return val_null(); } if (!is_integer(events_val)) { free(pfds); free(original_fds); - ctx->exception_state.exception_value = val_string("poll() events must be an integer"); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string("poll() events must be an integer")); return val_null(); } @@ -1038,9 +1020,7 @@ Value builtin_poll(Value *args, int num_args, ExecutionContext *ctx) { free(original_fds); char err_msg[256]; snprintf(err_msg, sizeof(err_msg), "poll() failed: %s", strerror(errno)); - ctx->exception_state.exception_value = val_string(err_msg); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(err_msg)); return val_null(); } diff --git a/src/backends/interpreter/builtins/os.c b/src/backends/interpreter/builtins/os.c index aff72f84..02be2942 100644 --- a/src/backends/interpreter/builtins/os.c +++ b/src/backends/interpreter/builtins/os.c @@ -45,8 +45,7 @@ Value builtin_arch(Value *args, int num_args, ExecutionContext *ctx) { if (uname(&info) != 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "arch() failed: %s", strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -64,8 +63,7 @@ Value builtin_hostname(Value *args, int num_args, ExecutionContext *ctx) { if (gethostname(hostname, sizeof(hostname)) != 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "hostname() failed: %s", strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -99,8 +97,7 @@ Value builtin_username(Value *args, int num_args, ExecutionContext *ctx) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "username() failed: could not determine username"); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -125,8 +122,7 @@ Value builtin_homedir(Value *args, int num_args, ExecutionContext *ctx) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "homedir() failed: could not determine home directory"); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -157,8 +153,7 @@ Value builtin_total_memory(Value *args, int num_args, ExecutionContext *ctx) { if (sysinfo(&info) != 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "total_memory() failed: %s", strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } return val_i64((int64_t)info.totalram * (int64_t)info.mem_unit); @@ -169,8 +164,7 @@ Value builtin_total_memory(Value *args, int num_args, ExecutionContext *ctx) { if (sysctl(mib, 2, &memsize, &len, NULL, 0) != 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "total_memory() failed: %s", strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } return val_i64(memsize); @@ -181,8 +175,7 @@ Value builtin_total_memory(Value *args, int num_args, ExecutionContext *ctx) { if (pages < 0 || page_size < 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "total_memory() failed: could not determine memory"); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } return val_i64((int64_t)pages * (int64_t)page_size); @@ -201,8 +194,7 @@ Value builtin_free_memory(Value *args, int num_args, ExecutionContext *ctx) { if (sysinfo(&info) != 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "free_memory() failed: %s", strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } // On Linux, freeram doesn't include buffers/cache, so we add them for "available" memory @@ -219,16 +211,14 @@ Value builtin_free_memory(Value *args, int num_args, ExecutionContext *ctx) { if (host_page_size(host_port, &page_size) != KERN_SUCCESS) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "free_memory() failed: could not get page size"); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } if (host_statistics64(host_port, HOST_VM_INFO64, (host_info64_t)&vm_stat, &host_size) != KERN_SUCCESS) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "free_memory() failed: could not get VM statistics"); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -247,8 +237,7 @@ Value builtin_free_memory(Value *args, int num_args, ExecutionContext *ctx) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "free_memory() failed: could not determine free memory"); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); #endif } @@ -264,8 +253,7 @@ Value builtin_os_version(Value *args, int num_args, ExecutionContext *ctx) { if (uname(&info) != 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "os_version() failed: %s", strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -283,8 +271,7 @@ Value builtin_os_name(Value *args, int num_args, ExecutionContext *ctx) { if (uname(&info) != 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "os_name() failed: %s", strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } @@ -332,8 +319,7 @@ Value builtin_uptime(Value *args, int num_args, ExecutionContext *ctx) { if (sysinfo(&info) != 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "uptime() failed: %s", strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } return val_i64((int64_t)info.uptime); @@ -345,8 +331,7 @@ Value builtin_uptime(Value *args, int num_args, ExecutionContext *ctx) { if (sysctl(mib, 2, &boottime, &len, NULL, 0) != 0) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "uptime() failed: %s", strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } time_t now = time(NULL); @@ -355,8 +340,7 @@ Value builtin_uptime(Value *args, int num_args, ExecutionContext *ctx) { // Fallback: not supported char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "uptime() not supported on this platform"); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); #endif } diff --git a/src/backends/interpreter/builtins/websockets_http.c b/src/backends/interpreter/builtins/websockets_http.c index d2a31667..d60c43ca 100644 --- a/src/backends/interpreter/builtins/websockets_http.c +++ b/src/backends/interpreter/builtins/websockets_http.c @@ -466,14 +466,12 @@ Value builtin_lws_http_get(Value *args, int num_args, ExecutionContext *ctx) { lws_init_logging(); if (num_args < 1 || num_args > 2) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_get() expects 1-2 arguments (url, headers?)"); + exception_set_value(ctx, val_string("__lws_http_get() expects 1-2 arguments (url, headers?)")); return val_null(); } if (args[0].type != VAL_STRING) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_get() expects string URL"); + exception_set_value(ctx, val_string("__lws_http_get() expects string URL")); return val_null(); } @@ -482,15 +480,13 @@ Value builtin_lws_http_get(Value *args, int num_args, ExecutionContext *ctx) { int port, ssl; if (parse_url(url, host, &port, path, &ssl) < 0) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Invalid URL format"); + exception_set_value(ctx, val_string("Invalid URL format")); return val_null(); } http_response_t *resp = calloc(1, sizeof(http_response_t)); if (!resp) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate response"); + exception_set_value(ctx, val_string("Failed to allocate response")); return val_null(); } @@ -504,8 +500,7 @@ Value builtin_lws_http_get(Value *args, int num_args, ExecutionContext *ctx) { if (!resp->body) { free_custom_headers(resp); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate body buffer"); + exception_set_value(ctx, val_string("Failed to allocate body buffer")); return val_null(); } resp->body[0] = '\0'; @@ -516,8 +511,7 @@ Value builtin_lws_http_get(Value *args, int num_args, ExecutionContext *ctx) { free(resp->body); free_custom_headers(resp); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to create libwebsockets context"); + exception_set_value(ctx, val_string("Failed to create libwebsockets context")); return val_null(); } @@ -551,8 +545,7 @@ Value builtin_lws_http_get(Value *args, int num_args, ExecutionContext *ctx) { free(resp->body); free_custom_headers(resp); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to connect"); + exception_set_value(ctx, val_string("Failed to connect")); return val_null(); } @@ -570,8 +563,7 @@ Value builtin_lws_http_get(Value *args, int num_args, ExecutionContext *ctx) { if (resp->redirect_url) free(resp->redirect_url); free_custom_headers(resp); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("HTTP request failed or timed out"); + exception_set_value(ctx, val_string("HTTP request failed or timed out")); return val_null(); } @@ -589,14 +581,12 @@ Value builtin_lws_http_post(Value *args, int num_args, ExecutionContext *ctx) { lws_init_logging(); if (num_args < 3 || num_args > 4) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_post() expects 3-4 arguments (url, body, content_type, headers?)"); + exception_set_value(ctx, val_string("__lws_http_post() expects 3-4 arguments (url, body, content_type, headers?)")); return val_null(); } if (args[0].type != VAL_STRING || args[1].type != VAL_STRING || args[2].type != VAL_STRING) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_post() expects string arguments"); + exception_set_value(ctx, val_string("__lws_http_post() expects string arguments")); return val_null(); } @@ -608,15 +598,13 @@ Value builtin_lws_http_post(Value *args, int num_args, ExecutionContext *ctx) { int port, ssl; if (parse_url(url, host, &port, path, &ssl) < 0) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Invalid URL format"); + exception_set_value(ctx, val_string("Invalid URL format")); return val_null(); } http_response_t *resp = calloc(1, sizeof(http_response_t)); if (!resp) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate response"); + exception_set_value(ctx, val_string("Failed to allocate response")); return val_null(); } @@ -630,8 +618,7 @@ Value builtin_lws_http_post(Value *args, int num_args, ExecutionContext *ctx) { if (!resp->body) { free_custom_headers(resp); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate body buffer"); + exception_set_value(ctx, val_string("Failed to allocate body buffer")); return val_null(); } resp->body[0] = '\0'; @@ -642,8 +629,7 @@ Value builtin_lws_http_post(Value *args, int num_args, ExecutionContext *ctx) { free(resp->body); free_custom_headers(resp); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to create libwebsockets context"); + exception_set_value(ctx, val_string("Failed to create libwebsockets context")); return val_null(); } @@ -678,8 +664,7 @@ Value builtin_lws_http_post(Value *args, int num_args, ExecutionContext *ctx) { free(resp->body); free_custom_headers(resp); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate request body"); + exception_set_value(ctx, val_string("Failed to allocate request body")); return val_null(); } @@ -689,8 +674,7 @@ Value builtin_lws_http_post(Value *args, int num_args, ExecutionContext *ctx) { free_request_body(resp); free_custom_headers(resp); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to connect"); + exception_set_value(ctx, val_string("Failed to connect")); return val_null(); } @@ -711,8 +695,7 @@ Value builtin_lws_http_post(Value *args, int num_args, ExecutionContext *ctx) { if (resp->redirect_url) free(resp->redirect_url); free_custom_headers(resp); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("HTTP request failed or timed out"); + exception_set_value(ctx, val_string("HTTP request failed or timed out")); return val_null(); } @@ -731,15 +714,13 @@ Value builtin_lws_http_request(Value *args, int num_args, ExecutionContext *ctx) lws_init_logging(); if (num_args < 4 || num_args > 5) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_request() expects 4-5 arguments (method, url, body, content_type, headers?)"); + exception_set_value(ctx, val_string("__lws_http_request() expects 4-5 arguments (method, url, body, content_type, headers?)")); return val_null(); } if (args[0].type != VAL_STRING || args[1].type != VAL_STRING || args[2].type != VAL_STRING || args[3].type != VAL_STRING) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_request() expects string arguments"); + exception_set_value(ctx, val_string("__lws_http_request() expects string arguments")); return val_null(); } @@ -752,15 +733,13 @@ Value builtin_lws_http_request(Value *args, int num_args, ExecutionContext *ctx) int port, ssl; if (parse_url(url, host, &port, path, &ssl) < 0) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Invalid URL format"); + exception_set_value(ctx, val_string("Invalid URL format")); return val_null(); } http_response_t *resp = calloc(1, sizeof(http_response_t)); if (!resp) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate response"); + exception_set_value(ctx, val_string("Failed to allocate response")); return val_null(); } @@ -773,8 +752,7 @@ Value builtin_lws_http_request(Value *args, int num_args, ExecutionContext *ctx) resp->body = malloc(resp->body_capacity); if (!resp->body) { free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate body buffer"); + exception_set_value(ctx, val_string("Failed to allocate body buffer")); return val_null(); } resp->body[0] = '\0'; @@ -789,8 +767,7 @@ Value builtin_lws_http_request(Value *args, int num_args, ExecutionContext *ctx) if (!context) { free(resp->body); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to create libwebsockets context"); + exception_set_value(ctx, val_string("Failed to create libwebsockets context")); return val_null(); } @@ -825,8 +802,7 @@ Value builtin_lws_http_request(Value *args, int num_args, ExecutionContext *ctx) free(resp->body); free_custom_headers(resp); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate request body"); + exception_set_value(ctx, val_string("Failed to allocate request body")); return val_null(); } @@ -836,8 +812,7 @@ Value builtin_lws_http_request(Value *args, int num_args, ExecutionContext *ctx) free_request_body(resp); free_custom_headers(resp); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to connect"); + exception_set_value(ctx, val_string("Failed to connect")); return val_null(); } @@ -858,8 +833,7 @@ Value builtin_lws_http_request(Value *args, int num_args, ExecutionContext *ctx) if (resp->redirect_url) free(resp->redirect_url); free_custom_headers(resp); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("HTTP request failed or timed out"); + exception_set_value(ctx, val_string("HTTP request failed or timed out")); return val_null(); } @@ -878,14 +852,12 @@ Value builtin_lws_http_get_timeout(Value *args, int num_args, ExecutionContext * lws_init_logging(); if (num_args != 2) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_get_timeout() expects 2 arguments (url, timeout_ms)"); + exception_set_value(ctx, val_string("__lws_http_get_timeout() expects 2 arguments (url, timeout_ms)")); return val_null(); } if (args[0].type != VAL_STRING) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_get_timeout() expects string URL"); + exception_set_value(ctx, val_string("__lws_http_get_timeout() expects string URL")); return val_null(); } @@ -898,8 +870,7 @@ Value builtin_lws_http_get_timeout(Value *args, int num_args, ExecutionContext * } else if (args[1].type == VAL_F64) { timeout_ms = (int)args[1].as.as_f64; } else { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_get_timeout() expects numeric timeout_ms"); + exception_set_value(ctx, val_string("__lws_http_get_timeout() expects numeric timeout_ms")); return val_null(); } @@ -912,15 +883,13 @@ Value builtin_lws_http_get_timeout(Value *args, int num_args, ExecutionContext * int port, ssl; if (parse_url(url, host, &port, path, &ssl) < 0) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Invalid URL format"); + exception_set_value(ctx, val_string("Invalid URL format")); return val_null(); } http_response_t *resp = calloc(1, sizeof(http_response_t)); if (!resp) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate response"); + exception_set_value(ctx, val_string("Failed to allocate response")); return val_null(); } @@ -928,8 +897,7 @@ Value builtin_lws_http_get_timeout(Value *args, int num_args, ExecutionContext * resp->body = malloc(resp->body_capacity); if (!resp->body) { free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate body buffer"); + exception_set_value(ctx, val_string("Failed to allocate body buffer")); return val_null(); } resp->body[0] = '\0'; @@ -944,8 +912,7 @@ Value builtin_lws_http_get_timeout(Value *args, int num_args, ExecutionContext * if (!context) { free(resp->body); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to create libwebsockets context"); + exception_set_value(ctx, val_string("Failed to create libwebsockets context")); return val_null(); } @@ -974,8 +941,7 @@ Value builtin_lws_http_get_timeout(Value *args, int num_args, ExecutionContext * if (!ssl) lws_context_destroy(context); free(resp->body); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to connect"); + exception_set_value(ctx, val_string("Failed to connect")); return val_null(); } @@ -992,8 +958,7 @@ Value builtin_lws_http_get_timeout(Value *args, int num_args, ExecutionContext * if (resp->headers) free(resp->headers); if (resp->redirect_url) free(resp->redirect_url); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("HTTP request failed or timed out"); + exception_set_value(ctx, val_string("HTTP request failed or timed out")); return val_null(); } @@ -1012,14 +977,12 @@ Value builtin_lws_http_post_timeout(Value *args, int num_args, ExecutionContext lws_init_logging(); if (num_args != 4) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_post_timeout() expects 4 arguments (url, body, content_type, timeout_ms)"); + exception_set_value(ctx, val_string("__lws_http_post_timeout() expects 4 arguments (url, body, content_type, timeout_ms)")); return val_null(); } if (args[0].type != VAL_STRING || args[1].type != VAL_STRING || args[2].type != VAL_STRING) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_post_timeout() expects string arguments"); + exception_set_value(ctx, val_string("__lws_http_post_timeout() expects string arguments")); return val_null(); } @@ -1032,8 +995,7 @@ Value builtin_lws_http_post_timeout(Value *args, int num_args, ExecutionContext } else if (args[3].type == VAL_F64) { timeout_ms = (int)args[3].as.as_f64; } else { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_post_timeout() expects numeric timeout_ms"); + exception_set_value(ctx, val_string("__lws_http_post_timeout() expects numeric timeout_ms")); return val_null(); } @@ -1048,15 +1010,13 @@ Value builtin_lws_http_post_timeout(Value *args, int num_args, ExecutionContext int port, ssl; if (parse_url(url, host, &port, path, &ssl) < 0) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Invalid URL format"); + exception_set_value(ctx, val_string("Invalid URL format")); return val_null(); } http_response_t *resp = calloc(1, sizeof(http_response_t)); if (!resp) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate response"); + exception_set_value(ctx, val_string("Failed to allocate response")); return val_null(); } @@ -1064,8 +1024,7 @@ Value builtin_lws_http_post_timeout(Value *args, int num_args, ExecutionContext resp->body = malloc(resp->body_capacity); if (!resp->body) { free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate body buffer"); + exception_set_value(ctx, val_string("Failed to allocate body buffer")); return val_null(); } resp->body[0] = '\0'; @@ -1080,8 +1039,7 @@ Value builtin_lws_http_post_timeout(Value *args, int num_args, ExecutionContext if (!context) { free(resp->body); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to create libwebsockets context"); + exception_set_value(ctx, val_string("Failed to create libwebsockets context")); return val_null(); } @@ -1111,8 +1069,7 @@ Value builtin_lws_http_post_timeout(Value *args, int num_args, ExecutionContext if (!ssl) lws_context_destroy(context); free(resp->body); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate request body"); + exception_set_value(ctx, val_string("Failed to allocate request body")); return val_null(); } @@ -1121,8 +1078,7 @@ Value builtin_lws_http_post_timeout(Value *args, int num_args, ExecutionContext free(resp->body); free_request_body(resp); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to connect"); + exception_set_value(ctx, val_string("Failed to connect")); return val_null(); } @@ -1142,8 +1098,7 @@ Value builtin_lws_http_post_timeout(Value *args, int num_args, ExecutionContext if (resp->headers) free(resp->headers); if (resp->redirect_url) free(resp->redirect_url); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("HTTP request failed or timed out"); + exception_set_value(ctx, val_string("HTTP request failed or timed out")); return val_null(); } @@ -1162,15 +1117,13 @@ Value builtin_lws_http_request_timeout(Value *args, int num_args, ExecutionConte lws_init_logging(); if (num_args != 5) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_request_timeout() expects 5 arguments (method, url, body, content_type, timeout_ms)"); + exception_set_value(ctx, val_string("__lws_http_request_timeout() expects 5 arguments (method, url, body, content_type, timeout_ms)")); return val_null(); } if (args[0].type != VAL_STRING || args[1].type != VAL_STRING || args[2].type != VAL_STRING || args[3].type != VAL_STRING) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_request_timeout() expects string arguments"); + exception_set_value(ctx, val_string("__lws_http_request_timeout() expects string arguments")); return val_null(); } @@ -1183,8 +1136,7 @@ Value builtin_lws_http_request_timeout(Value *args, int num_args, ExecutionConte } else if (args[4].type == VAL_F64) { timeout_ms = (int)args[4].as.as_f64; } else { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_request_timeout() expects numeric timeout_ms"); + exception_set_value(ctx, val_string("__lws_http_request_timeout() expects numeric timeout_ms")); return val_null(); } @@ -1200,15 +1152,13 @@ Value builtin_lws_http_request_timeout(Value *args, int num_args, ExecutionConte int port, ssl; if (parse_url(url, host, &port, path, &ssl) < 0) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Invalid URL format"); + exception_set_value(ctx, val_string("Invalid URL format")); return val_null(); } http_response_t *resp = calloc(1, sizeof(http_response_t)); if (!resp) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate response"); + exception_set_value(ctx, val_string("Failed to allocate response")); return val_null(); } @@ -1216,8 +1166,7 @@ Value builtin_lws_http_request_timeout(Value *args, int num_args, ExecutionConte resp->body = malloc(resp->body_capacity); if (!resp->body) { free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate body buffer"); + exception_set_value(ctx, val_string("Failed to allocate body buffer")); return val_null(); } resp->body[0] = '\0'; @@ -1232,8 +1181,7 @@ Value builtin_lws_http_request_timeout(Value *args, int num_args, ExecutionConte if (!context) { free(resp->body); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to create libwebsockets context"); + exception_set_value(ctx, val_string("Failed to create libwebsockets context")); return val_null(); } @@ -1263,8 +1211,7 @@ Value builtin_lws_http_request_timeout(Value *args, int num_args, ExecutionConte if (!ssl) lws_context_destroy(context); free(resp->body); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate request body"); + exception_set_value(ctx, val_string("Failed to allocate request body")); return val_null(); } @@ -1273,8 +1220,7 @@ Value builtin_lws_http_request_timeout(Value *args, int num_args, ExecutionConte free(resp->body); free_request_body(resp); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to connect"); + exception_set_value(ctx, val_string("Failed to connect")); return val_null(); } @@ -1294,8 +1240,7 @@ Value builtin_lws_http_request_timeout(Value *args, int num_args, ExecutionConte if (resp->headers) free(resp->headers); if (resp->redirect_url) free(resp->redirect_url); free(resp); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("HTTP request failed or timed out"); + exception_set_value(ctx, val_string("HTTP request failed or timed out")); return val_null(); } @@ -1305,14 +1250,12 @@ Value builtin_lws_http_request_timeout(Value *args, int num_args, ExecutionConte // __lws_response_status(resp: ptr): i32 Value builtin_lws_response_status(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_response_status() expects 1 argument"); + exception_set_value(ctx, val_string("__lws_response_status() expects 1 argument")); return val_null(); } if (args[0].type != VAL_PTR) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_response_status() expects ptr"); + exception_set_value(ctx, val_string("__lws_response_status() expects ptr")); return val_null(); } @@ -1327,14 +1270,12 @@ Value builtin_lws_response_status(Value *args, int num_args, ExecutionContext *c // __lws_response_body(resp: ptr): string Value builtin_lws_response_body(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_response_body() expects 1 argument"); + exception_set_value(ctx, val_string("__lws_response_body() expects 1 argument")); return val_null(); } if (args[0].type != VAL_PTR) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_response_body() expects ptr"); + exception_set_value(ctx, val_string("__lws_response_body() expects ptr")); return val_null(); } @@ -1350,14 +1291,12 @@ Value builtin_lws_response_body(Value *args, int num_args, ExecutionContext *ctx // Returns the response body as a binary buffer (preserves null bytes) Value builtin_lws_response_body_binary(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_response_body_binary() expects 1 argument"); + exception_set_value(ctx, val_string("__lws_response_body_binary() expects 1 argument")); return val_null(); } if (args[0].type != VAL_PTR) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_response_body_binary() expects ptr"); + exception_set_value(ctx, val_string("__lws_response_body_binary() expects ptr")); return val_null(); } @@ -1366,15 +1305,13 @@ Value builtin_lws_response_body_binary(Value *args, int num_args, ExecutionConte // Return empty buffer Buffer *buf = calloc(1, sizeof(Buffer)); if (!buf) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Memory allocation failed"); + exception_set_value(ctx, val_string("Memory allocation failed")); return val_null(); } buf->data = malloc(1); if (!buf->data) { free(buf); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Memory allocation failed"); + exception_set_value(ctx, val_string("Memory allocation failed")); return val_null(); } buf->length = 0; @@ -1387,16 +1324,14 @@ Value builtin_lws_response_body_binary(Value *args, int num_args, ExecutionConte // Create buffer with full binary data Buffer *buf = calloc(1, sizeof(Buffer)); if (!buf) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Memory allocation failed"); + exception_set_value(ctx, val_string("Memory allocation failed")); return val_null(); } buf->data = malloc(resp->body_len); if (!buf->data) { free(buf); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Memory allocation failed"); + exception_set_value(ctx, val_string("Memory allocation failed")); return val_null(); } @@ -1412,14 +1347,12 @@ Value builtin_lws_response_body_binary(Value *args, int num_args, ExecutionConte // __lws_response_headers(resp: ptr): string Value builtin_lws_response_headers(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_response_headers() expects 1 argument"); + exception_set_value(ctx, val_string("__lws_response_headers() expects 1 argument")); return val_null(); } if (args[0].type != VAL_PTR) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_response_headers() expects ptr"); + exception_set_value(ctx, val_string("__lws_response_headers() expects ptr")); return val_null(); } @@ -1434,14 +1367,12 @@ Value builtin_lws_response_headers(Value *args, int num_args, ExecutionContext * // __lws_response_redirect(resp: ptr): string Value builtin_lws_response_redirect(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_response_redirect() expects 1 argument"); + exception_set_value(ctx, val_string("__lws_response_redirect() expects 1 argument")); return val_null(); } if (args[0].type != VAL_PTR) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_response_redirect() expects ptr"); + exception_set_value(ctx, val_string("__lws_response_redirect() expects ptr")); return val_null(); } @@ -1456,14 +1387,12 @@ Value builtin_lws_response_redirect(Value *args, int num_args, ExecutionContext // __lws_response_free(resp: ptr): null Value builtin_lws_response_free(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_response_free() expects 1 argument"); + exception_set_value(ctx, val_string("__lws_response_free() expects 1 argument")); return val_null(); } if (args[0].type != VAL_PTR) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_response_free() expects ptr"); + exception_set_value(ctx, val_string("__lws_response_free() expects ptr")); return val_null(); } diff --git a/src/backends/interpreter/builtins/websockets_stream.c b/src/backends/interpreter/builtins/websockets_stream.c index 66c8bc51..446e495e 100644 --- a/src/backends/interpreter/builtins/websockets_stream.c +++ b/src/backends/interpreter/builtins/websockets_stream.c @@ -245,14 +245,12 @@ Value builtin_lws_http_stream_start(Value *args, int num_args, ExecutionContext lws_init_logging(); if (num_args != 5) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_stream_start() expects 5 arguments"); + exception_set_value(ctx, val_string("__lws_http_stream_start() expects 5 arguments")); return val_null(); } if (args[0].type != VAL_STRING || args[1].type != VAL_STRING) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_stream_start() expects string method and URL"); + exception_set_value(ctx, val_string("__lws_http_stream_start() expects string method and URL")); return val_null(); } @@ -267,15 +265,13 @@ Value builtin_lws_http_stream_start(Value *args, int num_args, ExecutionContext char host[256], path[512]; int port, ssl; if (parse_url(url, host, &port, path, &ssl) < 0) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Invalid URL format"); + exception_set_value(ctx, val_string("Invalid URL format")); return val_null(); } http_stream_t *stream = calloc(1, sizeof(http_stream_t)); if (!stream) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate stream"); + exception_set_value(ctx, val_string("Failed to allocate stream")); return val_null(); } pthread_mutex_init(&stream->chunk_mutex, NULL); @@ -289,8 +285,7 @@ Value builtin_lws_http_stream_start(Value *args, int num_args, ExecutionContext pthread_mutex_destroy(&stream->chunk_mutex); pthread_cond_destroy(&stream->chunk_cond); free(stream); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate request body"); + exception_set_value(ctx, val_string("Failed to allocate request body")); return val_null(); } memcpy(stream->post_body, post_body, blen); @@ -303,8 +298,7 @@ Value builtin_lws_http_stream_start(Value *args, int num_args, ExecutionContext pthread_mutex_destroy(&stream->chunk_mutex); pthread_cond_destroy(&stream->chunk_cond); free(stream); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate content_type"); + exception_set_value(ctx, val_string("Failed to allocate content_type")); return val_null(); } } @@ -329,8 +323,7 @@ Value builtin_lws_http_stream_start(Value *args, int num_args, ExecutionContext pthread_mutex_destroy(&stream->chunk_mutex); pthread_cond_destroy(&stream->chunk_cond); free(stream); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to create libwebsockets context"); + exception_set_value(ctx, val_string("Failed to create libwebsockets context")); return val_null(); } @@ -359,8 +352,7 @@ Value builtin_lws_http_stream_start(Value *args, int num_args, ExecutionContext pthread_mutex_destroy(&stream->chunk_mutex); pthread_cond_destroy(&stream->chunk_cond); free(stream); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to connect for streaming"); + exception_set_value(ctx, val_string("Failed to connect for streaming")); return val_null(); } @@ -379,8 +371,7 @@ Value builtin_lws_http_stream_start(Value *args, int num_args, ExecutionContext pthread_mutex_destroy(&stream->chunk_mutex); pthread_cond_destroy(&stream->chunk_cond); free(stream); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Streaming HTTP connection failed or timed out"); + exception_set_value(ctx, val_string("Streaming HTTP connection failed or timed out")); return val_null(); } @@ -399,8 +390,7 @@ Value builtin_lws_http_stream_start(Value *args, int num_args, ExecutionContext pthread_mutex_destroy(&stream->chunk_mutex); pthread_cond_destroy(&stream->chunk_cond); free(stream); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to create stream service thread"); + exception_set_value(ctx, val_string("Failed to create stream service thread")); return val_null(); } @@ -410,13 +400,11 @@ Value builtin_lws_http_stream_start(Value *args, int num_args, ExecutionContext // __lws_http_stream_read(stream, timeout_ms): string|null Value builtin_lws_http_stream_read(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 2) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_stream_read() expects 2 arguments"); + exception_set_value(ctx, val_string("__lws_http_stream_read() expects 2 arguments")); return val_null(); } if (args[0].type != VAL_PTR) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_stream_read() expects ptr"); + exception_set_value(ctx, val_string("__lws_http_stream_read() expects ptr")); return val_null(); } @@ -459,8 +447,7 @@ Value builtin_lws_http_stream_read(Value *args, int num_args, ExecutionContext * // __lws_http_stream_status(stream): i32 Value builtin_lws_http_stream_status(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1 || args[0].type != VAL_PTR) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_stream_status() expects ptr"); + exception_set_value(ctx, val_string("__lws_http_stream_status() expects ptr")); return val_i32(0); } http_stream_t *stream = (http_stream_t *)args[0].as.as_ptr; @@ -470,8 +457,7 @@ Value builtin_lws_http_stream_status(Value *args, int num_args, ExecutionContext // __lws_http_stream_headers(stream): string Value builtin_lws_http_stream_headers(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1 || args[0].type != VAL_PTR) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_http_stream_headers() expects ptr"); + exception_set_value(ctx, val_string("__lws_http_stream_headers() expects ptr")); return val_string(""); } http_stream_t *stream = (http_stream_t *)args[0].as.as_ptr; diff --git a/src/backends/interpreter/builtins/websockets_ws.c b/src/backends/interpreter/builtins/websockets_ws.c index 0dd4aae5..8f2b022b 100644 --- a/src/backends/interpreter/builtins/websockets_ws.c +++ b/src/backends/interpreter/builtins/websockets_ws.c @@ -324,14 +324,12 @@ Value builtin_lws_ws_connect(Value *args, int num_args, ExecutionContext *ctx) { lws_init_logging(); if (num_args != 1) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_connect() expects 1 argument"); + exception_set_value(ctx, val_string("__lws_ws_connect() expects 1 argument")); return val_null(); } if (args[0].type != VAL_STRING) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_connect() expects string URL"); + exception_set_value(ctx, val_string("__lws_ws_connect() expects string URL")); return val_null(); } @@ -353,8 +351,7 @@ Value builtin_lws_ws_connect(Value *args, int num_args, ExecutionContext *ctx) { if (colon && (!slash || colon < slash)) { size_t host_len = colon - rest; if (host_len >= 256) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Host name too long"); + exception_set_value(ctx, val_string("Host name too long")); return val_null(); } strncpy(host, rest, host_len); @@ -367,8 +364,7 @@ Value builtin_lws_ws_connect(Value *args, int num_args, ExecutionContext *ctx) { } else if (slash) { size_t host_len = slash - rest; if (host_len >= 256) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Host name too long"); + exception_set_value(ctx, val_string("Host name too long")); return val_null(); } strncpy(host, rest, host_len); @@ -388,8 +384,7 @@ Value builtin_lws_ws_connect(Value *args, int num_args, ExecutionContext *ctx) { if (colon && (!slash || colon < slash)) { size_t host_len = colon - rest; if (host_len >= 256) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Host name too long"); + exception_set_value(ctx, val_string("Host name too long")); return val_null(); } strncpy(host, rest, host_len); @@ -402,8 +397,7 @@ Value builtin_lws_ws_connect(Value *args, int num_args, ExecutionContext *ctx) { } else if (slash) { size_t host_len = slash - rest; if (host_len >= 256) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Host name too long"); + exception_set_value(ctx, val_string("Host name too long")); return val_null(); } strncpy(host, rest, host_len); @@ -415,15 +409,13 @@ Value builtin_lws_ws_connect(Value *args, int num_args, ExecutionContext *ctx) { host[255] = '\0'; } } else { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Invalid WebSocket URL (must start with ws:// or wss://)"); + exception_set_value(ctx, val_string("Invalid WebSocket URL (must start with ws:// or wss://)")); return val_null(); } ws_connection_t *conn = calloc(1, sizeof(ws_connection_t)); if (!conn) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate connection"); + exception_set_value(ctx, val_string("Failed to allocate connection")); return val_null(); } conn->owns_memory = 1; @@ -442,8 +434,7 @@ Value builtin_lws_ws_connect(Value *args, int num_args, ExecutionContext *ctx) { conn->context = lws_create_context(&info); if (!conn->context) { free(conn); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to create libwebsockets context"); + exception_set_value(ctx, val_string("Failed to create libwebsockets context")); return val_null(); } @@ -469,8 +460,7 @@ Value builtin_lws_ws_connect(Value *args, int num_args, ExecutionContext *ctx) { if (!lws_client_connect_via_info(&connect_info)) { lws_context_destroy(conn->context); free(conn); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to connect"); + exception_set_value(ctx, val_string("Failed to connect")); return val_null(); } @@ -483,8 +473,7 @@ Value builtin_lws_ws_connect(Value *args, int num_args, ExecutionContext *ctx) { if (conn->failed || conn->closed || !conn->established) { lws_context_destroy(conn->context); free(conn); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("WebSocket connection failed or timed out"); + exception_set_value(ctx, val_string("WebSocket connection failed or timed out")); return val_null(); } @@ -499,8 +488,7 @@ Value builtin_lws_ws_connect(Value *args, int num_args, ExecutionContext *ctx) { if (conn_rc != 0) { lws_context_destroy(conn->context); free(conn); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to create service thread"); + exception_set_value(ctx, val_string("Failed to create service thread")); return val_null(); } @@ -508,8 +496,7 @@ Value builtin_lws_ws_connect(Value *args, int num_args, ExecutionContext *ctx) { WebSocketHandle *ws = calloc(1, sizeof(WebSocketHandle)); if (!ws) { ws_connection_close(conn); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate WebSocket handle"); + exception_set_value(ctx, val_string("Failed to allocate WebSocket handle")); return val_null(); } ws->handle = conn; @@ -517,8 +504,7 @@ Value builtin_lws_ws_connect(Value *args, int num_args, ExecutionContext *ctx) { if (!ws->url) { ws_connection_close(conn); free(ws); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate WebSocket URL"); + exception_set_value(ctx, val_string("Failed to allocate WebSocket URL")); return val_null(); } ws->host = NULL; @@ -533,14 +519,12 @@ Value builtin_lws_ws_connect(Value *args, int num_args, ExecutionContext *ctx) { // __lws_ws_send_text(conn: websocket, text: string): i32 Value builtin_lws_ws_send_text(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 2) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_send_text() expects 2 arguments"); + exception_set_value(ctx, val_string("__lws_ws_send_text() expects 2 arguments")); return val_null(); } if (args[1].type != VAL_STRING) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_send_text() expects string as second argument"); + exception_set_value(ctx, val_string("__lws_ws_send_text() expects string as second argument")); return val_null(); } @@ -552,8 +536,7 @@ Value builtin_lws_ws_send_text(Value *args, int num_args, ExecutionContext *ctx) } else if (args[0].type == VAL_PTR) { conn = (ws_connection_t *)args[0].as.as_ptr; } else { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_send_text() expects websocket or ptr"); + exception_set_value(ctx, val_string("__lws_ws_send_text() expects websocket or ptr")); return val_null(); } @@ -585,14 +568,12 @@ Value builtin_lws_ws_send_text(Value *args, int num_args, ExecutionContext *ctx) // Sends binary data over a WebSocket connection Value builtin_lws_ws_send_binary(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 2) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_send_binary() expects 2 arguments"); + exception_set_value(ctx, val_string("__lws_ws_send_binary() expects 2 arguments")); return val_null(); } if (args[1].type != VAL_BUFFER) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_send_binary() expects buffer as second argument"); + exception_set_value(ctx, val_string("__lws_ws_send_binary() expects buffer as second argument")); return val_null(); } @@ -604,8 +585,7 @@ Value builtin_lws_ws_send_binary(Value *args, int num_args, ExecutionContext *ct } else if (args[0].type == VAL_PTR) { conn = (ws_connection_t *)args[0].as.as_ptr; } else { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_send_binary() expects websocket or ptr"); + exception_set_value(ctx, val_string("__lws_ws_send_binary() expects websocket or ptr")); return val_null(); } @@ -636,8 +616,7 @@ Value builtin_lws_ws_send_binary(Value *args, int num_args, ExecutionContext *ct // __lws_ws_recv(conn: websocket, timeout_ms: i32): ptr Value builtin_lws_ws_recv(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 2) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_recv() expects 2 arguments"); + exception_set_value(ctx, val_string("__lws_ws_recv() expects 2 arguments")); return val_null(); } @@ -649,8 +628,7 @@ Value builtin_lws_ws_recv(Value *args, int num_args, ExecutionContext *ctx) { } else if (args[0].type == VAL_PTR) { conn = (ws_connection_t *)args[0].as.as_ptr; } else { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_recv() expects websocket or ptr as first argument"); + exception_set_value(ctx, val_string("__lws_ws_recv() expects websocket or ptr as first argument")); return val_null(); } if (!conn || conn->closed) { @@ -682,8 +660,7 @@ Value builtin_lws_ws_recv(Value *args, int num_args, ExecutionContext *ctx) { // __lws_msg_type(msg: ptr): i32 Value builtin_lws_msg_type(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_msg_type() expects 1 argument"); + exception_set_value(ctx, val_string("__lws_msg_type() expects 1 argument")); return val_null(); } @@ -702,8 +679,7 @@ Value builtin_lws_msg_type(Value *args, int num_args, ExecutionContext *ctx) { // __lws_msg_text(msg: ptr): string Value builtin_lws_msg_text(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_msg_text() expects 1 argument"); + exception_set_value(ctx, val_string("__lws_msg_text() expects 1 argument")); return val_null(); } @@ -722,8 +698,7 @@ Value builtin_lws_msg_text(Value *args, int num_args, ExecutionContext *ctx) { // __lws_msg_len(msg: ptr): i32 Value builtin_lws_msg_len(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_msg_len() expects 1 argument"); + exception_set_value(ctx, val_string("__lws_msg_len() expects 1 argument")); return val_null(); } @@ -743,8 +718,7 @@ Value builtin_lws_msg_len(Value *args, int num_args, ExecutionContext *ctx) { // Returns the binary data from a WebSocket message as a buffer Value builtin_lws_msg_binary(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_msg_binary() expects 1 argument"); + exception_set_value(ctx, val_string("__lws_msg_binary() expects 1 argument")); return val_null(); } @@ -759,8 +733,7 @@ Value builtin_lws_msg_binary(Value *args, int num_args, ExecutionContext *ctx) { Value buf_val = val_buffer((int)msg->len); if (buf_val.type == VAL_NULL) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_msg_binary() failed to allocate buffer"); + exception_set_value(ctx, val_string("__lws_msg_binary() failed to allocate buffer")); return val_null(); } memcpy(buf_val.as.as_buffer->data, msg->data, msg->len); @@ -770,8 +743,7 @@ Value builtin_lws_msg_binary(Value *args, int num_args, ExecutionContext *ctx) { // __lws_msg_free(msg: ptr): null Value builtin_lws_msg_free(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_msg_free() expects 1 argument"); + exception_set_value(ctx, val_string("__lws_msg_free() expects 1 argument")); return val_null(); } @@ -791,8 +763,7 @@ Value builtin_lws_msg_free(Value *args, int num_args, ExecutionContext *ctx) { // __lws_ws_close(conn: websocket): null Value builtin_lws_ws_close(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_close() expects 1 argument"); + exception_set_value(ctx, val_string("__lws_ws_close() expects 1 argument")); return val_null(); } @@ -815,8 +786,7 @@ Value builtin_lws_ws_close(Value *args, int num_args, ExecutionContext *ctx) { // __lws_ws_is_closed(conn: websocket): i32 Value builtin_lws_ws_is_closed(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_is_closed() expects 1 argument"); + exception_set_value(ctx, val_string("__lws_ws_is_closed() expects 1 argument")); return val_null(); } @@ -845,14 +815,12 @@ Value builtin_lws_ws_server_create(Value *args, int num_args, ExecutionContext * lws_init_logging(); if (num_args != 2) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_server_create() expects 2 arguments"); + exception_set_value(ctx, val_string("__lws_ws_server_create() expects 2 arguments")); return val_null(); } if (args[0].type != VAL_STRING) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_server_create() expects string host"); + exception_set_value(ctx, val_string("__lws_ws_server_create() expects string host")); return val_null(); } @@ -861,8 +829,7 @@ Value builtin_lws_ws_server_create(Value *args, int num_args, ExecutionContext * ws_server_t *server = calloc(1, sizeof(ws_server_t)); if (!server) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate server"); + exception_set_value(ctx, val_string("Failed to allocate server")); return val_null(); } @@ -886,8 +853,7 @@ Value builtin_lws_ws_server_create(Value *args, int num_args, ExecutionContext * if (!server->context) { pthread_mutex_destroy(&server->pending_mutex); free(server); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to create server context"); + exception_set_value(ctx, val_string("Failed to create server context")); return val_null(); } @@ -901,8 +867,7 @@ Value builtin_lws_ws_server_create(Value *args, int num_args, ExecutionContext * lws_context_destroy(server->context); pthread_mutex_destroy(&server->pending_mutex); free(server); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to create service thread"); + exception_set_value(ctx, val_string("Failed to create service thread")); return val_null(); } @@ -910,8 +875,7 @@ Value builtin_lws_ws_server_create(Value *args, int num_args, ExecutionContext * WebSocketHandle *ws = calloc(1, sizeof(WebSocketHandle)); if (!ws) { ws_server_close_internal(server); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate WebSocket server handle"); + exception_set_value(ctx, val_string("Failed to allocate WebSocket server handle")); return val_null(); } ws->handle = server; @@ -920,8 +884,7 @@ Value builtin_lws_ws_server_create(Value *args, int num_args, ExecutionContext * if (!ws->host) { ws_server_close_internal(server); free(ws); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate WebSocket host string"); + exception_set_value(ctx, val_string("Failed to allocate WebSocket host string")); return val_null(); } ws->port = port; @@ -935,8 +898,7 @@ Value builtin_lws_ws_server_create(Value *args, int num_args, ExecutionContext * // __lws_ws_server_accept(server: websocket, timeout_ms: i32): websocket Value builtin_lws_ws_server_accept(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 2) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_server_accept() expects 2 arguments"); + exception_set_value(ctx, val_string("__lws_ws_server_accept() expects 2 arguments")); return val_null(); } @@ -952,8 +914,7 @@ Value builtin_lws_ws_server_accept(Value *args, int num_args, ExecutionContext * } else if (args[0].type == VAL_PTR) { server = (ws_server_t *)args[0].as.as_ptr; } else { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_server_accept() expects websocket server"); + exception_set_value(ctx, val_string("__lws_ws_server_accept() expects websocket server")); return val_null(); } @@ -1019,8 +980,7 @@ Value builtin_lws_ws_server_accept(Value *args, int num_args, ExecutionContext * // __lws_ws_server_close(server: websocket): null Value builtin_lws_ws_server_close(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("__lws_ws_server_close() expects 1 argument"); + exception_set_value(ctx, val_string("__lws_ws_server_close() expects 1 argument")); return val_null(); } diff --git a/src/backends/interpreter/environment.c b/src/backends/interpreter/environment.c index 8baea794..d081d9d8 100644 --- a/src/backends/interpreter/environment.c +++ b/src/backends/interpreter/environment.c @@ -516,8 +516,7 @@ void env_define(Environment *env, const char *name, Value value, int is_const, E // Throw exception instead of exiting char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "Variable '%s' already defined in this scope", name); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return; } @@ -530,8 +529,7 @@ void env_define(Environment *env, const char *name, Value value, int is_const, E env->names[index] = strdup(name); if (!env->names[index]) { if (mutex) pthread_mutex_unlock(mutex); - ctx->exception_state.exception_value = val_string("Memory allocation failed in env_define"); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string("Memory allocation failed in env_define")); return; } VALUE_RETAIN(value); // Retain the value @@ -561,8 +559,7 @@ void env_define_borrowed(Environment *env, const char *name, Value value, int is // Throw exception instead of exiting char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "Variable '%s' already defined in this scope", name); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return; } @@ -647,8 +644,7 @@ void env_set(Environment *env, const char *name, Value value, ExecutionContext * // Throw exception instead of exiting char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "Cannot assign to const variable '%s'", name); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return; } // Release old value, retain new value @@ -676,8 +672,7 @@ void env_set(Environment *env, const char *name, Value value, ExecutionContext * // Throw exception instead of exiting char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "Cannot assign to const variable '%s'", name); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return; } // Release old value, retain new value @@ -707,8 +702,7 @@ void env_set(Environment *env, const char *name, Value value, ExecutionContext * env->names[index] = strdup(name); if (!env->names[index]) { if (mutex) pthread_mutex_unlock(mutex); - ctx->exception_state.exception_value = val_string("Memory allocation failed in env_set"); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string("Memory allocation failed in env_set")); return; } VALUE_RETAIN(value); // Retain the value @@ -759,8 +753,7 @@ Value env_get(Environment *env, const char *name, ExecutionContext *ctx) { // Variable not found - throw exception instead of exiting char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "Undefined variable '%s'", name); - ctx->exception_state.exception_value = val_string(error_msg); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); // Return dummy value when exception is thrown } diff --git a/src/backends/interpreter/ffi.c b/src/backends/interpreter/ffi.c index 55babb76..f116bb50 100644 --- a/src/backends/interpreter/ffi.c +++ b/src/backends/interpreter/ffi.c @@ -183,10 +183,9 @@ static FFILibrary* ffi_load_library(const char *path, ExecutionContext *ctx) { // SECURITY: Validate library path before loading const char *validation_error = validate_ffi_library_path(path); if (validation_error) { - ctx->exception_state.is_throwing = 1; char error_msg[512]; snprintf(error_msg, sizeof(error_msg), "FFI security error: %s (path: %s)", validation_error, path); - ctx->exception_state.exception_value = val_string(error_msg); + exception_set_value(ctx, val_string(error_msg)); return NULL; } @@ -231,10 +230,9 @@ static FFILibrary* ffi_load_library(const char *path, ExecutionContext *ctx) { #endif if (handle == NULL) { pthread_mutex_unlock(&ffi_cache_mutex); - ctx->exception_state.is_throwing = 1; char error_msg[512]; snprintf(error_msg, sizeof(error_msg), "Failed to load library '%s': %s", path, dlerror()); - ctx->exception_state.exception_value = val_string(error_msg); + exception_set_value(ctx, val_string(error_msg)); return NULL; } @@ -251,8 +249,7 @@ static FFILibrary* ffi_load_library(const char *path, ExecutionContext *ctx) { free(lib->path); free(lib); pthread_mutex_unlock(&ffi_cache_mutex); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("FFI library cache capacity overflow"); + exception_set_value(ctx, val_string("FFI library cache capacity overflow")); return NULL; } FFILibrary **new_libraries = realloc(g_ffi_state.libraries, sizeof(FFILibrary*) * new_capacity); @@ -260,8 +257,7 @@ static FFILibrary* ffi_load_library(const char *path, ExecutionContext *ctx) { free(lib->path); free(lib); pthread_mutex_unlock(&ffi_cache_mutex); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to expand FFI library cache"); + exception_set_value(ctx, val_string("Failed to expand FFI library cache")); return NULL; } g_ffi_state.libraries = new_libraries; @@ -457,8 +453,7 @@ FFIStructType* ffi_register_struct(const char *name, char **field_names, // Returns pointer to allocated struct memory (caller must free) static void* ffi_object_to_struct(Value obj, FFIStructType *struct_type, ExecutionContext *ctx) { if (obj.type != VAL_OBJECT || !obj.as.as_object) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("FFI struct conversion requires an object"); + exception_set_value(ctx, val_string("FFI struct conversion requires an object")); return NULL; } @@ -472,11 +467,10 @@ static void* ffi_object_to_struct(Value obj, FFIStructType *struct_type, Executi // Look up field in object int field_idx = object_lookup_field(o, field->name); if (field_idx < 0) { - ctx->exception_state.is_throwing = 1; char err[256]; snprintf(err, sizeof(err), "FFI struct '%s' missing required field '%s'", struct_type->name, field->name); - ctx->exception_state.exception_value = val_string(err); + exception_set_value(ctx, val_string(err)); free(struct_mem); return NULL; } @@ -893,10 +887,9 @@ static FFIFunction* ffi_declare_function( ); if (status != FFI_OK) { - ctx->exception_state.is_throwing = 1; char err[512]; snprintf(err, sizeof(err), "Failed to prepare FFI call interface for '%s'", name); - ctx->exception_state.exception_value = val_string(err); + exception_set_value(ctx, val_string(err)); free(func->arg_types); free(func->name); free(func); @@ -931,23 +924,21 @@ Value ffi_call_function(FFIFunction *func, Value *args, int num_args, ExecutionC func->func_ptr = dlsym(func->lib_handle, func->name); char *error_msg = dlerror(); if (error_msg != NULL || func->func_ptr == NULL) { - ctx->exception_state.is_throwing = 1; char err[512]; snprintf(err, sizeof(err), "FFI function '%s' not found in '%s'%s%s", func->name, func->lib_path, error_msg ? ": " : "", error_msg ? error_msg : ""); - ctx->exception_state.exception_value = val_string(err); + exception_set_value(ctx, val_string(err)); return val_null(); } } // Validate argument count if (num_args != func->num_params) { - ctx->exception_state.is_throwing = 1; char err[256]; snprintf(err, sizeof(err), "FFI function '%s' expects %d arguments, got %d", func->name, func->num_params, num_args); - ctx->exception_state.exception_value = val_string(err); + exception_set_value(ctx, val_string(err)); return val_null(); } @@ -1208,8 +1199,7 @@ static void ffi_callback_handler(ffi_cif *cif, void *ret, void **args, void *use FFICallback* ffi_create_callback(Function *fn, Type **param_types, int num_params, Type *return_type, ExecutionContext *ctx) { FFICallback *cb = malloc(sizeof(FFICallback)); if (!cb) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate FFI callback"); + exception_set_value(ctx, val_string("Failed to allocate FFI callback")); return NULL; } @@ -1241,8 +1231,7 @@ FFICallback* ffi_create_callback(Function *fn, Type **param_types, int num_param ffi_status status = ffi_prep_cif(cif, FFI_DEFAULT_ABI, num_params, ret_type, arg_types); if (status != FFI_OK) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to prepare FFI callback interface"); + exception_set_value(ctx, val_string("Failed to prepare FFI callback interface")); function_release(fn); free(cb->hemlock_params); free(arg_types); @@ -1255,8 +1244,7 @@ FFICallback* ffi_create_callback(Function *fn, Type **param_types, int num_param void *code_ptr; ffi_closure *closure = ffi_closure_alloc(sizeof(ffi_closure), &code_ptr); if (!closure) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to allocate FFI closure"); + exception_set_value(ctx, val_string("Failed to allocate FFI closure")); function_release(fn); free(cb->hemlock_params); free(arg_types); @@ -1271,8 +1259,7 @@ FFICallback* ffi_create_callback(Function *fn, Type **param_types, int num_param // Prepare the closure with our handler status = ffi_prep_closure_loc(closure, cif, ffi_callback_handler, cb, code_ptr); if (status != FFI_OK) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to prepare FFI closure"); + exception_set_value(ctx, val_string("Failed to prepare FFI closure")); ffi_closure_free(closure); function_release(fn); free(cb->hemlock_params); @@ -1296,8 +1283,7 @@ FFICallback* ffi_create_callback(Function *fn, Type **param_types, int num_param free(arg_types); free(cif); free(cb); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("FFI callback cache capacity overflow"); + exception_set_value(ctx, val_string("FFI callback cache capacity overflow")); return NULL; } FFICallback **new_callbacks = realloc(g_callback_state.callbacks, sizeof(FFICallback*) * new_capacity); @@ -1309,8 +1295,7 @@ FFICallback* ffi_create_callback(Function *fn, Type **param_types, int num_param free(arg_types); free(cif); free(cb); - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("Failed to expand FFI callback cache"); + exception_set_value(ctx, val_string("Failed to expand FFI callback cache")); return NULL; } g_callback_state.callbacks = new_callbacks; @@ -1543,8 +1528,7 @@ void execute_extern_fn(Stmt *stmt, Environment *env, ExecutionContext *ctx) { pthread_mutex_unlock(&ffi_cache_mutex); if (current_lib == NULL) { - ctx->exception_state.is_throwing = 1; - ctx->exception_state.exception_value = val_string("No library imported before extern declaration"); + exception_set_value(ctx, val_string("No library imported before extern declaration")); return; } diff --git a/src/backends/interpreter/internal.h b/src/backends/interpreter/internal.h index ffa79a02..6d6fb069 100644 --- a/src/backends/interpreter/internal.h +++ b/src/backends/interpreter/internal.h @@ -30,6 +30,11 @@ typedef struct { Value exception_value; } ExceptionState; +// Exception state helpers. These functions take ownership of assigned values +// and release any active exception that is being replaced or cleared. +void exception_set_value(ExecutionContext *ctx, Value value); +void exception_clear(ExecutionContext *ctx); + // Defer stack - stores deferred expressions (function calls) to execute later typedef struct { Expr **calls; // Array of deferred expressions diff --git a/src/backends/interpreter/io/array_methods.c b/src/backends/interpreter/io/array_methods.c index 1a28b5ce..c381f93b 100644 --- a/src/backends/interpreter/io/array_methods.c +++ b/src/backends/interpreter/io/array_methods.c @@ -11,9 +11,7 @@ static Value throw_runtime_error(ExecutionContext *ctx, const char *format, ...) vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); - ctx->exception_state.exception_value = val_string(buffer); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(buffer)); return val_null(); } @@ -31,9 +29,7 @@ static Value throw_runtime_error_at(ExecutionContext *ctx, int line, const char snprintf(full_buffer, sizeof(full_buffer), "%s", buffer); } - ctx->exception_state.exception_value = val_string(full_buffer); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(full_buffer)); return val_null(); } diff --git a/src/backends/interpreter/io/channel_methods.c b/src/backends/interpreter/io/channel_methods.c index 15c4a18a..409d0070 100644 --- a/src/backends/interpreter/io/channel_methods.c +++ b/src/backends/interpreter/io/channel_methods.c @@ -12,9 +12,7 @@ static Value throw_runtime_error(ExecutionContext *ctx, const char *format, ...) vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); - ctx->exception_state.exception_value = val_string(buffer); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(buffer)); return val_null(); } diff --git a/src/backends/interpreter/io/file_methods.c b/src/backends/interpreter/io/file_methods.c index f6f70551..abf6ff1d 100644 --- a/src/backends/interpreter/io/file_methods.c +++ b/src/backends/interpreter/io/file_methods.c @@ -11,9 +11,7 @@ static Value throw_runtime_error(ExecutionContext *ctx, const char *format, ...) vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); - ctx->exception_state.exception_value = val_string(buffer); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(buffer)); return val_null(); } @@ -318,9 +316,7 @@ Value call_file_method(FileHandle *file, const char *method, Value *args, int nu Value builtin_read_line(Value *args, int num_args, ExecutionContext *ctx) { (void)args; if (num_args != 0) { - ctx->exception_state.exception_value = val_string("read_line() expects no arguments"); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string("read_line() expects no arguments")); return val_null(); } @@ -354,9 +350,7 @@ Value builtin_read_line(Value *args, int num_args, ExecutionContext *ctx) { Value builtin_eprint(Value *args, int num_args, ExecutionContext *ctx) { if (num_args != 1) { - ctx->exception_state.exception_value = val_string("eprint() expects 1 argument"); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string("eprint() expects 1 argument")); return val_null(); } @@ -406,16 +400,12 @@ Value builtin_eprint(Value *args, int num_args, ExecutionContext *ctx) { Value builtin_open(Value *args, int num_args, ExecutionContext *ctx) { if (num_args < 1 || num_args > 2) { - ctx->exception_state.exception_value = val_string("open() expects 1-2 arguments (path, [mode])"); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string("open() expects 1-2 arguments (path, [mode])")); return val_null(); } if (args[0].type != VAL_STRING) { - ctx->exception_state.exception_value = val_string("open() path must be a string"); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string("open() path must be a string")); return val_null(); } @@ -424,9 +414,7 @@ Value builtin_open(Value *args, int num_args, ExecutionContext *ctx) { if (num_args == 2) { if (args[1].type != VAL_STRING) { - ctx->exception_state.exception_value = val_string("open() mode must be a string"); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string("open() mode must be a string")); return val_null(); } mode = args[1].as.as_string->data; @@ -450,9 +438,7 @@ Value builtin_open(Value *args, int num_args, ExecutionContext *ctx) { char error_msg[512]; snprintf(error_msg, sizeof(error_msg), "Failed to open '%s' with mode '%s': %s", path, mode, strerror(errno)); - ctx->exception_state.exception_value = val_string(error_msg); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(error_msg)); return val_null(); } diff --git a/src/backends/interpreter/io/serialization.c b/src/backends/interpreter/io/serialization.c index a13d1cef..76f1acae 100644 --- a/src/backends/interpreter/io/serialization.c +++ b/src/backends/interpreter/io/serialization.c @@ -13,9 +13,7 @@ static Value throw_runtime_error(ExecutionContext *ctx, const char *format, ...) vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); - ctx->exception_state.exception_value = val_string(buffer); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(buffer)); return val_null(); } diff --git a/src/backends/interpreter/io/string_methods.c b/src/backends/interpreter/io/string_methods.c index ff8cc320..5fe782ed 100644 --- a/src/backends/interpreter/io/string_methods.c +++ b/src/backends/interpreter/io/string_methods.c @@ -11,9 +11,7 @@ static Value throw_runtime_error(ExecutionContext *ctx, const char *format, ...) vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); - ctx->exception_state.exception_value = val_string(buffer); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(buffer)); return val_null(); } @@ -31,9 +29,7 @@ static Value throw_runtime_error_at(ExecutionContext *ctx, int line, const char snprintf(full_buffer, sizeof(full_buffer), "%s", buffer); } - ctx->exception_state.exception_value = val_string(full_buffer); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(full_buffer)); return val_null(); } diff --git a/src/backends/interpreter/runtime/context.c b/src/backends/interpreter/runtime/context.c index 2e02081f..77c21802 100644 --- a/src/backends/interpreter/runtime/context.c +++ b/src/backends/interpreter/runtime/context.c @@ -205,8 +205,30 @@ ExecutionContext* exec_context_new(void) { return ctx; } +void exception_clear(ExecutionContext *ctx) { + if (!ctx) return; + + if (ctx->exception_state.is_throwing) { + VALUE_RELEASE(ctx->exception_state.exception_value); + } + ctx->exception_state.exception_value = val_null(); + ctx->exception_state.is_throwing = 0; +} + +void exception_set_value(ExecutionContext *ctx, Value value) { + if (!ctx) { + VALUE_RELEASE(value); + return; + } + + exception_clear(ctx); + ctx->exception_state.exception_value = value; + ctx->exception_state.is_throwing = 1; +} + void exec_context_free(ExecutionContext *ctx) { if (ctx) { + exception_clear(ctx); call_stack_free(&ctx->call_stack); defer_stack_free(&ctx->defer_stack); if (ctx->sandbox_root) { @@ -449,6 +471,7 @@ void defer_stack_execute(DeferStack *stack, ExecutionContext *ctx) { // Temporarily clear exception state to allow defer to run ctx->exception_state.is_throwing = 0; + ctx->exception_state.exception_value = val_null(); // Execute the deferred call eval_expr(stack->calls[i], stack->envs[i], ctx); @@ -458,6 +481,8 @@ void defer_stack_execute(DeferStack *stack, ExecutionContext *ctx) { if (!ctx->exception_state.is_throwing) { ctx->exception_state.is_throwing = was_throwing; ctx->exception_state.exception_value = saved_exception; + } else if (was_throwing) { + VALUE_RELEASE(saved_exception); } // Clean up the deferred expression and release environment @@ -499,13 +524,11 @@ void runtime_error(ExecutionContext *ctx, const char *format, ...) { if (ctx->current_line > 0) { const char *file = ctx->current_source_file ? ctx->current_source_file : get_current_source_file(); char *formatted = format_error_with_context(file, ctx->current_line, 0, buffer); - ctx->exception_state.exception_value = val_string(formatted); + exception_set_value(ctx, val_string(formatted)); free(formatted); } else { - ctx->exception_state.exception_value = val_string(buffer); + exception_set_value(ctx, val_string(buffer)); } - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; } else { // No context - print error and exit fprintf(stderr, "Runtime error: %s\n", buffer); @@ -531,9 +554,7 @@ void runtime_error_at(ExecutionContext *ctx, int line, const char *format, ...) // Set exception state for catchable errors if (ctx) { - ctx->exception_state.exception_value = val_string(full_buffer); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(full_buffer)); } else { // No context - print error and exit fprintf(stderr, "Runtime error: %s\n", full_buffer); @@ -554,9 +575,7 @@ void runtime_error_with_context(ExecutionContext *ctx, const char *file, int lin // Set exception state for catchable errors if (ctx) { - ctx->exception_state.exception_value = val_string(formatted); - value_retain(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string(formatted)); } else { // No context - print error and exit fprintf(stderr, "Runtime error: %s\n", formatted); diff --git a/src/backends/interpreter/runtime/statements.c b/src/backends/interpreter/runtime/statements.c index 4997c24f..06abc216 100644 --- a/src/backends/interpreter/runtime/statements.c +++ b/src/backends/interpreter/runtime/statements.c @@ -306,8 +306,7 @@ void eval_stmt(Stmt *stmt, Environment *env, ExecutionContext *ctx) { // Validate iterable type before creating loop environment if (iterable.type != VAL_ARRAY && iterable.type != VAL_OBJECT && iterable.type != VAL_STRING) { VALUE_RELEASE(iterable); // Release iterable before breaking - ctx->exception_state.exception_value = val_string("for-in requires array, object, or string"); - ctx->exception_state.is_throwing = 1; + exception_set_value(ctx, val_string("for-in requires array, object, or string")); break; } @@ -730,8 +729,7 @@ void eval_stmt(Stmt *stmt, Environment *env, ExecutionContext *ctx) { // Clear exception state and release the exception value // (env_set retained it, so we can release the context's reference) - VALUE_RELEASE(ctx->exception_state.exception_value); - ctx->exception_state.is_throwing = 0; + exception_clear(ctx); // Execute catch block eval_stmt(stmt->as.try_stmt.catch_block, catch_env, ctx); @@ -753,6 +751,7 @@ void eval_stmt(Stmt *stmt, Environment *env, ExecutionContext *ctx) { // Clear states before finally ctx->return_state.is_returning = 0; ctx->exception_state.is_throwing = 0; + ctx->exception_state.exception_value = val_null(); ctx->loop_state.is_breaking = 0; ctx->loop_state.is_continuing = 0; @@ -768,6 +767,8 @@ void eval_stmt(Stmt *stmt, Environment *env, ExecutionContext *ctx) { ctx->exception_state.exception_value = saved_exception; ctx->loop_state.is_breaking = was_breaking; ctx->loop_state.is_continuing = was_continuing; + } else if (was_throwing) { + VALUE_RELEASE(saved_exception); } } break; @@ -777,8 +778,12 @@ void eval_stmt(Stmt *stmt, Environment *env, ExecutionContext *ctx) { // Evaluate the value to throw // Ownership is transferred to the context (no retain needed) // The value will survive unwinding as long as exception_state holds it - ctx->exception_state.exception_value = eval_expr(stmt->as.throw_stmt.value, env, ctx); - ctx->exception_state.is_throwing = 1; + Value thrown = eval_expr(stmt->as.throw_stmt.value, env, ctx); + if (ctx->exception_state.is_throwing) { + VALUE_RELEASE(thrown); + break; + } + exception_set_value(ctx, thrown); // Push throw location onto stack trace call_stack_push_line(&ctx->call_stack, "", stmt->line); @@ -924,7 +929,7 @@ void eval_program(Stmt **stmts, int count, Environment *env, ExecutionContext *c // Clear stack for next execution (REPL mode) call_stack_free(&ctx->call_stack); // Release exception value before exiting - VALUE_RELEASE(ctx->exception_state.exception_value); + exception_clear(ctx); exit(1); } } diff --git a/src/backends/interpreter/wasm_interp_main.c b/src/backends/interpreter/wasm_interp_main.c index e17049d0..c71a5adc 100644 --- a/src/backends/interpreter/wasm_interp_main.c +++ b/src/backends/interpreter/wasm_interp_main.c @@ -285,8 +285,7 @@ int hemlock_context_eval(int handle, const char *source) { wc->ctx->loop_state.is_breaking = 0; wc->ctx->loop_state.is_continuing = 0; if (wc->ctx->exception_state.is_throwing) { - VALUE_RELEASE(wc->ctx->exception_state.exception_value); - wc->ctx->exception_state.is_throwing = 0; + exception_clear(wc->ctx); } /* Parse */ @@ -340,8 +339,7 @@ int hemlock_context_eval(int handle, const char *source) { /* Clean up exception state so the context is reusable */ call_stack_free(&wc->ctx->call_stack); - VALUE_RELEASE(wc->ctx->exception_state.exception_value); - wc->ctx->exception_state.is_throwing = 0; + exception_clear(wc->ctx); had_runtime_error = 1; break; } @@ -423,8 +421,7 @@ const char *hemlock_context_get(int handle, const char *varname) { /* If the variable was not found, env_get sets the exception state */ if (wc->ctx->exception_state.is_throwing) { - VALUE_RELEASE(wc->ctx->exception_state.exception_value); - wc->ctx->exception_state.is_throwing = 0; + exception_clear(wc->ctx); return NULL; } @@ -439,8 +436,7 @@ const char *hemlock_context_get(int handle, const char *varname) { /* If serialization failed, clear the exception and return NULL */ if (!json && wc->ctx->exception_state.is_throwing) { - VALUE_RELEASE(wc->ctx->exception_state.exception_value); - wc->ctx->exception_state.is_throwing = 0; + exception_clear(wc->ctx); } return json; /* caller (JS) frees via Module._free() */ @@ -470,8 +466,7 @@ int hemlock_context_set(int handle, const char *varname, const char *json) { Value val = json_parse_value(&jp, wc->ctx); if (wc->ctx->exception_state.is_throwing) { - VALUE_RELEASE(wc->ctx->exception_state.exception_value); - wc->ctx->exception_state.is_throwing = 0; + exception_clear(wc->ctx); VALUE_RELEASE(val); return 1; } @@ -485,14 +480,12 @@ int hemlock_context_set(int handle, const char *varname, const char *json) { if (wc->ctx->exception_state.is_throwing) { /* Variable not found — clear exception and define it instead */ - VALUE_RELEASE(wc->ctx->exception_state.exception_value); - wc->ctx->exception_state.is_throwing = 0; + exception_clear(wc->ctx); env_define(wc->env, varname, val, 0 /* mutable */, wc->ctx); if (wc->ctx->exception_state.is_throwing) { - VALUE_RELEASE(wc->ctx->exception_state.exception_value); - wc->ctx->exception_state.is_throwing = 0; + exception_clear(wc->ctx); VALUE_RELEASE(val); return 1; } @@ -660,8 +653,7 @@ int hemlock_run_script(int ctx_handle, int script_handle) { wc->ctx->loop_state.is_breaking = 0; wc->ctx->loop_state.is_continuing = 0; if (wc->ctx->exception_state.is_throwing) { - VALUE_RELEASE(wc->ctx->exception_state.exception_value); - wc->ctx->exception_state.is_throwing = 0; + exception_clear(wc->ctx); } /* @@ -683,8 +675,7 @@ int hemlock_run_script(int ctx_handle, int script_handle) { get_current_source_code()); call_stack_free(&wc->ctx->call_stack); - VALUE_RELEASE(wc->ctx->exception_state.exception_value); - wc->ctx->exception_state.is_throwing = 0; + exception_clear(wc->ctx); had_runtime_error = 1; break; } diff --git a/tests/compiler/memory/exception_override_cleanup.hml b/tests/compiler/memory/exception_override_cleanup.hml new file mode 100644 index 00000000..e56b6702 --- /dev/null +++ b/tests/compiler/memory/exception_override_cleanup.hml @@ -0,0 +1,24 @@ +// Test: compiled exception replacement in finally does not leak saved exceptions + +fn run_once(i) { + try { + throw "try exception " + i; + } finally { + throw "finally replacement " + i; + } +} + +fn run() { + let i = 0; + while (i < 50000) { + try { + run_once(i); + } catch (e) { + // caught; exception storage should be released each iteration + } + i = i + 1; + } +} + +run(); +print("ok"); diff --git a/tests/memory/exception_override_cleanup.hml b/tests/memory/exception_override_cleanup.hml new file mode 100644 index 00000000..b1d34acc --- /dev/null +++ b/tests/memory/exception_override_cleanup.hml @@ -0,0 +1,32 @@ +// Test: Exceptions overridden by defer/finally should release the original exception +// Expected: No memory leaks when saved exception state is superseded during unwinding + +fn throw_defer_replacement() { + throw "defer replacement"; +} + +fn defer_overrides_body_exception() { + defer throw_defer_replacement(); + + throw "body exception"; +} + +try { + defer_overrides_body_exception(); + print("ERROR: defer override should have thrown"); +} catch (e) { + print("Caught defer override: " + e); +} + +try { + try { + throw "try exception"; + } finally { + throw "finally replacement"; + } + print("ERROR: finally override should have thrown"); +} catch (e) { + print("Caught finally override: " + e); +} + +print("Exception override cleanup tests passed"); diff --git a/tests/memory/regression/run_leak_tests.sh b/tests/memory/regression/run_leak_tests.sh index 21974fc8..f3dffdbb 100755 --- a/tests/memory/regression/run_leak_tests.sh +++ b/tests/memory/regression/run_leak_tests.sh @@ -85,6 +85,7 @@ echo "▶ Phase 1: Exception Safety" run_test "$HEMLOCK_DIR/tests/memory/exception_in_array.hml" run_test "$HEMLOCK_DIR/tests/memory/exception_in_object.hml" run_test "$HEMLOCK_DIR/tests/memory/exception_in_call_args.hml" +run_test "$HEMLOCK_DIR/tests/memory/exception_override_cleanup.hml" echo "" # Phase 1 Regression Tests: Task/Channel Cleanup