]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
More fleshing out of sqlite3.capi.wasm.pstack.
authorstephan <stephan@noemail.net>
Sun, 2 Oct 2022 03:11:13 +0000 (03:11 +0000)
committerstephan <stephan@noemail.net>
Sun, 2 Oct 2022 03:11:13 +0000 (03:11 +0000)
FossilOrigin-Name: eb5726677a727a958df11f1fba078d30c7c0ba2a9bdb158e8641b35b5f971af3

ext/wasm/api/sqlite3-api-glue.js
ext/wasm/api/sqlite3-api-oo1.js
ext/wasm/api/sqlite3-api-prologue.js
ext/wasm/api/sqlite3-wasm.c
ext/wasm/common/whwasmutil.js
ext/wasm/testing1.js
manifest
manifest.uuid

index ab9424acabf5ac2ee7ed9d1a4f1ee76df0c25c90..b6ec1695e12a385647aaf18337823ad1e05a1d66 100644 (file)
@@ -55,10 +55,11 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
   
   if(1){// WhWasmUtil.xWrap() bindings...
     /**
-       Add some descriptive xWrap() aliases for '*' intended to
-       (A) initially improve readability/correctness of capi.signatures
-       and (B) eventually perhaps provide some sort of type-safety
-       in their conversions.
+       Add some descriptive xWrap() aliases for '*' intended to (A)
+       initially improve readability/correctness of capi.signatures
+       and (B) eventually perhaps provide automatic conversion from
+       higher-level representations, e.g. capi.sqlite3_vfs to
+       `sqlite3_vfs*` via capi.sqlite3_vfs.pointer.
     */
     const aPtr = wasm.xWrap.argAdapter('*');
     wasm.xWrap.argAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr);
@@ -248,6 +249,56 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     };
   }/*sqlite3_prepare_v2/v3()*/;
 
+  if(1){// Extend wasm.pstack, now that the wasm utils are installed
+    /**
+       Allocates n chunks, each sz bytes, as a single memory block and
+       returns the addresses as an array of n element, each holding
+       the address of one chunk.
+
+       Throws a WasmAllocError if allocation fails.
+
+       Example:
+
+       ```
+       const [p1, p2, p3] = wasm.pstack.allocChunks(3,4);
+       ```
+    */
+    wasm.pstack.allocChunks = (n,sz)=>{
+      const mem = wasm.pstack.alloc(n * sz);
+      const rc = [];
+      let i = 0, offset = 0;
+      for(; i < n; offset = (sz * ++i)){
+        rc.push(mem + offset);
+      }
+      return rc;
+    };
+
+    /**
+       A convenience wrapper for allocChunks() which sizes each chunks
+       as either 8 bytes (safePtrSize is truthy) or wasm.ptrSizeof (if
+       safePtrSize is truthy).
+
+       How it returns its result differs depending on its first
+       argument: if it's 1, it returns a single pointer value. If it's
+       more than 1, it returns the same as allocChunks().
+
+       When one of the pointers refers to a 64-bit value, e.g. a
+       double or int64, and that value must be written or fetch,
+       e.g. using wasm.setMemValue() or wasm.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.
+
+       However, when all pointers involved are "small", it is safe to
+       pass a falsy value to save to memory.
+    */
+    wasm.pstack.allocPtr = (n=1,safePtrSize=true) =>{
+      return 1===n
+        ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptrSizeof)
+        : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptrSizeof);
+    };
+  }/*wasm.pstack filler*/
+
   /**
      Install JS<->C struct bindings for the non-opaque struct types we
      need... */
index 627af2e8a595ed1747296e3cb312af6d7b4e2d99..07f0657fa4b5cb73ff190b898661c479a28a69d4 100644 (file)
@@ -18,7 +18,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
   const toss = (...args)=>{throw new Error(args.join(' '))};
   const toss3 = (...args)=>{throw new sqlite3.SQLite3Error(...args)};
 
