SQLite

Changes On Branch wasm-fts5
Login

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

Changes In Branch wasm-fts5 Excluding Merge-Ins

This is equivalent to a diff from ea0b9aec to ce2a65d8

2023-08-04
08:39
More work towards fts5 customzation in JS. (Leaf check-in: ce2a65d8 user: stephan tags: wasm-fts5)
2023-08-03
22:43
Minor internal cleanups in the JS-side fts5 cleanup steps. (check-in: 2666f52e user: stephan tags: wasm-fts5)
12:41
Unix builds now assume the presence of nanosleep() in the standard library. The -DHAVE_NANOSLEEP=0 compile-time option can be used to build on systems (if any still exist) where this is not the case. (check-in: 779d5dc8 user: drh tags: trunk)
07:20
Initial work on exposing the FTS5 APIs to wasm, per request in the forum. This builds and loads the structs into JS but is completely untested. (check-in: 52c8b73a user: stephan tags: wasm-fts5)
2023-08-02
18:20
If a query has an ORDER BY clause that only refers to result columns of the left-most table and the left most table is a MATERIALIZED common table expresion, then attempt to push the ORDER BY clause down into the subquery. (Leaf check-in: 8e7a70b2 user: drh tags: order-by-push-down)
16:06
Performance optimization for JSON rendering logic. (check-in: ea0b9aec user: drh tags: trunk)
13:45
Remove an unreachable branch in the ascii-to-floating-point conversion that was added by [e989a37ff9d5b52e]. (check-in: c4347e44 user: drh tags: trunk)

Changes to ext/wasm/GNUmakefile.

345
346
347
348
349
350
351






352
353
354
355
356
357
358
# EXPORTED_FUNCTIONS.* = files for use with Emscripten's
# -sEXPORTED_FUNCTION flag.
EXPORTED_FUNCTIONS.api.main := $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api)
EXPORTED_FUNCTIONS.api.in := $(EXPORTED_FUNCTIONS.api.main)
ifeq (1,$(SQLITE_C_IS_SEE))
  EXPORTED_FUNCTIONS.api.in += $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-see)
endif






EXPORTED_FUNCTIONS.api := $(dir.tmp)/EXPORTED_FUNCTIONS.api
$(EXPORTED_FUNCTIONS.api): $(EXPORTED_FUNCTIONS.api.in) $(sqlite3.c) $(MAKEFILE)
	cat $(EXPORTED_FUNCTIONS.api.in) > $@

# sqlite3-license-version.js = generated JS file with the license
# header and version info.
sqlite3-license-version.js := $(dir.tmp)/sqlite3-license-version.js







>
>
>
>
>
>







345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
# EXPORTED_FUNCTIONS.* = files for use with Emscripten's
# -sEXPORTED_FUNCTION flag.
EXPORTED_FUNCTIONS.api.main := $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api)
EXPORTED_FUNCTIONS.api.in := $(EXPORTED_FUNCTIONS.api.main)
ifeq (1,$(SQLITE_C_IS_SEE))
  EXPORTED_FUNCTIONS.api.in += $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-see)
endif
ifeq (1,$(emcc.WASM_BIGINT))
  ifneq (,$(filter -DSQLITE_ENABLE_FTS5,$(SQLITE_OPT)))
    EXPORTED_FUNCTIONS.api.in += $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-fts5)
  endif
endif

EXPORTED_FUNCTIONS.api := $(dir.tmp)/EXPORTED_FUNCTIONS.api
$(EXPORTED_FUNCTIONS.api): $(EXPORTED_FUNCTIONS.api.in) $(sqlite3.c) $(MAKEFILE)
	cat $(EXPORTED_FUNCTIONS.api.in) > $@

# sqlite3-license-version.js = generated JS file with the license
# header and version info.
sqlite3-license-version.js := $(dir.tmp)/sqlite3-license-version.js
371
372
373
374
375
376
377



378
379
380
381
382
383
384
sqlite3-api.jses += $(dir.api)/sqlite3-api-glue.js
sqlite3-api.jses += $(sqlite3-api-build-version.js)
sqlite3-api.jses += $(dir.api)/sqlite3-api-oo1.js
sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.js
sqlite3-api.jses += $(dir.api)/sqlite3-v-helper.js
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs-sahpool.c-pp.js



sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js

# SOAP.js is an external API file which is part of our distribution
# but not part of the sqlite3-api.js amalgamation.
SOAP.js := $(dir.api)/sqlite3-opfs-async-proxy.js
SOAP.js.bld := $(dir.dout)/$(notdir $(SOAP.js))
sqlite3-api.ext.jses += $(SOAP.js.bld)







>
>
>







377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
sqlite3-api.jses += $(dir.api)/sqlite3-api-glue.js
sqlite3-api.jses += $(sqlite3-api-build-version.js)
sqlite3-api.jses += $(dir.api)/sqlite3-api-oo1.js
sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.js
sqlite3-api.jses += $(dir.api)/sqlite3-v-helper.js
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs-sahpool.c-pp.js
ifneq (,$(filter -DSQLITE_ENABLE_FTS5,$(SQLITE_OPT)))
  sqlite3-api.jses += $(dir.api)/sqlite3-fts5-helper.js
endif
sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js

# SOAP.js is an external API file which is part of our distribution
# but not part of the sqlite3-api.js amalgamation.
SOAP.js := $(dir.api)/sqlite3-opfs-async-proxy.js
SOAP.js.bld := $(dir.dout)/$(notdir $(SOAP.js))
sqlite3-api.ext.jses += $(SOAP.js.bld)

Added ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-fts5.



>
1
_fts5_api_from_db

Changes to ext/wasm/api/sqlite3-api-cleanup.js.

48
49
50
51
52
53
54
55
56
57

58
59
60
61
62
63
  }catch(e){
    console.error("sqlite3ApiBootstrap() error:",e);
    throw e;
  }finally{
    delete globalThis.sqlite3ApiBootstrap;
    delete globalThis.sqlite3ApiConfig;
  }

  Module.sqlite3 = sqlite3 /* Needed for customized sqlite3InitModule() to be able to
                              pass the sqlite3 object off to the client. */;

}else{
  console.warn("This is not running in an Emscripten module context, so",
               "globalThis.sqlite3ApiBootstrap() is _not_ being called due to lack",
               "of config info for the WASM environment.",
               "It must be called manually.");
}







|


>






48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  }catch(e){
    console.error("sqlite3ApiBootstrap() error:",e);
    throw e;
  }finally{
    delete globalThis.sqlite3ApiBootstrap;
    delete globalThis.sqlite3ApiConfig;
  }
  delete sqlite3.__dbCleanupMap;
  Module.sqlite3 = sqlite3 /* Needed for customized sqlite3InitModule() to be able to
                              pass the sqlite3 object off to the client. */;
  sqlite3.wasm.xWrap.FuncPtrAdapter.warnOnUse = true;
}else{
  console.warn("This is not running in an Emscripten module context, so",
               "globalThis.sqlite3ApiBootstrap() is _not_ being called due to lack",
               "of config info for the WASM environment.",
               "It must be called manually.");
}

Changes to ext/wasm/api/sqlite3-api-glue.js.

257
258
259
260
261
262
263












