1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-27 20:41:58 +03:00

Refactor JS API amalgamation such that the bootstrapping/configuration is deferred until the whole amalgamation is available, to facilitate providing clients with a way to initialize the API with their own config (noting that we're still one small level of refactoring away from being able to actually do that).

FossilOrigin-Name: 9dbe9a6aecec43b51057375ef1d2d632db0d17eac8b7552c20cc91fc2f1a55d1
This commit is contained in:
stephan
2022-08-22 13:34:13 +00:00
parent 64d04a8d9f
commit e3cd67603d
9 changed files with 206 additions and 107 deletions

View File

@ -78,25 +78,88 @@
*/
/**
This global symbol is is only a temporary measure: the JS-side
post-processing will remove that object from the global scope when
setup is complete. We require it there temporarily in order to glue
disparate parts together during the loading of the API (which spans
several components).
sqlite3ApiBootstrap() is the only global symbol exposed by this
API. It is intended to be called one time at the end of the API
amalgamation process, passed configuration details for the current
environment, and then optionally be removed from the global object
using `delete self.sqlite3ApiBootstrap`.
This function requires a configuration object intended to abstract
This function expects a configuration object, intended to abstract
away details specific to any given WASM environment, primarily so
that it can be used without any _direct_ dependency on Emscripten.
(That said, OO API #1 requires, as of this writing, Emscripten's
virtual filesystem API. Baby steps.)
that it can be used without any _direct_ dependency on
Emscripten. The config object is only honored the first time this
is called. Subsequent calls ignore the argument and return the same
(configured) object which gets initialized by the first call.
The config object properties include:
- `Module`: Emscripten-style module object. Currently only required
by certain test code and is _not_ part of the public interface.
(TODO: rename this to EmscriptenModule to be more explicit.)
- `exports`: the "exports" object for the current WASM
environment. In an Emscripten build, this should be set to
`Module['asm']`.
- `memory`: optional WebAssembly.Memory object, defaulting to
`exports.memory`. In Emscripten environments this should be set
to `Module.wasmMemory` if the build uses `-sIMPORT_MEMORY`, or be
left undefined/falsy to default to `exports.memory` when using
WASM-exported memory.
- `bigIntEnabled`: true if BigInt support is enabled. Defaults to
true if self.BigInt64Array is available, else false. Some APIs
will throw exceptions if called without BigInt support, as BigInt
is required for marshalling C-side int64 into and out of JS.
- `allocExportName`: the name of the function, in `exports`, of the
`malloc(3)`-compatible routine for the WASM environment. Defaults
to `"malloc"`.
- `deallocExportName`: the name of the function, in `exports`, of
the `free(3)`-compatible routine for the WASM
environment. Defaults to `"free"`.
- `persistentDirName`: if the environment supports persistent storage, this
directory names the "mount point" for that directory. It must be prefixed
by `/` and may currently contain only a single directory-name part. Using
the root directory name is not supported by any current persistent backend.
*/
self.sqlite3ApiBootstrap = function(config){
self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(apiConfig){
'use strict';
if(sqlite3ApiBootstrap.sqlite3){ /* already initalized */
console.warn("sqlite3ApiBootstrap() called multiple times.",
"Config and external initializers are ignored on calls after the first.");
return sqlite3ApiBootstrap.sqlite3;
}
apiConfig = apiConfig||{};
const config = Object.create(null);
{
const configDefaults = {
Module: undefined/*needed for some test code, not part of the public API*/,
exports: undefined,
memory: undefined,
bigIntEnabled: !!self.BigInt64Array,
allocExportName: 'malloc',
deallocExportName: 'free',
persistentDirName: '/persistent'
};
Object.keys(configDefaults).forEach(function(k){
config[k] = Object.prototype.hasOwnProperty.call(apiConfig, k)
? apiConfig[k] : configDefaults[k];
});
}
/** Throws a new Error, the message of which is the concatenation
all args with a space between each. */
const toss = (...args)=>{throw new Error(args.join(' '))};
if(config.persistentDirName && !/^\/[^/]+$/.test(config.persistentDirName)){
toss("config.persistentDirName must be falsy or in the form '/dir-name'.");
}
/**
Returns true if n is a 32-bit (signed) integer, else
false. This is used for determining when we need to switch to
@ -143,7 +206,18 @@ self.sqlite3ApiBootstrap = function(config){
};
const utf8Decoder = new TextDecoder('utf-8');
const typedArrayToString = (str)=>utf8Decoder.decode(str);
/** 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)
);
};
/**
An Error subclass specifically for reporting Wasm-level malloc()
@ -591,9 +665,6 @@ self.sqlite3ApiBootstrap = function(config){
TODOs and caveats:
- The directory name (mount point) for persistent storage is
currently hard-coded. It needs to be configurable.
- If persistent storage is available at the root of the virtual
filesystem, this interface cannot currently distinguish that
from the lack of persistence. That case cannot currently (with
@ -604,12 +675,17 @@ self.sqlite3ApiBootstrap = function(config){
capi.sqlite3_web_persistent_dir = function(){
if(undefined !== __persistentDir) return __persistentDir;
// If we have no OPFS, there is no persistent dir
if(!self.FileSystemHandle || !self.FileSystemDirectoryHandle
const pdir = config.persistentDirName;
if(!pdir
|| !self.FileSystemHandle
|| !self.FileSystemDirectoryHandle
|| !self.FileSystemFileHandle){
return __persistentDir = "";
}
try{
if(0===this.wasm.xCall('sqlite3_wasm_init_opfs')){
if(pdir && 0===this.wasm.xCallWrapped(
'sqlite3_wasm_init_opfs', 'i32', ['string'], pdir
)){
/** OPFS does not support locking and will trigger errors if
we try to lock. We don't _really_ want to
_unconditionally_ install a non-locking sqlite3 VFS as the
@ -617,13 +693,12 @@ self.sqlite3ApiBootstrap = function(config){
time being. That said: locking is a no-op on all of the
current WASM storage, so this isn't (currently) as bad as
it may initially seem. */
const pVfs = this.sqlite3_vfs_find("unix-none");
const pVfs = sqlite3.capi.sqlite3_vfs_find("unix-none");
if(pVfs){
this.sqlite3_vfs_register(pVfs,1);
//warn("Installed 'unix-none' as the default sqlite3 VFS.");
capi.sqlite3_vfs_register(pVfs,1);
console.warn("Installed 'unix-none' as the default sqlite3 VFS.");
}
return __persistentDir =
"/persistent" /* name is hard-coded in sqlite3_wasm_init_opfs()!*/;
return __persistentDir = pdir;
}else{
return __persistentDir = "";
}
@ -644,19 +719,29 @@ self.sqlite3ApiBootstrap = function(config){
}.bind(capi);
/* The remainder of the API will be set up in later steps. */
return {
/**
An Error subclass which is thrown by this.wasm.alloc() on OOM.
*/
const sqlite3 = {
WasmAllocError: WasmAllocError,
capi,
postInit: [
/* some pieces of the API may install functions into this array,
and each such function will be called, passed (self,sqlite3),
at the very end of the API load/init process, where self is
the current global object and sqlite3 is the object returned
from sqlite3ApiBootstrap(). This array will be removed at the
end of the API setup process. */],
config
};
sqlite3ApiBootstrap.initializers.forEach((f)=>f(sqlite3));
delete sqlite3ApiBootstrap.initializers;
sqlite3ApiBootstrap.sqlite3 = sqlite3;
return sqlite3;
}/*sqlite3ApiBootstrap()*/;
/**
self.sqlite3ApiBootstrap.initializers is an internal detail used by
the various pieces of the sqlite3 API's amalgamation process. It
must not be modified by client code except when plugging such code
into the amalgamation process.
Each component of the amalgamation is expected to append a function
to this array. When sqlite3ApiBootstrap() is called for the first
time, each such function will be called (in their appended order)
and passed the sqlite3 namespace object, into which they can install
their features (noting that most will also require that certain
features alread have been installed). At the end of that process,
this array is deleted.
*/
self.sqlite3ApiBootstrap.initializers = [];
self.sqlite3ApiBootstrap.sqlite3 = undefined /* installed at first call */;