-  const capi = sqlite3.capi, util = capi.util;
+  const capi = sqlite3.capi, wasm = capi.wasm, util = capi.util;
   /* What follows is colloquially known as "OO API #1". It is a
      binding of the sqlite3 API which is designed to be run within
      the same thread (main or worker) as the one in which the
@@ -33,7 +33,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
      accessor and store their real values in this map. Keys = DB/Stmt
      objects, values = pointer values. This also unifies how those are
      accessed, for potential use downstream via custom
-     capi.wasm.xWrap() function signatures which know how to extract
+     wasm.xWrap() function signatures which know how to extract
      it.
   */
   const __ptrMap = new WeakMap();
@@ -72,7 +72,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
      being-construct DB object as its "this". See the DB constructor
      for the argument docs. This is split into a separate function
      in order to enable simple creation of special-case DB constructors,
-     e.g. a hypothetical LocalStorageDB or OpfsDB.
+     e.g. JsStorageDB and OpfsDB.
 
      Expects to be passed a configuration object with the following
      properties:
@@ -123,7 +123,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
       console.error("Invalid DB ctor args",opt,arguments);
       toss3("Invalid arguments for DB constructor.");
     }
-    let fnJs = ('number'===typeof fn) ? capi.wasm.cstringToJs(fn) : fn;
+    let fnJs = ('number'===typeof fn) ? wasm.cstringToJs(fn) : fn;
     const vfsCheck = ctor._name2vfs[fnJs];
     if(vfsCheck){
       vfsName = vfsCheck.vfs;
@@ -136,20 +136,20 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     if( flagsStr.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE;
     if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY;
     oflags |= capi.SQLITE_OPEN_EXRESCODE;
-    const stack = capi.wasm.scopedAllocPush();
+    const scope = wasm.scopedAllocPush();
     try {
-      const ppDb = capi.wasm.scopedAllocPtr() /* output (sqlite3**) arg */;
+      const ppDb = wasm.allocPtr() /* output (sqlite3**) arg */;
       const pVfsName = vfsName ? (
-        ('number'===typeof vfsName ? vfsName : capi.wasm.scopedAllocCString(vfsName))
+        ('number'===typeof vfsName ? vfsName : wasm.scopedAllocCString(vfsName))
       ): 0;
       const rc = capi.sqlite3_open_v2(fn, ppDb, oflags, pVfsName);
-      ptr = capi.wasm.getPtrValue(ppDb);
+      ptr = wasm.getPtrValue(ppDb);
       checkSqlite3Rc(ptr, rc);
     }catch( e ){
       if( ptr ) capi.sqlite3_close_v2(ptr);
       throw e;
     }finally{
-      capi.wasm.scopedAllocPop(stack);
+      wasm.scopedAllocPop(scope);
     }
     this.filename = fnJs;
     __ptrMap.set(this, ptr);
@@ -265,7 +265,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     blob: 5
   };
   BindTypes['undefined'] == BindTypes.null;
-  if(capi.wasm.bigIntEnabled){
+  if(wasm.bigIntEnabled){
     BindTypes.bigint = BindTypes.number;
   }
 
@@ -454,7 +454,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
           if(s && s.pointer) s.finalize();
         });
         Object.values(__udfMap.get(this)).forEach(
-          capi.wasm.uninstallFunction.bind(capi.wasm)
+          wasm.uninstallFunction.bind(capi.wasm)
         );
         __ptrMap.delete(this);
         __stmtMap.delete(this);
