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:
@ -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 */;
|
||||
|
Reference in New Issue
Block a user