disruption
This commit is contained in:
@@ -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); \
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
542
source/runtime.c
542
source/runtime.c
File diff suppressed because it is too large
Load Diff
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user