@@ -539,15 +539,15 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     */
     prepare: function(sql){
       affirmDbOpen(this);
-      const stack = capi.wasm.pstack.pointer;
+      const stack = wasm.pstack.pointer;
       let ppStmt, pStmt;
       try{
-        ppStmt = capi.wasm.pstack.alloc(8)/* output (sqlite3_stmt**) arg */;
+        ppStmt = wasm.pstack.alloc(8)/* output (sqlite3_stmt**) arg */;
         DB.checkRc(this, capi.sqlite3_prepare_v2(this.pointer, sql, -1, ppStmt, null));
-        pStmt = capi.wasm.getPtrValue(ppStmt);
+        pStmt = wasm.getPtrValue(ppStmt);
       }
       finally {
-        capi.wasm.pstack.restore(stack);
+        wasm.pstack.restore(stack);
       }
       if(!pStmt) toss3("Cannot prepare empty SQL.");
       const stmt = new Stmt(this, pStmt, BindTypes);
@@ -846,7 +846,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
           let i, pVal, valType, arg;
           const tgt = [];
           for(i = 0; i < argc; ++i){
-            pVal = capi.wasm.getPtrValue(pArgv + (capi.wasm.ptrSizeof * i));
+            pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i));
             /**
                Curiously: despite ostensibly requiring 8-byte
                alignment, the pArgv array is parcelled into chunks of
@@ -868,7 +868,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
                   const pBlob = capi.sqlite3_value_blob(pVal);
                   arg = new Uint8Array(n);
                   let i;
-                  const heap = n ? capi.wasm.heap8() : false;
+                  const heap = n ? wasm.heap8() : false;
                   for(i = 0; i < n; ++i) arg[i] = heap[pBlob+i];
                   break;
                 }
@@ -902,10 +902,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
                   capi.sqlite3_result_null(pCx);
                   break;
                 }else if(util.isBindableTypedArray(val)){
-                  const pBlob = capi.wasm.allocFromTypedArray(val);
+                  const pBlob = wasm.allocFromTypedArray(val);
                   capi.sqlite3_result_blob(pCx, pBlob, val.byteLength,
                                           capi.SQLITE_TRANSIENT);
-                  capi.wasm.dealloc(pBlob);
+                  wasm.dealloc(pBlob);
                   break;
                 }
                 // else fall through
@@ -925,7 +925,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
           }
         }
       };
-      const pUdf = capi.wasm.installFunction(wrapper, "v(iii)");
+      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;
@@ -938,12 +938,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
           capi.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf,
           null/*xStep*/, null/*xFinal*/, null/*xDestroy*/));
       }catch(e){
-        capi.wasm.uninstallFunction(pUdf);
+        wasm.uninstallFunction(pUdf);
         throw e;
       }
       const udfMap = __udfMap.get(this);
       if(udfMap[name]){
-        try{capi.wasm.uninstallFunction(udfMap[name])}
+        try{wasm.uninstallFunction(udfMap[name])}
         catch(e){/*ignore*/}
       }
       udfMap[name] = pUdf;
@@ -1049,7 +1049,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
         case BindTypes.string:
           return t;
         case BindTypes.bigint:
-          if(capi.wasm.bigIntEnabled) return t;
+          if(wasm.bigIntEnabled) return t;
           /* else fall through */
         default:
           //console.log("isSupportedBindType",t,v);