264
265
266
267
268
269
270
              return e.resultCode || capi.SQLITE_ERROR;
            }
          }
        }
      }),
      "*"/*pUserData*/
    ]],












    ["sqlite3_set_auxdata", undefined, [
      "sqlite3_context*", "int", "*",
      new wasm.xWrap.FuncPtrAdapter({
        name: 'xDestroyAuxData',
        signature: 'v(*)',
        contextKey: (argv, argIndex)=>argv[0/* sqlite3_context* */]
      })







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







257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
              return e.resultCode || capi.SQLITE_ERROR;
            }
          }
        }
      }),
      "*"/*pUserData*/
    ]],
    /**
       Achtung: when specifying an xDestroy() method via
       sqlite3_set_auxdata(), it is up to the client to re-set it to
       0/NULL at the end of its lifetime (e.g. in the associated UDF's
       xFinal() impl), The C library will be able to call the
       destructor but _not_ uninstall the temporary WASM-bound proxy
       function because it does not have enough information to do
       so. Alternately, clients may create the function pointer
       themselves using wasm.createFunction() and pass that pointer
       here, in which case they avoid creating a stranded "temporary"
       function binding.
    */
    ["sqlite3_set_auxdata", undefined, [
      "sqlite3_context*", "int", "*",
      new wasm.xWrap.FuncPtrAdapter({
        name: 'xDestroyAuxData',
        signature: 'v(*)',
        contextKey: (argv, argIndex)=>argv[0/* sqlite3_context* */]
      })
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
    ["sqlite3_vtab_nochange","int", "sqlite3_context*"],
    ["sqlite3_vtab_on_conflict","int", "sqlite3*"],
    ["sqlite3_vtab_rhs_value","int", "sqlite3_index_info*", "int", "**"]
  ];

  // Add session/changeset APIs...
  if(wasm.bigIntEnabled && !!wasm.exports.sqlite3changegroup_add){
    /* ACHTUNG: 2022-12-23: the session/changeset API bindings are
       COMPLETELY UNTESTED. */
    /**
       FuncPtrAdapter options for session-related callbacks with the
       native signature "i(ps)". This proxy converts the 2nd argument
       from a C string to a JS string before passing the arguments on
       to the client-provided JS callback.
    */
    const __ipsProxy = {







<
<







429
430
431
432
433
434
435


436
437
438
439
440
441
442
    ["sqlite3_vtab_nochange","int", "sqlite3_context*"],
    ["sqlite3_vtab_on_conflict","int", "sqlite3*"],
    ["sqlite3_vtab_rhs_value","int", "sqlite3_index_info*", "int", "**"]
  ];

  // Add session/changeset APIs...
  if(wasm.bigIntEnabled && !!wasm.exports.sqlite3changegroup_add){


    /**
       FuncPtrAdapter options for session-related callbacks with the
       native signature "i(ps)". This proxy converts the 2nd argument
       from a C string to a JS string before passing the arguments on
       to the client-provided JS callback.
    */
    const __ipsProxy = {
593
594
595
596
597
598
599






600
601
602
603
604
605
606
          contextKey: (argv,argIndex)=>argv[0/* (sqlite3_session*) */]
        }),
        '*'
      ]]
    ]);
  }/*session/changeset APIs*/







  /**
     Functions which are intended solely for API-internal use by the
     WASM components, not client code. These get installed into
     sqlite3.wasm. Some of them get exposed to clients via variants
     named sqlite3_js_...().
  */
  wasm.bindingSignatures.wasm = [







>
>
>
>
>
>







603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
          contextKey: (argv,argIndex)=>argv[0/* (sqlite3_session*) */]
        }),
        '*'
      ]]
    ]);
  }/*session/changeset APIs*/

  if(wasm.bigIntEnabled && !!wasm.exports.fts5_api_from_db){
    wasm.bindingSignatures.int64.push(
      ['fts5_api_from_db', 'fts5_api*', 'sqlite3*']
    );
  }/* fts5 APIs */

  /**
     Functions which are intended solely for API-internal use by the
     WASM components, not client code. These get installed into
     sqlite3.wasm. Some of them get exposed to clients via variants
     named sqlite3_js_...().
  */
  wasm.bindingSignatures.wasm = [
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
       need it to.
    */
    wasm.xWrap.argAdapter(
      'string:static',
      function(v){
        if(wasm.isPtr(v)) return v;
        v = ''+v;
        let rc = this[v];
        return rc || (this[v] = wasm.allocCString(v));
      }.bind(Object.create(null))
    );

    /**
       Add some descriptive xWrap() aliases for '*' intended to (A)
       initially improve readability/correctness of
       wasm.bindingSignatures and (B) provide automatic conversion







|
|







671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
       need it to.
    */
    wasm.xWrap.argAdapter(
      'string:static',
      function(v){
        if(wasm.isPtr(v)) return v;
        v = ''+v;
        const rc = this[v];
        return (undefined===rc) ? (this[v] = wasm.allocCString(v)) : rc;
      }.bind(Object.create(null))
    );

    /**
       Add some descriptive xWrap() aliases for '*' intended to (A)
       initially improve readability/correctness of
       wasm.bindingSignatures and (B) provide automatic conversion
713
714
715
716
717
718
719
720


721
722
723
724
725
726
727
          );
      }
      return __xArgPtr((v instanceof (capi.sqlite3_vfs || nilType))
                       ? v.pointer : v);
    });

    const __xRcPtr = wasm.xWrap.resultAdapter('*');
    wasm.xWrap.resultAdapter('sqlite3*', __xRcPtr)


    ('sqlite3_context*', __xRcPtr)
    ('sqlite3_stmt*', __xRcPtr)
    ('sqlite3_value*', __xRcPtr)
    ('sqlite3_vfs*', __xRcPtr)
    ('void*', __xRcPtr);

    /**







|
>
>







729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
          );
      }
      return __xArgPtr((v instanceof (capi.sqlite3_vfs || nilType))
                       ? v.pointer : v);
    });

    const __xRcPtr = wasm.xWrap.resultAdapter('*');
    wasm.xWrap.resultAdapter
    ('fts5_api*', __xRcPtr)
    ('sqlite3*', __xRcPtr)
    ('sqlite3_context*', __xRcPtr)
    ('sqlite3_stmt*', __xRcPtr)
    ('sqlite3_value*', __xRcPtr)
    ('sqlite3_vfs*', __xRcPtr)
    ('void*', __xRcPtr);

    /**
819
820
821
822
823
824
825



826
827
828
829
830
831
832
                          'prepareFlags', 'resultCodes',
                          'sqlite3Status',
                          'stmtStatus', 'syncFlags',
                          'trace', 'txnState', 'udfFlags',
                          'version' ];
    if(wasm.bigIntEnabled){
      defineGroups.push('serialize', 'session', 'vtab');



    }
    for(const t of defineGroups){
      for(const e of Object.entries(wasm.ctype[t])){
        // ^^^ [k,v] there triggers a buggy code transformation via
        // one of the Emscripten-driven optimizers.
        capi[e[0]] = e[1];
      }







>
>
>







837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
                          'prepareFlags', 'resultCodes',
                          'sqlite3Status',
                          'stmtStatus', 'syncFlags',
                          'trace', 'txnState', 'udfFlags',
                          'version' ];
    if(wasm.bigIntEnabled){
      defineGroups.push('serialize', 'session', 'vtab');
      if(!!wasm.ctype.fts5){
        defineGroups.push('fts5');
      }
    }
    for(const t of defineGroups){
      for(const e of Object.entries(wasm.ctype[t])){
        // ^^^ [k,v] there triggers a buggy code transformation via
        // one of the Emscripten-driven optimizers.
        capi[e[0]] = e[1];
      }
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
  };

  /**
     __dbCleanupMap is infrastructure for recording registration of
     UDFs and collations so that sqlite3_close_v2() can clean up any
     automated JS-to-WASM function conversions installed by those.
  */
  const __argPDb = (pDb)=>wasm.xWrap.argAdapter('sqlite3*')(pDb);
  const __argStr = (str)=>wasm.isPtr(str) ? wasm.cstrToJs(str) : str;
  const __dbCleanupMap = function(
    pDb, mode/*0=remove, >0=create if needed, <0=do not create if missing*/
  ){
    pDb = __argPDb(pDb);
    let m = this.dbMap.get(pDb);
    if(!mode){
      this.dbMap.delete(pDb);
      return m;







|

|







923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
  };

  /**
     __dbCleanupMap is infrastructure for recording registration of
     UDFs and collations so that sqlite3_close_v2() can clean up any
     automated JS-to-WASM function conversions installed by those.
  */
  const __argPDb = wasm.xWrap.argAdapter('sqlite3*');
  const __argStr = (str)=>wasm.isPtr(str) ? wasm.cstrToJs(str) : str;
  const __dbCleanupMap = sqlite3.__dbCleanupMap = function(
    pDb, mode/*0=remove, >0=create if needed, <0=do not create if missing*/
  ){
    pDb = __argPDb(pDb);
    let m = this.dbMap.get(pDb);
    if(!mode){
      this.dbMap.delete(pDb);
      return m;
969
970
971
972
973
974
975

976
977
978
979
980
981
982

     The issue being addressed here is covered at:

     https://sqlite.org/wasm/doc/trunk/api-c-style.md#convert-func-ptr
  */
  __dbCleanupMap.cleanup = function(pDb){
    pDb = __argPDb(pDb);

    //wasm.xWrap.FuncPtrAdapter.debugFuncInstall = false;
    /**
       Installing NULL functions in the C API will remove those
       bindings. The FuncPtrAdapter which sits between us and the C
       API will also treat that as an opportunity to
       wasm.uninstallFunction() any WASM function bindings it has
       installed for pDb.







>







990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004

     The issue being addressed here is covered at:

     https://sqlite.org/wasm/doc/trunk/api-c-style.md#convert-func-ptr
  */
  __dbCleanupMap.cleanup = function(pDb){
    pDb = __argPDb(pDb);
    //console.warn("db cleanup",pDb);
    //wasm.xWrap.FuncPtrAdapter.debugFuncInstall = false;
    /**
       Installing NULL functions in the C API will remove those
       bindings. The FuncPtrAdapter which sits between us and the C
       API will also treat that as an opportunity to
       wasm.uninstallFunction() any WASM function bindings it has
       installed for pDb.
996
997
998
999
1000
1001
1002



1003
1004
1005
1006
1007
1008
1009
      closeArgs.length = x.length/*==argument count*/
      /* recall that undefined entries translate to 0 when passed to
         WASM. */;
      try{ capi[name](...closeArgs) }
      catch(e){
        console.warn("close-time call of",name+"(",closeArgs,") threw:",e);
      }



    }
    const m = __dbCleanupMap(pDb, 0);
    if(!m) return;
    if(m.collation){
      for(const name of m.collation){
        try{
          capi.sqlite3_create_collation_v2(







>
>
>







1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
      closeArgs.length = x.length/*==argument count*/
      /* recall that undefined entries translate to 0 when passed to
         WASM. */;
      try{ capi[name](...closeArgs) }
      catch(e){
        console.warn("close-time call of",name+"(",closeArgs,") threw:",e);
      }
    }
    for(const callback of __dbCleanupMap.extraCallbacks){
      callback(pDb);
    }
    const m = __dbCleanupMap(pDb, 0);
    if(!m) return;
    if(m.collation){
      for(const name of m.collation){
        try{
          capi.sqlite3_create_collation_v2(
1033
1034
1035
1036
1037
1038
1039
















1040
1041
1042

1043
1044

1045
1046
1047
1048






1049
1050
1051
1052
1053
1054
1055
        arities.clear();
      }
      fmap.clear();
    }
    delete m.udf;
    delete m.wudf;
  }/*__dbCleanupMap.cleanup()*/;

















  {/* Binding of sqlite3_close_v2() */
    const __sqlite3CloseV2 = wasm.xWrap("sqlite3_close_v2", "int", "sqlite3*");

    capi.sqlite3_close_v2 = function(pDb){
      if(1!==arguments.length) return __dbArgcMismatch(pDb, 'sqlite3_close_v2', 1);

      if(pDb){
        try{__dbCleanupMap.cleanup(pDb)} catch(e){/*ignored*/}
      }
      return __sqlite3CloseV2(pDb);






    };
  }/*sqlite3_close_v2()*/

  if(capi.sqlite3session_table_filter){
    const __sqlite3SessionDelete = wasm.xWrap(
      'sqlite3session_delete', undefined, ['sqlite3_session*']
    );







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



>


>



|
>
>
>
>
>
>







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
1098
1099
1100
1101
1102
1103
1104
        arities.clear();
      }
      fmap.clear();
    }
    delete m.udf;
    delete m.wudf;
  }/*__dbCleanupMap.cleanup()*/;
  /**
     Downstream code, namely sqlite3-fts5-helper.js, should add any custom
     cleanup handlers to __dbCleanupMap.extraCallbacks. Each function in this
     array will be called during sqlite3_close_v2() and passed a pointer to
     the being-destroyed (sqlite3*) object.
  */
  __dbCleanupMap.extraCallbacks = [];
  /**
     Downstream code, namely sqlite3-fts5-helper.js, should add any
     custom cleanup handlers to __dbCleanupMap.postCloseCallbacks.
     Each function in this array will be called during
     sqlite3_close_v2(), AFTER the db is closed, and passed a pointer
     to the being-destroyed (sqlite3*) object. The memory is NOT A
     VALID OBJECT but its address is still valid as a lookup key.
  */
  __dbCleanupMap.postCloseCallbacks = [];

  {/* Binding of sqlite3_close_v2() */
    const __sqlite3CloseV2 = wasm.xWrap("sqlite3_close_v2", "int", "sqlite3*");
    const __xArgDb = wasm.xWrap.argAdapter('sqlite3*');
    capi.sqlite3_close_v2 = function(pDb){
      if(1!==arguments.length) return __dbArgcMismatch(pDb, 'sqlite3_close_v2', 1);
      pDb = __xArgDb(pDb);
      if(pDb){
        try{__dbCleanupMap.cleanup(pDb)} catch(e){/*ignored*/}
      }
      const rc = __sqlite3CloseV2(pDb);
      if(pDb/*noting that it's not valid anymore*/){
        for(const f of __dbCleanupMap.postCloseCallbacks){
          try{f(pDb)}catch(e){/*ingored*/}
        }
      }
      return rc;
    };
  }/*sqlite3_close_v2()*/

  if(capi.sqlite3session_table_filter){
    const __sqlite3SessionDelete = wasm.xWrap(
      'sqlite3session_delete', undefined, ['sqlite3_session*']
    );
1150
1151
1152
1153
1154
1155
1156



1157
1158
1159
1160
1161
1162
1163
          +':'+wasm.cstrToJs(argv[1]).toLowerCase()
      )
    };

    /**
       JS proxies for the various sqlite3_create[_window]_function()
       callbacks, structured in a form usable by wasm.xWrap.FuncPtrAdapter.



    */
    const __cfProxy = Object.assign(Object.create(null), {
      xInverseAndStep: {
        signature:'v(pip)', contextKey,
        callProxy: (callback)=>{
          return (pCtx, argc, pArgv)=>{
            try{ callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)) }







>
>
>







1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
          +':'+wasm.cstrToJs(argv[1]).toLowerCase()
      )
    };

    /**
       JS proxies for the various sqlite3_create[_window]_function()
       callbacks, structured in a form usable by wasm.xWrap.FuncPtrAdapter.

       TODO: explore an API option which more closely resembles the
       /ext/jni mapping, which is much friendlier at the client level.
    */
    const __cfProxy = Object.assign(Object.create(null), {
      xInverseAndStep: {
        signature:'v(pip)', contextKey,
        callProxy: (callback)=>{
          return (pCtx, argc, pArgv)=>{
            try{ callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)) }
1661
1662
1663
1664
1665
1666
1667
1668
1669
      /* Worker thread: unregister kvvfs to avoid it being used
         for anything other than local/sessionStorage. It "can"
         be used that way but it's not really intended to be. */
      capi.sqlite3_vfs_unregister(pKvvfs);
    }
  }/*pKvvfs*/

  wasm.xWrap.FuncPtrAdapter.warnOnUse = true;
});







<

1713
1714
1715
1716
1717
1718
1719

1720
      /* Worker thread: unregister kvvfs to avoid it being used
         for anything other than local/sessionStorage. It "can"
         be used that way but it's not really intended to be. */
      capi.sqlite3_vfs_unregister(pKvvfs);
    }
  }/*pKvvfs*/


});

Changes to ext/wasm/api/sqlite3-api-prologue.js.

1641
1642
1643
1644
1645
1646
1647
1648

1649
1650
1651
1652
1653
1654
1655
         Curiously: despite ostensibly requiring 8-byte
         alignment, the pArgv array is parcelled into chunks of
         4 bytes (1 pointer each). The values those point to
         have 8-byte alignment but the individual argv entries
         do not.
      */
      tgt.push(capi.sqlite3_value_to_js(
        wasm.peekPtr(pArgv + (wasm.ptrSizeof * i))

      ));
    }
    return tgt;
  };

  /**
     Calls either sqlite3_result_error_nomem(), if e is-a







|
>







1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
         Curiously: despite ostensibly requiring 8-byte
         alignment, the pArgv array is parcelled into chunks of
         4 bytes (1 pointer each). The values those point to
         have 8-byte alignment but the individual argv entries
         do not.
      */
      tgt.push(capi.sqlite3_value_to_js(
        wasm.peekPtr(pArgv + (wasm.ptrSizeof * i)),
        throwIfCannotConvert
      ));
    }
    return tgt;
  };

  /**
     Calls either sqlite3_result_error_nomem(), if e is-a

Added ext/wasm/api/sqlite3-fts5-helper.js.























































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
/*
** 2023-08-03
**
** The author disclaims copyright to this source code.  In place of a
** legal notice, here is a blessing:
**
** *   May you do good and not evil.
** *   May you find forgiveness for yourself and forgive others.
** *   May you share freely, never taking more than you give.
*/

