]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Wasm: (A) diverse internal doc updates. (B) when generating automated JS-to-WASM...
authorstephan <stephan@noemail.net>
Fri, 19 Sep 2025 14:21:09 +0000 (14:21 +0000)
committerstephan <stephan@noemail.net>
Fri, 19 Sep 2025 14:21:09 +0000 (14:21 +0000)
FossilOrigin-Name: 5e5139c2a162562cee0071d03954ebc0b8938da0b045ec3f5eba32dc8e19604d

ext/wasm/api/sqlite3-wasm.c
ext/wasm/common/whwasmutil.js
manifest
manifest.uuid

index e3ded9198759c35c1940d8735c24315f9631b37f..16545270df3a41224018012049a1717bdb9bdf32 100644 (file)
 ** Which sqlite3.c we're using needs to be configurable to enable
 ** building against a custom copy, e.g. the SEE variant. We #include
 ** the .c file, rather than the header, so that the WASM extensions
-** have access to private API internals.
+** have access to private API internals (namely for kvvfs and
+** SQLTester pieces).
 **
 ** The caveat here is that custom variants need to account for
 ** exporting any necessary symbols (e.g. sqlite3_activate_see()).  We
@@ -1753,7 +1754,7 @@ int sqlite3__wasm_init_wasmfs(const char *zMountPoint){
 SQLITE_WASM_EXPORT
 int sqlite3__wasm_init_wasmfs(const char *zUnused){
   //emscripten_console_warn("WASMFS OPFS is not compiled in.");
-  if(zUnused){/*unused*/}
+  (void)zUnused;
   return SQLITE_NOTFOUND;
 }
 #endif /* __EMSCRIPTEN__ && SQLITE_ENABLE_WASMFS */
index e856695797d72f93bc3cecbeb770c013c3c3fd95..48af31ceb057c35927166b24cd52cac92a2a990c 100644 (file)
 
   ***********************************************************************
 
-  The whwasmutil is developed in conjunction with the Jaccwabyt
-  project:
+  This code is developed in conjunction with the Jaccwabyt project:
 
   https://fossil.wanderinghorse.net/r/jaccwabyt
 
-  and sqlite3:
+  and SQLite:
 
   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
-  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 those
-  projects, else it risks getting overwritten.
 */
 /**
-   This function is intended to simplify porting around various bits
-   of WASM-related utility code from project to project.
-
-   The primary goal of this code is to replace, where possible,
+   The primary goal of this function is to replace, where possible,
    Emscripten-generated glue code with equivalent utility code which
    can be used in arbitrary WASM environments built with toolchains
    other than Emscripten. As of this writing, this code is capable of
    APIs such as its "FS" (virtual filesystem) API. Loading of such
    things still requires using Emscripten's glue, but the post-load
    utility APIs provided by this code are still usable as replacements
-   for their sub-optimally-documented Emscripten counterparts.
+   for their Emscripten counterparts.
+
+   Forewarning: this API explicitly targets only browser
+   environments. If a given non-browser environment has the
+   capabilities needed for a given feature (e.g. TextEncoder), great,
+   but it does not go out of its way to account for them and does not
+   provide compatibility crutches for them.
 
    Intended usage:
 
    ```
-   globalThis.WhWasmUtilInstaller(appObject);
+   const target = {}; // ... some object ...
+   globalThis.WhWasmUtilInstaller(target);
    delete globalThis.WhWasmUtilInstaller;
    ```
 
-   Its global-scope symbol is intended only to provide an easy way to
-   make it available to 3rd-party scripts and "should" be deleted
-   after calling it. That symbol is _not_ used within the library.
+   The `target` object then holds the APIs. It may have certain
+   properties set to configure it, as documented below.
 
-   Forewarning: this API explicitly targets only browser
-   environments. If a given non-browser environment has the
-   capabilities needed for a given feature (e.g. TextEncoder), great,
-   but it does not go out of its way to account for them and does not
-   provide compatibility crutches for them.
+   The global-scope symbol for this function is intended only to
+   provide an easy way to make it available to 3rd-party scripts and
+   "should" be deleted after calling it. That symbol is _not_ used
+   within the library.
 
    It currently offers alternatives to the following
    Emscripten-generated APIs:
    symbol.
 
    This code requires that the target object have the following
-   properties, noting that they needn't be available until the first
-   time one of the installed APIs is used (as opposed to when this
-   function is called) except where explicitly noted:
+   properties, though they needn't be available until the first time
+   one of the installed APIs is used (as opposed to when this function
+   is called) except where explicitly noted:
 
    - `exports` must be a property of the target object OR a property
      of `target.instance` (a WebAssembly.Module instance) and it must
    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. 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.
+   constructor is available, else it will default to false.  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:
    APIs which require allocation routines are explicitly documented as
    such and/or have "alloc" in their names.
 
+   Optional configuration values which may be set on target before
+   calling this:
+
+   - `pointerIR`: an IR-format string for the WASM environment's
+      pointer size. If set it must be either 'i32' or 'i64'. If not
+      set, it defaults to whatever this code thinks the pointer size
+      is. Modifying it after this call has no effect.
+
+
    This code is developed and maintained in conjunction with the
    Jaccwabyt project:
 
@@ -240,10 +243,17 @@ globalThis.WhWasmUtilInstaller = function(target){
       installFunction() extracts them. */
   cache.freeFuncIndexes = [];
   /**
-     Used by scopedAlloc() and friends.
+     List-of-lists used by scopedAlloc() and friends.
   */
   cache.scopedAlloc = [];
 
+  /** Push the pointer ptr to the current cache.scopedAlloc list
+      (which must already exist) and return ptr. */
+  cache.scopedAlloc.pushPtr = (ptr)=>{
+    cache.scopedAlloc[cache.scopedAlloc.length-1].push(ptr);
+    return ptr;
+  };
+
   cache.utf8Decoder = new TextDecoder();
   cache.utf8Encoder = new TextEncoder('utf-8');
 
@@ -320,25 +330,20 @@ globalThis.WhWasmUtilInstaller = function(target){
      BigInt64Array/BigUint64Array, else it throws if passed 64 or one
      of those constructors.
 
-     Returns an integer-based TypedArray view of the WASM heap
-     memory buffer associated with the given block size. If passed
-     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 ignored. Note that Float32Array and
-     Float64Array views are not supported by this function.
+     Returns an integer-based TypedArray view of the WASM heap memory
+     buffer associated with the given block size. If passed 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 ignored. Float32Array and Float64Array views are not
+     supported by this function.
 
-     Note that growth of the heap will invalidate any references to
-     this heap, so do not hold a reference longer than needed and do
-     not use a reference after any operation which may
-     allocate. Instead, re-fetch the reference by calling this
-     function again.
+     Growth of the heap will invalidate any references to this heap,
+     so do not hold a reference longer than needed and do not use a
+     reference after any operation which may allocate. Instead,
+     re-fetch the reference by calling this function again.
 
      Throws if passed an invalid n.
-
-     Pedantic side note: the name "heap" is a bit of a misnomer. In a
-     WASM environment, the stack and heap memory are all accessed via
-     the same view(s) of the memory.
   */
   target.heapForSize = function(n,unsigned = true){
     let ctor;
@@ -522,7 +527,7 @@ globalThis.WhWasmUtilInstaller = function(target){
      is stashed in the current scoped-alloc scope and will be
      cleaned up at the matching scopedAllocPop(), else it
      is not stashed there.
-   */
+  */
   const __installFunction = function f(func, sig, scoped){
     if(scoped && !cache.scopedAlloc.length){
       toss("No scopedAllocPush() scope is active.");
@@ -556,7 +561,7 @@ globalThis.WhWasmUtilInstaller = function(target){
       /*this will only work if func is a WASM-exported function*/
       ft.set(ptr, func);
       if(scoped){
-        cache.scopedAlloc[cache.scopedAlloc.length-1].push(ptr);
+        cache.scopedAlloc.pushPtr(ptr);
       }
       return ptr;
     }catch(e){
@@ -570,7 +575,7 @@ globalThis.WhWasmUtilInstaller = function(target){
       const fptr = target.jsFuncToWasm(func, sig);
       ft.set(ptr, fptr);
       if(scoped){
-        cache.scopedAlloc[cache.scopedAlloc.length-1].push(ptr);
+        cache.scopedAlloc.pushPtr(ptr);
       }
     }catch(e){
       if(ptr===oldLen) cache.freeFuncIndexes.push(oldLen);
@@ -649,8 +654,8 @@ globalThis.WhWasmUtilInstaller = function(target){
      Given a WASM heap memory address and a data type name in the form
      (i8, i16, i32, i64, float (or f32), double (or f64)), this
      fetches the numeric value from that address and returns it as a
-     number or, for the case of type='i64', a BigInt (noting that that
-     type triggers an exception if this.bigIntEnabled is
+     number or, for the case of type='i64', a BigInt (with the caveat
+     BigInt will trigger an exception if this.bigIntEnabled is
      falsy). Throws if given an invalid type.
 
      If the first argument is an array, it is treated as an array of
@@ -660,7 +665,7 @@ globalThis.WhWasmUtilInstaller = function(target){
 
      As a special case, if type ends with a `*`, it is considered to
      be a pointer type and is treated as the WASM numeric type
-     appropriate for the pointer size (`i32`).
+     appropriate for the pointer size (==this.pointerIR).
 
      While likely not obvious, this routine and its poke()
      counterpart are how pointer-to-value _output_ parameters
@@ -668,9 +673,9 @@ globalThis.WhWasmUtilInstaller = function(target){
 
      ```
      const ptr = alloc(4);
-     poke(ptr, 0, 'i32'); // clear the ptr's value
+     poke32(ptr, 0); // clear the ptr's value
      aCFuncWithOutputPtrToInt32Arg( ptr ); // e.g. void foo(int *x);
-     const result = peek(ptr, 'i32'); // fetch ptr's value
+     const result = peek32(ptr); // fetch ptr's value
      dealloc(ptr);
      ```
 
@@ -682,23 +687,23 @@ globalThis.WhWasmUtilInstaller = function(target){
      const scope = scopedAllocPush();
      try{
        const ptr = scopedAlloc(4);
-       poke(ptr, 0, 'i32');
+       poke32(ptr, 0);
        aCFuncWithOutputPtrArg( ptr );
-       result = peek(ptr, 'i32');
+       result = peek32(ptr);
      }finally{
        scopedAllocPop(scope);
      }
      ```
 
-     As a rule poke() must be called to set (typically zero
-     out) the pointer's value, else it will contain an essentially
-     random value.
+     As a rule poke() must be called to set (typically zero out) the
+     pointer's value, else it will contain an essentially random
+     value.
 
      ACHTUNG: calling this often, e.g. in a loop, can have a noticably
      painful impact on performance. Rather than doing so, use
      heapForSize() to fetch the heap object and read directly from it.
 
-     See: poke()
+     See also: poke()
   */
   target.peek = function f(ptr, type='i8'){
     if(type.endsWith('*')) type = ptrIR;
@@ -735,7 +740,7 @@ globalThis.WhWasmUtilInstaller = function(target){
      bytes are written. Throws if given an invalid type. See peek()
      for details about the `type` argument. If the 3rd argument ends
      with `*` then it is treated as a pointer type and this function
-     behaves as if the 3rd argument were `i32`.
+     behaves as if the 3rd argument were this.pointerIR.
 
      If the first argument is an array, it is treated like a list
      of pointers and the given value is written to each one.
@@ -1151,11 +1156,13 @@ globalThis.WhWasmUtilInstaller = function(target){
      Cleans up all allocations made using scopedAlloc() in the context
      of the given opaque state object, which must be a value returned
      by scopedAllocPush(). See that function for an example of how to
-     use this function.
+     use this function. It also uninstalls any WASM functions
+     installed with scopedInstallFunction().
 
      Though scoped allocations are managed like a stack, this API
      behaves properly if allocation scopes are popped in an order
-     other than the order they were pushed.
+     other than the order they were pushed. The intent is that it
+     _always_ be used in a stack-like manner.
 
      If called with no arguments, it pops the most recent
      scopedAllocPush() result:
@@ -1181,8 +1188,9 @@ globalThis.WhWasmUtilInstaller = function(target){
       if(target.functionEntry(p)){
         //console.warn("scopedAllocPop() uninstalling function",p);
         target.uninstallFunction(p);
+      }else{
+        target.dealloc(p);
       }
-      else target.dealloc(p);
     }
   };
 
@@ -1206,9 +1214,7 @@ globalThis.WhWasmUtilInstaller = function(target){
     if(!cache.scopedAlloc.length){
       toss("No scopedAllocPush() scope is active.");
     }
-    const p = target.alloc(n);
-    cache.scopedAlloc[cache.scopedAlloc.length-1].push(p);
-    return p;
+    return cache.scopedAlloc.pushPtr(target.alloc(n));
   };
 
   Object.defineProperty(target.scopedAlloc, 'level', {
@@ -1343,7 +1349,7 @@ globalThis.WhWasmUtilInstaller = function(target){
      value to dealloc(). The others are part of the same memory chunk
      and must not be freed separately.
 
-     The reason for the 2nd argument is..
+     The reason for the 2nd argument is...
 
      When one of the returned pointers will refer to a 64-bit value,
      e.g. a double or int64, an that value must be written or fetched,
@@ -1404,7 +1410,7 @@ globalThis.WhWasmUtilInstaller = function(target){
   };
 
   /**
-     State for use with xWrap()
+     State for use with xWrap().
   */
   cache.xWrap = Object.create(null);
   cache.xWrap.convert = Object.create(null);
@@ -1438,8 +1444,9 @@ globalThis.WhWasmUtilInstaller = function(target){
     .set('null', (v)=>v)
     .set(null, xResult.get('null'));
 
-  { /* Copy certain xArg[...] handlers to xResult[...] and
-       add pointer-style variants of them. */
+  { /* Copy xArg[...] handlers to xResult[...] for cases which have
+       identical semantics. Also add pointer-style variants of
+       them. */
     const copyToResult = ['i8', 'i16', 'i32', 'int',
                           'f32', 'float', 'f64', 'double'];
     if(target.bigIntEnabled) copyToResult.push('i64');
@@ -1461,7 +1468,7 @@ globalThis.WhWasmUtilInstaller = function(target){
      - If v is a string, scopeAlloc() a new C-string from it and return
        that temp string's pointer.
 
-     - Else return the value from the arg adapter defined for ptrIR.
+     - Else return the value from the arg adapter defined for `ptrIR`.
 
      TODO? Permit an Int8Array/Uint8Array and convert it to a string?
      Would that be too much magic concentrated in one place, ready to
@@ -1494,12 +1501,11 @@ globalThis.WhWasmUtilInstaller = function(target){
      Internal-use-only base class for FuncPtrAdapter and potentially
      additional stateful argument adapter classes.
 
-     Note that its main interface (convertArg()) is strictly
-     internal, not to be exposed to client code, as it may still
-     need re-shaping. Only the constructors of concrete subclasses
-     should be exposed to clients, and those in such a way that
-     does not hinder internal redesign of the convertArg()
-     interface.
+     Its main interface (convertArg()) is strictly internal, not to be
+     exposed to client code, as it may still need re-shaping. Only the
+     constructors of concrete subclasses should be exposed to clients,
+     and those in such a way that does not hinder internal redesign of
+     the convertArg() interface.
   */
   const AbstractArgAdapter = class {
     constructor(opt){
@@ -1518,6 +1524,11 @@ globalThis.WhWasmUtilInstaller = function(target){
        indexes must never be relied upon for anything because their
        types are indeterminate, whereas the LHS values will be
        WASM-compatible values by the time this is called.
+
+       The reason for the argv and argIndex arguments is that we
+       frequently need more context than v for a specific conversion,
+       and that context invariably lies in the LHS arguments of v.
+       Examples of how this is useful can be found in FuncPtrAdapter.
     */
     convertArg(v,argv,argIndex){
       toss("AbstractArgAdapter must be subclassed.");
@@ -1525,12 +1536,11 @@ globalThis.WhWasmUtilInstaller = function(target){
   };
 
   /**
-     An attempt at adding function pointer conversion support to
-     xWrap(). This type is recognized by xWrap() as a proxy for
-     converting a JS function to a C-side function, either
-     permanently, for the duration of a single call into the C layer,
-     or semi-contextual, where it may keep track of a single binding
-     for a given context and uninstall the binding if it's replaced.
+     This type is recognized by xWrap() as a proxy for converting a JS
+     function to a C-side function, either permanently, for the
+     duration of a single call into the C layer, or semi-contextual,
+     where it may keep track of a single binding for a given context
+     and uninstall the binding if it's replaced.
 
      The constructor requires an options object with these properties:
 
@@ -1566,7 +1576,7 @@ globalThis.WhWasmUtilInstaller = function(target){
 
        - 'permanent': the function is installed and left there
          forever. There is no way to recover its pointer address
-         later on.
+         later on for cleanup purposes. i.e. it effectively leaks.
 
      - callProxy (function): if set, this must be a function which
        will act as a proxy for any "converted" JS function. It is
@@ -1574,12 +1584,12 @@ globalThis.WhWasmUtilInstaller = function(target){
        either that function or a function which acts on its
        behalf. The returned function will be the one which gets
        installed into the WASM function table. The proxy must perform
-       any required argument conversion (noting that it will be called
-       from C code, so will receive C-format arguments) before passing
-       them on to the being-converted function. Whether or not the
-       proxy itself must return a value depends on the context. If it
-       does, it must be a WASM-friendly value, as it will be returning
-       from a call made from native code.
+       any required argument conversion (it will be called from C
+       code, so will receive C-format arguments) before passing them
+       on to the being-converted function. Whether or not the proxy
+       itself must return a value depends on the context. If it does,
+       it must be a WASM-friendly value, as it will be returning from
+       a call made from WASM code.
 
      - contextKey (function): is only used if bindScope is 'context'
        or if bindScope is not set and this function is, in which case
@@ -1598,10 +1608,10 @@ globalThis.WhWasmUtilInstaller = function(target){
        FuncPtrAdapter instance (other instances are not considered),
        taking into account that C functions often take some sort of
        state object as one or more of their arguments. As an example,
-       if the xWrap()'d function takes `(int,T*,functionPtr,X*)` and
-       this FuncPtrAdapter is the argv[2]nd arg, contextKey(argv,2)
+       if the xWrap()'d function takes `(int,T*,functionPtr,X*)` then
+       this FuncPtrAdapter instance is argv[2], and contextKey(argv,2)
        might return 'T@'+argv[1], or even just argv[1].  Note,
-       however, that the (X*) argument will not yet have been
+       however, that the (`X*`) argument will not yet have been
        processed by the time this is called and should not be used as
        part of that key because its pre-conversion data type might be
        unpredictable. Similarly, care must be taken with C-string-type
@@ -1609,13 +1619,17 @@ globalThis.WhWasmUtilInstaller = function(target){
        be WASM pointers, whereas those to the right might (and likely
        do) have another data type. When using C-strings in keys, never
        use their pointers in the key because most C-strings in this
-       constellation are transient.
+       constellation are transient. Conversely, the pointer address
+       makes an ideal key for longer-lived native pointer types.
 
-     Yes, that ^^^ is quite awkward, but it's what we have.
+     Yes, that ^^^ is quite awkward, but it's what we have. In
+     context, as it were, it actually makes some sense, but one must
+     look under its hook a bit to understand why it's shaped the
+     way it is.
 
      The constructor only saves the above state for later, and does
-     not actually bind any functions. Its convertArg() method is
-     called via xWrap() to perform any bindings.
+     not actually bind any functions. The conversion, if any, is
+     performed when its convertArg() method is called via xWrap().
 
      Shortcomings:
 
@@ -1627,10 +1641,9 @@ globalThis.WhWasmUtilInstaller = function(target){
      - Function pointers which include C-string arguments may still
        need a level of hand-written wrappers around them, depending on
        how they're used, in order to provide the client with JS
-       strings. Alternately, clients will need to perform such conversions
-       on their own, e.g. using cstrToJs(). Or maybe we can find a way
-       to perform such conversions here, via addition of an xWrap()-style
-       function signature to the options argument.
+       strings. Alternately, clients will need to perform such
+       conversions on their own, e.g. using cstrToJs(). The purpose of
+       the callProxy() method is to account for such cases.
   */
   xArg.FuncPtrAdapter = class FuncPtrAdapter extends AbstractArgAdapter {
     constructor(opt) {
@@ -1662,9 +1675,9 @@ globalThis.WhWasmUtilInstaller = function(target){
     }
 
     /**
-       Note that static class members are defined outside of the class
-       to work around an emcc toolchain build problem: one of the
-       tools in emsdk v3.1.42 does not support the static keyword.
+       The static class members are defined outside of the class to
+       work around an emcc toolchain build problem: one of the tools
+       in emsdk v3.1.42 does not support the static keyword.
     */
 
     /* Dummy impl. Overwritten per-instance as needed. */
@@ -1672,9 +1685,15 @@ globalThis.WhWasmUtilInstaller = function(target){
       return this;
     }
 
-    /* Returns this objects mapping for the given context key, in the
+    /**
+       Returns this object's mapping for the given context key, in the
        form of an an array, creating the mapping if needed. The key
-       may be anything suitable for use in a Map. */
+       may be anything suitable for use in a Map.
+
+       The returned array is intended to be used as a pair of
+       [JSValue, WasmFuncPtr], where the first element is one passed
+       to this.convertArg() and the second is its WASM form.
+    */
     contextMap(key){
       const cm = (this.__cmap || (this.__cmap = new Map));
       let rc = cm.get(key);
@@ -1684,19 +1703,21 @@ globalThis.WhWasmUtilInstaller = function(target){
 
     /**
        Gets called via xWrap() to "convert" v to a WASM-bound function
-       pointer. If v is one of (a pointer, null, undefined) then
+       pointer. If v is one of (a WASM pointer, null, undefined) then
        (v||0) is returned and any earlier function installed by this
        mapping _might_, depending on how it's bound, be uninstalled.
        If v is not one of those types, it must be a Function, for
-       which it creates (if needed) a WASM function binding and
-       returns the WASM pointer to that binding. If this instance is
-       not in 'transient' mode, it will remember the binding for at
-       least the next call, to avoid recreating the function binding
-       unnecessarily.
+       which this method creates (if needed) a WASM function binding
+       and returns the WASM pointer to that binding.
+
+       If this instance is not in 'transient' mode, it will remember
+       the binding for at least the next call, to avoid recreating the
+       function binding unnecessarily.
 
-       If it's passed a pointer(ish) value for v, it does _not_
-       perform any function binding, so this object's bindMode is
-       irrelevant for such cases.
+       If it's passed a pointer(ish) value for v, it assumes it's a
+       WASM function pointer and does _not_ perform any function
+       binding, so this object's bindMode is irrelevant/ignored for
+       such cases.
 
        See the parent class's convertArg() docs for details on what
        exactly the 2nd and 3rd arguments are.
@@ -1708,11 +1729,16 @@ globalThis.WhWasmUtilInstaller = function(target){
         pair = this.contextMap(this.contextKey(argv,argIndex));
         //FuncPtrAdapter.debugOut(this.name, this.signature, "contextKey() =",this.contextKey(argv,argIndex), pair);
       }
-      if(pair && pair[0]===v) return pair[1];
+      if(pair && pair[0]===v){
+        /* We have already handled this function. */
+        return pair[1];
+      }
       if(v instanceof Function){
         /* Install a WASM binding and return its pointer. */
         //FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.name,this.signature,this.transient,v,pair);
-        if(this.callProxy) v = this.callProxy(v);
+        if(this.callProxy){
+          v = this.callProxy(v);
+        }
         const fp = __installFunction(v, this.signature, this.isTransient);
         if(FuncPtrAdapter.debugFuncInstall){
           FuncPtrAdapter.debugOut("FuncPtrAdapter installed", this,
@@ -1735,11 +1761,11 @@ globalThis.WhWasmUtilInstaller = function(target){
                  function. We're relying very much here on xWrap()
                  having pushed an alloc scope.
               */
-              cache.scopedAlloc[cache.scopedAlloc.length-1].push(pair[1]);
+              cache.scopedAlloc.pushPtr(pair[1]);
             }
             catch(e){/*ignored*/}
           }
-          pair[0] = v;
+          pair[0] = arguments[0]/*the original v*/;
           pair[1] = fp;
         }
         return fp;
@@ -1751,7 +1777,7 @@ globalThis.WhWasmUtilInstaller = function(target){
             FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this,
                                     this.contextKey(argv,argIndex), '@'+pair[1], v);
           }
-          try{ cache.scopedAlloc[cache.scopedAlloc.length-1].push(pair[1]) }
+          try{ cache.scopedAlloc.pushPtr(pair[1]); }
           catch(e){/*ignored*/}
           pair[0] = pair[1] = (v | 0);
         }
@@ -1782,13 +1808,19 @@ globalThis.WhWasmUtilInstaller = function(target){
   /** Function used for debug output. */
   xArg.FuncPtrAdapter.debugOut = console.debug.bind(console);
 
+  /**
+     List of legal values for the FuncPtrAdapter bindScope config
+     option.
+  */
   xArg.FuncPtrAdapter.bindScopes = [
     'transient', 'context', 'singleton', 'permanent'
   ];
 
+  /** Throws if xArg.get(t) returns falsy. */
   const __xArgAdapterCheck =
         (t)=>xArg.get(t) || toss("Argument adapter not found:",t);
 
+  /** Throws if xResult.get(t) returns falsy. */
   const __xResultAdapterCheck =
         (t)=>xResult.get(t) || toss("Result adapter not found:",t);
 
@@ -1942,8 +1974,8 @@ globalThis.WhWasmUtilInstaller = function(target){
 
 ```js
    target.xWrap.resultAdapter('string:my_free',(i)=>{
-      try { return i ? target.cstrToJs(i) : null }
-      finally{ target.exports.my_free(i) }
+     try { return i ? target.cstrToJs(i) : null; }
+     finally{ target.exports.my_free(i); }
    };
 ```
 
@@ -2003,8 +2035,7 @@ globalThis.WhWasmUtilInstaller = function(target){
     if(fIsFunc) fArg = xf.name || 'unnamed function';
     if(argTypes.length!==xf.length) __argcMismatch(fArg, xf.length);
     if((null===resultType) && 0===xf.length){
-      /* Func taking no args with an as-is return. We don't need a wrapper.
-         We forego the argc check here, though. */
+      /* Func taking no args with an as-is return. We don't need a wrapper. */
       return xf;
     }
     /*Verify the arg type conversions are valid...*/;
@@ -2041,7 +2072,9 @@ globalThis.WhWasmUtilInstaller = function(target){
           _not_ stable.
 
           Maintenance reminder: the Ember framework modifies the core
-          Array type, breaking for-in loops.
+          Array type, breaking for-in loops:
+
+          https://sqlite.org/forum/forumpost/b549992634b55104
         */
         let i = 0;
         for(; i < args.length; ++i) args[i] = cxw.convertArgNoCheck(
@@ -2054,7 +2087,28 @@ globalThis.WhWasmUtilInstaller = function(target){
     };
   }/*xWrap()*/;
 
-  /** Internal impl for xWrap.resultAdapter() and argAdapter(). */
+  /**
+     Internal impl for xWrap.resultAdapter() and argAdapter().
+
+     func = one of xWrap.resultAdapter or xWrap.argAdapter.
+
+     argc = the number of args in the wrapping call to this
+     function.
+
+     typeName = the first arg to the wrapping function.
+
+     adapter = the second arg to the wrapping function.
+
+     modeName = a descriptive name of the wrapping function for
+     error-reporting purposes.
+
+     xcvPart = one of xResult or xArg.
+
+     This acts as either a getter (if 1===argc) or setter (if
+     2===argc) for the given adapter. Returns func on success or
+     throws if (A) called with 2 args but adapter is-not-a Function or
+     (B) typeName is not a string or (C) argc is not one of (1, 2).
+  */
   const __xAdapter = function(func, argc, typeName, adapter, modeName, xcvPart){
     if('string'===typeof typeName){
       if(1===argc) return xcvPart.get(typeName);
@@ -2089,16 +2143,16 @@ globalThis.WhWasmUtilInstaller = function(target){
      xWrap.resultAdapter('twice',(v)=>v+v);
      ```
 
-     xWrap.resultAdapter() MUST NOT use the scopedAlloc() family of
-     APIs to allocate a result value. xWrap()-generated wrappers run
-     in the context of scopedAllocPush() so that argument adapters can
-     easily convert, e.g., to C-strings, and have them cleaned up
+     Result adapters MUST NOT use the scopedAlloc() family of APIs to
+     allocate a result value. xWrap()-generated wrappers run in the
+     context of scopedAllocPush() so that argument adapters can easily
+     convert, e.g., to C-strings, and have them cleaned up
      automatically before the wrapper returns to the caller. Likewise,
      if a _result_ adapter uses scoped allocation, the result will be
      freed before the wrapper returns, leading to chaos and undefined
      behavior.
 
-     Except when called as a getter, this function returns itself.
+     When called as a setter, this function returns itself.
   */
   target.xWrap.resultAdapter = function f(typeName, adapter){
     return __xAdapter(f, arguments.length, typeName, adapter,
@@ -2122,13 +2176,13 @@ globalThis.WhWasmUtilInstaller = function(target){
      };
      ```
 
-     Contrariwise, xWrap.resultAdapter() must _not_ use scopedAlloc()
-     to allocate its results because they would be freed before the
+     Contrariwise, _result_ adapters _must not_ use scopedAlloc() to
+     allocate results because they would be freed before the
      xWrap()-created wrapper returns.
 
-     Note that it is perfectly legitimate to use these adapters to
-     perform argument validation, as opposed (or in addition) to
-     conversion.
+     It is perfectly legitimate to use these adapters to perform
+     argument validation, as opposed (or in addition) to conversion.
+     When used that way, they should throw for invalid arguments.
   */
   target.xWrap.argAdapter = function f(typeName, adapter){
     return __xAdapter(f, arguments.length, typeName, adapter,
@@ -2151,8 +2205,7 @@ globalThis.WhWasmUtilInstaller = function(target){
      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
-     arguably more efficient because it will hypothetically free the
-     wrapper function quickly.
+     simpler.
   */
   target.xCallWrapped = function(fArg, resultType, argTypes, ...args){
     if(Array.isArray(arguments[3])) args = arguments[3];
@@ -2239,8 +2292,9 @@ globalThis.WhWasmUtilInstaller = function(target){
    }
    ```
 
-   (Note that the initial `then()` attached to the promise gets only
-   that object, and not the `config` one.)
+   (The initial `then()` attached to the promise gets only that
+   object, and not the `config` object, thus the potential need for a
+   `config.onload` handler.)
 
    Error handling is up to the caller, who may attach a `catch()` call
    to the promise.
index d11a18e8cb205fa3c9b4c6685a39b85cf003ad7a..e79e5c8b1e0f2fa9c061d1565ed62d8ada84bdd4 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\stypo\sin\sthe\sname\sof\sthe\sCursorHints\soptimization\sin\sthe\sCLI.
-D 2025-09-19T09:18:22.076
+C Wasm:\s(A)\sdiverse\sinternal\sdoc\supdates.\s(B)\swhen\sgenerating\sautomated\sJS-to-WASM\sfunction\sproxies\sfor\sconverters\swhich\srequire\san\sadditional\smiddle-man\sproxy,\se.g.\ssqlite3_exec(),\suse\sthe\sclient-provided\sfunction,\snot\sthe\sproxy\sfunction,\sas\sthe\scache\skey,\sto\skeep\sfrom\sre-generating\sthe\sconversion\sin\ssome\scommon\suse\spatterns.
+D 2025-09-19T14:21:09.961
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -607,7 +607,7 @@ F ext/wasm/api/sqlite3-vfs-helper.c-pp.js 3f828cc66758acb40e9c5b4dcfd87fd478a14c
 F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 0f68a64e508598910e7c01214ae27d603dfc8baec6a184506fafac603a901931
 F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 4ab0704ee198de7d1059eccedc7703c931510b588d10af0ee36ea5b3ebbac284
 F ext/wasm/api/sqlite3-vtab-helper.c-pp.js e809739d71e8b35dfe1b55d24d91f02d04239e6aef7ca1ea92a15a29e704f616
-F ext/wasm/api/sqlite3-wasm.c 7b207c10c6b4019cf667c4e332bdd33c98afc08c943f8cc0aea7693ad8635f7c
+F ext/wasm/api/sqlite3-wasm.c 404cc1f0f5c307210a8d7c3a7dda57834e0e8b3d406ba51977a97a6d14a74734
 F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js 4ad256b4ff7f839ad18931ed35d46cced544207bd2209665ec552e193f7f4544
 F ext/wasm/api/sqlite3-worker1.c-pp.js 5e8706c2c4af2a57fbcdc02f4e7ef79869971bc21bb8ede777687786ce1c92d5
 F ext/wasm/batch-runner-sahpool.html e9a38fdeb36a13eac7b50241dfe7ae066fe3f51f5c0b0151e7baee5fce0d07a7
@@ -618,7 +618,7 @@ F ext/wasm/c-pp.c cca55c5b55ebd8d29916adbedb0e40baa12caa9a2e8429f812683c308f9b0e
 F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51
 F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15
 F ext/wasm/common/testing.css e97549bab24126c24e0daabfe2de9bb478fb0a69fdb2ddd0a73a992c091aad6f
-F ext/wasm/common/whwasmutil.js ea50e847cf08c2a96694f87d00b49fb97ee5fe62c15439cc269c8e1d9ab74c0a
+F ext/wasm/common/whwasmutil.js 4ea5a413016d9a561a26976c699a2670f2385b93be04cd2d463dd6c1955bd175
 F ext/wasm/config.make.in c424ae1cc3c89274520ad312509d36c4daa34a3fce5d0c688e5f8f4365e1049a
 F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
 F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
@@ -2175,8 +2175,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350
 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
 F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 42c225a2ed7fc95f9b01467c64ba2bf97bca216fdcd6ab1ba3fb49c068650de9
-R fec5e9cf9c4aa5198306438cd736e135
-U drh
-Z 1e7399cfaf854450b90c6eb04c4097ba
+P 468a11fd415710042b23880772f6c2c7771008208823fe3b554227a9244dbf92
+R 45b30de6d5f1d826a12d811876f55290
+U stephan
+Z 31a53fb96f385e882a14f4d3fa47d2d3
 # Remove this line to create a well-formed Fossil manifest.
index 5f03ba8a91583029f21c90ddd5c3ae124e1fc348..4dc27434ecf28bc7df4c01a94a8f80f9a4db9c7f 100644 (file)
@@ -1 +1 @@
-468a11fd415710042b23880772f6c2c7771008208823fe3b554227a9244dbf92
+5e5139c2a162562cee0071d03954ebc0b8938da0b045ec3f5eba32dc8e19604d