1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

More work on how to configure the sqlite3 JS API bootstrapping process from higher-level code. Initial version of sqlite3-worker1-promiser, a Promise-based proxy for the Worker API #1.

FossilOrigin-Name: b030f321bd5a38cdd5d6f6735f201afa62d30d2b0ba02e67f055b4895553a878
This commit is contained in:
stephan
2022-08-24 05:59:23 +00:00
parent efeee19a95
commit 9a34509a06
9 changed files with 537 additions and 204 deletions

View File

@@ -18,44 +18,24 @@
'use strict';
if('undefined' !== typeof Module){ // presumably an Emscripten build
/**
Replace sqlite3ApiBootstrap() with a variant which plugs in the
Emscripten-based config for all config options which the client
does not provide.
Install a suitable default configuration for sqlite3ApiBootstrap().
*/
const SAB = self.sqlite3ApiBootstrap;
self.sqlite3ApiBootstrap = function(apiConfig){
apiConfig = apiConfig || {};
const configDefaults = {
Module: Module /* ==> Emscripten-style Module object. Currently
needs to be exposed here for test code. NOT part
of the public API. */,
exports: Module['asm'],
memory: Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */
};
const config = {};
Object.keys(configDefaults).forEach(function(k){
config[k] = Object.getOwnPropertyDescriptor(apiConfig, k)
? apiConfig[k] : configDefaults[k];
});
// Copy over any properties apiConfig defines but configDefaults does not...
Object.keys(apiConfig).forEach(function(k){
if(!Object.getOwnPropertyDescriptor(config, k)){
config[k] = apiConfig[k];
}
});
return SAB(config);
};
const SABC = self.sqlite3ApiBootstrap.defaultConfig;
SABC.Module = Module /* ==> Current needs to be exposed here for test code. NOT part
of the public API. */;
SABC.exports = Module['asm'];
SABC.memory = Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */;
/**
For current (2022-08-22) purposes, automatically call
sqlite3ApiBootstrap(). That decision will be revisited at some
point, as we really want client code to be able to call this to
configure certain parts. If the global sqliteApiConfig property
is available, it is assumed to be a config object for
sqlite3ApiBootstrap().
configure certain parts. Clients may modify
self.sqlite3ApiBootstrap.defaultConfig to tweak the default
configuration used by a no-args call to sqlite3ApiBootstrap().
*/
//console.warn("self.sqlite3ApiConfig = ",self.sqlite3ApiConfig);
const sqlite3 = self.sqlite3ApiBootstrap(self.sqlite3ApiConfig || Object.create(null));
const sqlite3 = self.sqlite3ApiBootstrap();
delete self.sqlite3ApiBootstrap;
if(self.location && +self.location.port > 1024){
@@ -67,4 +47,9 @@ if('undefined' !== typeof Module){ // presumably an Emscripten build
delete sqlite3.capi.util /* arguable, but these are (currently) internal-use APIs */;
//console.warn("Module.sqlite3 =",Module.sqlite3);
Module.sqlite3 = sqlite3 /* Currently needed by test code and sqlite3-worker1.js */;
}else{
console.warn("This is not running in an Emscripten module context, so",
"self.sqlite3ApiBootstrap() is _not_ being called due to lack",
"of config info for the WASM environment.",
"It must be called manually.");
}

View File

@@ -93,16 +93,16 @@
The config object properties include:
- `Module`: Emscripten-style module object. Currently only required
- `Module`[^1]: 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
- `exports`[^1]: 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
- `memory`[^1]: 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.
@@ -120,20 +120,26 @@
the `free(3)`-compatible routine for the WASM
environment. Defaults to `"free"`.
- `persistentDirName`: if the environment supports persistent storage, this
- `persistentDirName`[^1]: 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.
[^1] = This property may optionally be a function, in which case this
function re-assigns it to the value returned from that function,
enabling delayed evaluation.
*/
self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(apiConfig){
'use strict';
'use strict';
self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
apiConfig = (sqlite3ApiBootstrap.defaultConfig || self.sqlite3ApiConfig)
){
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);
{
@@ -158,6 +164,16 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(apiConfig){
});
}
[
// If any of these config options are functions, replace them with
// the result of calling that function...
'Module', 'exports', 'memory', 'persistentDirName'
].forEach((k)=>{
if('function' === typeof config[k]){
config[k] = config[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(' '))};
@@ -750,4 +766,16 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(apiConfig){
this array is deleted.
*/
self.sqlite3ApiBootstrap.initializers = [];
self.sqlite3ApiBootstrap.sqlite3 = undefined /* installed at first call */;
/**
Client code may assign sqlite3ApiBootstrap.defaultConfig an
object-type value before calling sqlite3ApiBootstrap() (without
arguments) in order to tell that call to use this object as its
default config value. The intention of this is to provide
downstream clients with a reasonably flexible approach for plugging in
an environment-suitable configuration without having to define a new
global-scope symbol.
*/
self.sqlite3ApiBootstrap.defaultConfig = Object.create(null);
/** Placeholder: gets installed by the first call to
self.sqlite3ApiBootstrap(). */
self.sqlite3ApiBootstrap.sqlite3 = undefined;

View File

@@ -92,11 +92,8 @@ sqlite3.initWorker1API = function(){
defaultDb: undefined,
idSeq: 0,
idMap: new WeakMap,
open: function(arg){
// TODO? if arg is a filename, look for a db in this.dbs with the
// same filename and close/reopen it (or just pass it back as is?).
if(!arg && this.defaultDb) return this.defaultDb;
const db = (Array.isArray(arg) ? new DB(...arg) : new DB(arg));
open: function(opt){
const db = new DB(opt.filename);
this.dbs[getDbId(db)] = db;
if(!this.defaultDb) this.defaultDb = db;
return db;
@@ -169,14 +166,26 @@ sqlite3.initWorker1API = function(){
envelope to other calls in this API to tell them which
db to use. If it is not provided to future calls, they
will default to operating on the first-opened db.
persistent: prepend sqlite3.capi.sqlite3_web_persistent_dir()
to the given filename so that it is stored
in persistent storage _if_ the environment supports it.
If persistent storage is not supported, the filename
is used as-is.
}
*/
open: function(ev){
const oargs = [], args = (ev.args || {});
const oargs = Object.create(null), args = (ev.args || Object.create(null));
if(args.simulateError){ // undocumented internal testing option
toss("Throwing because of simulateError flag.");
}
if(args.filename) oargs.push(args.filename);
if(args.persistent && args.filename){
oargs.filaname = sqlite3.capi.sqlite3_web_persistent_dir() + args.filename;
}else if('' === args.filename){
oargs.filename = args.filename;
}else{
oargs.filename = args.filename || ':memory:';
}
const db = wState.open(oargs);
return {
filename: db.filename,
@@ -184,15 +193,15 @@ sqlite3.initWorker1API = function(){
};
},
/**
Proxy for DB.close(). ev.args may either be a boolean or an
object with an `unlink` property. If that value is truthy then
the db file (if the db is currently open) will be unlinked from
the virtual filesystem, else it will be kept intact. The
result object is:
Proxy for DB.close(). ev.args may be elided or an object with
an `unlink` property. If that value is truthy then the db file
(if the db is currently open) will be unlinked from the virtual
filesystem, else it will be kept intact. The result object is:
{
filename: db filename _if_ the db is opened when this
is called, else the undefined value
dbId: the ID of the closed b, or undefined if none is closed
}
It does not error if the given db is already closed or no db is
@@ -356,6 +365,7 @@ sqlite3.initWorker1API = function(){
dbId: DB handle ID,
[messageId: if set in the inbound message],
result: {
operation: "inbound message's 'type' value",
message: error string,
errorClass: class name of the error type,
input: ev.data
@@ -378,6 +388,7 @@ sqlite3.initWorker1API = function(){
}catch(err){
evType = 'error';
result = {
operation: ev.type,
message: err.message,
errorClass: err.name,
input: ev
@@ -405,7 +416,7 @@ sqlite3.initWorker1API = function(){
result: result
}, wMsgHandler.xfer);
};
setTimeout(()=>self.postMessage({type:'sqlite3-api',result:'worker1-ready'}), 0);
self.postMessage({type:'sqlite3-api',result:'worker1-ready'});
}.bind({self, sqlite3});
});