From dc1c85faa42c2bfc36e31810f55ae45162ffc7cd Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sun, 4 Oct 2020 14:04:56 -0700 Subject: [PATCH] more friendly javascript arguments --- win/shim/winshim.c | 158 +++++++++++++++++++++------------------------ 1 file changed, 75 insertions(+), 83 deletions(-) diff --git a/win/shim/winshim.c b/win/shim/winshim.c index 8218b7382..2910f0be6 100644 --- a/win/shim/winshim.c +++ b/win/shim/winshim.c @@ -262,6 +262,10 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r let cbName = UTF8ToString(cb_name); // console.log("local_callback:", cbName, fmt, name); + // get pointer / type conversion helpers + let getPointerValue = globalThis.nethackGlobal.helpers.getPointerValue; + let setPointerValue = globalThis.nethackGlobal.helpers.setPointerValue; + reentryMutexLock(name); let argTypes = fmt.split(""); @@ -271,7 +275,7 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r let jsArgs = []; for (let i = 0; i < argTypes.length; i++) { let ptr = args + (4*i); - let val = typeLookup(argTypes[i], ptr); + let val = getArg(name, ptr, argTypes[i]); jsArgs.push(val); } @@ -281,7 +285,7 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r let userCallback = globalThis[cbName]; runJsEventLoop(() => userCallback.call(this, name, ... jsArgs)).then((retVal) => { // save the return value - setReturn(name, ret_ptr, retType, retVal); + setPointerValue(name, ret_ptr, retType, retVal); // return setTimeout(() => { reentryMutexUnlock(); @@ -289,37 +293,6 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r }, 0); }); - - // convert 'ptr' to the type indicated by 'type' - function typeLookup(type, ptr) { - switch(type) { - case "s": // string - return UTF8ToString(getValue(ptr, "*")); - case "p": // pointer - ptr = getValue(ptr, "*"); - if(!ptr) return 0; // null pointer - return getValue(ptr, "*"); - case "c": // char - return String.fromCharCode(getValue(getValue(ptr, "*"), "i8")); - case "0": /* 2^0 = 1 byte */ - return getValue(getValue(ptr, "*"), "i8"); - case "1": /* 2^1 = 2 bytes */ - return getValue(getValue(ptr, "*"), "i16"); - case "2": /* 2^2 = 4 bytes */ - case "i": // integer - case "n": // number - return getValue(getValue(ptr, "*"), "i32"); - case "f": // float - return getValue(getValue(ptr, "*"), "float"); - case "d": // double - return getValue(getValue(ptr, "*"), "double"); - case "o": // overloaded: multiple types - return ptr; - default: - throw new TypeError ("unknown type:" + type); - } - } - // make callback arguments friendly: convert numbers to strings where possible function decodeArgs(name, args) { // if (!globalThis.nethackGlobal.makeArgsFriendly) return; @@ -333,14 +306,77 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r args[0] = globalThis.nethackGlobal.constants["STATUS_FIELD"][args[0]]; // arg[1] is a string unless it is BL_CONDITION, BL_RESET, BL_FLUSH, BL_CHARACTERISTICS if(["BL_CONDITION", "BL_RESET", "BL_FLUSH", "BL_CHARACTERISTICS"].indexOf(args[0] && args[1]) < 0) { - args[1] = typeLookup("s", args[1]); + args[1] = getArg(name, args[1], "s"); } else { - args[1] = typeLookup("p", args[1]); + args[1] = getArg(name, args[1], "p"); } break; + case "shim_display_file": + args[1] = !!args[1]; + case "shim_display_nhwindow": + args[0] = decodeWindow(args[0]); + args[1] = !!args[1]; + break; + case "shim_getmsghistory": + args[0] = !!args[0]; + break; + case "shim_putmsghistory": + args[1] = !!args[1]; + break; + case "shim_status_enablefield": + console.log("shim_status_enablefield arg 1:", args[1]); + args[3] = !!args[3]; + break; + case "shim_add_menu": + args[0] = decodeWindow(args[0]); + // args[1] = mapglyphHelper(args[1]); + // args[5] = decodeAttr(args[5]); + break; + case "shim_putstr": + args[0] = decodeWindow(args[0]); + break; + case "shim_select_menu": + args[0] = decodeWindow(args[0]); + args[1] = decodeSelected(args[1]); + break; + case "shim_clear_nhwindow": + case "shim_destroy_nhwindow": + case "shim_curs": + case "shim_start_menu": + case "shim_end_menu": + case "shim_print_glyph": + args[0] = decodeWindow(args[0]); + break; } } + function decodeWindow(winid) { + let { WIN_MAP, WIN_INVEN, WIN_STATUS, WIN_MESSAGE } = globalThis.nethackGlobal.globals; + switch(winid) { + case WIN_MAP: return "WIN_MAP"; + case WIN_MESSAGE: return "WIN_MESSAGE"; + case WIN_STATUS: return "WIN_STATUS"; + case WIN_INVEN: return "WIN_INVEN"; + default: return winid; + } + // return winid; + } + + function decodeSelected(how) { + let { PICK_NONE, PICK_ONE, PICK_ANY } = globalThis.nethackGlobal.constants.MENU_SELECT; + switch(how) { + case PICK_NONE: return "PICK_NONE"; + case PICK_ONE: return "PICK_ONE"; + case PICK_ANY: return "PICK_ANY"; + default: return how; + } + + } + + function getArg(name, ptr, type) { + return (type === "o")?ptr:getPointerValue(name, getValue(ptr, "*"), type); + } + // setTimeout() with value of '0' is similar to setImmediate() (but setImmediate isn't standard) // this lets the JS loop run for a tick so that other events can occur // XXX: I also tried replacing the for(;;) in allmain.c:moveloop() with emscripten_set_main_loop() @@ -357,60 +393,16 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r }); } - // sets the return value of the function to the type expected - function setReturn(name, ptr, type, value = 0) { - switch (type) { - case "p": - throw new Error("not implemented"); - case "s": - if(typeof value !== "string") - throw new TypeError(`expected ${name} return type to be string`); - value=value?value:"(no value)"; - var strPtr = getValue(ptr, "i32"); - stringToUTF8(value, strPtr, 1024); - break; - case "i": - if(typeof value !== "number" || !Number.isInteger(value)) - throw new TypeError(`expected ${name} return type to be integer`); - setValue(ptr, value, "i32"); - break; - case "c": - if(typeof value !== "number" || value < 0 || value > 128) - throw new TypeError(`expected ${name} return type to be integer representing an ASCII character`); - setValue(ptr, value, "i8"); - break; - case "f": - if(typeof value !== "number" || isFloat(value)) - throw new TypeError(`expected ${name} return type to be float`); - // XXX: I'm not sure why 'double' works and 'float' doesn't - setValue(ptr, value, "double"); - break; - case "d": - if(typeof value !== "number" || isFloat(value)) - throw new TypeError(`expected ${name} return type to be double`); - setValue(ptr, value, "double"); - break; - case "v": - break; - default: - throw new Error("unknown type"); - } - - function isFloat(n){ - return n === +n && n !== (n|0) && !Number.isInteger(n); - } - } - function reentryMutexLock(name) { globalThis.nethackGlobal = globalThis.nethackGlobal || {}; - if(globalThis.nethackGlobal.shimPreventReentry) { - throw new Error(`'${name}' attempting second call to 'local_callback' before '${globalThis.nethackGlobal.shimPreventReentry}' has finished, will crash emscripten Asyncify. For details see: emscripten.org/docs/porting/asyncify.html#reentrancy`); + if(globalThis.nethackGlobal.shimFunctionRunning) { + throw new Error(`'${name}' attempting second call to 'local_callback' before '${globalThis.nethackGlobal.shimFunctionRunning}' has finished, will crash emscripten Asyncify. For details see: emscripten.org/docs/porting/asyncify.html#reentrancy`); } - globalThis.nethackGlobal.shimPreventReentry = name; + globalThis.nethackGlobal.shimFunctionRunning = name; } function reentryMutexUnlock() { - globalThis.nethackGlobal.shimPreventReentry = null; + globalThis.nethackGlobal.shimFunctionRunning = null; } }); }) -- 2.50.1