more friendly javascript arguments
This commit is contained in:
@@ -262,6 +262,10 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r
|
|||||||
let cbName = UTF8ToString(cb_name);
|
let cbName = UTF8ToString(cb_name);
|
||||||
// console.log("local_callback:", cbName, fmt, 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);
|
reentryMutexLock(name);
|
||||||
|
|
||||||
let argTypes = fmt.split("");
|
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 = [];
|
let jsArgs = [];
|
||||||
for (let i = 0; i < argTypes.length; i++) {
|
for (let i = 0; i < argTypes.length; i++) {
|
||||||
let ptr = args + (4*i);
|
let ptr = args + (4*i);
|
||||||
let val = typeLookup(argTypes[i], ptr);
|
let val = getArg(name, ptr, argTypes[i]);
|
||||||
jsArgs.push(val);
|
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];
|
let userCallback = globalThis[cbName];
|
||||||
runJsEventLoop(() => userCallback.call(this, name, ... jsArgs)).then((retVal) => {
|
runJsEventLoop(() => userCallback.call(this, name, ... jsArgs)).then((retVal) => {
|
||||||
// save the return value
|
// save the return value
|
||||||
setReturn(name, ret_ptr, retType, retVal);
|
setPointerValue(name, ret_ptr, retType, retVal);
|
||||||
// return
|
// return
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
reentryMutexUnlock();
|
reentryMutexUnlock();
|
||||||
@@ -289,37 +293,6 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r
|
|||||||
}, 0);
|
}, 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
|
// make callback arguments friendly: convert numbers to strings where possible
|
||||||
function decodeArgs(name, args) {
|
function decodeArgs(name, args) {
|
||||||
// if (!globalThis.nethackGlobal.makeArgsFriendly) return;
|
// 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]];
|
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
|
// 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) {
|
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 {
|
} else {
|
||||||
args[1] = typeLookup("p", args[1]);
|
args[1] = getArg(name, args[1], "p");
|
||||||
}
|
}
|
||||||
break;
|
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)
|
// 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
|
// 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()
|
// 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) {
|
function reentryMutexLock(name) {
|
||||||
globalThis.nethackGlobal = globalThis.nethackGlobal || {};
|
globalThis.nethackGlobal = globalThis.nethackGlobal || {};
|
||||||
if(globalThis.nethackGlobal.shimPreventReentry) {
|
if(globalThis.nethackGlobal.shimFunctionRunning) {
|
||||||
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`);
|
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() {
|
function reentryMutexUnlock() {
|
||||||
globalThis.nethackGlobal.shimPreventReentry = null;
|
globalThis.nethackGlobal.shimFunctionRunning = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user