SQLite

Check-in [333e67076b]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Expose sqlite3_randomness() to WASM and add a custom binding for it which can populate a JS byte array. Add WhWasmUtil.isPtr().
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 333e67076b4bc967bb543ef8e265c63f6e3498c38ac121a7d1eff4a1d7a71c63
User & Date: stephan 2022-10-27 03:03:16.460
Context
2022-10-27
03:56
Restructure and simplify the feature-detection #defines of the recovery support in shell.c.in and disable it when building fiddle because it uses features we elide from the wasm build (e.g. utf16), leading to link errors. (check-in: ddd10c05c5 user: stephan tags: trunk)
03:03
Expose sqlite3_randomness() to WASM and add a custom binding for it which can populate a JS byte array. Add WhWasmUtil.isPtr(). (check-in: 333e67076b user: stephan tags: trunk)
2022-10-26
21:14
Disable the push-down optimization for sub-queries that are INTERSECT, UNION or EXCEPT compounds. dbsqlfuzz a34f455c91ad75a0cf8cd9476841903f42930a7a. This corrects an issue that was introduce 12 days earlier by [ed14863dd72e35fa]. (check-in: 346a3b12b8 user: dan tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api.
48
49
50
51
52
53
54

55
56
57
58
59
60
61
_sqlite3_malloc
_sqlite3_malloc64
_sqlite3_msize
_sqlite3_open
_sqlite3_open_v2
_sqlite3_prepare_v2
_sqlite3_prepare_v3

_sqlite3_realloc
_sqlite3_realloc64
_sqlite3_reset
_sqlite3_result_blob
_sqlite3_result_double
_sqlite3_result_error
_sqlite3_result_error_code







>







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
_sqlite3_malloc
_sqlite3_malloc64
_sqlite3_msize
_sqlite3_open
_sqlite3_open_v2
_sqlite3_prepare_v2
_sqlite3_prepare_v3
_sqlite3_randomness
_sqlite3_realloc
_sqlite3_realloc64
_sqlite3_reset
_sqlite3_result_blob
_sqlite3_result_double
_sqlite3_result_error
_sqlite3_result_error_code
Changes to ext/wasm/api/sqlite3-api-glue.js.
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
    /*  "The problem" is that the following isn't even remotely
        type-safe. OTOH, nothing about WASM pointers is. */
    const argPointer = wasm.xWrap.argAdapter('*');
    wasm.xWrap.argAdapter('StructType', (v)=>{
      if(v && v.constructor && v instanceof StructBinder.StructType){
        v = v.pointer;
      }
      return (v === (v | 0) /* v is a 32-bit integer */)
        ? argPointer(v)
        : toss("Invalid (object) type for StructType-type argument.");
    });
  }

  if(1){/* Convert Arrays and certain TypedArrays to strings for
           'flexible-string'-type arguments */







|







41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
    /*  "The problem" is that the following isn't even remotely
        type-safe. OTOH, nothing about WASM pointers is. */
    const argPointer = wasm.xWrap.argAdapter('*');
    wasm.xWrap.argAdapter('StructType', (v)=>{
      if(v && v.constructor && v instanceof StructBinder.StructType){
        v = v.pointer;
      }
      return wasm.isPtr(v)
        ? argPointer(v)
        : toss("Invalid (object) type for StructType-type argument.");
    });
  }

  if(1){/* Convert Arrays and certain TypedArrays to strings for
           'flexible-string'-type arguments */
Changes to ext/wasm/api/sqlite3-api-prologue.js.
211
212
213
214
215
216
217


























218
219
220
221
222
223
224
  };

  /** Returns v if v appears to be a TypedArray, else false. */
  const isTypedArray = (v)=>{
    return (v && v.constructor && isInt32(v.constructor.BYTES_PER_ELEMENT)) ? v : false;
  };



























  /**
     Returns true if v appears to be one of our bind()-able
     TypedArray types: Uint8Array or Int8Array. Support for
     TypedArrays with element sizes >1 is TODO.
  */
  const isBindableTypedArray = (v)=>{
    return v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT);







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
  };

  /** Returns v if v appears to be a TypedArray, else false. */
  const isTypedArray = (v)=>{
    return (v && v.constructor && isInt32(v.constructor.BYTES_PER_ELEMENT)) ? v : false;
  };


  /** Internal helper to use in operations which need to distinguish
      between TypedArrays which are backed by a SharedArrayBuffer
      from those which are not. */
  const __SAB = ('undefined'===typeof SharedArrayBuffer)
        ? function(){} : SharedArrayBuffer;
  /** Returns true if the given TypedArray object is backed by a
      SharedArrayBuffer, else false. */
  const isSharedTypedArray = (aTypedArray)=>(aTypedArray.buffer instanceof __SAB);

  /**
     Returns either aTypedArray.slice(begin,end) (if
     aTypedArray.buffer is a SharedArrayBuffer) or
     aTypedArray.subarray(begin,end) (if it's not).

     This distinction is important for APIs which don't like to
     work on SABs, e.g. TextDecoder, and possibly for our
     own APIs which work on memory ranges which "might" be
     modified by other threads while it's working.
   */
  const typedArrayPart = (aTypedArray, begin, end)=>{
    return isSharedTypedArray(aTypedArray)
      ? aTypedArray.slice(begin, end)
      : aTypedArray.subarray(begin, end);
  };

  /**
     Returns true if v appears to be one of our bind()-able
     TypedArray types: Uint8Array or Int8Array. Support for
     TypedArrays with element sizes >1 is TODO.
  */
  const isBindableTypedArray = (v)=>{
    return v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT);