@@ -1109,7 +1109,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
   const bindOne = function f(stmt,ndx,bindType,val){
     affirmUnlocked(stmt, 'bind()');
     if(!f._){
-      if(capi.wasm.bigIntEnabled){
+      if(wasm.bigIntEnabled){
         f._maxInt = BigInt("0x7fffffffffffffff");
         f._minInt = ~f._maxInt;
       }
@@ -1120,25 +1120,25 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
         string: function(stmt, ndx, val, asBlob){
           if(1){
             /* _Hypothetically_ more efficient than the impl in the 'else' block. */
-            const stack = capi.wasm.scopedAllocPush();
+            const stack = wasm.scopedAllocPush();
             try{
-              const n = capi.wasm.jstrlen(val);
-              const pStr = capi.wasm.scopedAlloc(n);
-              capi.wasm.jstrcpy(val, capi.wasm.heap8u(), pStr, n, false);
+              const n = wasm.jstrlen(val);
+              const pStr = wasm.scopedAlloc(n);
+              wasm.jstrcpy(val, wasm.heap8u(), pStr, n, false);
               const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text;
               return f(stmt.pointer, ndx, pStr, n, capi.SQLITE_TRANSIENT);
             }finally{
-              capi.wasm.scopedAllocPop(stack);
+              wasm.scopedAllocPop(stack);
             }
           }else{
-            const bytes = capi.wasm.jstrToUintArray(val,false);
-            const pStr = capi.wasm.alloc(bytes.length || 1);
-            capi.wasm.heap8u().set(bytes.length ? bytes : [0], pStr);
+            const bytes = wasm.jstrToUintArray(val,false);
+            const pStr = wasm.alloc(bytes.length || 1);
+            wasm.heap8u().set(bytes.length ? bytes : [0], pStr);
             try{
               const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text;
               return f(stmt.pointer, ndx, pStr, bytes.length, capi.SQLITE_TRANSIENT);
             }finally{
-              capi.wasm.dealloc(pStr);
+              wasm.dealloc(pStr);
             }
           }
         }
@@ -1160,7 +1160,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
           else if('bigint'===typeof val){
             if(val<f._minInt || val>f._maxInt){
               toss3("BigInt value is out of range for storing as int64: "+val);
-            }else if(capi.wasm.bigIntEnabled){
+            }else if(wasm.bigIntEnabled){
               m = capi.sqlite3_bind_int64;
             }else if(val >= Number.MIN_SAFE_INTEGER && val <= Number.MAX_SAFE_INTEGER){
               val = Number(val);
@@ -1170,7 +1170,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
             }
           }else{ // !int32, !bigint
             val = Number(val);
-            if(capi.wasm.bigIntEnabled && Number.isInteger(val)){
+            if(wasm.bigIntEnabled && Number.isInteger(val)){
               m = capi.sqlite3_bind_int64;
             }else{
               m = capi.sqlite3_bind_double;
@@ -1190,22 +1190,22 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
                   "that it be a string, Uint8Array, or Int8Array.");
           }else if(1){
             /* _Hypothetically_ more efficient than the impl in the 'else' block. */
-            const stack = capi.wasm.scopedAllocPush();
+            const stack = wasm.scopedAllocPush();
             try{
-              const pBlob = capi.wasm.scopedAlloc(val.byteLength || 1);
-              capi.wasm.heap8().set(val.byteLength ? val : [0], pBlob)
+              const pBlob = wasm.scopedAlloc(val.byteLength || 1);
+              wasm.heap8().set(val.byteLength ? val : [0], pBlob)
               rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength,
                                          capi.SQLITE_TRANSIENT);
             }finally{
-              capi.wasm.scopedAllocPop(stack);
+              wasm.scopedAllocPop(stack);
             }
           }else{
-            const pBlob = capi.wasm.allocFromTypedArray(val);
+            const pBlob = wasm.allocFromTypedArray(val);
             try{
               rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength,
                                          capi.SQLITE_TRANSIENT);
             }finally{
-              capi.wasm.dealloc(pBlob);
+              wasm.dealloc(pBlob);
             }
           }
           break;
@@ -1518,7 +1518,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
              : asType){
           case capi.SQLITE_NULL: return null;
           case capi.SQLITE_INTEGER:{
-            if(capi.wasm.bigIntEnabled){
+            if(wasm.bigIntEnabled){
               const rc = capi.sqlite3_column_int64(this.pointer, ndx);
               if(rc>=Number.MIN_SAFE_INTEGER && rc<=Number.MAX_SAFE_INTEGER){
                 /* Coerce "normal" number ranges to normal number values,
@@ -1549,8 +1549,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
             const n = capi.sqlite3_column_bytes(this.pointer, ndx),
                   ptr = capi.sqlite3_column_blob(this.pointer, ndx),
                   rc = new Uint8Array(n);
-            //heap = n ? capi.wasm.heap8() : false;
-            if(n) rc.set(capi.wasm.heap8u().slice(ptr, ptr+n), 0);
+            //heap = n ? wasm.heap8() : false;
+            if(n) rc.set(wasm.heap8u().slice(ptr, ptr+n), 0);
             //for(let i = 0; i < n; ++i) rc[i] = heap[ptr + i];
             if(n && this.db._blobXfer instanceof Array){
               /* This is an optimization soley for the
index 97376be6c89ecea4d12e2f59c0791c30302029e0..59533815fbf533b2c539933bd4fee70781a26aca 100644 (file)
@@ -265,6 +265,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
       this.name = 'WasmAllocError';
     }
   };
+  WasmAllocError.toss = (...args)=>{
+    throw new WasmAllocError(args.join(' '));
+  };
 
   /** 
       The main sqlite3 binding API gets installed into this object,
@@ -733,6 +736,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
      Functions which are intended solely for API-internal use by the
      WASM components, not client code. These get installed into
      capi.wasm.
+
+     TODO: get rid of sqlite3_wasm_vfs_unlink(). It is ill-conceived
+     and only rarely actually useful.
   */
   capi.wasm.bindingSignatures.wasm = [
     ["sqlite3_wasm_vfs_unlink", "int", "string"]
@@ -781,15 +787,21 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
        Attempts to allocate the given number of bytes from the
        pstack. On success, it zeroes out a block of memory of the
        given size, adjusts the pstack pointer, and returns a pointer
-       to the memory. On error, returns 0. The memory must eventually
-       be released using restore().
+       to the memory. On error, returns throws a WasmAllocError. The
+       memory must eventually be released using restore().
 
        This method always adjusts the given value to be a multiple
        of 8 bytes because failing to do so can lead to incorrect
        results when reading and writing 64-bit values from/to the WASM
        heap.
     */
-    alloc: capi.wasm.exports.sqlite3_wasm_pstack_alloc
+    alloc: (n)=>{
+      return capi.wasm.exports.sqlite3_wasm_pstack_alloc(n)
+        || WasmAllocError.toss("Could not allocate",n,
+                               "bytes from the pstack.");
+    }
+    // More methods get added after the capi.wasm object is populated
+    // by WhWasmUtilInstaller.
   });
   /**
      sqlite3.capi.wasm.pstack.pointer resolves to the current pstack
@@ -828,7 +840,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
       this.name = 'SQLite3Error';
     }
   };
-
+  SQLite3Error.toss = (...args)=>{
+    throw new SQLite3Error(args.join(' '));
+  };
 
   /** State for sqlite3_wasmfs_opfs_dir(). */
   let __persistentDir = undefined;
index a2de8ca7103c8493511350e3dabd2ee69e33e612..b9454155d7f5792c60ff4f8b0a0b3b46dae93805 100644 (file)
 #include <assert.h>
 #include "sqlite3.c" /* yes, .c instead of .h. */
 
+#if defined(__EMSCRIPTEN__)
+#  include <emscripten/console.h>
+#endif
+
 /*
 ** WASM_KEEP is identical to EMSCRIPTEN_KEEPALIVE but is not
 ** Emscripten-specific. It explicitly marks functions for export into
@@ -667,6 +671,9 @@ WASM_KEEP
 int sqlite3_wasm_vfs_unlink(const char * zName){
   int rc = SQLITE_MISUSE /* ??? */;
   sqlite3_vfs * const pVfs = sqlite3_vfs_find(0);
+#if defined(__EMSCRIPTEN__)
+  emscripten_console_warn("sqlite3_wasm_vfs_unlink() will be removed.");
+#endif
   if( zName && pVfs && pVfs->xDelete ){
     rc = pVfs->xDelete(pVfs, zName, 1);
   }
@@ -750,9 +757,7 @@ int sqlite3_wasm_db_serialize( sqlite3* pDb, unsigned char **pOut, sqlite3_int64
 }
 
 
-#if defined(__EMSCRIPTEN__)
-#include <emscripten/console.h>
-#if defined(SQLITE_WASM_WASMFS)
+#if defined(__EMSCRIPTEN__) && defined(SQLITE_WASM_WASMFS)
 #include <emscripten/wasmfs.h>
 
 /*
@@ -809,6 +814,5 @@ int sqlite3_wasm_init_wasmfs(const char *zUnused){
   return SQLITE_NOTFOUND;
 }
 #endif /* __EMSCRIPTEN__ && SQLITE_WASM_WASMFS */
-#endif
 
 #undef WASM_KEEP
index 39a214be0020ce0c7db76f250641be6cd1b257bd..6b08d4f78ec173c26344d74aa541703b8ba1cb25 100644 (file)
@@ -1091,28 +1091,36 @@ self.WhWasmUtilInstaller = function(target){
   };
 
   /** Internal impl for allocPtr() and scopedAllocPtr(). */
-  const __allocPtr = function(howMany, method){
+  const __allocPtr = function(howMany, safePtrSize, method){
     __affirmAlloc(target, method);
-    let m = target[method](howMany * ptrSizeof);
-    target.setMemValue(m, 0, ptrIR)
+    const pIr = safePtrSize ? 'i64' : ptrIR;
+    let m = target[method](howMany * (safePtrSize ? 8 : ptrSizeof));
+    target.setMemValue(m, 0, pIr)
     if(1===howMany){
       return m;
     }
     const a = [m];
     for(let i = 1; i < howMany; ++i){
-      m += ptrSizeof;
+      m += (safePtrSize ? 8 : ptrSizeof);
       a[i] = m;
-      target.setMemValue(m, 0, ptrIR);
+      target.setMemValue(m, 0, pIr);
     }
     return a;
   };
 
   /**
-     Allocates a single chunk of memory capable of holding `howMany`
-     pointers and zeroes them out. If `howMany` is 1 then the memory
-     chunk is returned directly, else an array of pointer addresses is
-     returned, which can optionally be used with "destructuring
-     assignment" like this:
+     Allocates one or more pointers as a single chunk of memory and
+     zeroes them out.
+
+     The first argument is the number of pointers to allocate. The
+     second specifies whether they should use a "safe" pointer size (8
+     bytes) or whether they may use the default pointer size
+     (typically 4 but also possibly 8).
+
+     How the result is returned depends on its first argument: if
+     passed 1, it returns the allocated memory address. If passed more
+     than one then an array of pointer addresses is returned, which
+     can optionally be used with "destructuring assignment" like this:
 
      ```
      const [p1, p2, p3] = allocPtr(3);
@@ -1121,14 +1129,27 @@ self.WhWasmUtilInstaller = function(target){
      ACHTUNG: when freeing the memory, pass only the _first_ result
      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..
+
+     When one of the pointers will refer to a 64-bit value, e.g. a
+     double or int64, an that value must be written or fetch,
+     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
+     client code is certain that it will only get/fetch 4-byte values
+     (or smaller).
   */
-  target.allocPtr = (howMany=1)=>__allocPtr(howMany, 'alloc');
+  target.allocPtr =
+    (howMany=1, safePtrSize=true)=>__allocPtr(howMany, safePtrSize, 'alloc');
 
   /**
      Identical to allocPtr() except that it allocates using scopedAlloc()
      instead of alloc().
   */
-  target.scopedAllocPtr = (howMany=1)=>__allocPtr(howMany, 'scopedAlloc');
+  target.scopedAllocPtr =
+    (howMany=1, safePtrSize=true)=>__allocPtr(howMany, safePtrSize, 'scopedAlloc');
 
   /**
      If target.exports[name] exists, it is returned, else an
index dd61ab28141d2052a79848ef7770d73a22028647..053fb6f2773a71a3b4cf23fa5b49f75879abf652 100644 (file)
    */
   const testPstack = function(db,sqlite3){
     const w = sqlite3.capi.wasm, P = w.pstack;
+    const isAllocErr = (e)=>e instanceof sqlite3.WasmAllocError;
     const stack = P.pointer;
     T.assert(0===stack % 8 /* must be 8-byte aligned */);
     try{
       const quota = P.remaining;
       log("pstack quota",quota);
       T.assert(quota >= 4096)
-        .assert(0 === P.alloc(0))
-        .assert(0 === P.alloc(-1));
+        .mustThrowMatching(()=>P.alloc(0), isAllocErr)
+        .mustThrowMatching(()=>P.alloc(-1), isAllocErr);
       let p1 = P.alloc(12);
       T.assert(p1 === stack - 16/*8-byte aligned*/)
         .assert(P.pointer === p1);
       let p2 = P.alloc(7);
       T.assert(p2 === p1-8/*8-byte aligned, stack grows downwards*/)
-        .assert(0 === P.alloc(quota))
+        .mustThrowMatching(()=>P.alloc(quota), isAllocErr)
         .assert(24 === stack - p2)
         .assert(P.pointer === p2);
       let n = quota - (stack - p2);
       let p3 = P.alloc(n);
       T.assert(p3 === stack-quota)
-        .assert(0 === P.alloc(1));
+        .mustThrowMatching(()=>P.alloc(1), isAllocErr);
     }finally{
       P.restore(stack);
-      T.assert(P.pointer === stack);
     }
-  }/*testPstack()*/;
+
+    T.assert(P.pointer === stack);
+    try {
+      const [p1, p2, p3] = P.allocChunks(3,4);
+      T.assert(P.pointer === stack-16/*always rounded to multiple of 8*/)
+        .assert(p2 === p1 + 4)
+        .assert(p3 === p2 + 4);
+      T.mustThrowMatching(()=>P.allocChunks(1024, 1024 * 16),
+                          (e)=>e instanceof sqlite3.WasmAllocError)
+    }finally{
+      P.restore(stack);
+    }
+
+    T.assert(P.pointer === stack);
+    try {
+      let [p1, p2, p3] = P.allocPtr(3,false);
+      let sPos = stack-16/*always rounded to multiple of 8*/;
+      T.assert(P.pointer === sPos)
+        .assert(p2 === p1 + 4)
+        .assert(p3 === p2 + 4);
+      [p1, p2, p3] = P.allocPtr(3);
+      T.assert(P.pointer === sPos-24/*3 x 8 bytes*/)
+        .assert(p2 === p1 + 8)
+        .assert(p3 === p2 + 8);
+      p1 = P.allocPtr();
+      T.assert('number'===typeof p1);
+    }finally{
+      P.restore(stack);
+    }
+}/*testPstack()*/;
 
   const clearKvvfs = function(){
     const sz = sqlite3.capi.sqlite3_web_kvvfs_size();
index d3947b8c26f2fde3d929c871ea2aa186a0dc7498..c9a71476e039e5541b33fdc421313ba88b058326 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Document\sthe\sroles\sof\sthe\snew\s(this\spast\sweek)\sJS\sfiles\sadded\sto\sthe\sbuild\sprocess.
-D 2022-10-02T01:48:14.286
+C More\sfleshing\sout\sof\ssqlite3.capi.wasm.pstack.
+D 2022-10-02T03:11:13.806
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -485,19 +485,19 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814
 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 b15a51b88aaa472d36bf82d5123dbfdafe8ddf6ca75fba934510e4a20bbe4adb
-F ext/wasm/api/sqlite3-api-oo1.js 7667d320f6b9fb5252050a2f9c0b1769e11b84dbc0763b999baf65b451b14369
+F ext/wasm/api/sqlite3-api-glue.js 474a5e0bf8016e22aefee44ca45408f08f4159c0b782295ac1d86c4556e91d9a
+F ext/wasm/api/sqlite3-api-oo1.js 066e67f3033e1b300140d431557c468f5cd0a4c17253f156e05b8a2e2c802da7
 F ext/wasm/api/sqlite3-api-opfs.js 1b097808b7b081b0f0700cf97d49ef19760e401706168edff9cd45cf9169f541
-F ext/wasm/api/sqlite3-api-prologue.js a93bd69969eb8b8f9c4cb34e5d86dcbbe5adbeeea39c1cce57194256c5f28434
+F ext/wasm/api/sqlite3-api-prologue.js 9b0c5150f0129b3dc558fec0bfc9c8bf7ebda402b58965bcf98b2ed117b55239
 F ext/wasm/api/sqlite3-api-worker1.js 7f4f46cb6b512a48572d7567233896e6a9c46570c44bdc3d13419730c7c221c8
 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
-F ext/wasm/api/sqlite3-wasm.c d72aecf0e50a4403402095ef4e8d6a814fdc2256589944c1dc974c70d2f65b7e
+F ext/wasm/api/sqlite3-wasm.c 2a0f9e4bf1b141a787918951360601128d6a0a190a31a8e5cfe237c99fa640c6
 F ext/wasm/batch-runner.html c363032aba7a525920f61f8be112a29459f73f07e46f0ba3b7730081a617826e
 F ext/wasm/batch-runner.js ce92650a6681586c89bef26ceae96674a55ca5a9727815202ca62e1a00ff5015
 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 d2557d6ef1ebaaf3c9a0cea2231fd398b0d8ca8129b51580af1c92f8d04335e0
+F ext/wasm/common/whwasmutil.js cdb33775fdc55c9b1cbb617d22d24b4a29dc9c1389b827a5b14886a291480d70
 F ext/wasm/demo-123-worker.html e419b66495d209b5211ec64903b4cfb3ca7df20d652b41fcd28bf018a773234f
 F ext/wasm/demo-123.html aa281d33b7eefa755f3122b7b5a18f39a42dc5fb69c8879171bf14b4c37c4ec4
 F ext/wasm/demo-123.js 536579fd587974c2511c5bf82034b253d4fdeceabb726927ad7599ef6b7578e8
@@ -530,7 +530,7 @@ F ext/wasm/test-opfs-vfs.js a59ff9210b17d46b0c6fbf6a0ba60143c033327865f2e556e14f
 F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893
 F ext/wasm/testing-worker1-promiser.js bd788e33c1807e0a6dda9c9a9d784bd3350ca49c9dd8ae2cc8719b506b6e013e
 F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae
-F ext/wasm/testing1.js 51ef1ced0669f804787ce96f19dcf64367550a7923a998514be56076326988d7
+F ext/wasm/testing1.js bdea170b16189028c1f63023c620df52ddf31ed416bad56d729c60031b1e27ae
 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3
 F ext/wasm/testing2.js 88f40ef3cd8201bdadd120a711c36bbf0ce56cc0eab1d5e7debb71fed7822494
 F ext/wasm/wasmfs.make 3cce1820006196de140f90f2da4b4ea657083fb5bfee7d125be43f7a85748c8f
@@ -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 f9db664f756f3707afcb5dce87f6d946625848f27ea84337af68de72d4ad6c6b
-R 8201c0f212d8f73dc23b363b8bdcd606
+P 8b3bc7313aff551e5ee0b7aeb927095cf19b9b96abbdd922066c130656b8aa7d
+R a0d2cb1d1b18288fcfda68347c547fdd
 U stephan
-Z 7616e552f06f17aa339add7ed573ed79
+Z 5fc153c99e29258d724616b67e70d32b
 # Remove this line to create a well-formed Fossil manifest.
index 29067e24bfab7deda1832155d21bb9331512ba61..fa1c36d777f50469dc2e3169b684edb26484492a 100644 (file)
@@ -1 +1 @@
-8b3bc7313aff551e5ee0b7aeb927095cf19b9b96abbdd922066c130656b8aa7d
\ No newline at end of file
+eb5726677a727a958df11f1fba078d30c7c0ba2a9bdb158e8641b35b5f971af3
\ No newline at end of file