From 9892883e9e285bd8f5445ad51be9f6ce8b6cc92b Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 2 Oct 2022 18:47:39 +0000 Subject: [PATCH] js: implement a hand-written wrapper for sqlite3_create_function_v2() which converts, if necessary, JS-function-type args to WASM function wrappers. Replace DB.createFunction() impl with the new one. FossilOrigin-Name: 435ab33384017967e46f52b70bee851a85a28808990a0e58dd5288f606b89c9c --- ext/wasm/api/README.md | 10 +- ext/wasm/api/sqlite3-api-glue.js | 177 +++++++++++++++++++++++++++ ext/wasm/api/sqlite3-api-oo1.js | 137 ++------------------- ext/wasm/api/sqlite3-api-prologue.js | 105 +++++++++++++--- ext/wasm/common/whwasmutil.js | 55 ++++++--- manifest | 20 +-- manifest.uuid | 2 +- 7 files changed, 324 insertions(+), 182 deletions(-) diff --git a/ext/wasm/api/README.md b/ext/wasm/api/README.md index fd85e215b4..b612bfcc22 100644 --- a/ext/wasm/api/README.md +++ b/ext/wasm/api/README.md @@ -47,13 +47,9 @@ browser client: independent spinoff project, conceived for the sqlite3 project but maintained separately. - `sqlite3-api-glue.js`\ - Invokes the function exposed by `sqlite3-api-prologue.js`, passing - it a configuration object to configure it for the current WASM - toolchain (noting that it currently requires Emscripten), then - removes that function from the global scope. The result of this file - is a global-scope `sqlite3` object which acts as a namespace for the - API's functionality. This object gets removed from the global scope - after the following files have attached their own features to it. + Invokes functionality exposed by the previous two files to + flesh out low-level parts of `sqlite3-api-prologue.js`. Most of + these pieces related to the `sqlite3.capi.wasm` object. - `sqlite3-api-oo1.js`\ Provides a high-level object-oriented wrapper to the lower-level C API, colloquially known as OO API #1. Its API is similar to other diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index 28bba5a74d..268824d6d0 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -192,6 +192,183 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }; }/*sqlite3_exec() proxy*/; + if(1){/* Special-case handling of sqlite3_create_function_v2() */ + const sqlite3CreateFunction = wasm.xWrap( + "sqlite3_create_function_v2", "int", + ["sqlite3*", "string", "int", "int", "*", + "*", "*", "*", "*"] + ); + const __setResult = function(pCx, val){ + switch(typeof val) { + case 'boolean': + capi.sqlite3_result_int(pCx, val ? 1 : 0); + break; + case 'number': { + (util.isInt32(val) + ? capi.sqlite3_result_int + : capi.sqlite3_result_double)(pCx, val); + break; + } + case 'string': + capi.sqlite3_result_text(pCx, val, -1, capi.SQLITE_TRANSIENT); + break; + case 'object': + if(null===val) { + capi.sqlite3_result_null(pCx); + break; + }else if(util.isBindableTypedArray(val)){ + const pBlob = wasm.allocFromTypedArray(val); + capi.sqlite3_result_blob(pCx, pBlob, val.byteLength, + capi.SQLITE_TRANSIENT); + wasm.dealloc(pBlob); + break; + } + // else fall through + default: + toss3("Don't not how to handle this UDF result value:",val); + }; + }/*__setResult()*/; + const __extractArgs = function(argc, pArgv){ + let i, pVal, valType, arg; + const tgt = []; + for(i = 0; i < argc; ++i){ + pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i)); + /** + Curiously: despite ostensibly requiring 8-byte + alignment, the pArgv array is parcelled into chunks of + 4 bytes (1 pointer each). The values those point to + have 8-byte alignment but the individual argv entries + do not. + */ + valType = capi.sqlite3_value_type(pVal); + switch(valType){ + case capi.SQLITE_INTEGER: + case capi.SQLITE_FLOAT: + arg = capi.sqlite3_value_double(pVal); + break; + case capi.SQLITE_TEXT: + arg = capi.sqlite3_value_text(pVal); + break; + case capi.SQLITE_BLOB:{ + const n = capi.sqlite3_value_bytes(pVal); + const pBlob = capi.sqlite3_value_blob(pVal); + arg = new Uint8Array(n); + let i; + const heap = n ? wasm.heap8() : false; + for(i = 0; i < n; ++i) arg[i] = heap[pBlob+i]; + break; + } + case capi.SQLITE_NULL: + arg = null; break; + default: + toss3("Unhandled sqlite3_value_type()",valType, + "is possibly indicative of incorrect", + "pointer size assumption."); + } + tgt.push(arg); + } + return tgt; + }/*__extractArgs()*/; + + const __setCxErr = (pCx, e)=>{ + if(e instanceof capi.WasmAllocError){ + capi.sqlite3_result_error_nomem(pCx); + }else{ + capi.sqlite3_result_error(pCx, e.message, -1); + } + }; + + const __xFunc = function(callback){ + return function(pCx, argc, pArgv){ + try{__setResult(pCx, callback(...__extractArgs(argc, pArgv)))} + catch(e){ __setCxErr(pCx, e) } + }; + }; + + const __xStep = function(callback){ + return function(pCx, argc, pArgv){ + try{ callback(...__extractArgs(argc, pArgv)) } + catch(e){ __setCxErr(pCx, e) } + }; + }; + + const __xFinal = function(callback){ + return function(pCx){ + try{ __setResult(pCx, callback()) } + catch(e){ __setCxErr(pCx, e) } + }; + }; + + const __xDestroy = function(callback){ + return function(pVoid){ + try{ callback(pVoid) } + catch(e){ + console.error("UDF xDestroy method threw:",e); + } + }; + }; + /* Documented in the api object's initializer. */ + capi.sqlite3_create_function_v2 = function f( + pDb, funcName, nArg, eTextRep, pApp, + xFunc, //void (*xFunc)(sqlite3_context*,int,sqlite3_value**) + xStep, //void (*xStep)(sqlite3_context*,int,sqlite3_value**) + xFinal, //void (*xFinal)(sqlite3_context*) + xDestroy //void (*xDestroy)(void*) + ){ + if(9!==arguments.length){ + return __dbArgcMismatch(pDb,"sqlite3_create_function_v2",9); + } + if(!f._sigs){ + f._wrap = Object.assign(Object.create(null), { + xFunc: {sig:'v(pip)', f:__xFunc}, + xStep: {sig:'v(pip)', f:__xStep}, + xFinal: {sig:'v(p)', f:__xFinal}, + xDestroy: {sig:'v(p)', f:__xDestroy} + }); + } + const callbacks = []; + /* Wrap the callbacks in a WASM-bound functions... */ + const wasm = capi.wasm; + const funcArgs = [], uninstall = [/*funcs to uninstall on error*/], + theFuncs = {xFunc, xStep, xFinal, xDestroy}; + let rc; + try{ + let k; + for(k in theFuncs){ + let fArg = theFuncs[k]; + if('function'===typeof fArg){ + const w = f._wrap[k]; + fArg = wasm.installFunction(w.sig, w.f(fArg)); + uninstall.push(fArg); + } + funcArgs.push(fArg); + } + rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, + pApp, ...funcArgs); + }catch(e){ + console.error("sqlite3_create_function_v2() setup threw:",e); + for(let v of uninstall){ + wasm.uninstallFunction(v); + } + rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR, + "Creation of UDF threw: "+e.message); + } + return rc; + }; + + capi.sqlite3_create_function = function( + pDb, funcName, nArg, eTextRep, pApp, + xFunc, xStep, xFinal + ){ + if(8!==arguments.length){ + return __dbArgcMismatch(pDb,"sqlite3_create_function",8); + } + return capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep, + pApp, xFunc, xStep, xFinal, 0); + + }; + }/*sqlite3_create_function_v2() proxy*/; + if(1){/* Special-case handling of sqlite3_prepare_v2() and sqlite3_prepare_v3() */ /** diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 07f0657fa4..f701c463f0 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -37,12 +37,6 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ it. */ const __ptrMap = new WeakMap(); - /** - Map of DB instances to objects, each object being a map of UDF - names to wasm function _pointers_ added to that DB handle via - createFunction(). - */ - const __udfMap = new WeakMap(); /** Map of DB instances to objects, each object being a map of Stmt wasm pointers to Stmt objects. @@ -154,7 +148,6 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ this.filename = fnJs; __ptrMap.set(this, ptr); __stmtMap.set(this, Object.create(null)); - __udfMap.set(this, Object.create(null)); }; /** @@ -453,12 +446,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ Object.keys(__stmtMap.get(this)).forEach((k,s)=>{ if(s && s.pointer) s.finalize(); }); - Object.values(__udfMap.get(this)).forEach( - wasm.uninstallFunction.bind(capi.wasm) - ); __ptrMap.delete(this); __stmtMap.delete(this); - __udfMap.delete(this); capi.sqlite3_close_v2(pDb); if(this.onclose && (this.onclose.after instanceof Function)){ try{this.onclose.after(this)} @@ -785,15 +774,11 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ On success, returns this object. Throws on error. - When called from SQL, arguments to the UDF, and its result, - will be converted between JS and SQL with as much fidelity - as is feasible, triggering an exception if a type - conversion cannot be determined. Some freedom is afforded - to numeric conversions due to friction between the JS and C - worlds: integers which are larger than 32 bits will be - treated as doubles, as JS does not support 64-bit integers - and it is (as of this writing) illegal to use WASM - functions which take or return 64-bit integers from JS. + When called from SQL arguments to the UDF, and its result, + will be converted between JS and SQL with as much fidelity as + is feasible, triggering an exception if a type conversion + cannot be determined. The docs for sqlite3_create_function_v2() + describe the conversions in more detail. The optional options object may contain flags to modify how the function is defined: @@ -813,11 +798,6 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ - .deterministic = SQLITE_DETERMINISTIC - .directOnly = SQLITE_DIRECTONLY - .innocuous = SQLITE_INNOCUOUS - - Maintenance reminder: the ability to add new - WASM-accessible functions to the runtime requires that the - WASM build is compiled with emcc's `-sALLOW_TABLE_GROWTH` - flag. */ createFunction: function f(name, callback,opt){ switch(arguments.length){ @@ -840,113 +820,16 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }else if('string' !== typeof name){ toss3("Invalid arguments: missing function name."); } - if(!f._extractArgs){ - /* Static init */ - f._extractArgs = function(argc, pArgv){ - let i, pVal, valType, arg; - const tgt = []; - for(i = 0; i < argc; ++i){ - pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i)); - /** - Curiously: despite ostensibly requiring 8-byte - alignment, the pArgv array is parcelled into chunks of - 4 bytes (1 pointer each). The values those point to - have 8-byte alignment but the individual argv entries - do not. - */ - valType = capi.sqlite3_value_type(pVal); - switch(valType){ - case capi.SQLITE_INTEGER: - case capi.SQLITE_FLOAT: - arg = capi.sqlite3_value_double(pVal); - break; - case capi.SQLITE_TEXT: - arg = capi.sqlite3_value_text(pVal); - break; - case capi.SQLITE_BLOB:{ - const n = capi.sqlite3_value_bytes(pVal); - const pBlob = capi.sqlite3_value_blob(pVal); - arg = new Uint8Array(n); - let i; - const heap = n ? wasm.heap8() : false; - for(i = 0; i < n; ++i) arg[i] = heap[pBlob+i]; - break; - } - case capi.SQLITE_NULL: - arg = null; break; - default: - toss3("Unhandled sqlite3_value_type()",valType, - "is possibly indicative of incorrect", - "pointer size assumption."); - } - tgt.push(arg); - } - return tgt; - }/*_extractArgs()*/; - f._setResult = function(pCx, val){ - switch(typeof val) { - case 'boolean': - capi.sqlite3_result_int(pCx, val ? 1 : 0); - break; - case 'number': { - (util.isInt32(val) - ? capi.sqlite3_result_int - : capi.sqlite3_result_double)(pCx, val); - break; - } - case 'string': - capi.sqlite3_result_text(pCx, val, -1, capi.SQLITE_TRANSIENT); - break; - case 'object': - if(null===val) { - capi.sqlite3_result_null(pCx); - break; - }else if(util.isBindableTypedArray(val)){ - const pBlob = wasm.allocFromTypedArray(val); - capi.sqlite3_result_blob(pCx, pBlob, val.byteLength, - capi.SQLITE_TRANSIENT); - wasm.dealloc(pBlob); - break; - } - // else fall through - default: - toss3("Don't not how to handle this UDF result value:",val); - }; - }/*_setResult()*/; - }/*static init*/ - const wrapper = function(pCx, argc, pArgv){ - try{ - f._setResult(pCx, callback.apply(null, f._extractArgs(argc, pArgv))); - }catch(e){ - if(e instanceof capi.WasmAllocError){ - capi.sqlite3_result_error_nomem(pCx); - }else{ - capi.sqlite3_result_error(pCx, e.message, -1); - } - } - }; - const pUdf = wasm.installFunction(wrapper, "v(iii)"); let fFlags = 0 /*flags for sqlite3_create_function_v2()*/; if(getOwnOption(opt, 'deterministic')) fFlags |= capi.SQLITE_DETERMINISTIC; if(getOwnOption(opt, 'directOnly')) fFlags |= capi.SQLITE_DIRECTONLY; if(getOwnOption(opt, 'innocuous')) fFlags |= capi.SQLITE_INNOCUOUS; name = name.toLowerCase(); - try { - DB.checkRc(this, capi.sqlite3_create_function_v2( - this.pointer, name, - (opt.hasOwnProperty('arity') ? +opt.arity : callback.length), - capi.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf, - null/*xStep*/, null/*xFinal*/, null/*xDestroy*/)); - }catch(e){ - wasm.uninstallFunction(pUdf); - throw e; - } - const udfMap = __udfMap.get(this); - if(udfMap[name]){ - try{wasm.uninstallFunction(udfMap[name])} - catch(e){/*ignore*/} - } - udfMap[name] = pUdf; + DB.checkRc(this, capi.sqlite3_create_function_v2( + this.pointer, name, + (opt.hasOwnProperty('arity') ? +opt.arity : callback.length), + capi.SQLITE_UTF8 | fFlags, null/*pApp*/, callback, + null/*xStep*/, null/*xFinal*/, null/*xDestroy*/)); return this; }/*createFunction()*/, /** diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 59533815fb..fd66eccfd6 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -285,29 +285,94 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( */ const capi = { /** - When using sqlite3_open_v2() it is important to keep the following - in mind: + sqlite3_create_function_v2() differs from its native + counterpart only in the following ways: - https://www.sqlite.org/c3ref/open.html + 1) The fourth argument (`eTextRep`) argument must not specify + any encoding other than sqlite.SQLITE_UTF8. The JS API does not + currently support any other encoding and likely never + will. This function does not replace that argument on its own + because it may contain other flags. - - The flags for use with its 3rd argument are installed in this - object using their C-side names, e.g. SQLITE_OPEN_CREATE. + 2) Any of the four final arguments may be either WASM pointers + (assumed to be function pointers) or JS Functions. In the + latter case, each gets bound to WASM using + sqlite3.capi.wasm.installFunction() and that wrapper is passed + on to the native implementation. - - If the combination of flags passed to it are invalid, - behavior is undefined. Thus is is never okay to call this - with fewer than 3 arguments, as JS will default the - missing arguments to `undefined`, which will result in a - flag value of 0. Most of the available SQLITE_OPEN_xxx - flags are meaningless in the WASM build, e.g. the mutext- - and cache-related flags, but they are retained in this - API for consistency's sake. + The semantics of JS functions are: - - The final argument to this function specifies the VFS to use, - which is largely (but not entirely!) meaningless in the WASM - environment. It may be null, undefined, or 0 to denote the - default. + xFunc: is passed `(arrayOfValues)`. Its return value becomes + the new SQL function's result. + + xStep: is passed `(arrayOfValues)`. Its return value is + ignored. + + xFinal: is passed no arguments. Its return value becomes the + new aggragate SQL function's result. + + xDestroy: is passed `(void*)`. Its return value is ignored. The + pointer passed to it is the one from the 5th argument to + sqlite3_create_function_v2(). + + Note that JS callback implementations have different call + signatures than their native counterparts (namely, they do not + get passed an `sqlite3_context*` argument) because practice has + shown that this is almost always more convenient and desirable + in JS code. Clients which need access to the full range of + native arguments will have to create a WASM-bound function + themselves (using wasm.installFunction() or equivalent) and + pass that function's WASM pointer to this function, rather than + passing a JS function. Be warned, however, that working with + UDFs at that level from JS is quite tedious. + + For xFunc(), xStep(), and xFinal(): + + - When called from SQL, arguments to the UDF, and its result, + will be converted between JS and SQL with as much fidelity as + is feasible, triggering an exception if a type conversion + cannot be determined. Some freedom is afforded to numeric + conversions due to friction between the JS and C worlds: + integers which are larger than 32 bits will be treated as + doubles. TODO: use BigInt support if enabled. That feature + was added after this functionality was implemented. + + If any JS-side functions throw, those exceptions are + intercepted and converted to database-side errors with + the exception of xFinal(): any exception from it is + ignored, possibly generating a console.error() message. + Destructors must not throw. + + Once installed, there is currently no way to uninstall the + bound methods from WASM. They can be uninstalled from the + database as documented in the C API, but this wrapper currently + has no infrastructure in place to also free the WASM-bound JS + wrappers, effectively resulting in a memory leak if the client + uninstalls the UDF. Improving that is a potential TODO, but + removing client-installed UDFs is rare in practice. + + Maintenance reminder: the ability to add new + WASM-accessible functions to the runtime requires that the + WASM build is compiled with emcc's `-sALLOW_TABLE_GROWTH` + flag. */ - sqlite3_open_v2: function(filename,dbPtrPtr,flags,vfsStr){}/*installed later*/, + sqlite3_create_function_v2: function( + pDb, funcName, nArg, eTextRep, pApp, + xFunc, //function(arrayOfValues) + xStep, //function(arrayOfValues) + xFinal, //function() + xDestroy //function(void*) + ){/*installed later*/}, + /** + Equivalent to passing the same arguments to + sqlite3_create_function_v2(), with 0 as the final argument. + */ + sqlite3_create_function:function( + pDb, funcName, nArg, eTextRep, pApp, + xFunc, //function(arrayOfValues) + xStep, //function(arrayOfValues) + xFinal //function() + ){/*installed later*/}, /** The sqlite3_prepare_v3() binding handles two different uses with differing JS/WASM semantics: @@ -642,8 +707,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( ["sqlite3_column_type","int", "sqlite3_stmt*", "int"], ["sqlite3_compileoption_get", "string", "int"], ["sqlite3_compileoption_used", "int", "string"], - ["sqlite3_create_function_v2", "int", - "sqlite3*", "string", "int", "int", "*", "*", "*", "*", "*"], + /* sqlite3_create_function_v2() is handled separate to simplify conversion + of its callback argument */ ["sqlite3_data_count", "int", "sqlite3_stmt*"], ["sqlite3_db_filename", "string", "sqlite3*", "string"], ["sqlite3_db_handle", "sqlite3*", "sqlite3_stmt*"], diff --git a/ext/wasm/common/whwasmutil.js b/ext/wasm/common/whwasmutil.js index 6e8fa00dc2..ed3d7714bc 100644 --- a/ext/wasm/common/whwasmutil.js +++ b/ext/wasm/common/whwasmutil.js @@ -15,11 +15,17 @@ https://fossil.wanderinghorse.net/r/jaccwabyt + and sqlite3: + + https://sqlite.org + + This file is kept in sync between both of those trees. + Maintenance reminder: If you're reading this in a tree other than - the Jaccwabyt tree, note that this copy may be replaced with + one of those listed above, note that this copy may be replaced with upstream copies of that one from time to time. Thus the code - installed by this function "should not" be edited outside of that - project, else it risks getting overwritten. + installed by this function "should not" be edited outside of those + projects, else it risks getting overwritten. */ /** This function is intended to simplify porting around various bits @@ -109,8 +115,8 @@ following symbols: - `memory`: a WebAssembly.Memory object representing the WASM - memory. _Alternately_, the `memory` property can be set on the - target instance, in particular if the WASM heap memory is + memory. _Alternately_, the `memory` property can be set as + `target.memory`, in particular if the WASM heap memory is initialized in JS an _imported_ into WASM, as opposed to being initialized in WASM and exported to JS. @@ -132,7 +138,11 @@ false. If it is false, certain BigInt-related features will trigger an exception if invoked. This property, if not set when this is called, will get a default value of true only if the BigInt64Array - constructor is available, else it will default to false. + constructor is available, else it will default to false. Note that + having the BigInt type is not sufficient for full int64 integration + with WASM: the target WASM file must also have been built with + that support. In Emscripten that's done using the `-sWASM_BIGINT` + flag. Some optional APIs require that the target have the following methods: @@ -295,7 +305,7 @@ self.WhWasmUtilInstaller = function(target){ an integer as the first argument and unsigned is truthy then the "U" (unsigned) variant of that view is returned, else the signed variant is returned. If passed a TypedArray value, the - 2nd argument is ignores. Note that Float32Array and + 2nd argument is ignored. Note that Float32Array and Float64Array views are not supported by this function. Note that growth of the heap will invalidate any references to @@ -511,7 +521,7 @@ self.WhWasmUtilInstaller = function(target){ if(2!==arguments.length){ toss("installFunction() requires exactly 2 arguments"); } - if('string'===typeof func && sig instanceof Function){ + if('string'===typeof func){ const x = sig; sig = func; func = x; @@ -1137,7 +1147,7 @@ self.WhWasmUtilInstaller = function(target){ e.g. using setMemValue() or getMemValue(), it is important that the pointer in question be aligned to an 8-byte boundary or else it will not be fetched or written properly and will corrupt or - read neighboring memory. It i only safe to pass false when the + read neighboring memory. It is only safe to pass false when the client code is certain that it will only get/fetch 4-byte values (or smaller). */ @@ -1239,12 +1249,12 @@ self.WhWasmUtilInstaller = function(target){ return v ? xcv.arg[ptrIR](v) : null; }; xcv.result.string = (i)=>target.cstringToJs(i); - xcv.result['string:free'] = function(i){ + xcv.result['string:free'] = (i)=>{ try { return i ? target.cstringToJs(i) : null } finally{ target.dealloc(i) } }; xcv.result.json = (i)=>JSON.parse(target.cstringToJs(i)); - xcv.result['json:free'] = function(i){ + xcv.result['json:free'] = (i)=>{ try{ return i ? JSON.parse(target.cstringToJs(i)) : null } finally{ target.dealloc(i) } } @@ -1374,14 +1384,26 @@ self.WhWasmUtilInstaller = function(target){ C-string, ownership of which has just been transfered to the caller. It copies the C-string to a JS string, frees the C-string, and returns the JS string. If such a result value is - NULL, the JS result is `null`. + NULL, the JS result is `null`. Achtung: when using an API which + returns results from a specific allocator, e.g. `my_malloc()`, + this conversion _is not legal_. Instead, an equivalent conversion + which uses the appropriate deallocator is required. For example: + +```js + target.xWrap.resultAdaptor('string:my_free',(i)=>{ + try { return i ? target.cstringToJs(i) : null } + finally{ target.exports.my_free(i) } + }; +``` - `json` (results): treats the result as a const C-string and returns the result of passing the converted-to-JS string to JSON.parse(). Returns `null` if the C-string is a NULL pointer. - `json:free` (results): works exactly like `string:free` but - returns the same thing as the `json` adapter. + returns the same thing as the `json` adapter. Note the + warning in `string:free` regarding maching allocators and + deallocators. The type names for results and arguments are validated when xWrap() is called and any unknown names will trigger an @@ -1532,11 +1554,10 @@ self.WhWasmUtilInstaller = function(target){ type name, as documented for xWrap() (use a falsy value or an empty array for nullary functions). The 4th+ arguments are arguments for the call, with the special case that if the 4th - argument is an array, it is used as the arguments for the call - (again, falsy or an empty array for nullary functions). Returns - the converted result of the call. + argument is an array, it is used as the arguments for the + call. Returns the converted result of the call. - This is just a thin wrapp around xWrap(). If the given function + This is just a thin wrapper around xWrap(). If the given function is to be called more than once, it's more efficient to use xWrap() to create a wrapper, then to call that wrapper as many times as needed. For one-shot calls, however, this variant is diff --git a/manifest b/manifest index 2f055f89b0..c522e4391c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Doc\stypo\sfixes. -D 2022-10-02T03:14:38.605 +C js:\simplement\sa\shand-written\swrapper\sfor\ssqlite3_create_function_v2()\swhich\sconverts,\sif\snecessary,\sJS-function-type\sargs\sto\sWASM\sfunction\swrappers.\sReplace\sDB.createFunction()\simpl\swith\sthe\snew\sone. +D 2022-10-02T18:47:39.889 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -478,17 +478,17 @@ F ext/wasm/GNUmakefile b313a82060c733c990b91afa981e10f5e21a0b33a483f33b739ce932e F ext/wasm/README.md 1e5b28158b74ab3ffc9d54fcbc020f0bbeb82c2ff8bbd904214c86c70e8a3066 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api c6b05641d733227a995cc05a2ba7ddc9ef2323081ae0cae5ed1d1f83d5b54b36 F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 -F ext/wasm/api/README.md b6d19392984fdd428ae7260512d45742160c06baefed9da703da3fdeb68e7124 +F ext/wasm/api/README.md 1e350b611465566cfa2e5eccf7c9b29a34f48ee38bbf6d5fb086dd06ce32b3ff F ext/wasm/api/extern-post-js.js dc68cbf552d8ea085181400a6963907c32e0b088b03ffd8969b1869fea246629 F ext/wasm/api/extern-pre-js.js 20143b16b672d0a576fbf768a786d12ee1f84e222126477072389b992542a5b2 F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c F ext/wasm/api/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1ecc5ba9e64ef90a8b F ext/wasm/api/pre-js.js 2db711eb637991b383fc6b5c0f3df65ec48a7201e5730e304beba8de2d3f9b0b F ext/wasm/api/sqlite3-api-cleanup.js 5d22d1d3818ecacb23bfa223d5970cd0617d8cdbb48c8bc4bbd463f05b021a99 -F ext/wasm/api/sqlite3-api-glue.js 20b98987dd0c6d02ca308e0cf9299750d618459e0876b354380b87ae2dc5cba0 -F ext/wasm/api/sqlite3-api-oo1.js 066e67f3033e1b300140d431557c468f5cd0a4c17253f156e05b8a2e2c802da7 +F ext/wasm/api/sqlite3-api-glue.js d834733fbdc216beb65382655e61a436e29018412005aab413ff022d4e5c25eb +F ext/wasm/api/sqlite3-api-oo1.js 48d2269544301cd97726ef2d9a82e4384350d12dcf832fa417f211811ae5272d F ext/wasm/api/sqlite3-api-opfs.js 1b097808b7b081b0f0700cf97d49ef19760e401706168edff9cd45cf9169f541 -F ext/wasm/api/sqlite3-api-prologue.js 9b0c5150f0129b3dc558fec0bfc9c8bf7ebda402b58965bcf98b2ed117b55239 +F ext/wasm/api/sqlite3-api-prologue.js 32795679b72a5ad685c58a9599d10dac04787907f623825408d539b703e080a4 F ext/wasm/api/sqlite3-api-worker1.js 7f4f46cb6b512a48572d7567233896e6a9c46570c44bdc3d13419730c7c221c8 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 2a0f9e4bf1b141a787918951360601128d6a0a190a31a8e5cfe237c99fa640c6 @@ -497,7 +497,7 @@ F ext/wasm/batch-runner.js ce92650a6681586c89bef26ceae96674a55ca5a9727815202ca62 F ext/wasm/common/SqliteTestUtil.js 647bf014bd30bdd870a7e9001e251d12fc1c9ec9ce176a1004b838a4b33c5c05 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 3a5143699c2b73a85b962271e1a9b3241b30d90e30d895e4f55665e648572962 -F ext/wasm/common/whwasmutil.js af9bb6586d8a2ab7c55c7f1554bb6b0328a7bd6a86ea5fa9ef5273a738156ac3 +F ext/wasm/common/whwasmutil.js 427eb8b695bd5f38497601a6bda933e83d1a5900b75f5f1af8dbb381898d2ee4 F ext/wasm/demo-123-worker.html e419b66495d209b5211ec64903b4cfb3ca7df20d652b41fcd28bf018a773234f F ext/wasm/demo-123.html aa281d33b7eefa755f3122b7b5a18f39a42dc5fb69c8879171bf14b4c37c4ec4 F ext/wasm/demo-123.js 536579fd587974c2511c5bf82034b253d4fdeceabb726927ad7599ef6b7578e8 @@ -2029,8 +2029,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P eb5726677a727a958df11f1fba078d30c7c0ba2a9bdb158e8641b35b5f971af3 -R c02b54a31af3ce39f04c3d79de05d508 +P e528675da3971907666c7d2d09763975a105ec585dc5122145f65905d535bed8 +R 905afd6e962345097748ac52db8a0889 U stephan -Z a6c5eed947d3361089be1f642079a7a3 +Z 8de102f5519af227a27849d21742ab64 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2e83e50aa0..e0800ed178 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e528675da3971907666c7d2d09763975a105ec585dc5122145f65905d535bed8 \ No newline at end of file +435ab33384017967e46f52b70bee851a85a28808990a0e58dd5288f606b89c9c \ No newline at end of file