242
243
244
245
246
247
248
249
250
251

252



253
254
255
256
257
258
259
260
261
262
263
264
265
  const affirmBindableTypedArray = (v)=>{
    return isBindableTypedArray(v)
      || toss("Value is not of a supported TypedArray type.");
  };

  const utf8Decoder = new TextDecoder('utf-8');

  /** Internal helper to use in operations which need to distinguish
      between SharedArrayBuffer heap memory and non-shared heap. */
  const __SAB = ('undefined'===typeof SharedArrayBuffer)

        ? function(){} : SharedArrayBuffer;



  const typedArrayToString = function(arrayBuffer, begin, end){
    return utf8Decoder.decode(
      (arrayBuffer.buffer instanceof __SAB)
        ? arrayBuffer.slice(begin, end)
        : arrayBuffer.subarray(begin, end)
    );
  };

  /**
     If v is-a Array, its join('') result is returned.  If
     isSQLableTypedArray(v) is true then typedArrayToString(v) is
     returned. Else v is returned as-is.
  */







|
|
|
>
|
>
>
>
|
|
<
<
<
<







268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284




285
286
287
288
289
290
291
  const affirmBindableTypedArray = (v)=>{
    return isBindableTypedArray(v)
      || toss("Value is not of a supported TypedArray type.");
  };

  const utf8Decoder = new TextDecoder('utf-8');

  /**
     Uses TextDecoder to decode the given half-open range of the
     given TypedArray to a string. This differs from a simple
     call to TextDecoder in that it accounts for whether the
     first argument is based by a SharedArrayBuffer or not,
     and can work more efficiently if it's not (TextDecoder
     refuses to act upon an SAB).
  */
  const typedArrayToString = function(typedArray, begin, end){
    return utf8Decoder.decode(typedArrayPart(typedArray, begin,end));




  };

  /**
     If v is-a Array, its join('') result is returned.  If
     isSQLableTypedArray(v) is true then typedArrayToString(v) is
     returned. Else v is returned as-is.
  */
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519















520
521
522
523
524
525
526
527
528
529
530
531
532
533


