disruption

This commit is contained in:
2026-02-18 16:47:33 -06:00
parent 91b73f923a
commit c0cd6a61a6
33 changed files with 889 additions and 948 deletions

View File

@@ -189,6 +189,12 @@ JS_SetClassProto(js, js_##TYPE##_id, TYPE##_proto); \
return JS_EXCEPTION; \
} while (0)
// Safe integer property extraction (null → 0)
#define JS_GETINT(JS, TARGET, VALUE, PROP) { \
JSValue __##PROP##__v = JS_GetPropertyStr(JS, VALUE, #PROP); \
TARGET = JS_IsNull(__##PROP##__v) ? 0 : (int)js2number(JS, __##PROP##__v); \
JS_FreeValue(JS, __##PROP##__v); }
// Common macros for property access
#define JS_GETPROP(JS, TARGET, VALUE, PROP, TYPE) {\
JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#PROP); \

View File

@@ -675,7 +675,7 @@ static JSValue reg_vm_binop(JSContext *ctx, int op, JSValue a, JSValue b) {
}
/* Type mismatch — disrupt */
return JS_ThrowTypeError(ctx, "type mismatch in binary operation");
return JS_RaiseDisrupt(ctx, "type mismatch in binary operation");
}
@@ -809,7 +809,7 @@ vm_dispatch:
if (pc >= code->instr_count) {
fprintf(stderr, "mach VM: pc %u overran code->instr_count %u (missing RETURN/RETNIL?)\n",
pc, code->instr_count);
result = JS_ThrowInternalError(ctx, "pc overrun");
result = JS_RaiseDisrupt(ctx, "pc overrun");
goto done;
}
#endif
@@ -886,6 +886,8 @@ vm_dispatch:
};
#undef DT
#define VM_DECODE() do { \
ctx->reg_current_frame = frame_ref.val; \
ctx->current_register_pc = pc; \
instr = code->instructions[pc++]; \
op = MACH_GET_OP(instr); \
a = MACH_GET_A(instr); \
@@ -899,6 +901,8 @@ vm_dispatch:
#define VM_BREAK() VM_DISPATCH()
#else
#define VM_DECODE() do { \
ctx->reg_current_frame = frame_ref.val; \
ctx->current_register_pc = pc; \
instr = code->instructions[pc++]; \
op = MACH_GET_OP(instr); \
a = MACH_GET_A(instr); \
@@ -1258,7 +1262,7 @@ vm_dispatch:
if (mist_is_function(obj)) {
JSFunction *fn_chk = JS_VALUE_GET_FUNCTION(obj);
if (fn_chk->length != 2) {
JS_ThrowTypeError(ctx, "cannot read property of non-proxy function");
JS_RaiseDisrupt(ctx, "cannot read property of non-proxy function");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt;
}
@@ -1314,7 +1318,7 @@ vm_dispatch:
if (has <= 0) {
char buf[128];
JS_KeyGetStr(ctx, buf, sizeof(buf), key);
JS_ThrowReferenceError(ctx, "'%s' is not defined", buf);
JS_RaiseDisrupt(ctx, "'%s' is not defined", buf);
goto disrupt;
}
}
@@ -1359,7 +1363,7 @@ vm_dispatch:
if (!target) {
fprintf(stderr, "GETUP: NULL outer_frame at depth 0! pc=%d a=%d depth=%d slot=%d nr_slots=%d instr=0x%08x\n",
pc-1, a, depth, c, code->nr_slots, instr);
result = JS_ThrowInternalError(ctx, "GETUP: NULL outer_frame");
result = JS_RaiseDisrupt(ctx, "GETUP: NULL outer_frame");
goto disrupt;
}
for (int d = 1; d < depth; d++) {
@@ -1368,7 +1372,7 @@ vm_dispatch:
if (!next) {
fprintf(stderr, "GETUP: NULL outer_frame at depth %d! pc=%d a=%d depth=%d slot=%d nr_slots=%d instr=0x%08x\n",
d, pc-1, a, depth, c, code->nr_slots, instr);
result = JS_ThrowInternalError(ctx, "GETUP: NULL outer_frame at depth %d", d);
result = JS_RaiseDisrupt(ctx, "GETUP: NULL outer_frame at depth %d", d);
goto disrupt;
}
target = next;
@@ -1396,7 +1400,7 @@ vm_dispatch:
if (offset < 0) {
int pf = atomic_load_explicit(&ctx->pause_flag, memory_order_relaxed);
if (pf == 2) {
result = JS_ThrowInternalError(ctx, "interrupted");
result = JS_RaiseDisrupt(ctx, "interrupted");
goto done;
}
if (pf == 1) {
@@ -1421,7 +1425,7 @@ vm_dispatch:
if (offset < 0) {
int pf = atomic_load_explicit(&ctx->pause_flag, memory_order_relaxed);
if (pf == 2) {
result = JS_ThrowInternalError(ctx, "interrupted");
result = JS_RaiseDisrupt(ctx, "interrupted");
goto done;
}
if (pf == 1) {
@@ -1447,7 +1451,7 @@ vm_dispatch:
if (offset < 0) {
int pf = atomic_load_explicit(&ctx->pause_flag, memory_order_relaxed);
if (pf == 2) {
result = JS_ThrowInternalError(ctx, "interrupted");
result = JS_RaiseDisrupt(ctx, "interrupted");
goto done;
}
if (pf == 1) {
@@ -1817,7 +1821,7 @@ vm_dispatch:
JSValue obj = frame->slots[b];
JSValue key = code->cpool[c];
if (mist_is_function(obj)) {
JS_ThrowTypeError(ctx, "cannot read property of function");
JS_RaiseDisrupt(ctx, "cannot read property of function");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt;
}
@@ -1878,10 +1882,10 @@ vm_dispatch:
JSValue r = JS_SetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(key), val);
ret = JS_IsException(r) ? -1 : 0;
} else if (mist_is_array(obj)) {
JS_ThrowTypeError(ctx, "array index must be a number");
JS_RaiseDisrupt(ctx, "array index must be a number");
ret = -1;
} else if (JS_IsBool(key) || JS_IsNull(key) || mist_is_array(key) || mist_is_function(key)) {
JS_ThrowTypeError(ctx, "object key must be text");
JS_RaiseDisrupt(ctx, "object key must be text");
ret = -1;
} else {
ret = JS_SetProperty(ctx, obj, key, val);
@@ -1907,7 +1911,7 @@ vm_dispatch:
/* A=frame_slot, B=func_reg, C=argc */
JSValue func_val = frame->slots[b];
if (!mist_is_function(func_val)) {
JS_ThrowTypeError(ctx, "not a function");
JS_RaiseDisrupt(ctx, "not a function");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt;
}
@@ -2105,7 +2109,7 @@ vm_dispatch:
}
VM_DEFAULT:
result = JS_ThrowInternalError(ctx, "unknown register VM opcode %d: %s", op, mach_opcode_names[op]);
result = JS_RaiseDisrupt(ctx, "unknown register VM opcode %d: %s", op, mach_opcode_names[op]);
goto done;
}
continue;
@@ -2136,49 +2140,11 @@ vm_dispatch:
break;
}
if (JS_IsNull(frame->caller)) {
if (!ctx->disruption_reported) {
/* Use faulting frame (frame_ref.val) for the header */
JSFrameRegister *fault = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
JSFunction *fault_fn = JS_VALUE_GET_FUNCTION(fault->function);
JSCodeRegister *fault_code = fault_fn->u.reg.code;
const char *fn_name = fault_code->name_cstr ? fault_code->name_cstr : "<anonymous>";
const char *file = fault_code->filename_cstr ? fault_code->filename_cstr : "<unknown>";
uint16_t line = 0, col = 0;
uint32_t fpc = pc > 0 ? pc - 1 : 0;
if (fault_code->line_table && fpc < fault_code->instr_count) {
line = fault_code->line_table[fpc].line;
col = fault_code->line_table[fpc].col;
}
fprintf(stderr, "unhandled disruption in %s (%s:%u:%u)\n", fn_name, file, line, col);
/* Walk intact caller chain for stack trace */
{
JSFrameRegister *trace_frame = fault;
int first = 1;
while (trace_frame) {
if (!mist_is_function(trace_frame->function)) break;
JSFunction *trace_fn = JS_VALUE_GET_FUNCTION(trace_frame->function);
if (trace_fn->kind == JS_FUNC_KIND_REGISTER && trace_fn->u.reg.code) {
JSCodeRegister *tc = trace_fn->u.reg.code;
uint32_t tpc = first ? fpc
: (uint32_t)(JS_VALUE_GET_INT(trace_frame->address) >> 16);
uint16_t tl = 0, tcol = 0;
if (tc->line_table && tpc < tc->instr_count) {
tl = tc->line_table[tpc].line;
tcol = tc->line_table[tpc].col;
}
fprintf(stderr, " at %s (%s:%u:%u)\n",
tc->name_cstr ? tc->name_cstr : "<anonymous>",
tc->filename_cstr ? tc->filename_cstr : "<unknown>", tl, tcol);
}
if (JS_IsNull(trace_frame->caller)) break;
trace_frame = (JSFrameRegister *)JS_VALUE_GET_PTR(trace_frame->caller);
first = 0;
}
}
ctx->disruption_reported = TRUE;
}
/* Stack trace was already included in the JS_RaiseDisrupt log via the callback. */
ctx->disruption_reported = TRUE;
frame_ref.val = JS_MKPTR(frame); /* update root for GC / done */
result = JS_Throw(ctx, JS_NULL);
ctx->current_exception = JS_NULL;
result = JS_EXCEPTION;
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto done;
}
@@ -2215,7 +2181,7 @@ done:
JSValue JS_ResumeRegisterVM(JSContext *ctx) {
if (!ctx->suspended)
return JS_ThrowInternalError(ctx, "no suspended VM to resume");
return JS_RaiseDisrupt(ctx, "no suspended VM to resume");
/* ctx->suspended is set; JS_CallRegisterVM will take the resume path */
return JS_CallRegisterVM(ctx, NULL, JS_NULL, 0, NULL, JS_NULL, JS_NULL);
}
@@ -3057,7 +3023,7 @@ static void dump_register_code(JSContext *ctx, JSCodeRegister *code, int indent)
JSValue JS_RunMachBin(JSContext *ctx, const uint8_t *data, size_t size, JSValue env) {
MachCode *mc = JS_DeserializeMachCode(data, size);
if (!mc)
return JS_ThrowSyntaxError(ctx, "failed to deserialize MACH bytecode");
return JS_RaiseDisrupt(ctx, "failed to deserialize MACH bytecode");
JSGCRef env_ref;
JS_PushGCRef(ctx, &env_ref);
@@ -3075,12 +3041,12 @@ JSValue JS_RunMachMcode(JSContext *ctx, const char *json_str, size_t len, JSValu
(void)len;
cJSON *mcode = cJSON_Parse(json_str);
if (!mcode)
return JS_ThrowSyntaxError(ctx, "failed to parse mcode JSON");
return JS_RaiseDisrupt(ctx, "failed to parse mcode JSON");
MachCode *mc = mach_compile_mcode(mcode);
cJSON_Delete(mcode);
if (!mc)
return JS_ThrowInternalError(ctx, "mcode compilation failed");
return JS_RaiseDisrupt(ctx, "mcode compilation failed");
JSGCRef env_ref;
JS_PushGCRef(ctx, &env_ref);

View File

@@ -109,7 +109,7 @@ static void qbe_dbgfile(char *fn) {
*/
JSValue js_os_qbe(JSContext *js, JSValue self, int argc, JSValue *argv) {
if (argc < 1)
return JS_ThrowTypeError(js, "os.qbe requires an IR string argument");
return JS_RaiseDisrupt(js, "os.qbe requires an IR string argument");
const char *ir = JS_ToCString(js, argv[0]);
if (!ir)
@@ -140,7 +140,7 @@ JSValue js_os_qbe(JSContext *js, JSValue self, int argc, JSValue *argv) {
FILE *inf = fmemopen((void *)ir, ir_len, "r");
if (!inf) {
JS_FreeCString(js, ir);
return JS_ThrowInternalError(js, "os.qbe: fmemopen failed");
return JS_RaiseDisrupt(js, "os.qbe: fmemopen failed");
}
/* Open output memory stream */
@@ -150,7 +150,7 @@ JSValue js_os_qbe(JSContext *js, JSValue self, int argc, JSValue *argv) {
if (!qbe_outf) {
fclose(inf);
JS_FreeCString(js, ir);
return JS_ThrowInternalError(js, "os.qbe: open_memstream failed");
return JS_RaiseDisrupt(js, "os.qbe: open_memstream failed");
}
/* Run the QBE pipeline */

View File

@@ -58,7 +58,7 @@ JSValue cell_rt_add(JSContext *ctx, JSValue a, JSValue b) {
return JS_ConcatString(ctx, a, b);
if (JS_IsNumber(a) && JS_IsNumber(b))
return qbe_float_binop(ctx, a, b, op_add);
JS_ThrowTypeError(ctx, "cannot add incompatible types");
JS_RaiseDisrupt(ctx, "cannot add incompatible types");
return JS_NULL;
}
@@ -224,7 +224,7 @@ JSValue qbe_shift_shr(JSContext *ctx, JSValue a, JSValue b) {
JSValue cell_rt_load_field(JSContext *ctx, JSValue obj, const char *name) {
if (JS_IsFunction(obj)) {
JS_ThrowTypeError(ctx, "cannot read property of function");
JS_RaiseDisrupt(ctx, "cannot read property of function");
return JS_EXCEPTION;
}
return JS_GetPropertyStr(ctx, obj, name);
@@ -252,9 +252,9 @@ void cell_rt_store_dynamic(JSContext *ctx, JSValue val, JSValue obj,
if (JS_IsInt(key)) {
JS_SetPropertyNumber(ctx, obj, (uint32_t)JS_VALUE_GET_INT(key), val);
} else if (JS_IsArray(obj) && !JS_IsInt(key)) {
JS_ThrowTypeError(ctx, "array index must be a number");
JS_RaiseDisrupt(ctx, "array index must be a number");
} else if (JS_IsBool(key) || JS_IsNull(key) || JS_IsArray(key) || JS_IsFunction(key)) {
JS_ThrowTypeError(ctx, "object key must be text");
JS_RaiseDisrupt(ctx, "object key must be text");
} else {
JS_SetProperty(ctx, obj, key, val);
}
@@ -315,7 +315,7 @@ JSValue cell_rt_get_intrinsic(JSContext *ctx, const char *name) {
return rec->slots[i].val;
}
}
JS_ThrowReferenceError(ctx, "'%s' is not defined", name);
JS_RaiseDisrupt(ctx, "'%s' is not defined", name);
return JS_EXCEPTION;
}
@@ -383,7 +383,7 @@ static int stack_space_ok(void) {
JSValue *cell_rt_enter_frame(JSContext *ctx, int64_t nr_slots) {
if (g_aot_depth >= MAX_AOT_DEPTH || !stack_space_ok()) {
JS_ThrowTypeError(ctx, "native call stack overflow (depth %d)", g_aot_depth);
JS_RaiseDisrupt(ctx, "native call stack overflow (depth %d)", g_aot_depth);
return NULL;
}
JSFrameRegister *frame = alloc_frame_register(ctx, (int)nr_slots);
@@ -481,7 +481,7 @@ static void reclaim_native_fns(JSContext *ctx, int saved_count) {
static JSValue cell_fn_trampoline(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv, int magic) {
if (magic < 0 || magic >= g_native_fn_count)
return JS_ThrowTypeError(ctx, "invalid native function id %d", magic);
return JS_RaiseDisrupt(ctx, "invalid native function id %d", magic);
void *handle = g_native_fn_registry[magic].dl_handle;
int fn_idx = g_native_fn_registry[magic].fn_idx;
@@ -491,7 +491,7 @@ static JSValue cell_fn_trampoline(JSContext *ctx, JSValue this_val,
cell_compiled_fn fn = (cell_compiled_fn)dlsym(handle, name);
if (!fn)
return JS_ThrowTypeError(ctx, "native function %s not found in dylib", name);
return JS_RaiseDisrupt(ctx, "native function %s not found in dylib", name);
/* Allocate GC-managed frame: slot 0 = this, slots 1..argc = args */
JSValue *fp = cell_rt_enter_frame(ctx, 512);
@@ -527,7 +527,7 @@ static JSValue cell_fn_trampoline(JSContext *ctx, JSValue this_val,
/* Ensure there is a pending exception. QBE @_exc_ret returns 15
but may not have set one (e.g. if cell_rt_enter_frame failed). */
if (!JS_HasException(ctx))
JS_Throw(ctx, JS_NULL);
ctx->current_exception = JS_NULL;
return JS_EXCEPTION;
}
return result;
@@ -537,7 +537,7 @@ JSValue cell_rt_make_function(JSContext *ctx, int64_t fn_idx, void *outer_fp,
int64_t nr_args) {
(void)outer_fp;
if (g_native_fn_count >= MAX_NATIVE_FN)
return JS_ThrowTypeError(ctx, "too many native functions (max %d)", MAX_NATIVE_FN);
return JS_RaiseDisrupt(ctx, "too many native functions (max %d)", MAX_NATIVE_FN);
int global_id = g_native_fn_count++;
g_native_fn_registry[global_id].dl_handle = g_current_dl_handle;
@@ -562,7 +562,7 @@ JSValue cell_rt_make_function(JSContext *ctx, int64_t fn_idx, void *outer_fp,
JSValue cell_rt_frame(JSContext *ctx, JSValue fn, int64_t nargs) {
if (!JS_IsFunction(fn)) {
JS_ThrowTypeError(ctx, "not a function");
JS_RaiseDisrupt(ctx, "not a function");
return JS_EXCEPTION;
}
int nr_slots = (int)nargs + 2;
@@ -586,7 +586,7 @@ JSValue cell_rt_invoke(JSContext *ctx, JSValue frame_val) {
JSValue fn_val = fr->function;
if (!JS_IsFunction(fn_val)) {
JS_ThrowTypeError(ctx, "not a function");
JS_RaiseDisrupt(ctx, "not a function");
return JS_EXCEPTION;
}
@@ -766,7 +766,7 @@ void cell_rt_clear_exception(JSContext *ctx) {
/* --- Disruption --- */
void cell_rt_disrupt(JSContext *ctx) {
JS_ThrowTypeError(ctx, "type error in native code");
JS_RaiseDisrupt(ctx, "type error in native code");
}
/* --- in: key in obj --- */
@@ -796,7 +796,7 @@ JSValue cell_rt_regexp(JSContext *ctx, const char *pattern, const char *flags) {
JSValue cell_rt_native_module_load(JSContext *ctx, void *dl_handle, JSValue env) {
cell_compiled_fn fn = (cell_compiled_fn)dlsym(dl_handle, "cell_main");
if (!fn)
return JS_ThrowTypeError(ctx, "cell_main not found in native module dylib");
return JS_RaiseDisrupt(ctx, "cell_main not found in native module dylib");
/* Set current handle so cell_rt_make_function registers closures
against this module's dylib */
@@ -810,7 +810,7 @@ JSValue cell_rt_native_module_load(JSContext *ctx, void *dl_handle, JSValue env)
JSValue *fp = cell_rt_enter_frame(ctx, 512);
if (!fp) {
g_current_dl_handle = prev_handle;
return JS_ThrowTypeError(ctx, "frame allocation failed");
return JS_RaiseDisrupt(ctx, "frame allocation failed");
}
/* Clear any stale exception left by a previous interpreted run */
@@ -834,7 +834,7 @@ JSValue cell_rt_native_module_load_named(JSContext *ctx, void *dl_handle, const
if (!fn)
fn = (cell_compiled_fn)dlsym(dl_handle, "cell_main");
if (!fn)
return JS_ThrowTypeError(ctx, "symbol not found in native module dylib");
return JS_RaiseDisrupt(ctx, "symbol not found in native module dylib");
void *prev_handle = g_current_dl_handle;
g_current_dl_handle = dl_handle;
@@ -845,7 +845,7 @@ JSValue cell_rt_native_module_load_named(JSContext *ctx, void *dl_handle, const
JSValue *fp = cell_rt_enter_frame(ctx, 512);
if (!fp) {
g_current_dl_handle = prev_handle;
return JS_ThrowTypeError(ctx, "frame allocation failed");
return JS_RaiseDisrupt(ctx, "frame allocation failed");
}
JSValue result = fn(ctx, fp);

View File

@@ -1,5 +1,6 @@
#include "cell.h"
#include "cell_internal.h"
#include "quickjs-internal.h"
#include <stdlib.h>
#include <string.h>
@@ -7,24 +8,24 @@
JSC_CCALL(os_createactor,
cell_rt *rt = JS_GetContextOpaque(js);
if (rt->disrupt)
return JS_ThrowInternalError(js, "Can't start a new actor while disrupting.");
return JS_RaiseDisrupt(js, "Can't start a new actor while disrupting.");
void *startup = value2wota(js, argv[0], JS_NULL, NULL);
if (!startup)
return JS_ThrowInternalError(js, "Failed to encode startup data");
return JS_RaiseDisrupt(js, "Failed to encode startup data");
create_actor(startup);
)
JSC_CCALL(os_mailbox_push,
if (argc < 2) return JS_ThrowInternalError(js, "Need an actor and a message");
if (!js_is_blob(js, argv[1])) return JS_ThrowInternalError(js, "Can only send blobs.");
if (argc < 2) return JS_RaiseDisrupt(js, "Need an actor and a message");
if (!js_is_blob(js, argv[1])) return JS_RaiseDisrupt(js, "Can only send blobs.");
const char *id = JS_ToCString(js, argv[0]);
int exist = actor_exists(id);
if (!exist) {
JS_FreeCString(js, id);
return JS_ThrowInternalError(js, "No mailbox found for given ID");
return JS_RaiseDisrupt(js, "No mailbox found for given ID");
}
size_t size;
@@ -35,13 +36,13 @@ JSC_CCALL(os_mailbox_push,
}
if (size == 0) {
JS_FreeCString(js, id);
return JS_ThrowInternalError(js, "No data present in blob");
return JS_RaiseDisrupt(js, "No data present in blob");
}
blob *msg_blob = blob_new(size * 8);
if (!msg_blob) {
JS_FreeCString(js, id);
return JS_ThrowInternalError(js, "Could not allocate blob");
return JS_RaiseDisrupt(js, "Could not allocate blob");
}
blob_write_bytes(msg_blob, data, size);
@@ -51,7 +52,7 @@ JSC_CCALL(os_mailbox_push,
JS_FreeCString(js, id);
if (err) {
blob_destroy(msg_blob);
return JS_ThrowInternalError(js, "Could not send message: %s", err);
return JS_RaiseDisrupt(js, "Could not send message: %s", err);
}
)
@@ -61,7 +62,7 @@ JSC_CCALL(os_register_actor,
double ar;
JS_ToFloat64(js, &ar, argv[3]);
const char *err = register_actor(id, rt, JS_ToBool(js, argv[2]), ar);
if (err) return JS_ThrowInternalError(js, "Could not register actor: %s", err);
if (err) return JS_RaiseDisrupt(js, "Could not register actor: %s", err);
rt->message_handle_ref.val = argv[1];
rt->context = js;
JS_FreeCString(js, id);
@@ -99,7 +100,7 @@ JSC_CCALL(actor_on_exception,
JSC_CCALL(actor_clock,
if (!JS_IsFunction(argv[0]))
return JS_ThrowReferenceError(js, "Argument must be a function.");
return JS_RaiseDisrupt(js, "Argument must be a function.");
cell_rt *actor = JS_GetContextOpaque(js);
actor_clock(actor, argv[0]);
@@ -107,7 +108,7 @@ JSC_CCALL(actor_clock,
JSC_CCALL(actor_delay,
if (!JS_IsFunction(argv[0]))
return JS_ThrowReferenceError(js, "Argument must be a function.");
return JS_RaiseDisrupt(js, "Argument must be a function.");
cell_rt *actor = JS_GetContextOpaque(js);
double seconds;
@@ -129,6 +130,32 @@ JSC_CCALL(actor_removetimer,
JS_FreeValue(js, removed);
)
/* Log callback bridge: called from JS_Log, calls ƿit log(channel, [msg, stack])
Captures the register VM stack trace so the log system can show the real error site. */
static void js_log_callback(JSContext *ctx, const char *channel, const char *msg) {
if (JS_IsNull(ctx->log_callback_js)) {
fprintf(stderr, "%s\n", msg);
return;
}
JS_FRAME(ctx);
JS_ROOT(stack, JS_GetStack(ctx));
JS_ROOT(args_array, JS_NewArray(ctx));
JS_SetPropertyNumber(ctx, args_array.val, 0, JS_NewString(ctx, msg));
JS_SetPropertyNumber(ctx, args_array.val, 1, stack.val);
JSValue argv[2];
argv[0] = JS_NewString(ctx, channel);
argv[1] = args_array.val;
JS_Call(ctx, ctx->log_callback_js, JS_NULL, 2, argv);
JS_RestoreFrame(ctx, _js_gc_frame, _js_local_frame);
}
JSC_CCALL(actor_set_log,
if (argc < 1 || !JS_IsFunction(argv[0]))
return JS_RaiseDisrupt(js, "set_log requires a function");
js->log_callback_js = argv[0];
js->log_callback = js_log_callback;
)
static const JSCFunctionListEntry js_actor_funcs[] = {
MIST_FUNC_DEF(os, createactor, 1),
MIST_FUNC_DEF(os, mailbox_push, 2),
@@ -141,6 +168,7 @@ static const JSCFunctionListEntry js_actor_funcs[] = {
MIST_FUNC_DEF(actor, setname, 1),
MIST_FUNC_DEF(actor, on_exception, 1),
MIST_FUNC_DEF(actor, clock, 1),
MIST_FUNC_DEF(actor, set_log, 1),
};
JSValue js_core_actor_use(JSContext *js) {

View File

@@ -264,18 +264,9 @@ enum {
JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
};
typedef enum JSErrorEnum {
JS_EVAL_ERROR,
JS_RANGE_ERROR,
JS_REFERENCE_ERROR,
JS_SYNTAX_ERROR,
JS_TYPE_ERROR,
JS_URI_ERROR,
JS_INTERNAL_ERROR,
JS_AGGREGATE_ERROR,
JS_NATIVE_ERROR_COUNT, /* number of different error name strings */
} JSErrorEnum;
/* Log callback — set by engine.cm via $_set_log to route JS_Log through ƿit.
For "memory" channel (OOM), the callback is skipped (can't allocate). */
typedef void (*JSLogCallback)(JSContext *ctx, const char *channel, const char *msg);
/* the variable and scope indexes must fit on 16 bits. The (-1) and
ARG_SCOPE_END values are reserved. */
@@ -313,8 +304,8 @@ void heap_check_fail(void *ptr, struct JSContext *ctx);
/* Compatibility: JS_TAG_STRING is an alias for text type checks */
#define JS_TAG_STRING JS_TAG_STRING_IMM
/* JS_ThrowMemoryError is an alias for JS_ThrowOutOfMemory */
#define JS_ThrowMemoryError(ctx) JS_ThrowOutOfMemory(ctx)
/* JS_ThrowMemoryError is an alias for JS_RaiseOOM */
#define JS_ThrowMemoryError(ctx) JS_RaiseOOM(ctx)
/* Helper to set cap in objhdr */
static inline objhdr_t objhdr_set_cap56 (objhdr_t h, uint64_t cap) {
@@ -1146,6 +1137,10 @@ struct JSContext {
JS_BOOL disruption_reported;
/* Log routing — set by $_set_log intrinsic */
JSLogCallback log_callback;
JSValue log_callback_js; /* the ƿit log function (rooted) */
/* Actor identity key — used by wota/nota PRIVATE serialization */
JSValue actor_sym;
@@ -1365,7 +1360,7 @@ JSValue JS_CallInternal (JSContext *ctx, JSValue func_obj, JSValue this_obj, int
JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, JSValue this_obj, int argc, JSValue *argv, JSValue env, JSValue outer_frame);
int JS_DeleteProperty (JSContext *ctx, JSValue obj, JSValue prop);
JSValue __attribute__ ((format (printf, 2, 3)))
JS_ThrowInternalError (JSContext *ctx, const char *fmt, ...);
JS_RaiseDisrupt (JSContext *ctx, const char *fmt, ...);
__maybe_unused void JS_DumpString (JSRuntime *rt, const JSText *text);
__maybe_unused void JS_DumpObjectHeader (JSRuntime *rt);
__maybe_unused void JS_DumpObject (JSRuntime *rt, JSRecord *rec);
@@ -1428,7 +1423,7 @@ static JSValue js_cell_object (JSContext *ctx, JSValue this_val, int argc, JSVal
static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
static JSValue js_print (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
JSValue JS_GetStack(JSContext *ctx);
JSValue JS_ThrowOutOfMemory (JSContext *ctx);
JSValue JS_RaiseOOM (JSContext *ctx);
JSValue JS_ToNumber (JSContext *ctx, JSValue val);
@@ -1536,11 +1531,9 @@ static inline void set_value (JSContext *ctx, JSValue *pval, JSValue new_val) {
*pval = new_val;
}
void JS_ThrowInterrupted (JSContext *ctx);
static inline __exception int js_poll_interrupts (JSContext *ctx) {
if (unlikely (atomic_load_explicit (&ctx->pause_flag, memory_order_relaxed) >= 2)) {
JS_ThrowInterrupted (ctx);
JS_RaiseDisrupt (ctx, "interrupted");
return -1;
}
return 0;
@@ -1595,8 +1588,6 @@ uint64_t get_text_hash (JSText *text);
void pack_utf32_to_words (const uint32_t *utf32, uint32_t len, uint64_t *packed);
int text_equal (JSText *a, const uint64_t *packed_b, uint32_t len_b);
void print_backtrace (JSContext *ctx, JSValue stack, const char *filename, int line_num, int col_num);
JSValue JS_ThrowError2 (JSContext *ctx, JSErrorEnum error_num, const char *fmt, va_list ap, BOOL add_backtrace);
JSValue gc_copy_value (JSContext *ctx, JSValue v, uint8_t *from_base, uint8_t *from_end, uint8_t *to_base, uint8_t **to_free, uint8_t *to_end);
PPretext *ppretext_init (int capacity);
PPretext *ppretext_putc (PPretext *p, uint32_t c);

View File

@@ -570,20 +570,23 @@ JS_BOOL JS_StrictEq (JSContext *ctx, JSValue op1, JSValue op2);
7. Error Handling
============================================================ */
JSValue JS_Throw (JSContext *ctx, JSValue obj);
JSValue JS_GetException (JSContext *ctx);
JS_BOOL JS_HasException (JSContext *ctx);
/* Set the disruption flag. No logging. */
JSValue JS_Disrupt (JSContext *ctx);
/* Log a message to a named channel. Routes through the ƿit log callback
if one has been set, otherwise falls back to fprintf(stderr). */
void __js_printf_like (3, 4)
JS_Log (JSContext *ctx, const char *channel, const char *fmt, ...);
/* Log to "error" channel + raise disruption. The common case. */
JSValue __js_printf_like (2, 3)
JS_ThrowSyntaxError (JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like (2, 3)
JS_ThrowTypeError (JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like (2, 3)
JS_ThrowReferenceError (JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like (2, 3)
JS_ThrowRangeError (JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like (2, 3)
JS_ThrowInternalError (JSContext *ctx, const char *fmt, ...);
JSValue JS_ThrowOutOfMemory (JSContext *ctx);
JS_RaiseDisrupt (JSContext *ctx, const char *fmt, ...);
/* Log to "memory" channel + disrupt. Skips JS callback (can't allocate). */
JSValue JS_RaiseOOM (JSContext *ctx);
/* ============================================================
8. Function Creation and Invocation

File diff suppressed because it is too large Load Diff

View File

@@ -1741,7 +1741,7 @@ TEST(has_exception_initially_false) {
TEST(throw_and_check_exception) {
ASSERT(!JS_HasException(ctx));
JS_ThrowTypeError(ctx, "test error");
JS_RaiseDisrupt(ctx, "test error");
ASSERT(JS_HasException(ctx));
JS_GetException(ctx); /* clear it */
ASSERT(!JS_HasException(ctx));