ext/wasm/api/README.md
This is the README for the files sqlite3-*.js and
sqlite3-wasm.c. This collection of files is used to build a
single-file distribution of the sqlite3 JS/WASM API. It is broken into
multiple JS files because:
To facilitate including or excluding certain components for
specific use cases. e.g. by removing sqlite3-api-oo1.js if the
OO#1 API is not needed.
To facilitate modularizing the pieces for use in different WASM
build environments. e.g. the files post-js-*.js are for use with
Emscripten's --post-js feature, and nowhere else.
As-yet-hypothetical comparable toolchains would necessarily have
similar facilities.
Certain components must be in their own standalone files in order to be loaded as JS Workers.
The structure described here is the current state of things, as of this writing, but is not set in stone forever and may change at any time. This doc targets maintainers of this code and those wanting to dive in to the details, not end user.
First off, a pikchr of the proverbial onion:
scale = 0.85
D0: dot invis
define matryoshka {
$rad = $rad + $2
circle with se at D0.c radius $rad
text at last.nw $1 below ljust
# $anchor = last circle.e
}
$rad = 5mm
C0: circle with se at D0.c "sqlite3-api.js" fit
$rad = C0.width / 2
matryoshka("--post-js",5mm)
matryoshka("Emscripten",12mm)
circle with nw at 7mm s of 3mm e of last circle.nw "--pre-js" fit
matryoshka("--extern-pre/post-js",9mm)
matryoshka("sqlite3.js",6mm)
#CX: last circle
right
CW: file with w at 15mm e of 3rd circle.ne "sqlite3.wasm" fit
arrow from CW.w to 3rd circle.ne
CC: circle with e at 1cm w of last circle.w "End User" fit radius 1cm
arrow <- from CC.e to 6th circle.w
Actually, sqlite3.js and --extern-js are the same. The former is
what the client sees and the latter is how it looks from a code
maintenance point of view.
At the center of the onion is sqlite3-api.js, which gets generated
by concatenating the following files together in their listed order:
sqlite3-api-prologue.js../common/whwasmutil.js../jaccwabyt/jaccwabyt.jssqlite3-api-glue.jssqlite3-api-prologue.js. Most of these pieces
involve populating the sqlite3.capi.wasm object and creating
sqlite3.capi.sqlite3_...() bindings. This file also deletes most
global-scope symbols the above files create, effectively moving them
into the scope being used for initializing the API.<build>/sqlite3-api-build-version.jssqlite3.version object. This part is not critical, but records the
version of the library against which this module was built.sqlite3-api-oo1.jssqlite3-api-worker1.jssqlite3-worker1.jssqlite3-api-worker1.js.sqlite3-worker1-promiser.jssqlite3-vfs-helper.c-pp.jssqlite3.vfs namespace, which contain helpers for use
by downstream code which creates sqlite3_vfs implementations.sqlite3-vtab-helper.c-pp.jssqlite3.vtab namespace, which contain helpers for use
by downstream code which creates sqlite3_module implementations.sqlite3-vfs-opfs.c-pp.jssqlite3-opfs-async-proxy.jssqlite3-vfs-opfs-sahpool.c-pp.jsThe previous files do not immediately extend the library. Instead they
install a global function sqlite3ApiBootstrap(), which downstream
code must call to configure the library for the current JS/WASM
environment. Each file listed above pushes a callback into the
bootstrapping queue, to be called as part of sqlite3ApiBootstrap().
Some files also temporarily create global objects in order to
communicate their state to the files which follow them. Those
get cleaned up vi post-js-footer.js, described below.
Adapting the build for non-Emscripten toolchains essentially requires packaging
the above files, concatated together, into that toolchain's "JS glue"
and, in the final stage of that glue, call sqlite3ApiBootstrap() and
return its result to the end user.
Files with the extension .c-pp.js are intended to be processed
with c-pp, noting that such preprocessing may be applied
after all of the relevant files are concatenated. The .c-pp.js
extension is used primarily to keep the code maintainers cognisant of
the fact that those files contain constructs which may not run as-is
in any given JavaScript environment.
The build process glues those files together, resulting in
sqlite3-api.js, which is everything except for the Emscripten-specific
files detailed below (into which sqlite3-api.js gets injected).
The non-JS outlier file is sqlite3-wasm.c: it is a proxy for
sqlite3.c which #include's that file and adds a handful of
WASM-specific helper functions, at least two of which requires access
to private/static sqlite3.c internals. sqlite3.wasm is compiled
from this file rather than sqlite3.c.
The following Emscripten-specific files are injected into the
build-generated sqlite3.js along with sqlite3-api.js.
extern-pre-js.js
Emscripten-specific header for Emscripten's --extern-pre-js
flag. As of this writing, that file is only used for experimentation
purposes and holds no code relevant to the production deliverables.
pre-js.c-pp.js
Emscripten-specific header for Emscripten's --pre-js flag. This
file overrides certain Emscripten behavior before Emscripten does
most of its work.
post-js-header.js
Emscripten-specific header for the --post-js input. It opens up,
but does not close, a function used for initializing the library.
sqlite3-api.js gets sandwiched between these ↑ two
↓ files.
post-js-footer.js
Emscripten-specific footer for the --post-js input. This closes
off the function opened by post-js-header.js. This file cleans up
any dangling globals and runs sqlite3ApiBootstrap(). As of this
writing, this code ensures that the previous files leave no more
than a single global symbol installed - sqlite3InitModule().
extern-post-js.c-pp.js
Emscripten-specific header for Emscripten's --extern-post-js
flag. This file is run in the global scope. It overwrites the
Emscripten-installed sqlite3InitModule() function with one which
first runs the original implementation, then runs the function
installed by post-js-header/footer.js to initialize the library,
then initializes the asynchronous parts of the sqlite3 module (for
example, the OPFS VFS support). Its final step is to return a
Promise of the sqlite3 namespace object to the caller of
sqlite3InitModule() (the library user).
Certain files in the build require preprocessing to filter in/out
parts which differ between vanilla JS, ES6 Modules, and node.js
builds. The preprocessor application itself is in
c-pp-lite.c and the complete technical
details of such preprocessing are maintained in
GNUMakefile.