534
535
536
537
538
539
540
                         stmtPtrPtr,strPtrPtr)=>{}/*installed later*/,

    /**
       This binding enables the callback argument to be a JavaScript.

       If the callback is a function, then for the duration of the
       sqlite3_exec() call, it installs a WASM-bound function which
       acts as a proxy for the given callback. That proxy will
       also perform a conversion of the callback's arguments from
       `(char**)` to JS arrays of strings. However, for API
       consistency's sake it will still honor the C-level
       callback parameter order and will call it like:

       `callback(pVoid, colCount, listOfValues, listOfColNames)`

       If the callback is not a JS function then this binding performs
       no translation of the callback, but the sql argument is still
       converted to a WASM string for the call using the
       "flexible-string" argument converter.
    */
    sqlite3_exec: (pDb, sql, callback, pVoid, pErrMsg)=>{}/*installed later*/,
















    /**
       Various internal-use utilities are added here as needed. They
       are bound to an object only so that we have access to them in
       the differently-scoped steps of the API bootstrapping
       process. At the end of the API setup process, this object gets
       removed.
    */
    util:{
      affirmBindableTypedArray, flexibleString,
      bigIntFits32, bigIntFits64, bigIntFitsDouble,
      isBindableTypedArray,
      isInt32, isSQLableTypedArray, isTypedArray, 
      typedArrayToString,
      isUIThread: ()=>'undefined'===typeof WorkerGlobalScope


    },
    
    /**
       Holds state which are specific to the WASM-related
       infrastructure and glue code. It is not expected that client
       code will normally need these, but they're exposed here in case
       it does. These APIs are _not_ to be considered an







|
|

|
|










>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





|







|
>
>







524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
                         stmtPtrPtr,strPtrPtr)=>{}/*installed later*/,

    /**
       This binding enables the callback argument to be a JavaScript.

       If the callback is a function, then for the duration of the
       sqlite3_exec() call, it installs a WASM-bound function which
       acts as a proxy for the given callback. That proxy will also
       perform a conversion of the callback's arguments from
       `(char**)` to JS arrays of strings. However, for API
       consistency's sake it will still honor the C-level callback
       parameter order and will call it like:

       `callback(pVoid, colCount, listOfValues, listOfColNames)`

       If the callback is not a JS function then this binding performs
       no translation of the callback, but the sql argument is still
       converted to a WASM string for the call using the
       "flexible-string" argument converter.
    */
    sqlite3_exec: (pDb, sql, callback, pVoid, pErrMsg)=>{}/*installed later*/,

    /**
       If passed a single argument which appears to be a byte-oriented
       TypedArray (Int8Array or Uint8Array), this function treats that
       TypedArray as an output target, fetches `theArray.byteLength`
       bytes of randomness, and populates the whole array with it. As
       a special case, if the array's length is 0, this function
       behaves as if it were passed (0,0). When called this way, it
       returns its argument, else it returns the `undefined` value.

       If called with any other arguments, they are passed on as-is
       to the C API. Results are undefined if passed any incompatible
       values.
     */
    sqlite3_randomness: (n, outPtr)=>{/*installed later*/},

    /**
       Various internal-use utilities are added here as needed. They
       are bound to an object only so that we have access to them in
       the differently-scoped steps of the API bootstrapping
       process. At the end of the API setup process, this object gets
       removed. These are NOT part of the public API.
    */
    util:{
      affirmBindableTypedArray, flexibleString,
      bigIntFits32, bigIntFits64, bigIntFitsDouble,
      isBindableTypedArray,
      isInt32, isSQLableTypedArray, isTypedArray, 
      typedArrayToString,
      isUIThread: ()=>'undefined'===typeof WorkerGlobalScope,
      isSharedTypedArray,
      typedArrayPart
    },
    
    /**
       Holds state which are specific to the WASM-related
       infrastructure and glue code. It is not expected that client
       code will normally need these, but they're exposed here in case
       it does. These APIs are _not_ to be considered an
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
      */
      dealloc: undefined/*installed later*/

      /* Many more wasm-related APIs get installed later on. */
    }/*wasm*/
  }/*capi*/;

  const wasm = capi.wasm;

  /**
     wasm.alloc()'s srcTypedArray.byteLength bytes,
     populates them with the values from the source
     TypedArray, and returns the pointer to that memory. The
     returned pointer must eventually be passed to
     wasm.dealloc() to clean it up.







|







656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
      */
      dealloc: undefined/*installed later*/

      /* Many more wasm-related APIs get installed later on. */
    }/*wasm*/
  }/*capi*/;

  const wasm = capi.wasm, util = capi.util;

  /**
     wasm.alloc()'s srcTypedArray.byteLength bytes,
     populates them with the values from the source
     TypedArray, and returns the pointer to that memory. The
     returned pointer must eventually be passed to
     wasm.dealloc() to clean it up.
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774

775
776
777
778
779
780
781
782
783
784
785
786
787
788
     result/argument type strings gets plugged in at a later phase in
     the API initialization process.
  */
  wasm.bindingSignatures = [
    // Please keep these sorted by function name!
    ["sqlite3_aggregate_context","void*", "sqlite3_context*", "int"],
    ["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*"
     /* We should arguably write a custom wrapper which knows how
        to handle Blob, TypedArrays, and JS strings. */
    ],
    ["sqlite3_bind_double","int", "sqlite3_stmt*", "int", "f64"],
    ["sqlite3_bind_int","int", "sqlite3_stmt*", "int", "int"],
    ["sqlite3_bind_null",undefined, "sqlite3_stmt*", "int"],
    ["sqlite3_bind_parameter_count", "int", "sqlite3_stmt*"],
    ["sqlite3_bind_parameter_index","int", "sqlite3_stmt*", "string"],
    ["sqlite3_bind_text","int", "sqlite3_stmt*", "int", "string", "int", "int"
     /* We should arguably create a hand-written binding
        which does more flexible text conversion, along the lines of
        sqlite3_prepare_v3(). The slightly problematic part is the
        final argument (text destructor). */
    ],
    ["sqlite3_close_v2", "int", "sqlite3*"],
    ["sqlite3_changes", "int", "sqlite3*"],
    ["sqlite3_clear_bindings","int", "sqlite3_stmt*"],
    ["sqlite3_column_blob","*", "sqlite3_stmt*", "int"],
    ["sqlite3_column_bytes","int", "sqlite3_stmt*", "int"],
    ["sqlite3_column_count", "int", "sqlite3_stmt*"],
    ["sqlite3_column_double","f64", "sqlite3_stmt*", "int"],
    ["sqlite3_column_int","int", "sqlite3_stmt*", "int"],
    ["sqlite3_column_name","string", "sqlite3_stmt*", "int"],
    ["sqlite3_column_text","string", "sqlite3_stmt*", "int"],
    ["sqlite3_column_type","int", "sqlite3_stmt*", "int"],
    ["sqlite3_compileoption_get", "string", "int"],
    ["sqlite3_compileoption_used", "int", "string"],
    /* 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*"],
    ["sqlite3_db_name", "string", "sqlite3*", "int"],
    ["sqlite3_deserialize", "int", "sqlite3*", "string", "*", "i64", "i64", "int"]
    /* Careful! Short version: de/serialize() are problematic because they
       might use a different allocator that the user for managing the
       deserialized block. de/serialize() are ONLY safe to use with
       sqlite3_malloc(), sqlite3_free(), and its 64-bit variants. */,
    ["sqlite3_errmsg", "string", "sqlite3*"],
    ["sqlite3_error_offset", "int", "sqlite3*"],
    ["sqlite3_errstr", "string", "int"],
    /*["sqlite3_exec", "int", "sqlite3*", "string", "*", "*", "**"
      Handled seperately to perform translation of the callback







|
|







|
|
|
|














|
|
>






|







782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
     result/argument type strings gets plugged in at a later phase in
     the API initialization process.
  */
  wasm.bindingSignatures = [
    // Please keep these sorted by function name!
    ["sqlite3_aggregate_context","void*", "sqlite3_context*", "int"],
    ["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*"
     /* TODO: we should arguably write a custom wrapper which knows
        how to handle Blob, TypedArrays, and JS strings. */
    ],
    ["sqlite3_bind_double","int", "sqlite3_stmt*", "int", "f64"],
    ["sqlite3_bind_int","int", "sqlite3_stmt*", "int", "int"],
    ["sqlite3_bind_null",undefined, "sqlite3_stmt*", "int"],
    ["sqlite3_bind_parameter_count", "int", "sqlite3_stmt*"],
    ["sqlite3_bind_parameter_index","int", "sqlite3_stmt*", "string"],
    ["sqlite3_bind_text","int", "sqlite3_stmt*", "int", "string", "int", "int"
     /* We should arguably create a hand-written binding of
        bind_text() which does more flexible text conversion, along
        the lines of sqlite3_prepare_v3(). The slightly problematic
        part is the final argument (text destructor). */
    ],
    ["sqlite3_close_v2", "int", "sqlite3*"],
    ["sqlite3_changes", "int", "sqlite3*"],
    ["sqlite3_clear_bindings","int", "sqlite3_stmt*"],
    ["sqlite3_column_blob","*", "sqlite3_stmt*", "int"],
    ["sqlite3_column_bytes","int", "sqlite3_stmt*", "int"],
    ["sqlite3_column_count", "int", "sqlite3_stmt*"],
    ["sqlite3_column_double","f64", "sqlite3_stmt*", "int"],
    ["sqlite3_column_int","int", "sqlite3_stmt*", "int"],
    ["sqlite3_column_name","string", "sqlite3_stmt*", "int"],
    ["sqlite3_column_text","string", "sqlite3_stmt*", "int"],
    ["sqlite3_column_type","int", "sqlite3_stmt*", "int"],
    ["sqlite3_compileoption_get", "string", "int"],
    ["sqlite3_compileoption_used", "int", "string"],
    /* sqlite3_create_function(), sqlite3_create_function_v2(), and
       sqlite3_create_window_function() use hand-written bindings to
       simplify handling of their function-type arguments. */
    ["sqlite3_data_count", "int", "sqlite3_stmt*"],
    ["sqlite3_db_filename", "string", "sqlite3*", "string"],
    ["sqlite3_db_handle", "sqlite3*", "sqlite3_stmt*"],
    ["sqlite3_db_name", "string", "sqlite3*", "int"],
    ["sqlite3_deserialize", "int", "sqlite3*", "string", "*", "i64", "i64", "int"]
    /* Careful! Short version: de/serialize() are problematic because they
       might use a different allocator than the user for managing the
       deserialized block. de/serialize() are ONLY safe to use with
       sqlite3_malloc(), sqlite3_free(), and its 64-bit variants. */,
    ["sqlite3_errmsg", "string", "sqlite3*"],
    ["sqlite3_error_offset", "int", "sqlite3*"],
    ["sqlite3_errstr", "string", "int"],
    /*["sqlite3_exec", "int", "sqlite3*", "string", "*", "*", "**"
      Handled seperately to perform translation of the callback
802
803
804
805
806
807
808


809
810
811
812
813
814
815
    ["sqlite3_libversion_number", "int"],
    ["sqlite3_malloc", "*","int"],
    ["sqlite3_open", "int", "string", "*"],
    ["sqlite3_open_v2", "int", "string", "*", "int", "string"],
    /* sqlite3_prepare_v2() and sqlite3_prepare_v3() are handled
       separately due to us requiring two different sets of semantics
       for those, depending on how their SQL argument is provided. */


    ["sqlite3_realloc", "*","*","int"],
    ["sqlite3_reset", "int", "sqlite3_stmt*"],
    ["sqlite3_result_blob",undefined, "*", "*", "int", "*"],
    ["sqlite3_result_double",undefined, "*", "f64"],
    ["sqlite3_result_error",undefined, "*", "string", "int"],
    ["sqlite3_result_error_code", undefined, "*", "int"],
    ["sqlite3_result_error_nomem", undefined, "*"],







>
>







846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
    ["sqlite3_libversion_number", "int"],
    ["sqlite3_malloc", "*","int"],
    ["sqlite3_open", "int", "string", "*"],
    ["sqlite3_open_v2", "int", "string", "*", "int", "string"],
    /* sqlite3_prepare_v2() and sqlite3_prepare_v3() are handled
       separately due to us requiring two different sets of semantics
       for those, depending on how their SQL argument is provided. */
    /* sqlite3_randomness() uses a hand-written wrapper to extend
       the range of supported argument types. */
    ["sqlite3_realloc", "*","*","int"],
    ["sqlite3_reset", "int", "sqlite3_stmt*"],
    ["sqlite3_result_blob",undefined, "*", "*", "int", "*"],
    ["sqlite3_result_double",undefined, "*", "f64"],
    ["sqlite3_result_error",undefined, "*", "string", "int"],
    ["sqlite3_result_error_code", undefined, "*", "int"],
    ["sqlite3_result_error_nomem", undefined, "*"],
1047
1048
1049
1050
1051
1052
1053
1054

































1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
     ```
     return someFunction(x) || SQLite3Error.toss(...);
     ```
  */
  SQLite3Error.toss = (...args)=>{
    throw new SQLite3Error(...args);
  };


































  /** State for sqlite3_wasmfs_opfs_dir(). */
  let __persistentDir = undefined;
  /**
     If the wasm environment has a WASMFS/OPFS-backed persistent
     storage directory, its path is returned by this function. If it
     does not then it returns "" (noting that "" is a falsy value).

     The first time this is called, this function inspects the current
     environment to determine whether persistence support is available
     and, if it is, enables it (if needed).

     This function currently only recognizes the WASMFS/OPFS storage
     combination and its path refers to storage rooted in the
     Emscripten-managed virtual filesystem.
  */
  capi.sqlite3_wasmfs_opfs_dir = function(){
    if(undefined !== __persistentDir) return __persistentDir;
    // If we have no OPFS, there is no persistent dir
    const pdir = config.wasmfsOpfsDir;
    if(!pdir
       || !self.FileSystemHandle
       || !self.FileSystemDirectoryHandle
       || !self.FileSystemFileHandle){
      return __persistentDir = "";
    }
    try{
      if(pdir && 0===wasm.xCallWrapped(
        'sqlite3_wasm_init_wasmfs', 'i32', ['string'], pdir
      )){
        return __persistentDir = pdir;
      }else{
        return __persistentDir = "";
      }
    }catch(e){
      // sqlite3_wasm_init_wasmfs() is not available
      return __persistentDir = "";
    }
  };

  /**
     Experimental and subject to change or removal.

     Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|














|






|





|

|



|







1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
     ```
     return someFunction(x) || SQLite3Error.toss(...);
     ```
  */
  SQLite3Error.toss = (...args)=>{
    throw new SQLite3Error(...args);
  };

  capi.sqlite3_randomness = (...args)=>{
    if(1===args.length && util.isTypedArray(args[0])
      && 1===args[0].BYTES_PER_ELEMENT){
      const ta = args[0];
      if(0===ta.byteLength){
        wasm.exports.sqlite3_randomness(0,0);
        return ta;
      }
      const stack = wasm.pstack.pointer;
      try {
        let n = ta.byteLength, offset = 0;
        const r = wasm.exports.sqlite3_randomness;
        const heap = wasm.heap8u();
        const nAlloc = n < 512 ? n : 512;
        const ptr = wasm.pstack.alloc(nAlloc);
        do{
          const j = (n>nAlloc ? nAlloc : n);
          r(j, ptr);
          ta.set(typedArrayPart(heap, ptr, ptr+j), offset);
          n -= j;
          offset += j;
        } while(n > 0);
      }catch(e){
        console.error("Highly unexpected (and ignored!) "+
                      "exception in sqlite3_randomness():",e);
      }finally{
        wasm.pstack.restore(stack);
      }
      return ta;
    }
    capi.wasm.exports.sqlite3_randomness(...args);
  };

  /** State for sqlite3_wasmfs_opfs_dir(). */
  let __wasmfsOpfsDir = undefined;
  /**
     If the wasm environment has a WASMFS/OPFS-backed persistent
     storage directory, its path is returned by this function. If it
     does not then it returns "" (noting that "" is a falsy value).

     The first time this is called, this function inspects the current
     environment to determine whether persistence support is available
     and, if it is, enables it (if needed).

     This function currently only recognizes the WASMFS/OPFS storage
     combination and its path refers to storage rooted in the
     Emscripten-managed virtual filesystem.
  */
  capi.sqlite3_wasmfs_opfs_dir = function(){
    if(undefined !== __wasmfsOpfsDir) return __wasmfsOpfsDir;
    // If we have no OPFS, there is no persistent dir
    const pdir = config.wasmfsOpfsDir;
    if(!pdir
       || !self.FileSystemHandle
       || !self.FileSystemDirectoryHandle
       || !self.FileSystemFileHandle){
      return __wasmfsOpfsDir = "";
    }
    try{
      if(pdir && 0===wasm.xCallWrapped(
        'sqlite3_wasm_init_wasmfs', 'i32', ['string'], pdir
      )){
        return __wasmfsOpfsDir = pdir;
      }else{
        return __wasmfsOpfsDir = "";
      }
    }catch(e){
      // sqlite3_wasm_init_wasmfs() is not available
      return __wasmfsOpfsDir = "";
    }
  };

  /**
     Experimental and subject to change or removal.

     Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a
Changes to ext/wasm/common/whwasmutil.js.
700
701
702
703
704
705
706

















707
708
709
710
711
712
713
      pointer-to-pointer values. */
  target.getPtrValue = (ptr)=>target.getMemValue(ptr, ptrIR);

  /** Convenience form of setMemValue() intended for setting
      pointer-to-pointer values. */
  target.setPtrValue = (ptr, value)=>target.setMemValue(ptr, value, ptrIR);


















  /**
     Expects ptr to be a pointer into the WASM heap memory which
     refers to a NUL-terminated C-style string encoded as UTF-8.
     Returns the length, in bytes, of the string, as for `strlen(3)`.
     As a special case, if !ptr then it it returns `null`. Throws if
     ptr is out of range for target.heap8u().
  */







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
      pointer-to-pointer values. */
  target.getPtrValue = (ptr)=>target.getMemValue(ptr, ptrIR);

  /** Convenience form of setMemValue() intended for setting
      pointer-to-pointer values. */
  target.setPtrValue = (ptr, value)=>target.setMemValue(ptr, value, ptrIR);

  /**
     Returns true if the given value appears to be legal for use as
     a WASM pointer value. Its _range_ of values is not (cannot be)
     validated except to ensure that it is a 32-bit integer with a
     value of 0 or greater. Likewise, it cannot verify whether the
     value actually refers to allocated memory in the WASM heap.
  */
  target.isPtr32 = (ptr)=>('number'===typeof ptr && (ptr===(ptr|0)) && ptr>=0);

  /**
     isPtr() is an alias for isPtr32(). If/when 64-bit WASM pointer
     support becomes widespread, it will become an alias for either
     isPtr32() or the as-yet-hypothetical isPtr64(), depending on a
     configuration option.
  */
  target.isPtr = target.isPtr32;

  /**
     Expects ptr to be a pointer into the WASM heap memory which
     refers to a NUL-terminated C-style string encoded as UTF-8.
     Returns the length, in bytes, of the string, as for `strlen(3)`.
     As a special case, if !ptr then it it returns `null`. Throws if
     ptr is out of range for target.heap8u().
  */
1225
1226
1227
1228
1229
1230
1231
1232

1233
1234
1235
1236
1237
1238
1239
  xcv.arg.i8  = (i)=>((i | 0) & 0xFF);
  xcv.arg.f32 = xcv.arg.float = (i)=>Number(i).valueOf();
  xcv.arg.f64 = xcv.arg.double = xcv.arg.f32;
  xcv.arg.int = xcv.arg.i32;
  xcv.result['*'] = xcv.result['pointer'] = xcv.arg['**'] = xcv.arg[ptrIR];
  xcv.result['number'] = (v)=>Number(v);

  {

    const copyToResult = ['i8', 'i16', 'i32', 'int',
                          'f32', 'float', 'f64', 'double'];
    if(target.bigIntEnabled) copyToResult.push('i64');
    for(const t of copyToResult){
      xcv.arg[t+'*'] = xcv.result[t+'*'] = xcv.arg[ptrIR];
      xcv.result[t] = xcv.arg[t] || toss("Missing arg converter:",t);
    }







|
>







1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
  xcv.arg.i8  = (i)=>((i | 0) & 0xFF);
  xcv.arg.f32 = xcv.arg.float = (i)=>Number(i).valueOf();
  xcv.arg.f64 = xcv.arg.double = xcv.arg.f32;
  xcv.arg.int = xcv.arg.i32;
  xcv.result['*'] = xcv.result['pointer'] = xcv.arg['**'] = xcv.arg[ptrIR];
  xcv.result['number'] = (v)=>Number(v);

  { /* Copy certain xcv.arg[...] handlers to xcv.result[...] and
       add pointer-style variants of them. */
    const copyToResult = ['i8', 'i16', 'i32', 'int',
                          'f32', 'float', 'f64', 'double'];
    if(target.bigIntEnabled) copyToResult.push('i64');
    for(const t of copyToResult){
      xcv.arg[t+'*'] = xcv.result[t+'*'] = xcv.arg[ptrIR];
      xcv.result[t] = xcv.arg[t] || toss("Missing arg converter:",t);
    }
Changes to ext/wasm/tester1.js.
372
373
374
375
376
377
378













379
380
381
382
383
384
385
            assert(w.heapForSize(s.constructor) === s);
          const u = w.heapForSize(n,true);
          T.assert(bpe===u.BYTES_PER_ELEMENT).
            assert(s!==u).
            assert(w.heapForSize(u.constructor) === u);
        }
      }














      //log("jstrlen()...");
      {
        T.assert(3 === w.jstrlen("abc")).assert(4 === w.jstrlen("äbc"));
      }

      //log("jstrcpy()...");







>
>
>
>
>
>
>
>
>
>
>
>
>







372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
            assert(w.heapForSize(s.constructor) === s);
          const u = w.heapForSize(n,true);
          T.assert(bpe===u.BYTES_PER_ELEMENT).
            assert(s!==u).
            assert(w.heapForSize(u.constructor) === u);
        }
      }

      // isPtr32()
      {
        const ip = w.isPtr32;
        T.assert(ip(0))
          .assert(!ip(-1))
          .assert(!ip(1.1))
          .assert(!ip(0xffffffff))
          .assert(ip(0x7fffffff))
          .assert(!ip())
          .assert(!ip(null)/*might change: under consideration*/)
        ;
      }

      //log("jstrlen()...");
      {
        T.assert(3 === w.jstrlen("abc")).assert(4 === w.jstrlen("äbc"));
      }

      //log("jstrcpy()...");