/**
   This file installs sqlite3.fts5, a namespace which exists to assist
   in JavaScript-side extension of FTS5.
*/
'use strict';
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
  const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3;
  if(!capi.fts5_api_from_db){
    return /*this build does not have FTS5*/;
  }
  const fts = sqlite3.fts5 = Object.create(null);
  const __xArgDb = wasm.xWrap.argAdapter('sqlite3*');

  /**
     Move FTS-specific APIs (installed via automation) from
     sqlite3.capi to sqlite3.fts.
  */
  for(const c of [
    'Fts5ExtensionApi', 'Fts5PhraseIter', 'fts5_api',
    'fts5_api_from_db', 'fts5_tokenizer'
  ]){
    fts[c] = capi[c] || toss("Cannot find capi."+c);
    delete capi[c];
  }

  /**
     Requires a JS Function intended to be used as an xFunction()
     implementation. This function returns a proxy xFunction
     wrapper which:

     - Converts all of its sqlite3_value arguments to an array
       of JS values using sqlite3_values_to_js().

       - Calls the given callback, passing it:

       (pFtsXApi, pFtsCx, pCtx, array-of-values)

       where the first 3 arguments are the first 3 pointers
       in the xFunction interface.


       The call is intended to set a result value into the db, and may
       do so be either (A) explicitly returning non-undefined or (B)
       using one of the sqlite3_result_XYZ() functions and returning
       undefined. If the callback throws, its exception will be passed
       to sqlite3_result_error_js().
  */
  fts.xFunctionProxy1 = function(callback){
    return (pFtsXApi, pFtsCx, pCtx, argc, pArgv)=>{
      try{
        capi.sqlite3_result_js(pCtx, callback(
          pFtsXApi, pFtsCx, pCtx,
          capi.sqlite3_values_to_js(argc, pArgv)
        ));
      }catch(e){
        capi.sqlite3_result_error_js(pCtx, e);
      }
    }
  };

  /**
     Identical to xFunctionProxy1 except that the callback wrapper it
     creates does _not_ perform sqlite3_value-to-JS conversion in
     advance and calls the callback with:

     (pFtsXApi, pFtsCx, pCtx, array-of-ptr-to-sqlite3_value)

     It is up to the callback to use the sqlite3_value_XYZ() family of
     functions to inspect or convert the values.
  */
  fts.xFunctionProxy2 = function(callback){
    return (pFtsXApi, pFtsCx, pCtx, argc, pArgv)=>{
      try{
        const list = [];
        let i;
        for(i = 0; i < argc; ++i){
          list.push( wasm.peekPtr(pArgv + (wasm.ptrSizeof * i)) );
        }
        capi.sqlite3_result_js(pCtx, callback(
          pFtsXApi, pFtsCx, pCtx, list
        ));
      }catch(e){
        capi.sqlite3_result_error_js(pCtx, e);
      }
    }
  };

  /**
     JS-to-WASM arg adapter for xCreateFunction()'s xFunction arg.
     This binds JS impls of xFunction to WASM so that they can be
     called from native code. Its context is limited to the
     combination of ((fts5_api*) + functionNameCaseInsensitive), and
     will replace any existing impl for subsequent invocations for the
     same combination.

     The functions is creates are intended to set a result value into
     the db, and may do so be either (A) explicitly returning
     non-undefined or (B) using one of the sqlite3_result_XYZ()
     functions and returning undefined. If the callback throws, its
     exception will be passed to sqlite3_result_error_js().

     PENDING DESIGN DECISION: this framework currently converts each
     argument in its JS equivalent before passing them on to the
     xFunction impl. We could, and possibly should, instead pass a JS
     array of sqlite3_value pointers. The advantages would be:

     - No in-advance to-JS overhead which xFunction might not use.

     Disadvantages include:

     - xFunction would be required to call sqlite3_value_to_js(),
     or one of the many sqlite3_value_XYZ() functions on their own.
     This would be more cumbersome for most users.

     Regardless of which approach is chosen here, clients could
     provide a function of their own which takes the _other_ approach,
     install it with wasm.installFunction(), and then pass that
     generated pointer to createFunction(), in which case this layer
     does not proxying and passes all native-level arguments as-is to
     the client-defined function.
  */
  const xFunctionArgAdapter = new wasm.xWrap.FuncPtrAdapter({
    name: 'fts5_api::xCreateFunction(xFunction)',
    signature: 'v(pppip)',
    contextKey: (argv,argIndex)=>{
      return (argv[0]/*(fts5_api*)*/
              + wasm.cstrToJs(argv[1]).toLowerCase()/*name*/)
    },
    callProxy: fts.xFunctionProxy1
  });

  /** Map of (sqlite3*) to fts.fts5_api. */
  const __ftsApiToStruct = Object.create(null);
  const __fts5_api_from_db = function(pDb, createIfNeeded){
    let rc = __ftsApiToStruct[pDb];
    if(!rc && createIfNeeded){
      const fapi = fts.fts5_api_from_db(pDb)
            || toss("Internal error - cannot get FTS5 API object for db.");
      rc = new fts.fts5_api(fapi);
      __ftsApiToStruct[pDb] = rc;
    }
    return rc;
  };

  /**
     Arrange for WASM functions dynamically created via this API to be
     uninstalled when the db they were installed for is closed... */
  const __addCleanupForFunc = function(sfapi, name, pDestroy){
    if(!sfapi.$$cleanup){
      sfapi.$$cleanup = [];
    }
    sfapi.$$cleanup.push([name.toLowerCase(), pDestroy]);
  };

  /**
     Callback to be invoked via the JS binding of sqlite3_close_v2(),
     after the db has been closed, meaning that the argument to this
     function is not a valid object. We use its address only as a
     lookup key.
  */
  sqlite3.__dbCleanupMap.postCloseCallbacks.push(function(pDb){
    const sfapi = __fts5_api_from_db(pDb, false);
    if(sfapi){
      delete __ftsApiToStruct[pDb];
      if(sfapi.$$cleanup){
        const fapi = sfapi.pointer;
        const scope = wasm.scopedAllocPush();
        //wasm.xWrap.FuncPtrAdapter.debugFuncInstall = true;
        try{
          for(const [name, pDestroy] of sfapi.$$cleanup){
            try{
              /* Uninstall xFunctionArgAdapter's bindings via a
                 roundabout approach: its scoping rules uninstall each
                 new installation at the earliest opportunity, so we
                 simply need to fake a call with a 0-pointer for the
                 xFunction callback to uninstall the most recent
                 one. */
              const zName = wasm.scopedAllocCString(name);
              const argv = [fapi, zName, 0, 0, 0];
              xFunctionArgAdapter.convertArg(argv[3], argv, 3);
              /* xDestroy, on the other hand, requires some
                 hand-holding to ensure we don't prematurely
                 uninstall these when a function is replaced
                 (shadowed). */
              if(pDestroy) wasm.uninstallFunction(pDestroy);
            }catch(e){
              sqlite3.config.warn("Could not remove FTS func",name,e);
            }
          }
        }finally{
          wasm.scopedAllocPop(scope);
        }
        //wasm.xWrap.FuncPtrAdapter.debugFuncInstall = false;
      }
      sfapi.dispose();
    }
  });

  const __affirmDbArg = (arg)=>{
    arg = __xArgDb(arg);
    if(!arg || !wasm.isPtr(arg)) toss("Invalid db argument.");
    return arg;
  };

  /**
     Convenience wrapper to fts5_api::xCreateFunction.

     Creates a new FTS5 function for the given database. The arguments are:

     - db must be either an sqlite3.oo1.DB instance or a WASM pointer
       to (sqlite3*).

     - name: name (JS string) of the function

     - xFunction either a Function or a pointer to a WASM function. In
       the former case a WASM-bound wrapper, behaving as documented
       for fts5.xFunctionProxy1(), gets installed for the life of the
       given db handle. In the latter case the function is
       passed-through as-is, with no argument conversion or lifetime
       tracking.  In the former case the function is called as
       documented for xFunctionProxy1() and in the latter it must
       return void and is called with args (ptrToFts5ExtensionApi,
       ptrToFts5Context, ptrToSqlite3Context, int argc,
       C-array-of-sqlite3_value-pointers).

     - xDestroy optional Function or pointer to WASM function to call
       when the binding is destroyed (when the db handle is
       closed). The function will, in this context, always be passed 0
       as its only argument. A passed-in function must, however,
       have one parameter so that type signature checks will pass.
       It must return void and must not throw.

     The 2nd and subsequent aruguments may optionally be packed into
     a single Object with like-named properties.

     This function throws on error, of which there are many potential
     candidates. It returns `undefined`.
  */
  fts.createFunction = function(db, name, xFunction, xDestroy = 0){
    db = __affirmDbArg(db);
    if( 2 === arguments.length && 'string' !== typeof name){
      xDestroy = name.xDestroy || null;
      xFunction = name.xFunction || null;
      name = name.name;
    }
    if( !name || 'string' !== typeof name ) toss("Invalid name argument.");
    const sfapi = __fts5_api_from_db(db, true);
    let pDestroy = 0;
    try{
      /** Because of how fts5_api::xCreateFunction() replaces
          functions (by prepending new ones to a linked list but
          retaining old ones), we cannot use a FuncPtrAdapter to
          automatically convert xDestroy, lest we end up uninstalling
          a bound-to-wasm JS function's wasm pointer before fts5
          cleans it up when the db is closed. */
      if(xDestroy instanceof Function){
        pDestroy = wasm.installFunction(xDestroy, 'v(p)');
      }
      const xcf = sfapi.$$xCreateFunction || (
        sfapi.$$xCreateFunction = wasm.xWrap(sfapi.$xCreateFunction, 'int', [
          '*', 'string', '*', xFunctionArgAdapter, '*'
        ])
      );
      const rc = xcf(sfapi.pointer, name, 0, xFunction || 0, pDestroy || xDestroy || 0 );
      if(rc) toss(rc,"FTS5::xCreateFunction() failed.");
      __addCleanupForFunc(sfapi, name, pDestroy);
    }catch(e){
      if(pDestroy) wasm.uninstallFunction(pDestroy);
      sfapi.dispose();
      throw e;
    }
  };

  /**
     ! UNTESTED

     Convenience wrapper for fts5_api::xCreateTokenizer().

     - db = the db to install the tokenizer into.

     - name = the JS string name of the tokenizer.

     - pTokenizer = the tokenizer instance, which must be a
       fts5.fts5_tokenizer instance or a valid WASM pointer to one.

     - xDestroy = as documented for createFunction().

     The C layer makes a bitwise copy of the tokenizer, so any
     changes made to it after installation will have no effect.

     Throws on error.
  */
  const createTokenizer = function(db, name, pTokenizer, xDestroy = 0){
    db = __affirmDbArg(db);
    if( 2 === arguments.length && 'string' !== typeof name){
      pTokenizer = name.pTokenizer;
      xDestroy = name.xDestroy || null;
      name = name.name;
    }
    if( !name || 'string' !== typeof name ) toss("Invalid name argument.");
    if(pTokenizer instanceof fts.fts5_tokenizer){
      pTokenizer = pTokenizer.pointer;
    }
    if(!pTokenizer || !wasm.isPtr(pTokenizer)){
      toss("Invalid pTokenizer argument - must be a valid fts5.fts5_tokenizer",
           "instance or a WASM pointer to one.");
    }
    const sfapi = __fts5_api_from_db(db, true);
    let pDestroy = 0;
    const stackPos = wasm.pstack.pointer;
    try{
      if(xDestroy instanceof Function){
        pDestroy = wasm.installFunction(xDestroy, 'v(p)');
      }
      const xct = sfapi.$$xCreateTokenizer || (
        sfapi.$$xCreateTokenizer = wasm.xWrap(sfapi.$xCreateTokenizer, 'int', [
          '*', 'string', '*', '*', '*'
          /* fts5_api*, const char *zName, void *pContext,
             fts5_tokenizer *pTokenizer, void(*xDestroy)(void*) */
        ])
      );
      const outPtr = wasm.pstack.allocPtr();
      const rc = xct(fapi.pointer, name, 0, pTokenizer, pDestroy || xDestroy || 0 );
      if(rc) toss(rc,"FTS5::xCreateFunction() failed.");
      if(pDestroy) __addCleanupForFunc(sfapi, name, pDestroy);
    }catch(e){
      if(pDestroy) wasm.uninstallFunction(pDestroy);
      sfapi.dispose();
      throw e;
    }finally{
      wasm.pstack.restore(stackPost);
    }
  };
  //fts.createTokenizer = createTokenizer;

}/*sqlite3ApiBootstrap.initializers.push()*/);

