]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
js: implement a hand-written wrapper for sqlite3_create_function_v2() which converts...
authorstephan <stephan@noemail.net>
Sun, 2 Oct 2022 18:47:39 +0000 (18:47 +0000)
committerstephan <stephan@noemail.net>
Sun, 2 Oct 2022 18:47:39 +0000 (18:47 +0000)
FossilOrigin-Name: 435ab33384017967e46f52b70bee851a85a28808990a0e58dd5288f606b89c9c

ext/wasm/api/README.md
ext/wasm/api/sqlite3-api-glue.js
ext/wasm/api/sqlite3-api-oo1.js
ext/wasm/api/sqlite3-api-prologue.js
ext/wasm/common/whwasmutil.js
manifest
manifest.uuid

index fd85e215b4fe1ae97ed1b2928d8f165d660f1c57..b612bfcc220b2bf52d1d7e54ed209638655f2c68 100644 (file)
@@ -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
index 28bba5a74df6c21b972bb8202f327706c27d9e9f..268824d6d00100b5001d0273c11a11ca0895c038 100644 (file)
@@ -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() */
     /**
index 07f0657fa4b5cb73ff190b898661c479a28a69d4..f701c463f0e026846b7fb7c43c69195c139ca67c 100644 (file)
@@ -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()*/,
     /**
index 59533815fbf533b2c539933bd4fee70781a26aca..fd66eccfd66f2d74125c76b5810099a7d9c756f1 100644 (file)
@@ -285,29 +285,94 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
   */
   const capi = {
     /**
-       When using sqlite3_open_v2() it is important to keep the following
-       in mind:
-
-       https://www.sqlite.org/c3ref/open.html
-
-       - The flags for use with its 3rd argument are installed in this
-       object using their C-side names, e.g. SQLITE_OPEN_CREATE.
-
-       - 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 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.
+       sqlite3_create_function_v2() differs from its native
+       counterpart only in the following ways:
+
+       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.
+
+       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.
+
+       The semantics of JS functions are:
+
+       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*"],
index 6e8fa00dc24a3b7e1b59a4862a76d2ab33a62cc5..ed3d7714bc39a06dcae5fdac347804c53312cda8 100644 (file)
 
   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
      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.
 
    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
index 2f055f89b0f941e56945d435ea75e6bd1ceb1f29..c522e4391c2d67391fe9aaf400b746ef20eacd47 100644 (file)
--- 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.
index 2e83e50aa0591afe169db1135e36b8a1bd3b7817..e0800ed178df948e4ee78a291c2042d2668c32de 100644 (file)
@@ -1 +1 @@
-e528675da3971907666c7d2d09763975a105ec585dc5122145f65905d535bed8
\ No newline at end of file
+435ab33384017967e46f52b70bee851a85a28808990a0e58dd5288f606b89c9c
\ No newline at end of file