1052
1053
1054
1055
1056
1057
1058











































1059
1060
1061
1062
1063
1064
1065
        P.restore(stack);
      }
    }/*pstack tests*/)

  ////////////////////////////////////////////////////////////////////
  ;/*end of C/WASM utils checks*/












































  ////////////////////////////////////////////////////////////////////////
  T.g('sqlite3.oo1')
    .t('Create db', function(sqlite3){
      const dbFile = '/tester1.db';
      sqlite3.capi.wasm.sqlite3_wasm_vfs_unlink(0, dbFile);
      const db = this.db = new sqlite3.oo1.DB(dbFile);
      T.assert(Number.isInteger(db.pointer)).







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
        P.restore(stack);
      }
    }/*pstack tests*/)

  ////////////////////////////////////////////////////////////////////
  ;/*end of C/WASM utils checks*/

  T.g('sqlite3_randomness()')
    .t('To memory buffer', function(sqlite3){
      const stack = wasm.pstack.pointer;
      try{
        const n = 520;
        const p = wasm.pstack.alloc(n);
        T.assert(0===wasm.getMemValue(p))
          .assert(0===wasm.getMemValue(p+n-1));
        T.assert(undefined === capi.sqlite3_randomness(n - 10, p));
        let j, check = 0;
        const heap = wasm.heap8u();
        for(j = 0; j < 10 && 0===check; ++j){
          check += heap[p + j];
        }
        T.assert(check > 0);
        check = 0;
        // Ensure that the trailing bytes were not modified...
        for(j = n - 10; j < n && 0===check; ++j){
          check += heap[p + j];
        }
        T.assert(0===check);
      }finally{
        wasm.pstack.restore(stack);
      }
    })
    .t('To byte array', function(sqlite3){
      const ta = new Uint8Array(117);
      let i, n = 0;
      for(i=0; i<ta.byteLength && 0===n; ++i){
        n += ta[i];
      }
      T.assert(0===n)
        .assert(ta === capi.sqlite3_randomness(ta));
      for(i=ta.byteLength-10; i<ta.byteLength && 0===n; ++i){
        n += ta[i];
      }
      T.assert(n>0);
      const t0 = new Uint8Array(0);
      T.assert(t0 === capi.sqlite3_randomness(t0),
               "0-length array is a special case");
    })
  ;;/*end sqlite3_randomness() checks*/

  ////////////////////////////////////////////////////////////////////////
  T.g('sqlite3.oo1')
    .t('Create db', function(sqlite3){
      const dbFile = '/tester1.db';
      sqlite3.capi.wasm.sqlite3_wasm_vfs_unlink(0, dbFile);
      const db = this.db = new sqlite3.oo1.DB(dbFile);
      T.assert(Number.isInteger(db.pointer)).