Changes to ext/wasm/api/sqlite3-v-helper.js.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
** 2022-11-30
**
** The author disclaims copyright to this source code.  In place of a
** legal notice, here is a blessing:
**
** *   May you do good and not evil.
** *   May you find forgiveness for yourself and forgive others.
** *   May you share freely, never taking more than you give.
*/

/**
   This file installs sqlite3.vfs, and object which exists to assist
   in the creation of JavaScript implementations of sqlite3_vfs, along
   with its virtual table counterpart, sqlite3.vtab.
*/
'use strict';
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
  const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3;
  const vfs = Object.create(null), vtab = Object.create(null);












|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
** 2022-11-30
**
** The author disclaims copyright to this source code.  In place of a
** legal notice, here is a blessing:
**
** *   May you do good and not evil.
** *   May you find forgiveness for yourself and forgive others.
** *   May you share freely, never taking more than you give.
*/

/**
   This file installs sqlite3.vfs, an object which exists to assist
   in the creation of JavaScript implementations of sqlite3_vfs, along
   with its virtual table counterpart, sqlite3.vtab.
*/
'use strict';
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
  const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3;
  const vfs = Object.create(null), vtab = Object.create(null);

Changes to ext/wasm/api/sqlite3-wasm.c.

92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#endif
#ifndef SQLITE_ENABLE_DBSTAT_VTAB
#  define SQLITE_ENABLE_DBSTAT_VTAB 1
#endif
#ifndef SQLITE_ENABLE_EXPLAIN_COMMENTS
#  define SQLITE_ENABLE_EXPLAIN_COMMENTS 1
#endif
#ifndef SQLITE_ENABLE_FTS4
#  define SQLITE_ENABLE_FTS4 1
#endif
#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
#  define SQLITE_ENABLE_MATH_FUNCTIONS 1
#endif
#ifndef SQLITE_ENABLE_OFFSET_SQL_FUNC
#  define SQLITE_ENABLE_OFFSET_SQL_FUNC 1
#endif







