From 406c7ea5901e0441937032b177a0c621bf0604ce Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Fri, 17 Jan 2025 10:44:33 -0600 Subject: [PATCH] add uncaught exception handling --- scripts/engine.js | 61 ++++++++------ scripts/entity.js | 3 +- scripts/prosperon.js | 11 ++- scripts/render.js | 22 ++++- source/jsffi.c | 193 ++++++++++++++++++++++++++++++++----------- source/script.c | 31 +++---- source/script.h | 7 +- source/sprite.c | 11 ++- source/timer.c | 3 +- 9 files changed, 239 insertions(+), 103 deletions(-) diff --git a/scripts/engine.js b/scripts/engine.js index 11c7b0bb..511e9b48 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -239,10 +239,15 @@ globalThis.print = console.print; console.rec = function(category, priority, line, file, msg) { - return `${file}:${line}: [${category} ${priority}]: ${msg}`; + return `${file}:${line}: [${category} ${priority}]: ${msg}` + "\n"; } -console.pprint = function pprint(msg, lvl = 0) { +var logfile = io.open('.prosperon/log.txt') +//logfile.buffer(1024*1024) // 1MB buffer + +function pprint(msg, lvl = 0) { + if (lvl < console.stdout_lvl && !logfile) return; + if (typeof msg === "object") msg = JSON.stringify(msg, null, 2); var file = "nofile"; @@ -258,43 +263,49 @@ console.pprint = function pprint(msg, lvl = 0) { if (m) line = m; } var fmt = console.rec("script", lvl, line,file, msg); - console.log(fmt); + + if (lvl >= console.stdout_lvl) + console.print(fmt) + + if (logfile) + logfile.write(fmt) + if (tracy) tracy.message(fmt); }; console.spam = function spam(msg) { - console.pprint(msg, 0); + pprint(msg, 0); }; console.debug = function debug(msg) { - console.pprint(msg, 1); + pprint(msg, 1); }; console.info = function info(msg) { - console.pprint(msg, 2); + pprint(msg, 2); }; console.warn = function warn(msg) { - console.pprint(msg, 3); -}; -console.error = function error (e) { - console.log(e); - console.log(e.stack); -}; -console.panic = function (e) { - console.log("PANIC!") - console.error(e); - os.quit(); -}; -console.stackstr = function (skip = 0) { - var err = new Error(); - var stack = err.stack.split("\n"); - return stack.slice(skip, stack.length).join("\n"); + pprint(msg, 3); }; -console.stack = function (skip = 0) { - var stack = console.stackstr(skip + 1); - console.log(stack); - return stack; +console.log = function(msg) +{ + pprint(msg, 2) +} + +console.error = function(e) { + if (!e) + e = new Error(); + + pprint(e, 4) + pprint(e.stack, 4) }; +console.panic = function (e) { + console.pprint(msg, 5) + os.quit(); +}; + +os.on('uncaught_exception', function(e) { console.error(e); }); + console.stdout_lvl = 1; console.doc = { diff --git a/scripts/entity.js b/scripts/entity.js index 4acd1b35..897a9757 100644 --- a/scripts/entity.js +++ b/scripts/entity.js @@ -368,9 +368,8 @@ var entity = { rmactor(this); - for (var i in this) { + for (var i in this) if (this[i] instanceof Object || this[i] instanceof Function) delete this[i]; - } }, make_objs(objs) { diff --git a/scripts/prosperon.js b/scripts/prosperon.js index 64a98273..6b509b46 100644 --- a/scripts/prosperon.js +++ b/scripts/prosperon.js @@ -67,16 +67,21 @@ var physlag = 0; prosperon.SIGABRT = function() { - console.log("ABORT"); - console.stack(); + console.error(new Error('SIGABRT')); + os.exit(1); } prosperon.SIGSEGV = function() { - console.stack(); + console.error(new Error('SIGSEGV')); os.exit(1); } +prosperon.exit = function() +{ + +} + prosperon.init = function () { render.init(); // imgui.init(render._main); diff --git a/scripts/render.js b/scripts/render.js index ec940d7c..757ed00a 100644 --- a/scripts/render.js +++ b/scripts/render.js @@ -1071,20 +1071,34 @@ render.image = function image(image, rect = [0,0], rotation = 0, color, pipeline current_queue.push(cmd) }; +var sprite_buf = []; render.images = function images(image, rects, config) { if (!image) throw Error ('Need an image to render.'); if (typeof image === "string") image = game.texture(image); + var bb = []; bb.width = image.texture.width; bb.height = image.texture.height; + + var sprites = []; for (var rect of rects) { + // get sprite from sprite_buf, or make a new one rect.__proto__ = bb; - var cmd = Object.create(std_sprite_cmd); - cmd.rect = rect; - cmd.image = image; - current_queue.push(cmd); + var sprite; + if (sprite_buf.length) sprite = sprite_buf.pop(); + else sprite = os.make_sprite(); + sprite.set_affine(rect); + sprite.set_image(image); + sprites.push(sprite) } + +// var sprites = os.rects_to_sprites(image,rects); + var cmds = render._main.make_sprite_queue(sprites, prosperon.camera, sprite_pipeline) + for (var i = 0; i < cmds.length; i++) + current_queue.push(cmds[i]) + + sprite_buf = sprite_buf.concat(sprites) } var tile_def = {repeat_x:true, repeat_y:true}; diff --git a/source/jsffi.c b/source/jsffi.c index ec83a64f..ca90b2b0 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -1183,6 +1183,13 @@ QJSCLASS(SDL_GPUCopyPass) QJSCLASS(SDL_GPURenderPass) QJSCLASS(SDL_Cursor) +static void PHYSFS_File_free(JSRuntime *rt, PHYSFS_File *f) +{ + PHYSFS_close(f); +} + +QJSCLASS(PHYSFS_File) + void qtree_free(JSRuntime *rt, qtree *tree) { qtree_destroy(*tree); @@ -1431,30 +1438,6 @@ JSValue vecarr2js(JSContext *js,HMM_Vec2 *points, int n) { return array; } -int js_print_exception(JSContext *js, JSValue v) -{ - if (!js) return 0; -#ifndef NDEBUG - if (!JS_IsException(v)) - return 0; - - JSValue ex = JS_GetException(js); - - const char *name = JS_ToCString(js, js_getpropertystr(js,ex, "name")); - const char *msg = JS_ToCString(js, js_getpropertystr(js,ex, "message")); - const char *stack = JS_ToCString(js, js_getpropertystr(js,ex, "stack")); - printf("%s :: %s\n%s\n", name, msg, stack); - - JS_FreeCString(js, name); - JS_FreeCString(js, msg); - JS_FreeCString(js, stack); - JS_FreeValue(js,ex); - - return 1; -#endif - return 0; -} - rect js2rect(JSContext *js,JSValue v) { if (JS_IsUndefined(v)) return (rect){0,0,1,1}; rect rect; @@ -2763,7 +2746,6 @@ JSC_CCALL(game_engine_input, #endif JSValue e = event2js(js,event); JSValue ret = JS_Call(js,argv[0], JS_UNDEFINED, 1, &e); - js_print_exception(js,ret); JS_FreeValue(js,ret); } ) @@ -4068,6 +4050,26 @@ void better_sort_sprites(sprite *arr, size_t length) { free(temp); } +int sort_sprite_backtofront(const sprite *a, const sprite *b) +{ + if (a->layer != b->layer) return a->layer - b->layer; + if (a->tex != b->tex) return ((uintptr_t)a->tex < (uintptr_t)b->tex) ? -1 : 1; + return 0; +} + +int sort_sprite_fronttoback(const sprite *a, const sprite *b) +{ + if (a->layer != b->layer) return b->layer - a->layer; + if (a->tex != b->tex) return ((uintptr_t)a->tex < (uintptr_t)b->tex) ? -1 : 1; + return 0; +} + +int sort_sprite_texture(const sprite *a, const sprite *b) +{ + if (a->tex != b->tex) return ((uintptr_t)a->tex < (uintptr_t)b->tex) ? -1 : 1; + return 0; +} + int sort_sprite(const sprite *a, const sprite *b) { if (a->layer != b->layer) return a->layer - b->layer; @@ -4219,6 +4221,7 @@ JSC_CCALL(gpu_make_sprite_mesh, for (int i = 0; i < quads; i++) { JSValue sub = JS_GetPropertyUint32(js,argv[0],i); + transform *tr; rect src; HMM_Vec4 color; @@ -5535,7 +5538,6 @@ JSC_CCALL(input_mouse_show, JSC_CCALL(input_cursor_set, SDL_Cursor *c = js2SDL_Cursor(js,argv[0]); - printf("setting cursor to %p\n", c); if (!SDL_SetCursor(c)) return JS_ThrowReferenceError(js, "could not set cursor: %s", SDL_GetError()); ) @@ -5617,26 +5619,14 @@ static const JSCFunctionListEntry js_time_funcs[] = { MIST_FUNC_DEF(time, computer_zone, 0) }; -JSC_SCALL(console_log, - printf("%s\n", str); +JSC_SCALL(console_print, + printf("%s", str); ) static const JSCFunctionListEntry js_console_funcs[] = { - MIST_FUNC_DEF(console,log,1), + MIST_FUNC_DEF(console,print,1), }; -static JSValue instr_v = JS_UNDEFINED; -int iiihandle(JSRuntime *rt, void *data) -{ - script_call_sym(instr_v, 0, NULL); - return 0; -} - -JSC_CCALL(profile_gather, - instr_v = JS_DupValue(js, argv[1]); - JS_SetInterruptHandler(JS_GetRuntime(js), iiihandle, NULL); -) - JSC_CCALL(profile_gather_rate, JS_SetInterruptRate(js2number(js,argv[0])); ) @@ -5665,7 +5655,6 @@ JSC_CCALL(profile_now, return number2js(js, SDL_GetTicksNS()/1000000000.f)) static const JSCFunctionListEntry js_profile_funcs[] = { MIST_FUNC_DEF(profile,now,0), MIST_FUNC_DEF(profile,best_t, 1), - MIST_FUNC_DEF(profile,gather,2), MIST_FUNC_DEF(profile,gather_rate,1), MIST_FUNC_DEF(profile,gather_stop,0), }; @@ -5831,6 +5820,14 @@ JSC_CCALL(io_globfs, JSC_CCALL(io_basedir, return JS_NewString(js,PHYSFS_getBaseDir())) +JSC_SCALL(io_open, + PHYSFS_File *f = PHYSFS_openWrite(str); + if (!f) + return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + + return PHYSFS_File2js(js,f); +) + static const JSCFunctionListEntry js_io_funcs[] = { MIST_FUNC_DEF(io, rm, 1), MIST_FUNC_DEF(io, mkdir, 1), @@ -5844,6 +5841,55 @@ static const JSCFunctionListEntry js_io_funcs[] = { MIST_FUNC_DEF(io,writepath, 1), MIST_FUNC_DEF(io,basedir, 0), MIST_FUNC_DEF(io, gamemode, 2), + MIST_FUNC_DEF(io, open, 2), +}; + +JSC_CCALL(file_close, + PHYSFS_File *f = js2PHYSFS_File(js,self); + PHYSFS_close(f); +) + +JSC_CCALL(file_write, + PHYSFS_File *f = js2PHYSFS_File(js,self); + size_t len; + unsigned char *data; + if (JS_IsString(argv[0])) + data = JS_ToCStringLen(js,&len,argv[0]); + else + data = JS_GetArrayBuffer(js,&len, argv[0]); + + size_t wrote = PHYSFS_writeBytes(f,data,len); + if (wrote == -1 || wrote < len) + return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); +) + +JSC_CCALL(file_buffer, + PHYSFS_File *f = js2PHYSFS_File(js,self); + size_t size = js2number(js,argv[0]); + if (!PHYSFS_setBuffer(f,size)) + return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); +) + +JSC_CCALL(file_tell, + PHYSFS_File *f = js2PHYSFS_File(js,self); + size_t tell = PHYSFS_tell(f); + if (tell == -1) + return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + + return number2js(js,tell); +) + +JSC_CCALL(file_eof, + PHYSFS_File *f = js2PHYSFS_File(js,self); + return JS_NewBool(js, PHYSFS_eof(f)); +) + +static const JSCFunctionListEntry js_PHYSFS_File_funcs[] = { + MIST_FUNC_DEF(file, close, 0), + MIST_FUNC_DEF(file, write, 1), + MIST_FUNC_DEF(file, buffer, 1), + MIST_FUNC_DEF(file, tell, 0), + MIST_FUNC_DEF(file, eof, 0), }; JSC_GETSET_APPLY(transform, pos, vec3) @@ -6059,9 +6105,12 @@ JSC_CCALL(performance_pack_num, return number2js(js,1.0)) JSC_CCALL(performance_pack_string, return JS_NewStringLen(js, STRTEST, sizeof(*STRTEST))) JSC_CCALL(performance_unpack_string, JS_ToCString(js, argv[0])) JSC_CCALL(performance_call_fn_n, - for (int i = 0; i < js2number(js,argv[1]); i++) - script_call_sym(argv[0],0,NULL); - script_call_sym(argv[2],0,NULL); + for (int i = 0; i < js2number(js,argv[1]); i++) { + JSValue r = JS_Call(js, argv[0], JS_UNDEFINED, 0, NULL); + if (JS_IsException(r)) return r; + JS_FreeValue(js,r); + } + ret = JS_Call(js,argv[2], JS_UNDEFINED, 0, NULL); ) static const JSCFunctionListEntry js_performance_funcs[] = { @@ -6364,7 +6413,7 @@ JSC_CCALL(os_check_gc, return gc; ) -JSC_SSCALL(os_eval, ret = script_eval(str, str2)) +JSC_SSCALL(os_eval, return ret = script_eval(js,str, str2)) JSC_CCALL(os_make_timer, return timer2js(js,timer_make(js,argv[0]))) JSC_CCALL(os_update_timers, timer_update(js, js2number(js,argv[0]))) @@ -7067,6 +7116,11 @@ JSC_CCALL(os_insertion_sort, JSValue arr_j = JS_GetPropertyUint32(js, arr, j); JSValue ret = JS_Call(js, cmp, JS_UNDEFINED, 2, (JSValue[]){ arr_j, key }); + if (JS_IsException(ret)) { + JS_FreeValue(js,arr_j); + JS_FreeValue(js,key); + return ret; + } double c = js2number(js, ret); JS_FreeValue(js, ret); @@ -7165,6 +7219,40 @@ JSC_CCALL(os_make_rtree, return rtree2js(js,tree); ) +JSC_CCALL(os_rects_to_sprites, + ret = JS_NewArray(js); + + JSValue image = argv[0]; + JSValue jstex = JS_GetProperty(js,image,texture_atom); + double w, h = 0; + JS_GETATOM(js,w,jstex,width_atom,number) + JS_GETATOM(js,h,jstex,height_atom,number) + SDL_GPUTexture *tex = js2SDL_GPUTexture(js,jstex); + JS_FreeValue(js,jstex); + JSValue rects = argv[1]; + rect uv; + JS_GETATOM(js,uv,image,rect_atom,rect) + + ret = JS_NewArray(js); + int n = js_arrlen(js,rects); + for (int i = 0; i < n; i++) { + JSValue sub = JS_GetPropertyUint32(js,rects,i); + sprite *s = make_sprite(); + s->affine = js2rect(js,sub); + s->affine.w = w; + s->affine.h = h; + s->image = JS_DupValue(js,image); + s->tex = tex; + s->uv = uv; + JS_SetPropertyUint32(js,ret,i,sprite2js(js,s)); + JS_FreeValue(js,sub); + } +) + +JSC_CCALL(os_on, + on_exception = JS_DupValue(js,argv[1]); +) + static const JSCFunctionListEntry js_os_funcs[] = { MIST_FUNC_DEF(os, turbulence, 4), MIST_FUNC_DEF(os, model_buffer, 1), @@ -7230,6 +7318,8 @@ static const JSCFunctionListEntry js_os_funcs[] = { MIST_FUNC_DEF(os, power_state, 0), MIST_FUNC_DEF(os, insertion_sort, 2), MIST_FUNC_DEF(os, cull_sprites, 2), + MIST_FUNC_DEF(os, rects_to_sprites,2), + MIST_FUNC_DEF(os, on, 2), }; JSC_CCALL(qtree_insert, @@ -7372,8 +7462,10 @@ JSC_GETSET(sprite, color, color) JSC_CCALL(sprite_set_affine, sprite *sp = js2sprite(js,self); transform *t = js2transform(js,argv[0]); -// sp->affine = HMM_Mat4ToMat3(t->gcache); - sp->affine = transform2rect(t); + if (!t) + sp->affine = js2rect(js,argv[0]); + else + sp->affine = transform2rect(t); ) JSC_CCALL(sprite_set_image, @@ -7384,7 +7476,7 @@ JSC_CCALL(sprite_set_image, if (JS_IsUndefined(sp->image)) return JS_UNDEFINED; JSValue img = sp->image; JS_GETATOM(js, sp->uv, img, rect_atom, rect) - JS_GETATOM(js, sp->tex, img, texture_atom, SDL_GPUTexture) + JS_GETATOM(js, sp->tex, img, texture_atom, SDL_GPUTexture); ) static const JSCFunctionListEntry js_sprite_funcs[] = { @@ -7451,7 +7543,6 @@ void ffi_load(JSContext *js) { JSValue globalThis = JS_GetGlobalObject(js); - QJSCLASSPREP_FUNCS(qtree) QJSCLASSPREP_FUNCS(rtree) QJSCLASSPREP_FUNCS(SDL_Window) @@ -7480,6 +7571,8 @@ void ffi_load(JSContext *js) { QJSCLASSPREP_FUNCS(SDL_GPUBuffer) // QJSCLASSPREP_FUNCS(SDL_GPUTransferBuffer) + QJSCLASSPREP_FUNCS(PHYSFS_File) + QJSGLOBALCLASS(os); QJSCLASSPREP_FUNCS(transform); diff --git a/source/script.c b/source/script.c index 4d2f9e40..4194ce4d 100644 --- a/source/script.c +++ b/source/script.c @@ -15,6 +15,9 @@ static JSRuntime *rt = NULL; JSContext *global_js = NULL; +JSValue on_exception = JS_UNDEFINED; + + #ifndef NDEBUG #define JS_EVAL_FLAGS JS_EVAL_FLAG_STRICT #else @@ -78,7 +81,7 @@ void script_startup() { ffi_load(js); char *eng = read_file("core/scripts/engine.js"); - JSValue v = script_eval("core/scripts/engine.js", eng); + JSValue v = script_eval(js, "core/scripts/engine.js", eng); JS_FreeValue(js, v); free(eng); } @@ -92,6 +95,15 @@ void script_stop() rt = NULL; } +void uncaught_exception(JSContext *js, JSValue v) +{ + JSValue ret; + if (!JS_IsUndefined(on_exception) && JS_IsException(v)) + ret = JS_Call(js, on_exception, JS_UNDEFINED, 1, &v); + JS_FreeValue(js,ret); + JS_FreeValue(js,v); +} + void script_mem_limit(size_t limit) { JS_SetMemoryLimit(rt, limit); } void script_gc_threshold(size_t threshold) { JS_SetGCThreshold(rt, threshold); } void script_max_stacksize(size_t size) { JS_SetMaxStackSize(rt, size); } @@ -111,20 +123,11 @@ void script_evalf(const char *format, ...) obj = JS_Eval(js, eval, len, "C eval", JS_EVAL_FLAGS); free(eval); - js_print_exception(js,obj); - JS_FreeValue(js,obj); + + uncaught_exception(js,obj); } -JSValue script_eval(const char *file, const char *script) +JSValue script_eval(JSContext *js, const char *file, const char *script) { - JSValue v = JS_Eval(js, script, strlen(script), file, JS_EVAL_FLAGS); - js_print_exception(js,v); - return v; -} - -void script_call_sym(JSValue sym, int argc, JSValue *argv) { - if (!JS_IsFunction(js, sym)) return; - JSValue ret = JS_Call(js, sym, JS_UNDEFINED, argc, argv); - js_print_exception(js,ret); - JS_FreeValue(js, ret); + return JS_Eval(js, script, strlen(script), file, JS_EVAL_FLAGS); } diff --git a/source/script.h b/source/script.h index 008e03ee..85504702 100644 --- a/source/script.h +++ b/source/script.h @@ -3,13 +3,14 @@ #include "quickjs.h" +extern JSValue on_exception; + void script_startup(); void script_stop(); void script_evalf(const char *format, ...); -JSValue script_eval(const char *file, const char *script); -void script_call_sym(JSValue sym, int argc, JSValue *argv); - +JSValue script_eval(JSContext *js, const char *file, const char *script); +void uncaught_exception(JSContext *js, JSValue v); extern JSContext *global_js; #endif diff --git a/source/sprite.c b/source/sprite.c index 85077f35..e873b725 100644 --- a/source/sprite.c +++ b/source/sprite.c @@ -1,8 +1,17 @@ #include "sprite.h" +static sprite model = { + .affine = {x:0,y:0,w:0,h:0}, + .image = JS_UNDEFINED, + .tex = NULL, + .uv = {x:0,y:0,w:1,h:1}, + .layer = 0, + .color = {1,1,1,1} +}; sprite *make_sprite() { - sprite *sprite = calloc(sizeof(*sprite),1); + sprite *sprite = malloc(sizeof(*sprite)); + *sprite = model; return sprite; } diff --git a/source/timer.c b/source/timer.c index 32798234..98bede24 100644 --- a/source/timer.c +++ b/source/timer.c @@ -34,7 +34,8 @@ void timer_update(JSContext *js, double dt) if (timers[i]->remain <= 0) { timers[i]->remain = -10000; JSValue fn = JS_DupValue(js, timers[i]->fn); - script_call_sym(timers[i]->fn, 0, NULL); + JSValue r = JS_Call(js,fn, JS_UNDEFINED, 0, NULL); + uncaught_exception(js,r); JS_FreeValue(js, fn); } }