|
|







92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#endif
#ifndef SQLITE_ENABLE_DBSTAT_VTAB
#  define SQLITE_ENABLE_DBSTAT_VTAB 1
#endif
#ifndef SQLITE_ENABLE_EXPLAIN_COMMENTS
#  define SQLITE_ENABLE_EXPLAIN_COMMENTS 1
#endif
#ifndef SQLITE_ENABLE_FTS5
#  define SQLITE_ENABLE_FTS5 1
#endif
#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
#  define SQLITE_ENABLE_MATH_FUNCTIONS 1
#endif
#ifndef SQLITE_ENABLE_OFFSET_SQL_FUNC
#  define SQLITE_ENABLE_OFFSET_SQL_FUNC 1
#endif
359
360
361
362
363
364
365





















366
367
368
369
370
371
372
    }else{
      sqlite3ErrorWithMsg(db, err_code, NULL);
    }
  }
  return err_code;
}






















#if SQLITE_WASM_TESTS
struct WasmTestStruct {
  int v4;
  void * ppV;
  const char * cstr;
  int64_t v8;
  void (*xFunc)(void*);







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







359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
    }else{
      sqlite3ErrorWithMsg(db, err_code, NULL);
    }
  }
  return err_code;
}

#ifdef SQLITE_ENABLE_FTS5
/*
** Return a pointer to the fts5_api pointer for database connection db.
** If an error occurs, return NULL and leave an error in the database
** handle (accessible using sqlite3_errcode()/errmsg()).
**
** This function was taken verbatim from the /fts5.html docs.
*/
SQLITE_WASM_EXPORT
fts5_api *fts5_api_from_db(sqlite3 *db){
  fts5_api *pRet = 0;
  sqlite3_stmt *pStmt = 0;
  if( SQLITE_OK==sqlite3_prepare(db, "SELECT fts5(?1)", -1, &pStmt, 0) ){
    sqlite3_bind_pointer(pStmt, 1, (void*)&pRet, "fts5_api_ptr", NULL);
    sqlite3_step(pStmt);
  }
  sqlite3_finalize(pStmt);
  return pRet;
}
#endif /*SQLITE_ENABLE_FTS5*/

#if SQLITE_WASM_TESTS
struct WasmTestStruct {
  int v4;
  void * ppV;
  const char * cstr;
  int64_t v8;
  void (*xFunc)(void*);
398
399
400
401
402
403
404
405



406
407
408
409
410
411
412
**
** If this function returns NULL then it means that the internal
** buffer is not large enough for the generated JSON and needs to be
** increased. In debug builds that will trigger an assert().
*/
SQLITE_WASM_EXPORT
const char * sqlite3_wasm_enum_json(void){
  static char aBuffer[1024 * 20] = {0} /* where the JSON goes */;



  int n = 0, nChildren = 0, nStruct = 0
    /* output counters for figuring out where commas go */;
  char * zPos = &aBuffer[1] /* skip first byte for now to help protect
                            ** against a small race condition */;
  char const * const zEnd = &aBuffer[0] + sizeof(aBuffer) /* one-past-the-end */;
  if(aBuffer[0]) return aBuffer;
  /* Leave aBuffer[0] at 0 until the end to help guard against a tiny







|
>
>
>







419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
**
** If this function returns NULL then it means that the internal
** buffer is not large enough for the generated JSON and needs to be
** increased. In debug builds that will trigger an assert().
*/
SQLITE_WASM_EXPORT
const char * sqlite3_wasm_enum_json(void){
  static char aBuffer[1024 * 22] = {0}
    /* where the JSON goes. If this buffer is not large enough, this
       function will assert (in debug builds) and return 0. When it does
       so, this value needs to be increased. */;
  int n = 0, nChildren = 0, nStruct = 0
    /* output counters for figuring out where commas go */;
  char * zPos = &aBuffer[1] /* skip first byte for now to help protect
                            ** against a small race condition */;
  char const * const zEnd = &aBuffer[0] + sizeof(aBuffer) /* one-past-the-end */;
  if(aBuffer[0]) return aBuffer;
  /* Leave aBuffer[0] at 0 until the end to help guard against a tiny
646
647
648
649
650
651
652










653
654
655
656
657
658
659
  DefGroup(flock) {
    DefInt(SQLITE_LOCK_NONE);
    DefInt(SQLITE_LOCK_SHARED);
    DefInt(SQLITE_LOCK_RESERVED);
    DefInt(SQLITE_LOCK_PENDING);
    DefInt(SQLITE_LOCK_EXCLUSIVE);
  } _DefGroup;











  DefGroup(ioCap) {
    DefInt(SQLITE_IOCAP_ATOMIC);
    DefInt(SQLITE_IOCAP_ATOMIC512);
    DefInt(SQLITE_IOCAP_ATOMIC1K);
    DefInt(SQLITE_IOCAP_ATOMIC2K);
    DefInt(SQLITE_IOCAP_ATOMIC4K);







>
>
>
>
>
>
>
>
>
>







670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
  DefGroup(flock) {
    DefInt(SQLITE_LOCK_NONE);
    DefInt(SQLITE_LOCK_SHARED);
    DefInt(SQLITE_LOCK_RESERVED);
    DefInt(SQLITE_LOCK_PENDING);
    DefInt(SQLITE_LOCK_EXCLUSIVE);
  } _DefGroup;

#ifdef SQLITE_ENABLE_FTS5
  DefGroup(fts5) {
    DefInt(FTS5_TOKENIZE_QUERY);
    DefInt(FTS5_TOKENIZE_PREFIX);
    DefInt(FTS5_TOKENIZE_DOCUMENT);
    DefInt(FTS5_TOKENIZE_AUX);
    DefInt(FTS5_TOKEN_COLOCATED);
  } _DefGroup;
#endif

  DefGroup(ioCap) {
    DefInt(SQLITE_IOCAP_ATOMIC);
    DefInt(SQLITE_IOCAP_ATOMIC512);
    DefInt(SQLITE_IOCAP_ATOMIC1K);
    DefInt(SQLITE_IOCAP_ATOMIC2K);
    DefInt(SQLITE_IOCAP_ATOMIC4K);
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
    DefInt(SQLITE_STMTSTATUS_VM_STEP);
    DefInt(SQLITE_STMTSTATUS_REPREPARE);
    DefInt(SQLITE_STMTSTATUS_RUN);
    DefInt(SQLITE_STMTSTATUS_FILTER_MISS);
    DefInt(SQLITE_STMTSTATUS_FILTER_HIT);
    DefInt(SQLITE_STMTSTATUS_MEMUSED);
  } _DefGroup;
  
  DefGroup(syncFlags) {
    DefInt(SQLITE_SYNC_NORMAL);
    DefInt(SQLITE_SYNC_FULL);
    DefInt(SQLITE_SYNC_DATAONLY);
  } _DefGroup;

  DefGroup(trace) {







|







906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
    DefInt(SQLITE_STMTSTATUS_VM_STEP);
    DefInt(SQLITE_STMTSTATUS_REPREPARE);
    DefInt(SQLITE_STMTSTATUS_RUN);
    DefInt(SQLITE_STMTSTATUS_FILTER_MISS);
    DefInt(SQLITE_STMTSTATUS_FILTER_HIT);
    DefInt(SQLITE_STMTSTATUS_MEMUSED);
  } _DefGroup;

  DefGroup(syncFlags) {
    DefInt(SQLITE_SYNC_NORMAL);
    DefInt(SQLITE_SYNC_FULL);
    DefInt(SQLITE_SYNC_DATAONLY);
  } _DefGroup;

  DefGroup(trace) {
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
      M(xSavepoint,     "i(pi)");
      M(xRelease,       "i(pi)");
      M(xRollbackTo,    "i(pi)");
      // ^^^ v2. v3+ follows...
      M(xShadowName,    "i(s)");
    } _StructBinder;
#undef CurrentStruct
    
    /**
     ** Workaround: in order to map the various inner structs from
     ** sqlite3_index_info, we have to uplift those into constructs we
     ** can access by type name. These structs _must_ match their
     ** in-sqlite3_index_info counterparts byte for byte.
    */
    typedef struct {







|







1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
      M(xSavepoint,     "i(pi)");
      M(xRelease,       "i(pi)");
      M(xRollbackTo,    "i(pi)");
      // ^^^ v2. v3+ follows...
      M(xShadowName,    "i(s)");
    } _StructBinder;
#undef CurrentStruct

    /**
     ** Workaround: in order to map the various inner structs from
     ** sqlite3_index_info, we have to uplift those into constructs we
     ** can access by type name. These structs _must_ match their
     ** in-sqlite3_index_info counterparts byte for byte.
    */
    typedef struct {
1167
1168
1169
1170
1171
1172
1173










































































































1174
1175
1176
1177
1178
1179
1180
      M(estimatedCost,      "d");
      M(estimatedRows,      "j");
      M(idxFlags,           "i");
      M(colUsed,            "j");
    } _StructBinder;
#undef CurrentStruct











































































































#if SQLITE_WASM_TESTS
#define CurrentStruct WasmTestStruct
    StructBinder {
      M(v4,    "i");
      M(cstr,  "s");
      M(ppV,   "p");
      M(v8,    "j");







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







1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
      M(estimatedCost,      "d");
      M(estimatedRows,      "j");
      M(idxFlags,           "i");
      M(colUsed,            "j");
    } _StructBinder;
#undef CurrentStruct

#ifdef SQLITE_ENABLE_FTS5
#define CurrentStruct Fts5PhraseIter
    StructBinder {
      M(a,              "p");
      M(b,              "p");
    } _StructBinder;
#undef CurrentStruct

#define CurrentStruct Fts5ExtensionApi
    StructBinder {
      M(iVersion,           "i");
      M(xUserData,          "p(p)");// void *(*)(Fts5Context*);
      M(xColumnCount,       "i(p)");// int (*)(Fts5Context*);
      M(xRowCount,          "i(pp)");
      //^^^ int (*)(Fts5Context*, sqlite3_int64 *pnRow);
      M(xColumnTotalSize,   "i(pip)");
      //^^^ int (*)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
      M(xTokenize,          "i(ppipp)");
      //^^^ int (*)(Fts5Context*,
      //  const char *pText, int nText, /* Text to tokenize */
      //  void *pCtx,                   /* Context passed to xToken() */
      //  int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
      //);
      M(xPhraseCount,       "i(p)"); // int (*)(Fts5Context*);
      M(xPhraseSize,        "i(pi)"); // int (*)(Fts5Context*, int iPhrase);
      M(xInstCount,         "i(pp)"); // int (*)(Fts5Context*, int *pnInst);
      M(xInst,              "i(pippp)");
      //^^^ int (*)(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff);
      M(xRowid,             "j(p)"); // sqlite3_int64 (*)(Fts5Context*);
      M(xColumnText,        "i(pipp)");
      //^^^ int (*)(Fts5Context*, int iCol, const char **pz, int *pn);
      M(xColumnSize,        "i(pip)");
      //^^^ int (*)(Fts5Context*, int iCol, int *pnToken);
      M(xQueryPhrase,       "i(pipp)");
      //^^^ int (*xQueryPhrase)(Fts5Context*, int iPhrase, void *pUserData,
      //    int(*)(const Fts5ExtensionApi*,Fts5Context*,void*)
      // );
      M(xSetAuxdata,        "i(ppp)");
      //^^^ int (*)(Fts5Context*, void *pAux, void(*xDelete)(void*));
      M(xGetAuxdata,        "p(pi)"); // void *(*)(Fts5Context*, int bClear);
      M(xPhraseFirst,       "i(pippp)");
      //^^^ int (*)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
      M(xPhraseNext,        "v(pppp)");
      //^^^ void (*)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);
      M(xPhraseFirstColumn, "i(pipp)");
      //^^^ int (*)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
      M(xPhraseNextColumn,  "v(ppp)");
      //^^^ void (*)(Fts5Context*, Fts5PhraseIter*, int *piCol);
    } _StructBinder;
#undef CurrentStruct

#define CurrentStruct fts5_api
    StructBinder {
      M(iVersion,         "i");/* Currently always 2 */
      M(xCreateTokenizer, "i(ppppp)");
      //^^^ int (*)(
      //      fts5_api *pApi,
      //      const char *zName,
      //      void *pContext,
      //      fts5_tokenizer *pTokenizer,
      //      void (*xDestroy)(void*)
      // );
      M(xFindTokenizer,    "i(pppp)");
      //^^^ int (*)(
      //      fts5_api *pApi,
      //      const char *zName,
      //      void **ppContext,
      //      fts5_tokenizer *pTokenizer
      //    );
      M(xCreateFunction,    "i(ppppp)");
      //^^^ int (*)(
      //      fts5_api *pApi,
      //      const char *zName,
      //      void *pContext,
      //      fts5_extension_function xFunction,
      //      void (*xDestroy)(void*)
      //    );
    } _StructBinder;
#undef CurrentStruct

#define CurrentStruct fts5_tokenizer
    StructBinder {
      M(xCreate,     "i(ppip)");
      //^^^  int (*)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
      M(xDelete,     "v(p)");
      //^^^ void(Fts5Tokenizer*)
      M(xTokenize,   "i(ppipip)");
      /**^^^ int (*xTokenize)(Fts5Tokenizer*,
         void *pCtx,
         int flags,
         const char *pText,
         int nText,
         int (*xToken)(
           void *pCtx,
           int tflags,
           const char *pToken,
           int nToken,
           int iStart,
           int iEnd
         )
      ); */
    } _StructBinder;
#undef CurrentStruct

#endif /* SQLITE_ENABLE_FTS5 */

#if SQLITE_WASM_TESTS
#define CurrentStruct WasmTestStruct
    StructBinder {
      M(v4,    "i");
      M(cstr,  "s");
      M(ppV,   "p");
      M(v8,    "j");

Changes to ext/wasm/common/whwasmutil.js.

609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
     breaking clients which do not take care to avoid that case:

     https://github.com/emscripten-core/emscripten/issues/17323
  */
  target.installFunction = (func, sig)=>__installFunction(func, sig, false);

  /**
     EXPERIMENTAL! DO NOT USE IN CLIENT CODE!

     Works exactly like installFunction() but requires that a
     scopedAllocPush() is active and uninstalls the given function
     when that alloc scope is popped via scopedAllocPop().
     This is used for implementing JS/WASM function bindings which
     should only persist for the life of a call into a single
     C-side function.
  */







<
<







609
610
611
612
613
614
615


616
617
618
619
620
621
622
     breaking clients which do not take care to avoid that case:

     https://github.com/emscripten-core/emscripten/issues/17323
  */
  target.installFunction = (func, sig)=>__installFunction(func, sig, false);

  /**


     Works exactly like installFunction() but requires that a
     scopedAllocPush() is active and uninstalls the given function
     when that alloc scope is popped via scopedAllocPop().
     This is used for implementing JS/WASM function bindings which
     should only persist for the life of a call into a single
     C-side function.
  */
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
          ? cache.scopedAlloc.indexOf(state)
          : cache.scopedAlloc.length-1;
    if(n<0) toss("Invalid state object for scopedAllocPop().");
    if(0===arguments.length) state = cache.scopedAlloc[n];
    cache.scopedAlloc.splice(n,1);
    for(let p; (p = state.pop()); ){
      if(target.functionEntry(p)){
        //console.warn("scopedAllocPop() uninstalling transient function",p);
        target.uninstallFunction(p);
      }
      else target.dealloc(p);
    }
  };

  /**







|







1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
          ? cache.scopedAlloc.indexOf(state)
          : cache.scopedAlloc.length-1;
    if(n<0) toss("Invalid state object for scopedAllocPop().");
    if(0===arguments.length) state = cache.scopedAlloc[n];
    cache.scopedAlloc.splice(n,1);
    for(let p; (p = state.pop()); ){
      if(target.functionEntry(p)){
        //console.warn("scopedAllocPop() uninstalling function",p);
        target.uninstallFunction(p);
      }
      else target.dealloc(p);
    }
  };

  /**
1379
1380
1381
1382
1383
1384
1385




1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
  /**
     Looks up a WASM-exported function named fname from
     target.exports. If found, it is called, passed all remaining
     arguments, and its return value is returned to xCall's caller. If
     not found, an exception is thrown. This function does no
     conversion of argument or return types, but see xWrap() and
     xCallWrapped() for variants which do.





     As a special case, if passed only 1 argument after the name and
     that argument in an Array, that array's entries become the
     function arguments. (This is not an ambiguous case because it's
     not legal to pass an Array object to a WASM function.)
  */
  target.xCall = function(fname, ...args){
    const f = target.xGet(fname);
    if(!(f instanceof Function)) toss("Exported symbol",fname,"is not a function.");
    if(f.length!==args.length) __argcMismatch(fname,f.length)
    /* This is arguably over-pedantic but we want to help clients keep
       from shooting themselves in the foot when calling C APIs. */;
    return (2===arguments.length && Array.isArray(arguments[1]))
      ? f.apply(null, arguments[1])
      : f.apply(null, args);
  };








>
>
>
>







|

|







1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
  /**
     Looks up a WASM-exported function named fname from
     target.exports. If found, it is called, passed all remaining
     arguments, and its return value is returned to xCall's caller. If
     not found, an exception is thrown. This function does no
     conversion of argument or return types, but see xWrap() and
     xCallWrapped() for variants which do.

     If the first argument is a function is is assumed to be
     a WASM-bound function and is used as-is instead of looking up
     the function via xGet().

     As a special case, if passed only 1 argument after the name and
     that argument in an Array, that array's entries become the
     function arguments. (This is not an ambiguous case because it's
     not legal to pass an Array object to a WASM function.)
  */
  target.xCall = function(fname, ...args){
    const f = (fname instanceof Function) ? fname : target.xGet(fname);
    if(!(f instanceof Function)) toss("Exported symbol",fname,"is not a function.");
    if(f.length!==args.length) __argcMismatch(((f===fname) ? f.name : fname),f.length)
    /* This is arguably over-pedantic but we want to help clients keep
       from shooting themselves in the foot when calling C APIs. */;
    return (2===arguments.length && Array.isArray(arguments[1]))
      ? f.apply(null, arguments[1])
      : f.apply(null, args);
  };

1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
       is solely for debugging and error-reporting purposes. If not
       provided, an empty string is assumed.

     - signature: a function signature string compatible with
       jsFuncToWasm().

     - bindScope (string): one of ('transient', 'context',
       'singleton'). Bind scopes are:

       - 'transient': it will convert JS functions to WASM only for
         the duration of the xWrap()'d function call, using
         scopedInstallFunction(). Before that call returns, the
         WASM-side binding will be uninstalled.

       - 'singleton': holds one function-pointer binding for this







|







1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
       is solely for debugging and error-reporting purposes. If not
       provided, an empty string is assumed.

     - signature: a function signature string compatible with
       jsFuncToWasm().

     - bindScope (string): one of ('transient', 'context',
       'singleton', 'permanent'). Bind scopes are:

       - 'transient': it will convert JS functions to WASM only for
         the duration of the xWrap()'d function call, using
         scopedInstallFunction(). Before that call returns, the
         WASM-side binding will be uninstalled.

       - 'singleton': holds one function-pointer binding for this
1633
1634
1635
1636
1637
1638
1639

1640
1641
1642
1643
1644
1645
1646
    constructor(opt) {
      super(opt);
      if(xArg.FuncPtrAdapter.warnOnUse){
        console.warn('xArg.FuncPtrAdapter is an internal-only API',
                     'and is not intended to be invoked from',
                     'client-level code. Invoked with:',opt);
      }

      this.signature = opt.signature;
      if(opt.contextKey instanceof Function){
        this.contextKey = opt.contextKey;
        if(!opt.bindScope) opt.bindScope = 'context';
      }
      this.bindScope = opt.bindScope
        || toss("FuncPtrAdapter options requires a bindScope (explicit or implied).");







>







1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
    constructor(opt) {
      super(opt);
      if(xArg.FuncPtrAdapter.warnOnUse){
        console.warn('xArg.FuncPtrAdapter is an internal-only API',
                     'and is not intended to be invoked from',
                     'client-level code. Invoked with:',opt);
      }
      this.name = opt.name || "unnamed";
      this.signature = opt.signature;
      if(opt.contextKey instanceof Function){
        this.contextKey = opt.contextKey;
        if(!opt.bindScope) opt.bindScope = 'context';
      }
      this.bindScope = opt.bindScope
        || toss("FuncPtrAdapter options requires a bindScope (explicit or implied).");
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704

1705
1706
1707
1708

1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722











1723
1724
1725
1726
1727
1728
1729

1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
       perform any function binding, so this object's bindMode is
       irrelevant for such cases.

       See the parent class's convertArg() docs for details on what
       exactly the 2nd and 3rd arguments are.
    */
    convertArg(v,argv,argIndex){
      //FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.signature,this.transient,v);
      let pair = this.singleton;
      if(!pair && this.isContext){
        pair = this.contextMap(this.contextKey(argv,argIndex));

      }
      if(pair && pair[0]===v) return pair[1];
      if(v instanceof Function){
        /* Install a WASM binding and return its pointer. */

        if(this.callProxy) v = this.callProxy(v);
        const fp = __installFunction(v, this.signature, this.isTransient);
        if(FuncPtrAdapter.debugFuncInstall){
          FuncPtrAdapter.debugOut("FuncPtrAdapter installed", this,
                                  this.contextKey(argv,argIndex), '@'+fp, v);
        }
        if(pair){
          /* Replace existing stashed mapping */
          if(pair[1]){
            if(FuncPtrAdapter.debugFuncInstall){
              FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this,
                                      this.contextKey(argv,argIndex), '@'+pair[1], v);
            }
            try{target.uninstallFunction(pair[1])}











            catch(e){/*ignored*/}
          }
          pair[0] = v;
          pair[1] = fp;
        }
        return fp;
      }else if(target.isPtr(v) || null===v || undefined===v){

        if(pair && pair[1] && pair[1]!==v){
          /* uninstall stashed mapping and replace stashed mapping with v. */
          if(FuncPtrAdapter.debugFuncInstall){
            FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this,
                                    this.contextKey(argv,argIndex), '@'+pair[1], v);
          }
          try{target.uninstallFunction(pair[1])}
          catch(e){/*ignored*/}
          pair[0] = pair[1] = (v | 0);
        }
        return v || 0;
      }else{
        throw new TypeError("Invalid FuncPtrAdapter argument type. "+
                            "Expecting a function pointer or a "+







|



>




>













|
>
>
>
>
>
>
>
>
>
>
>







>






|







1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
       perform any function binding, so this object's bindMode is
       irrelevant for such cases.

       See the parent class's convertArg() docs for details on what
       exactly the 2nd and 3rd arguments are.
    */
    convertArg(v,argv,argIndex){
      //FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.name,this.signature,this.transient,v);
      let pair = this.singleton;
      if(!pair && this.isContext){
        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(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);
        const fp = __installFunction(v, this.signature, this.isTransient);
        if(FuncPtrAdapter.debugFuncInstall){
          FuncPtrAdapter.debugOut("FuncPtrAdapter installed", this,
                                  this.contextKey(argv,argIndex), '@'+fp, v);
        }
        if(pair){
          /* Replace existing stashed mapping */
          if(pair[1]){
            if(FuncPtrAdapter.debugFuncInstall){
              FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this,
                                      this.contextKey(argv,argIndex), '@'+pair[1], v);
            }
            try{
              /* Because the pending native call might rely on the
                 pointer we're replacing, e.g. as is normally the case
                 with sqlite3's xDestroy() methods, we don't
                 immediately uninstall but instead add its pointer to
                 the scopedAlloc stack, which will be cleared when the
                 xWrap() mechanism is done calling the native
                 function. We're relying very much here on xWrap()
                 having pushed an alloc scope.
              */
              cache.scopedAlloc[cache.scopedAlloc.length-1].push(pair[1]);
            }
            catch(e){/*ignored*/}
          }
          pair[0] = v;
          pair[1] = fp;
        }
        return fp;
      }else if(target.isPtr(v) || null===v || undefined===v){
        //FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.name,this.signature,this.transient,v,pair);
        if(pair && pair[1] && pair[1]!==v){
          /* uninstall stashed mapping and replace stashed mapping with v. */
          if(FuncPtrAdapter.debugFuncInstall){
            FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this,
                                    this.contextKey(argv,argIndex), '@'+pair[1], v);
          }
          try{ cache.scopedAlloc[cache.scopedAlloc.length-1].push(pair[1]) }
          catch(e){/*ignored*/}
          pair[0] = pair[1] = (v | 0);
        }
        return v || 0;
      }else{
        throw new TypeError("Invalid FuncPtrAdapter argument type. "+
                            "Expecting a function pointer or a "+
1770
1771
1772
1773
1774
1775
1776





1777




1778
1779





1780
1781




1782
1783
1784
1785
1786
1787
1788

  const __xArgAdapterCheck =
        (t)=>xArg.get(t) || toss("Argument adapter not found:",t);

  const __xResultAdapterCheck =
        (t)=>xResult.get(t) || toss("Result adapter not found:",t);






  cache.xWrap.convertArg = (t,...args)=>__xArgAdapterCheck(t)(...args);




  cache.xWrap.convertArgNoCheck = (t,...args)=>xArg.get(t)(...args);






  cache.xWrap.convertResult =
    (t,v)=>(null===t ? v : (t ? __xResultAdapterCheck(t)(v) : undefined));




  cache.xWrap.convertResultNoCheck =
    (t,v)=>(null===t ? v : (t ? xResult.get(t)(v) : undefined));

  /**
     Creates a wrapper for another function which converts the arguments
     of the wrapper to argument types accepted by the wrapped function,
     then converts the wrapped function's result to another form







>
>
>
>
>

>
>
>
>


>
>
>
>
>


>
>
>
>







1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823

  const __xArgAdapterCheck =
        (t)=>xArg.get(t) || toss("Argument adapter not found:",t);

  const __xResultAdapterCheck =
        (t)=>xResult.get(t) || toss("Result adapter not found:",t);

  /**
     Fetches the xWrap() argument adapter mapped to t, calls it,
     passing in all remaining arguments, and returns the result.
     Throws if t is not mapped to an argument converter.
  */
  cache.xWrap.convertArg = (t,...args)=>__xArgAdapterCheck(t)(...args);
  /**
     Identical to convertArg() except that it does not perform
     an is-defined check on the mapping to t before invoking it.
  */
  cache.xWrap.convertArgNoCheck = (t,...args)=>xArg.get(t)(...args);

  /**
     Fetches the xWrap() result adapter mapped to t, calls it, passing
     it v, and returns the result.  Throws if t is not mapped to an
     argument converter.
  */
  cache.xWrap.convertResult =
    (t,v)=>(null===t ? v : (t ? __xResultAdapterCheck(t)(v) : undefined));
  /**
     Identical to convertResult() except that it does not perform an
     is-defined check on the mapping to t before invoking it.
  */
  cache.xWrap.convertResultNoCheck =
    (t,v)=>(null===t ? v : (t ? xResult.get(t)(v) : undefined));

  /**
     Creates a wrapper for another function which converts the arguments
     of the wrapper to argument types accepted by the wrapped function,
     then converts the wrapped function's result to another form

Changes to ext/wasm/tester1.c-pp.js.

2895
2896
2897
2898
2899
2900
2901




















































2902
2903
2904
2905
2906
2907
2908
          wasm.pstack.restore(stackPtr);
          db1.close();
          db2.close();
        }
      }
    })/*session API sanity tests*/
  ;/*end of session API group*/;





















































  ////////////////////////////////////////////////////////////////////////
  T.g('OPFS: Origin-Private File System',
      (sqlite3)=>(sqlite3.capi.sqlite3_vfs_find("opfs")
                  || 'requires "opfs" VFS'))
    .t({
      name: 'OPFS db sanity checks',







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







2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
          wasm.pstack.restore(stackPtr);
          db1.close();
          db2.close();
        }
      }
    })/*session API sanity tests*/
  ;/*end of session API group*/;
  ////////////////////////////////////////////////////////////////////////
  T.g('FTS5')
    .t({
      name: "Sanity checks",
      predicate: (sqlite3)=>sqlite3.fts5 || "Missing sqlite3.fts5 namespace.",
      test: function(sqlite3){
        const db = new sqlite3.oo1.DB();
        db.exec([
          "create virtual table ft using fts5(a,b);",
          "insert into ft(a,b) values",
          "('a1','b1'),",
          "('a2','b2'),",
          "('a3','b3');"
        ]);
        const fts = sqlite3.fts5;
        let pApi = fts.fts5_api_from_db(db);
        T.assert( !!pApi );
        let fApi = new fts.fts5_api(pApi);
        T.assert( fApi.$iVersion >= 2 );
        fApi.dispose();
        fApi = undefined;
        let destroyCalled = false;
        fts.createFunction(db, {
          name: 'mymatch',
          xFunction: function(pFtsX, pFtsCx, pCtx, argv){
            // Both of these return-value approaches are equivalent:
            const rv = "MY+"+argv.join(':');
            if(0){
              return rv;
            }else{
              capi.sqlite3_result_text(pCtx, rv, -1, capi.SQLITE_TRANSIENT);
              // implicit return of undefined
            }
          },
          xDestroy: function(){
            destroyCalled = true;
          }
        });
        let list = db.selectValues(
          "select mymatch(ft,a,b) from ft where b match 'b2'"
        );
        T.assert( 1 === list.length )
          .assert( 'MY+a2:b2' === list[0] );

        //const fTok = new fts.fts5_tokenizer();
        //fTok.installMethods({});

        db.close();
        T.assert( destroyCalled );
        //toss("Testing");
      }
    })/*FTS5*/

  ////////////////////////////////////////////////////////////////////////
  T.g('OPFS: Origin-Private File System',
      (sqlite3)=>(sqlite3.capi.sqlite3_vfs_find("opfs")
                  || 'requires "opfs" VFS'))
    .t({
      name: 'OPFS db sanity checks',