mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-11 01:42:22 +03:00
Merge recent trunk changes into the coroutine-exp2 branch.
FossilOrigin-Name: c43f433bcab29db0f1f8afd3948f5a4149e1f277c853c66f99c31f226d82bc0f
This commit is contained in:
@@ -48,7 +48,7 @@ do_test 1.2 {
|
|||||||
|
|
||||||
do_test 1.3 {
|
do_test 1.3 {
|
||||||
set ::errlog
|
set ::errlog
|
||||||
} {SQLITE_NOTICE_RECOVER_WAL SQLITE_INTERNAL}
|
} {SQLITE_NOTICE_RECOVER_WAL SQLITE_NOTICE_RBU}
|
||||||
|
|
||||||
do_execsql_test 1.4 {
|
do_execsql_test 1.4 {
|
||||||
SELECT * FROM t1
|
SELECT * FROM t1
|
||||||
|
|||||||
@@ -3031,11 +3031,11 @@ static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){
|
|||||||
** no-ops. These locks will not be released until the connection
|
** no-ops. These locks will not be released until the connection
|
||||||
** is closed.
|
** is closed.
|
||||||
**
|
**
|
||||||
** * Attempting to xSync() the database file causes an SQLITE_INTERNAL
|
** * Attempting to xSync() the database file causes an SQLITE_NOTICE
|
||||||
** error.
|
** error.
|
||||||
**
|
**
|
||||||
** As a result, unless an error (i.e. OOM or SQLITE_BUSY) occurs, the
|
** As a result, unless an error (i.e. OOM or SQLITE_BUSY) occurs, the
|
||||||
** checkpoint below fails with SQLITE_INTERNAL, and leaves the aFrame[]
|
** checkpoint below fails with SQLITE_NOTICE, and leaves the aFrame[]
|
||||||
** array populated with a set of (frame -> page) mappings. Because the
|
** array populated with a set of (frame -> page) mappings. Because the
|
||||||
** WRITER, CHECKPOINT and READ0 locks are still held, it is safe to copy
|
** WRITER, CHECKPOINT and READ0 locks are still held, it is safe to copy
|
||||||
** data from the wal file into the database file according to the
|
** data from the wal file into the database file according to the
|
||||||
@@ -3045,7 +3045,7 @@ static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){
|
|||||||
int rc2;
|
int rc2;
|
||||||
p->eStage = RBU_STAGE_CAPTURE;
|
p->eStage = RBU_STAGE_CAPTURE;
|
||||||
rc2 = sqlite3_exec(p->dbMain, "PRAGMA main.wal_checkpoint=restart", 0, 0,0);
|
rc2 = sqlite3_exec(p->dbMain, "PRAGMA main.wal_checkpoint=restart", 0, 0,0);
|
||||||
if( rc2!=SQLITE_INTERNAL ) p->rc = rc2;
|
if( rc2!=SQLITE_NOTICE ) p->rc = rc2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( p->rc==SQLITE_OK && p->nFrame>0 ){
|
if( p->rc==SQLITE_OK && p->nFrame>0 ){
|
||||||
@@ -3091,7 +3091,7 @@ static int rbuCaptureWalRead(sqlite3rbu *pRbu, i64 iOff, int iAmt){
|
|||||||
|
|
||||||
if( pRbu->mLock!=mReq ){
|
if( pRbu->mLock!=mReq ){
|
||||||
pRbu->rc = SQLITE_BUSY;
|
pRbu->rc = SQLITE_BUSY;
|
||||||
return SQLITE_INTERNAL;
|
return SQLITE_NOTICE_RBU;
|
||||||
}
|
}
|
||||||
|
|
||||||
pRbu->pgsz = iAmt;
|
pRbu->pgsz = iAmt;
|
||||||
@@ -4478,7 +4478,7 @@ void sqlite3rbu_rename_handler(
|
|||||||
** database file are recorded. xShmLock() calls to unlock the same
|
** database file are recorded. xShmLock() calls to unlock the same
|
||||||
** locks are no-ops (so that once obtained, these locks are never
|
** locks are no-ops (so that once obtained, these locks are never
|
||||||
** relinquished). Finally, calls to xSync() on the target database
|
** relinquished). Finally, calls to xSync() on the target database
|
||||||
** file fail with SQLITE_INTERNAL errors.
|
** file fail with SQLITE_NOTICE errors.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void rbuUnlockShm(rbu_file *p){
|
static void rbuUnlockShm(rbu_file *p){
|
||||||
@@ -4757,7 +4757,7 @@ static int rbuVfsSync(sqlite3_file *pFile, int flags){
|
|||||||
rbu_file *p = (rbu_file *)pFile;
|
rbu_file *p = (rbu_file *)pFile;
|
||||||
if( p->pRbu && p->pRbu->eStage==RBU_STAGE_CAPTURE ){
|
if( p->pRbu && p->pRbu->eStage==RBU_STAGE_CAPTURE ){
|
||||||
if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
|
if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
|
||||||
return SQLITE_INTERNAL;
|
return SQLITE_NOTICE_RBU;
|
||||||
}
|
}
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,17 @@
|
|||||||
# by the target name. Rebuild is necessary for all components to get
|
# by the target name. Rebuild is necessary for all components to get
|
||||||
# the desired optimization level.
|
# the desired optimization level.
|
||||||
#
|
#
|
||||||
|
# quick, q = do just a minimal build (sqlite3.js/wasm, tester1) for
|
||||||
|
# faster development-mode turnaround.
|
||||||
|
#
|
||||||
|
# qo2, qoz = a combination of quick+o2/oz.
|
||||||
|
#
|
||||||
# dist = create end user deliverables. Add dist.build=oX to build
|
# dist = create end user deliverables. Add dist.build=oX to build
|
||||||
# with a specific optimization level, where oX is one of the
|
# with a specific optimization level, where oX is one of the
|
||||||
# above-listed o? target names.
|
# above-listed o? or qo? target names.
|
||||||
|
#
|
||||||
|
# snapshot = like dist, but uses a zip file name which clearly
|
||||||
|
# marks it as a prerelease/snapshot build.
|
||||||
#
|
#
|
||||||
# clean = clean up
|
# clean = clean up
|
||||||
#
|
#
|
||||||
@@ -289,7 +297,7 @@ sqlite3-api.jses += $(dir.api)/sqlite3-api-glue.js
|
|||||||
sqlite3-api.jses += $(sqlite3-api-build-version.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-oo1.js
|
||||||
sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.js
|
sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.js
|
||||||
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-helper.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.c-pp.js
|
||||||
sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js
|
sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js
|
||||||
|
|
||||||
@@ -307,7 +315,8 @@ $$(dir.dout)/$$(notdir $(1)): $(1) $$(MAKEFILE)
|
|||||||
endef
|
endef
|
||||||
$(foreach X,$(SOAP.js) $(sqlite3-worker1.js) $(sqlite3-worker1-promiser.js),\
|
$(foreach X,$(SOAP.js) $(sqlite3-worker1.js) $(sqlite3-worker1-promiser.js),\
|
||||||
$(eval $(call COPY_XAPI,$(X))))
|
$(eval $(call COPY_XAPI,$(X))))
|
||||||
all: $(sqlite3-api.ext.jses)
|
all quick: $(sqlite3-api.ext.jses)
|
||||||
|
q: quick
|
||||||
|
|
||||||
# sqlite3-api.js.in = the generated sqlite3-api.js before it gets
|
# sqlite3-api.js.in = the generated sqlite3-api.js before it gets
|
||||||
# preprocessed. It contains all of $(sqlite3-api.jses) but none of the
|
# preprocessed. It contains all of $(sqlite3-api.jses) but none of the
|
||||||
@@ -488,7 +497,7 @@ emcc.jsflags += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.$(emcc.INITIAL_MEMORY))
|
|||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
emcc.jsflags += $(emcc.environment)
|
emcc.jsflags += $(emcc.environment)
|
||||||
emcc.jsflags += -sSTACK_SIZE=1MB
|
emcc.jsflags += -sSTACK_SIZE=512KB
|
||||||
# ^^^ ACHTUNG: emsdk 3.1.27 reduced the default stack size from 5MB to
|
# ^^^ ACHTUNG: emsdk 3.1.27 reduced the default stack size from 5MB to
|
||||||
# a mere 64KB, which leads to silent memory corruption via the kvvfs
|
# a mere 64KB, which leads to silent memory corruption via the kvvfs
|
||||||
# VFS, which requires twice that for its xRead() and xWrite() methods.
|
# VFS, which requires twice that for its xRead() and xWrite() methods.
|
||||||
@@ -627,8 +636,9 @@ $(sqlite3.mjs):
|
|||||||
$(sqlite3.wasm): $(sqlite3.js)
|
$(sqlite3.wasm): $(sqlite3.js)
|
||||||
$(sqlite3.mjs): $(sqlite3.js)
|
$(sqlite3.mjs): $(sqlite3.js)
|
||||||
CLEAN_FILES += $(sqlite3.js) $(sqlite3.mjs) $(sqlite3.wasm)
|
CLEAN_FILES += $(sqlite3.js) $(sqlite3.mjs) $(sqlite3.wasm)
|
||||||
all: $(sqlite3.mjs)
|
all: $(sqlite3.js) $(sqlite3.mjs)
|
||||||
wasm: $(sqlite3.mjs)
|
quick: $(sqlite3.js)
|
||||||
|
quick: $(sqlite3.mjs) # for the sake of the snapshot build
|
||||||
# End main $(sqlite3.js) build
|
# End main $(sqlite3.js) build
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
@@ -673,7 +683,7 @@ speedtest1.eflags.common += -sSTRICT_JS
|
|||||||
speedtest1.eflags.common += -sMODULARIZE
|
speedtest1.eflags.common += -sMODULARIZE
|
||||||
speedtest1.eflags.common += -Wno-limited-postlink-optimizations
|
speedtest1.eflags.common += -Wno-limited-postlink-optimizations
|
||||||
EXPORTED_FUNCTIONS.speedtest1 := $(abspath $(dir.tmp)/EXPORTED_FUNCTIONS.speedtest1)
|
EXPORTED_FUNCTIONS.speedtest1 := $(abspath $(dir.tmp)/EXPORTED_FUNCTIONS.speedtest1)
|
||||||
speedtest1.eflags.common += -sSTACK_SIZE=1MB
|
speedtest1.eflags.common += -sSTACK_SIZE=512KB
|
||||||
speedtest1.eflags.common += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.speedtest1)
|
speedtest1.eflags.common += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.speedtest1)
|
||||||
speedtest1.eflags.common += $(emcc.exportedRuntimeMethods)
|
speedtest1.eflags.common += $(emcc.exportedRuntimeMethods)
|
||||||
speedtest1.eflags.common += -sALLOW_TABLE_GROWTH
|
speedtest1.eflags.common += -sALLOW_TABLE_GROWTH
|
||||||
@@ -748,7 +758,7 @@ $(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.mjs,$(c-pp.D.esm)))
|
|||||||
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1.html))
|
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1.html))
|
||||||
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1-esm.html,$(c-pp.D.esm)))
|
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1-esm.html,$(c-pp.D.esm)))
|
||||||
tester1: tester1.js tester1.mjs tester1.html tester1-esm.html
|
tester1: tester1.js tester1.mjs tester1.html tester1-esm.html
|
||||||
all: tester1
|
all quick: tester1
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
# Convenience rules to rebuild with various -Ox levels. Much
|
# Convenience rules to rebuild with various -Ox levels. Much
|
||||||
@@ -757,7 +767,8 @@ all: tester1
|
|||||||
# painful.
|
# painful.
|
||||||
|
|
||||||
.PHONY: o0 o1 o2 o3 os oz
|
.PHONY: o0 o1 o2 o3 os oz
|
||||||
o-xtra := -flto
|
o-xtra :=
|
||||||
|
#o-xtra ?= -flto
|
||||||
# ^^^^ -flto can have a considerably performance boost at -O0 but
|
# ^^^^ -flto can have a considerably performance boost at -O0 but
|
||||||
# doubles the build time and seems to have negligible, if any, effect
|
# doubles the build time and seems to have negligible, if any, effect
|
||||||
# on higher optimization levels.
|
# on higher optimization levels.
|
||||||
@@ -766,14 +777,18 @@ o0: clean
|
|||||||
o1: clean
|
o1: clean
|
||||||
$(MAKE) -e "emcc_opt=-O1 $(o-xtra)"
|
$(MAKE) -e "emcc_opt=-O1 $(o-xtra)"
|
||||||
o2: clean
|
o2: clean
|
||||||
$(MAKE) -e "emcc_opt=-O2 $(o-xtra)"
|
$(MAKE) -j2 -e "emcc_opt=-O2 $(o-xtra)"
|
||||||
|
qo2: clean
|
||||||
|
$(MAKE) -j2 -e "emcc_opt=-O2 $(o-xtra)" quick
|
||||||
o3: clean
|
o3: clean
|
||||||
$(MAKE) -e "emcc_opt=-O3 $(o-xtra)"
|
$(MAKE) -e "emcc_opt=-O3 $(o-xtra)"
|
||||||
os: clean
|
os: clean
|
||||||
@echo "WARNING: -Os can result in a build with mysteriously missing pieces!"
|
@echo "WARNING: -Os can result in a build with mysteriously missing pieces!"
|
||||||
$(MAKE) -e "emcc_opt=-Os $(o-xtra)"
|
$(MAKE) -e "emcc_opt=-Os $(o-xtra)"
|
||||||
oz: clean
|
oz: clean
|
||||||
$(MAKE) -e "emcc_opt=-Oz $(o-xtra)"
|
$(MAKE) -j2 -e "emcc_opt=-Oz $(o-xtra)"
|
||||||
|
qoz: clean
|
||||||
|
$(MAKE) -j2 -e "emcc_opt=-Oz $(o-xtra)" quick
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
# Sub-makes...
|
# Sub-makes...
|
||||||
|
|||||||
@@ -6,12 +6,15 @@ _sqlite3_bind_int64
|
|||||||
_sqlite3_bind_null
|
_sqlite3_bind_null
|
||||||
_sqlite3_bind_parameter_count
|
_sqlite3_bind_parameter_count
|
||||||
_sqlite3_bind_parameter_index
|
_sqlite3_bind_parameter_index
|
||||||
|
_sqlite3_bind_pointer
|
||||||
_sqlite3_bind_text
|
_sqlite3_bind_text
|
||||||
|
_sqlite3_busy_handler
|
||||||
_sqlite3_busy_timeout
|
_sqlite3_busy_timeout
|
||||||
_sqlite3_changes
|
_sqlite3_changes
|
||||||
_sqlite3_changes64
|
_sqlite3_changes64
|
||||||
_sqlite3_clear_bindings
|
_sqlite3_clear_bindings
|
||||||
_sqlite3_close_v2
|
_sqlite3_close_v2
|
||||||
|
_sqlite3_collation_needed
|
||||||
_sqlite3_column_blob
|
_sqlite3_column_blob
|
||||||
_sqlite3_column_bytes
|
_sqlite3_column_bytes
|
||||||
_sqlite3_column_count
|
_sqlite3_column_count
|
||||||
@@ -22,16 +25,26 @@ _sqlite3_column_int64
|
|||||||
_sqlite3_column_name
|
_sqlite3_column_name
|
||||||
_sqlite3_column_text
|
_sqlite3_column_text
|
||||||
_sqlite3_column_type
|
_sqlite3_column_type
|
||||||
|
_sqlite3_column_value
|
||||||
_sqlite3_compileoption_get
|
_sqlite3_compileoption_get
|
||||||
_sqlite3_compileoption_used
|
_sqlite3_compileoption_used
|
||||||
|
_sqlite3_complete
|
||||||
|
_sqlite3_create_collation
|
||||||
|
_sqlite3_create_collation_v2
|
||||||
_sqlite3_create_function
|
_sqlite3_create_function
|
||||||
_sqlite3_create_function_v2
|
_sqlite3_create_function_v2
|
||||||
|
_sqlite3_create_module
|
||||||
|
_sqlite3_create_module_v2
|
||||||
_sqlite3_create_window_function
|
_sqlite3_create_window_function
|
||||||
_sqlite3_data_count
|
_sqlite3_data_count
|
||||||
_sqlite3_db_filename
|
_sqlite3_db_filename
|
||||||
_sqlite3_db_handle
|
_sqlite3_db_handle
|
||||||
_sqlite3_db_name
|
_sqlite3_db_name
|
||||||
|
_sqlite3_db_status
|
||||||
|
_sqlite3_declare_vtab
|
||||||
_sqlite3_deserialize
|
_sqlite3_deserialize
|
||||||
|
_sqlite3_drop_modules
|
||||||
|
_sqlite3_errcode
|
||||||
_sqlite3_errmsg
|
_sqlite3_errmsg
|
||||||
_sqlite3_error_offset
|
_sqlite3_error_offset
|
||||||
_sqlite3_errstr
|
_sqlite3_errstr
|
||||||
@@ -42,16 +55,24 @@ _sqlite3_extended_result_codes
|
|||||||
_sqlite3_file_control
|
_sqlite3_file_control
|
||||||
_sqlite3_finalize
|
_sqlite3_finalize
|
||||||
_sqlite3_free
|
_sqlite3_free
|
||||||
|
_sqlite3_get_auxdata
|
||||||
_sqlite3_initialize
|
_sqlite3_initialize
|
||||||
|
_sqlite3_keyword_count
|
||||||
|
_sqlite3_keyword_name
|
||||||
|
_sqlite3_keyword_check
|
||||||
|
_sqlite3_last_insert_rowid
|
||||||
_sqlite3_libversion
|
_sqlite3_libversion
|
||||||
_sqlite3_libversion_number
|
_sqlite3_libversion_number
|
||||||
|
_sqlite3_limit
|
||||||
_sqlite3_malloc
|
_sqlite3_malloc
|
||||||
_sqlite3_malloc64
|
_sqlite3_malloc64
|
||||||
_sqlite3_msize
|
_sqlite3_msize
|
||||||
_sqlite3_open
|
_sqlite3_open
|
||||||
_sqlite3_open_v2
|
_sqlite3_open_v2
|
||||||
|
_sqlite3_overload_function
|
||||||
_sqlite3_prepare_v2
|
_sqlite3_prepare_v2
|
||||||
_sqlite3_prepare_v3
|
_sqlite3_prepare_v3
|
||||||
|
_sqlite3_progress_handler
|
||||||
_sqlite3_randomness
|
_sqlite3_randomness
|
||||||
_sqlite3_realloc
|
_sqlite3_realloc
|
||||||
_sqlite3_realloc64
|
_sqlite3_realloc64
|
||||||
@@ -65,19 +86,32 @@ _sqlite3_result_error_toobig
|
|||||||
_sqlite3_result_int
|
_sqlite3_result_int
|
||||||
_sqlite3_result_int64
|
_sqlite3_result_int64
|
||||||
_sqlite3_result_null
|
_sqlite3_result_null
|
||||||
|
_sqlite3_result_pointer
|
||||||
|
_sqlite3_result_subtype
|
||||||
_sqlite3_result_text
|
_sqlite3_result_text
|
||||||
_sqlite3_result_zeroblob
|
_sqlite3_result_zeroblob
|
||||||
_sqlite3_result_zeroblob64
|
_sqlite3_result_zeroblob64
|
||||||
_sqlite3_serialize
|
_sqlite3_serialize
|
||||||
|
_sqlite3_set_auxdata
|
||||||
|
_sqlite3_set_last_insert_rowid
|
||||||
_sqlite3_shutdown
|
_sqlite3_shutdown
|
||||||
_sqlite3_sourceid
|
_sqlite3_sourceid
|
||||||
_sqlite3_sql
|
_sqlite3_sql
|
||||||
|
_sqlite3_status
|
||||||
|
_sqlite3_status64
|
||||||
_sqlite3_step
|
_sqlite3_step
|
||||||
|
_sqlite3_stmt_isexplain
|
||||||
|
_sqlite3_stmt_readonly
|
||||||
|
_sqlite3_stmt_status
|
||||||
_sqlite3_strglob
|
_sqlite3_strglob
|
||||||
|
_sqlite3_stricmp
|
||||||
_sqlite3_strlike
|
_sqlite3_strlike
|
||||||
|
_sqlite3_strnicmp
|
||||||
|
_sqlite3_table_column_metadata
|
||||||
_sqlite3_total_changes
|
_sqlite3_total_changes
|
||||||
_sqlite3_total_changes64
|
_sqlite3_total_changes64
|
||||||
_sqlite3_trace_v2
|
_sqlite3_trace_v2
|
||||||
|
_sqlite3_txn_state
|
||||||
_sqlite3_uri_boolean
|
_sqlite3_uri_boolean
|
||||||
_sqlite3_uri_int64
|
_sqlite3_uri_int64
|
||||||
_sqlite3_uri_key
|
_sqlite3_uri_key
|
||||||
@@ -86,13 +120,28 @@ _sqlite3_user_data
|
|||||||
_sqlite3_value_blob
|
_sqlite3_value_blob
|
||||||
_sqlite3_value_bytes
|
_sqlite3_value_bytes
|
||||||
_sqlite3_value_double
|
_sqlite3_value_double
|
||||||
|
_sqlite3_value_dup
|
||||||
|
_sqlite3_value_free
|
||||||
|
_sqlite3_value_frombind
|
||||||
_sqlite3_value_int
|
_sqlite3_value_int
|
||||||
_sqlite3_value_int64
|
_sqlite3_value_int64
|
||||||
|
_sqlite3_value_nochange
|
||||||
|
_sqlite3_value_numeric_type
|
||||||
|
_sqlite3_value_pointer
|
||||||
|
_sqlite3_value_subtype
|
||||||
_sqlite3_value_text
|
_sqlite3_value_text
|
||||||
_sqlite3_value_type
|
_sqlite3_value_type
|
||||||
_sqlite3_vfs_find
|
_sqlite3_vfs_find
|
||||||
_sqlite3_vfs_register
|
_sqlite3_vfs_register
|
||||||
_sqlite3_vfs_unregister
|
_sqlite3_vfs_unregister
|
||||||
|
_sqlite3_vtab_collation
|
||||||
|
_sqlite3_vtab_distinct
|
||||||
|
_sqlite3_vtab_in
|
||||||
|
_sqlite3_vtab_in_first
|
||||||
|
_sqlite3_vtab_in_next
|
||||||
|
_sqlite3_vtab_nochange
|
||||||
|
_sqlite3_vtab_on_conflict
|
||||||
|
_sqlite3_vtab_rhs_value
|
||||||
_malloc
|
_malloc
|
||||||
_free
|
_free
|
||||||
_realloc
|
_realloc
|
||||||
|
|||||||
@@ -78,11 +78,10 @@ browser client:
|
|||||||
a Promise-based interface into the Worker #1 API. This is
|
a Promise-based interface into the Worker #1 API. This is
|
||||||
a far user-friendlier way to interface with databases running
|
a far user-friendlier way to interface with databases running
|
||||||
in a Worker thread.
|
in a Worker thread.
|
||||||
- **`sqlite3-vfs-helper.js`**\
|
- **`sqlite3-v-helper.js`**\
|
||||||
This internal-use-only file installs `sqlite3.VfsHelper` for use by
|
Installs `sqlite3.VfsHelper` and `sqlite3.VtabHelper` for use by
|
||||||
`sqlite3-*.js` files which create `sqlite3_vfs` implementations.
|
downstream code which creates `sqlite3_vfs` and `sqlite3_module`
|
||||||
`sqlite3.VfsHelper` gets removed from the the `sqlite3` object after
|
implementations.
|
||||||
the library is finished initializing.
|
|
||||||
- **`sqlite3-vfs-opfs.c-pp.js`**\
|
- **`sqlite3-vfs-opfs.c-pp.js`**\
|
||||||
is an sqlite3 VFS implementation which supports Google Chrome's
|
is an sqlite3 VFS implementation which supports Google Chrome's
|
||||||
Origin-Private FileSystem (OPFS) as a storage layer to provide
|
Origin-Private FileSystem (OPFS) as a storage layer to provide
|
||||||
|
|||||||
@@ -24,6 +24,257 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
self.WhWasmUtilInstaller(wasm);
|
self.WhWasmUtilInstaller(wasm);
|
||||||
delete self.WhWasmUtilInstaller;
|
delete self.WhWasmUtilInstaller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Signatures for the WASM-exported C-side functions. Each entry
|
||||||
|
is an array with 2+ elements:
|
||||||
|
|
||||||
|
[ "c-side name",
|
||||||
|
"result type" (wasm.xWrap() syntax),
|
||||||
|
[arg types in xWrap() syntax]
|
||||||
|
// ^^^ this needn't strictly be an array: it can be subsequent
|
||||||
|
// elements instead: [x,y,z] is equivalent to x,y,z
|
||||||
|
]
|
||||||
|
|
||||||
|
Note that support for the API-specific data types in the
|
||||||
|
result/argument type strings gets plugged in at a later phase in
|
||||||
|
the API initialization process.
|
||||||
|
*/
|
||||||
|
wasm.bindingSignatures = [
|
||||||
|
// Please keep these sorted by function name!
|
||||||
|
["sqlite3_aggregate_context","void*", "sqlite3_context*", "int"],
|
||||||
|
["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*"
|
||||||
|
/* TODO: we should arguably write a custom wrapper which knows
|
||||||
|
how to handle Blob, TypedArrays, and JS strings. */
|
||||||
|
],
|
||||||
|
["sqlite3_bind_double","int", "sqlite3_stmt*", "int", "f64"],
|
||||||
|
["sqlite3_bind_int","int", "sqlite3_stmt*", "int", "int"],
|
||||||
|
["sqlite3_bind_null",undefined, "sqlite3_stmt*", "int"],
|
||||||
|
["sqlite3_bind_parameter_count", "int", "sqlite3_stmt*"],
|
||||||
|
["sqlite3_bind_parameter_index","int", "sqlite3_stmt*", "string"],
|
||||||
|
["sqlite3_bind_pointer", "int",
|
||||||
|
"sqlite3_stmt*", "int", "*", "string:static", "*"],
|
||||||
|
["sqlite3_bind_text","int", "sqlite3_stmt*", "int", "string", "int", "int"
|
||||||
|
/* We should arguably create a hand-written binding of
|
||||||
|
bind_text() which does more flexible text conversion, along
|
||||||
|
the lines of sqlite3_prepare_v3(). The slightly problematic
|
||||||
|
part is the final argument (text destructor). */
|
||||||
|
],
|
||||||
|
//["sqlite3_busy_handler","int", "sqlite3*", "*", "*"],
|
||||||
|
// ^^^^ TODO: custom binding which auto-converts JS function arg
|
||||||
|
// to a WASM function, noting that calling it multiple times
|
||||||
|
// would introduce a leak.
|
||||||
|
["sqlite3_busy_timeout","int", "sqlite3*", "int"],
|
||||||
|
["sqlite3_close_v2", "int", "sqlite3*"],
|
||||||
|
["sqlite3_changes", "int", "sqlite3*"],
|
||||||
|
["sqlite3_clear_bindings","int", "sqlite3_stmt*"],
|
||||||
|
["sqlite3_collation_needed", "int", "sqlite3*", "*", "*"/*=>v(ppis)*/],
|
||||||
|
["sqlite3_column_blob","*", "sqlite3_stmt*", "int"],
|
||||||
|
["sqlite3_column_bytes","int", "sqlite3_stmt*", "int"],
|
||||||
|
["sqlite3_column_count", "int", "sqlite3_stmt*"],
|
||||||
|
["sqlite3_column_double","f64", "sqlite3_stmt*", "int"],
|
||||||
|
["sqlite3_column_int","int", "sqlite3_stmt*", "int"],
|
||||||
|
["sqlite3_column_name","string", "sqlite3_stmt*", "int"],
|
||||||
|
["sqlite3_column_text","string", "sqlite3_stmt*", "int"],
|
||||||
|
["sqlite3_column_type","int", "sqlite3_stmt*", "int"],
|
||||||
|
["sqlite3_column_value","sqlite3_value*", "sqlite3_stmt*", "int"],
|
||||||
|
["sqlite3_compileoption_get", "string", "int"],
|
||||||
|
["sqlite3_compileoption_used", "int", "string"],
|
||||||
|
["sqlite3_complete", "int", "string:flexible"],
|
||||||
|
/* sqlite3_create_function(), sqlite3_create_function_v2(), and
|
||||||
|
sqlite3_create_window_function() use hand-written bindings to
|
||||||
|
simplify handling of their function-type arguments. */
|
||||||
|
/* sqlite3_create_collation() and sqlite3_create_collation_v2()
|
||||||
|
use hand-written bindings to simplify passing of the callback
|
||||||
|
function.
|
||||||
|
["sqlite3_create_collation", "int",
|
||||||
|
"sqlite3*", "string", "int",//SQLITE_UTF8 is the only legal value
|
||||||
|
"*", "*"],
|
||||||
|
["sqlite3_create_collation_v2", "int",
|
||||||
|
"sqlite3*", "string", "int",//SQLITE_UTF8 is the only legal value
|
||||||
|
"*", "*", "*"],
|
||||||
|
*/
|
||||||
|
["sqlite3_data_count", "int", "sqlite3_stmt*"],
|
||||||
|
["sqlite3_db_filename", "string", "sqlite3*", "string"],
|
||||||
|
["sqlite3_db_handle", "sqlite3*", "sqlite3_stmt*"],
|
||||||
|
["sqlite3_db_name", "string", "sqlite3*", "int"],
|
||||||
|
["sqlite3_db_status", "int", "sqlite3*", "int", "*", "*", "int"],
|
||||||
|
["sqlite3_deserialize", "int", "sqlite3*", "string", "*", "i64", "i64", "int"]
|
||||||
|
/* Careful! Short version: de/serialize() are problematic because they
|
||||||
|
might use a different allocator than the user for managing the
|
||||||
|
deserialized block. de/serialize() are ONLY safe to use with
|
||||||
|
sqlite3_malloc(), sqlite3_free(), and its 64-bit variants. */,
|
||||||
|
["sqlite3_errcode", "int", "sqlite3*"],
|
||||||
|
["sqlite3_errmsg", "string", "sqlite3*"],
|
||||||
|
["sqlite3_error_offset", "int", "sqlite3*"],
|
||||||
|
["sqlite3_errstr", "string", "int"],
|
||||||
|
/*["sqlite3_exec", "int", "sqlite3*", "string", "*", "*", "**"
|
||||||
|
Handled seperately to perform translation of the callback
|
||||||
|
into a WASM-usable one. ],*/
|
||||||
|
["sqlite3_expanded_sql", "string", "sqlite3_stmt*"],
|
||||||
|
["sqlite3_extended_errcode", "int", "sqlite3*"],
|
||||||
|
["sqlite3_extended_result_codes", "int", "sqlite3*", "int"],
|
||||||
|
["sqlite3_file_control", "int", "sqlite3*", "string", "int", "*"],
|
||||||
|
["sqlite3_finalize", "int", "sqlite3_stmt*"],
|
||||||
|
["sqlite3_free", undefined,"*"],
|
||||||
|
["sqlite3_get_auxdata", "*", "sqlite3_context*", "int"],
|
||||||
|
["sqlite3_initialize", undefined],
|
||||||
|
/*["sqlite3_interrupt", undefined, "sqlite3*"
|
||||||
|
^^^ we cannot actually currently support this because JS is
|
||||||
|
single-threaded and we don't have a portable way to access a DB
|
||||||
|
from 2 SharedWorkers concurrently. ],*/
|
||||||
|
["sqlite3_keyword_count", "int"],
|
||||||
|
["sqlite3_keyword_name", "int", ["int", "**", "*"]],
|
||||||
|
["sqlite3_keyword_check", "int", ["string", "int"]],
|
||||||
|
["sqlite3_libversion", "string"],
|
||||||
|
["sqlite3_libversion_number", "int"],
|
||||||
|
["sqlite3_limit", "int", ["sqlite3*", "int", "int"]],
|
||||||
|
["sqlite3_malloc", "*","int"],
|
||||||
|
["sqlite3_open", "int", "string", "*"],
|
||||||
|
["sqlite3_open_v2", "int", "string", "*", "int", "string"],
|
||||||
|
/* sqlite3_prepare_v2() and sqlite3_prepare_v3() are handled
|
||||||
|
separately due to us requiring two different sets of semantics
|
||||||
|
for those, depending on how their SQL argument is provided. */
|
||||||
|
/* sqlite3_randomness() uses a hand-written wrapper to extend
|
||||||
|
the range of supported argument types. */
|
||||||
|
[
|
||||||
|
"sqlite3_progress_handler", undefined, [
|
||||||
|
"sqlite3*", "int", new wasm.xWrap.FuncPtrAdapter({
|
||||||
|
name: 'xProgressHandler',
|
||||||
|
signature: 'i(p)',
|
||||||
|
bindScope: 'context',
|
||||||
|
contextKey: (argIndex,argv)=>'sqlite3@'+argv[0]
|
||||||
|
}), "*"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
["sqlite3_realloc", "*","*","int"],
|
||||||
|
["sqlite3_reset", "int", "sqlite3_stmt*"],
|
||||||
|
["sqlite3_result_blob", undefined, "sqlite3_context*", "*", "int", "*"],
|
||||||
|
["sqlite3_result_double", undefined, "sqlite3_context*", "f64"],
|
||||||
|
["sqlite3_result_error", undefined, "sqlite3_context*", "string", "int"],
|
||||||
|
["sqlite3_result_error_code", undefined, "sqlite3_context*", "int"],
|
||||||
|
["sqlite3_result_error_nomem", undefined, "sqlite3_context*"],
|
||||||
|
["sqlite3_result_error_toobig", undefined, "sqlite3_context*"],
|
||||||
|
["sqlite3_result_int", undefined, "sqlite3_context*", "int"],
|
||||||
|
["sqlite3_result_null", undefined, "sqlite3_context*"],
|
||||||
|
["sqlite3_result_pointer", undefined,
|
||||||
|
"sqlite3_context*", "*", "string:static", "*"],
|
||||||
|
["sqlite3_result_subtype", undefined, "sqlite3_value*", "int"],
|
||||||
|
["sqlite3_result_text", undefined, "sqlite3_context*", "string", "int", "*"],
|
||||||
|
["sqlite3_result_zeroblob", undefined, "sqlite3_context*", "int"],
|
||||||
|
["sqlite3_serialize","*", "sqlite3*", "string", "*", "int"],
|
||||||
|
["sqlite3_set_auxdata", undefined, "sqlite3_context*", "int", "*", "*"/* => v(*) */],
|
||||||
|
["sqlite3_shutdown", undefined],
|
||||||
|
["sqlite3_sourceid", "string"],
|
||||||
|
["sqlite3_sql", "string", "sqlite3_stmt*"],
|
||||||
|
["sqlite3_status", "int", "int", "*", "*", "int"],
|
||||||
|
["sqlite3_step", "int", "sqlite3_stmt*"],
|
||||||
|
["sqlite3_stmt_isexplain", "int", ["sqlite3_stmt*"]],
|
||||||
|
["sqlite3_stmt_readonly", "int", ["sqlite3_stmt*"]],
|
||||||
|
["sqlite3_stmt_status", "int", "sqlite3_stmt*", "int", "int"],
|
||||||
|
["sqlite3_strglob", "int", "string","string"],
|
||||||
|
["sqlite3_stricmp", "int", "string", "string"],
|
||||||
|
["sqlite3_strlike", "int", "string", "string","int"],
|
||||||
|
["sqlite3_strnicmp", "int", "string", "string", "int"],
|
||||||
|
["sqlite3_table_column_metadata", "int",
|
||||||
|
"sqlite3*", "string", "string", "string",
|
||||||
|
"**", "**", "*", "*", "*"],
|
||||||
|
["sqlite3_total_changes", "int", "sqlite3*"],
|
||||||
|
["sqlite3_trace_v2", "int", "sqlite3*", "int",
|
||||||
|
new wasm.xWrap.FuncPtrAdapter({
|
||||||
|
name: 'sqlite3_trace_v2::callback',
|
||||||
|
signature: 'i(ippp)',
|
||||||
|
contextKey: (argIndex, argv)=>'sqlite3@'+argv[0]
|
||||||
|
}), "*"],
|
||||||
|
["sqlite3_txn_state", "int", ["sqlite3*","string"]],
|
||||||
|
/* Note that sqlite3_uri_...() have very specific requirements for
|
||||||
|
their first C-string arguments, so we cannot perform any value
|
||||||
|
conversion on those. */
|
||||||
|
["sqlite3_uri_boolean", "int", "sqlite3_filename", "string", "int"],
|
||||||
|
["sqlite3_uri_key", "string", "sqlite3_filename", "int"],
|
||||||
|
["sqlite3_uri_parameter", "string", "sqlite3_filename", "string"],
|
||||||
|
["sqlite3_user_data","void*", "sqlite3_context*"],
|
||||||
|
["sqlite3_value_blob", "*", "sqlite3_value*"],
|
||||||
|
["sqlite3_value_bytes","int", "sqlite3_value*"],
|
||||||
|
["sqlite3_value_double","f64", "sqlite3_value*"],
|
||||||
|
["sqlite3_value_dup", "sqlite3_value*", "sqlite3_value*"],
|
||||||
|
["sqlite3_value_free", undefined, "sqlite3_value*"],
|
||||||
|
["sqlite3_value_frombind", "int", "sqlite3_value*"],
|
||||||
|
["sqlite3_value_int","int", "sqlite3_value*"],
|
||||||
|
["sqlite3_value_nochange", "int", "sqlite3_value*"],
|
||||||
|
["sqlite3_value_numeric_type", "int", "sqlite3_value*"],
|
||||||
|
["sqlite3_value_pointer", "*", "sqlite3_value*", "string:static"],
|
||||||
|
["sqlite3_value_subtype", "int", "sqlite3_value*"],
|
||||||
|
["sqlite3_value_text", "string", "sqlite3_value*"],
|
||||||
|
["sqlite3_value_type", "int", "sqlite3_value*"],
|
||||||
|
["sqlite3_vfs_find", "*", "string"],
|
||||||
|
["sqlite3_vfs_register", "int", "sqlite3_vfs*", "int"],
|
||||||
|
["sqlite3_vfs_unregister", "int", "sqlite3_vfs*"]
|
||||||
|
]/*wasm.bindingSignatures*/;
|
||||||
|
|
||||||
|
if(false && wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){
|
||||||
|
/* ^^^ "the problem" is that this is an option feature and the
|
||||||
|
build-time function-export list does not currently take
|
||||||
|
optional features into account. */
|
||||||
|
wasm.bindingSignatures.push(["sqlite3_normalized_sql", "string", "sqlite3_stmt*"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Functions which require BigInt (int64) support are separated from
|
||||||
|
the others because we need to conditionally bind them or apply
|
||||||
|
dummy impls, depending on the capabilities of the environment.
|
||||||
|
|
||||||
|
Note that not all of these functions directly require int64
|
||||||
|
but are only for use with APIs which require int64. For example,
|
||||||
|
the vtab-related functions.
|
||||||
|
*/
|
||||||
|
wasm.bindingSignatures.int64 = [
|
||||||
|
["sqlite3_bind_int64","int", ["sqlite3_stmt*", "int", "i64"]],
|
||||||
|
["sqlite3_changes64","i64", ["sqlite3*"]],
|
||||||
|
["sqlite3_column_int64","i64", ["sqlite3_stmt*", "int"]],
|
||||||
|
["sqlite3_create_module", "int",
|
||||||
|
["sqlite3*","string","sqlite3_module*","*"]],
|
||||||
|
["sqlite3_create_module_v2", "int",
|
||||||
|
["sqlite3*","string","sqlite3_module*","*","*"]],
|
||||||
|
["sqlite3_declare_vtab", "int", ["sqlite3*", "string:flexible"]],
|
||||||
|
["sqlite3_drop_modules", "int", ["sqlite3*", "**"]],
|
||||||
|
["sqlite3_last_insert_rowid", "i64", ["sqlite3*"]],
|
||||||
|
["sqlite3_malloc64", "*","i64"],
|
||||||
|
["sqlite3_msize", "i64", "*"],
|
||||||
|
["sqlite3_overload_function", "int", ["sqlite3*","string","int"]],
|
||||||
|
["sqlite3_realloc64", "*","*", "i64"],
|
||||||
|
["sqlite3_result_int64", undefined, "*", "i64"],
|
||||||
|
["sqlite3_result_zeroblob64", "int", "*", "i64"],
|
||||||
|
["sqlite3_set_last_insert_rowid", undefined, ["sqlite3*", "i64"]],
|
||||||
|
["sqlite3_status64", "int", "int", "*", "*", "int"],
|
||||||
|
["sqlite3_total_changes64", "i64", ["sqlite3*"]],
|
||||||
|
["sqlite3_uri_int64", "i64", ["sqlite3_filename", "string", "i64"]],
|
||||||
|
["sqlite3_value_int64","i64", "sqlite3_value*"],
|
||||||
|
["sqlite3_vtab_collation","string","sqlite3_index_info*","int"],
|
||||||
|
["sqlite3_vtab_distinct","int", "sqlite3_index_info*"],
|
||||||
|
["sqlite3_vtab_in","int", "sqlite3_index_info*", "int", "int"],
|
||||||
|
["sqlite3_vtab_in_first", "int", "sqlite3_value*", "**"],
|
||||||
|
["sqlite3_vtab_in_next", "int", "sqlite3_value*", "**"],
|
||||||
|
/*["sqlite3_vtab_config" is variadic and requires a hand-written
|
||||||
|
proxy.] */
|
||||||
|
["sqlite3_vtab_nochange","int", "sqlite3_context*"],
|
||||||
|
["sqlite3_vtab_on_conflict","int", "sqlite3*"],
|
||||||
|
["sqlite3_vtab_rhs_value","int", "sqlite3_index_info*", "int", "**"]
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
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 = [
|
||||||
|
["sqlite3_wasm_db_reset", "int", "sqlite3*"],
|
||||||
|
["sqlite3_wasm_db_vfs", "sqlite3_vfs*", "sqlite3*","string"],
|
||||||
|
["sqlite3_wasm_vfs_create_file", "int",
|
||||||
|
"sqlite3_vfs*","string","*", "int"],
|
||||||
|
["sqlite3_wasm_vfs_unlink", "int", "sqlite3_vfs*","string"]
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Install JS<->C struct bindings for the non-opaque struct types we
|
Install JS<->C struct bindings for the non-opaque struct types we
|
||||||
need... */
|
need... */
|
||||||
@@ -31,52 +282,77 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
heap: 0 ? wasm.memory : wasm.heap8u,
|
heap: 0 ? wasm.memory : wasm.heap8u,
|
||||||
alloc: wasm.alloc,
|
alloc: wasm.alloc,
|
||||||
dealloc: wasm.dealloc,
|
dealloc: wasm.dealloc,
|
||||||
functionTable: wasm.functionTable,
|
|
||||||
bigIntEnabled: wasm.bigIntEnabled,
|
bigIntEnabled: wasm.bigIntEnabled,
|
||||||
memberPrefix: '$'
|
memberPrefix: /* Never change this: this prefix is baked into any
|
||||||
|
amount of code and client-facing docs. */ '$'
|
||||||
});
|
});
|
||||||
delete self.Jaccwabyt;
|
delete self.Jaccwabyt;
|
||||||
|
|
||||||
if(0){
|
|
||||||
/* "The problem" is that the following isn't even remotely
|
|
||||||
type-safe. OTOH, nothing about WASM pointers is. */
|
|
||||||
const argPointer = wasm.xWrap.argAdapter('*');
|
|
||||||
wasm.xWrap.argAdapter('StructType', (v)=>{
|
|
||||||
if(v && v.constructor && v instanceof StructBinder.StructType){
|
|
||||||
v = v.pointer;
|
|
||||||
}
|
|
||||||
return wasm.isPtr(v)
|
|
||||||
? argPointer(v)
|
|
||||||
: toss("Invalid (object) type for StructType-type argument.");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
{/* Convert Arrays and certain TypedArrays to strings for
|
{/* Convert Arrays and certain TypedArrays to strings for
|
||||||
'flexible-string'-type arguments */
|
'string:flexible'-type arguments */
|
||||||
const xString = wasm.xWrap.argAdapter('string');
|
const xString = wasm.xWrap.argAdapter('string');
|
||||||
wasm.xWrap.argAdapter(
|
wasm.xWrap.argAdapter(
|
||||||
'flexible-string', (v)=>xString(util.flexibleString(v))
|
'string:flexible', (v)=>xString(util.flexibleString(v))
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
The 'string:static' argument adapter treats its argument as
|
||||||
|
either...
|
||||||
|
|
||||||
|
- WASM pointer: assumed to be a long-lived C-string which gets
|
||||||
|
returned as-is.
|
||||||
|
|
||||||
|
- Anything else: gets coerced to a JS string for use as a map
|
||||||
|
key. If a matching entry is found (as described next), it is
|
||||||
|
returned, else wasm.allocCString() is used to create a a new
|
||||||
|
string, map its pointer to (''+v) for the remainder of the
|
||||||
|
application's life, and returns that pointer value for this
|
||||||
|
call and all future calls which are passed a
|
||||||
|
string-equivalent argument.
|
||||||
|
|
||||||
|
Use case: sqlite3_bind_pointer() and sqlite3_result_pointer()
|
||||||
|
call for "a static string and preferably a string
|
||||||
|
literal". This converter is used to ensure that the string
|
||||||
|
value seen by those functions is long-lived and behaves as they
|
||||||
|
need it to.
|
||||||
|
*/
|
||||||
|
wasm.xWrap.argAdapter(
|
||||||
|
'string:static',
|
||||||
|
function(v){
|
||||||
|
if(wasm.isPtr(v)) return v;
|
||||||
|
v = ''+v;
|
||||||
|
let rc = this[v];
|
||||||
|
return rc || (rc = this[v] = wasm.allocCString(v));
|
||||||
|
}.bind(Object.create(null))
|
||||||
|
);
|
||||||
|
}/* special-case string-type argument conversions */
|
||||||
|
|
||||||
if(1){// WhWasmUtil.xWrap() bindings...
|
if(1){// WhWasmUtil.xWrap() bindings...
|
||||||
/**
|
/**
|
||||||
Add some descriptive xWrap() aliases for '*' intended to (A)
|
Add some descriptive xWrap() aliases for '*' intended to (A)
|
||||||
initially improve readability/correctness of capi.signatures
|
initially improve readability/correctness of capi.signatures
|
||||||
and (B) eventually perhaps provide automatic conversion from
|
and (B) provide automatic conversion from higher-level
|
||||||
higher-level representations, e.g. capi.sqlite3_vfs to
|
representations, e.g. capi.sqlite3_vfs to `sqlite3_vfs*` via
|
||||||
`sqlite3_vfs*` via capi.sqlite3_vfs.pointer.
|
capi.sqlite3_vfs.pointer.
|
||||||
*/
|
*/
|
||||||
const aPtr = wasm.xWrap.argAdapter('*');
|
const aPtr = wasm.xWrap.argAdapter('*');
|
||||||
|
const nilType = function(){};
|
||||||
wasm.xWrap.argAdapter('sqlite3_filename', aPtr)
|
wasm.xWrap.argAdapter('sqlite3_filename', aPtr)
|
||||||
('sqlite3_stmt*', aPtr)
|
|
||||||
('sqlite3_context*', aPtr)
|
('sqlite3_context*', aPtr)
|
||||||
('sqlite3_value*', aPtr)
|
('sqlite3_value*', aPtr)
|
||||||
('void*', aPtr)
|
('void*', aPtr)
|
||||||
('sqlite3*', (v)=>{
|
('sqlite3_stmt*', (v)=>
|
||||||
if(sqlite3.oo1 && v instanceof sqlite3.oo1.DB) v = v.pointer;
|
aPtr((v instanceof (sqlite3?.oo1?.Stmt || nilType))
|
||||||
return aPtr(v);
|
? v.pointer : v))
|
||||||
})
|
('sqlite3*', (v)=>
|
||||||
|
aPtr((v instanceof (sqlite3?.oo1?.DB || nilType))
|
||||||
|
? v.pointer : v))
|
||||||
|
('sqlite3_index_info*', (v)=>
|
||||||
|
aPtr((v instanceof (capi.sqlite3_index_info || nilType))
|
||||||
|
? v.pointer : v))
|
||||||
|
('sqlite3_module*', (v)=>
|
||||||
|
aPtr((v instanceof (capi.sqlite3_module || nilType))
|
||||||
|
? v.pointer : v))
|
||||||
/**
|
/**
|
||||||
`sqlite3_vfs*`:
|
`sqlite3_vfs*`:
|
||||||
|
|
||||||
@@ -87,21 +363,23 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
*/
|
*/
|
||||||
('sqlite3_vfs*', (v)=>{
|
('sqlite3_vfs*', (v)=>{
|
||||||
if('string'===typeof v){
|
if('string'===typeof v){
|
||||||
const x = capi.sqlite3_vfs_find(v);
|
|
||||||
/* A NULL sqlite3_vfs pointer will be treated as the default
|
/* A NULL sqlite3_vfs pointer will be treated as the default
|
||||||
VFS in many contexts. We specifically do not want that
|
VFS in many contexts. We specifically do not want that
|
||||||
behavior here. */
|
behavior here. */
|
||||||
if(!x) sqlite3.SQLite3Error.toss("Unknown sqlite3_vfs name:",v);
|
return capi.sqlite3_vfs_find(v)
|
||||||
return x;
|
|| sqlite3.SQLite3Error.toss("Unknown sqlite3_vfs name:",v);
|
||||||
}else if(v instanceof sqlite3.capi.sqlite3_vfs) v = v.pointer;
|
}
|
||||||
return aPtr(v);
|
return aPtr((v instanceof (capi.sqlite3_vfs || nilType))
|
||||||
|
? v.pointer : v);
|
||||||
});
|
});
|
||||||
|
|
||||||
wasm.xWrap.resultAdapter('sqlite3*', aPtr)
|
const rPtr = wasm.xWrap.resultAdapter('*');
|
||||||
('sqlite3_context*', aPtr)
|
wasm.xWrap.resultAdapter('sqlite3*', rPtr)
|
||||||
('sqlite3_stmt*', aPtr)
|
('sqlite3_context*', rPtr)
|
||||||
('sqlite3_vfs*', aPtr)
|
('sqlite3_stmt*', rPtr)
|
||||||
('void*', aPtr);
|
('sqlite3_value*', rPtr)
|
||||||
|
('sqlite3_vfs*', rPtr)
|
||||||
|
('void*', rPtr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Populate api object with sqlite3_...() by binding the "raw" wasm
|
Populate api object with sqlite3_...() by binding the "raw" wasm
|
||||||
@@ -127,35 +405,48 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
: fI64Disabled(e[0]);
|
: fI64Disabled(e[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There's no(?) need to expose bindingSignatures to clients,
|
/* There's no need to expose bindingSignatures to clients,
|
||||||
implicitly making it part of the public interface. */
|
implicitly making it part of the public interface. */
|
||||||
delete wasm.bindingSignatures;
|
delete wasm.bindingSignatures;
|
||||||
|
|
||||||
if(wasm.exports.sqlite3_wasm_db_error){
|
if(wasm.exports.sqlite3_wasm_db_error){
|
||||||
util.sqlite3_wasm_db_error = wasm.xWrap(
|
const __db_err = wasm.xWrap(
|
||||||
'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string'
|
'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string'
|
||||||
);
|
);
|
||||||
|
/**
|
||||||
|
Sets the given db's error state. Accepts:
|
||||||
|
|
||||||
|
- (sqlite3*, int code, string msg)
|
||||||
|
- (sqlite3*, Error e [,string msg = ''+e])
|
||||||
|
|
||||||
|
If passed a WasmAllocError, the message is ingored and the
|
||||||
|
result code is SQLITE_NOMEM. If passed any other Error type,
|
||||||
|
the result code defaults to SQLITE_ERROR unless the Error
|
||||||
|
object has a resultCode property, in which case that is used
|
||||||
|
(e.g. SQLite3Error has that). If passed a non-WasmAllocError
|
||||||
|
exception, the message string defaults to theError.message.
|
||||||
|
|
||||||
|
Returns the resulting code. Pass (pDb,0,0) to clear the error
|
||||||
|
state.
|
||||||
|
*/
|
||||||
|
util.sqlite3_wasm_db_error = function(pDb, resultCode, message){
|
||||||
|
if(resultCode instanceof sqlite3.WasmAllocError){
|
||||||
|
resultCode = capi.SQLITE_NOMEM;
|
||||||
|
message = 0 /*avoid allocating message string*/;
|
||||||
|
}else if(resultCode instanceof Error){
|
||||||
|
message = message || ''+resultCode;
|
||||||
|
resultCode = (resultCode.resultCode || capi.SQLITE_ERROR);
|
||||||
|
}
|
||||||
|
return __db_err(pDb, resultCode, message);
|
||||||
|
};
|
||||||
}else{
|
}else{
|
||||||
util.sqlite3_wasm_db_error = function(pDb,errCode,msg){
|
util.sqlite3_wasm_db_error = function(pDb,errCode,msg){
|
||||||
console.warn("sqlite3_wasm_db_error() is not exported.",arguments);
|
console.warn("sqlite3_wasm_db_error() is not exported.",arguments);
|
||||||
return errCode;
|
return errCode;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}/*xWrap() bindings*/;
|
}/*xWrap() bindings*/;
|
||||||
|
|
||||||
/**
|
|
||||||
When registering a VFS and its related components it may be
|
|
||||||
necessary to ensure that JS keeps a reference to them to keep
|
|
||||||
them from getting garbage collected. Simply pass each such value
|
|
||||||
to this function and a reference will be held to it for the life
|
|
||||||
of the app.
|
|
||||||
*/
|
|
||||||
capi.sqlite3_vfs_register.addReference = function f(...args){
|
|
||||||
if(!f._) f._ = [];
|
|
||||||
f._.push(...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Internal helper to assist in validating call argument counts in
|
Internal helper to assist in validating call argument counts in
|
||||||
the hand-written sqlite3_xyz() wrappers. We do this only for
|
the hand-written sqlite3_xyz() wrappers. We do this only for
|
||||||
@@ -167,34 +458,103 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
(1===n?"":'s')+".");
|
(1===n?"":'s')+".");
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
if(1){/* Bindings for sqlite3_create_collation() */
|
||||||
Helper for flexible-string conversions which require a
|
|
||||||
byte-length counterpart argument. Passed a value and its
|
const __collationContextKey = (argIndex,argv)=>{
|
||||||
ostensible length, this function returns [V,N], where V
|
return 'argv['+argIndex+']:sqlite3@'+argv[0]+
|
||||||
is either v or a transformed copy of v and N is either n,
|
':'+((/*THIS IS WRONG. We can't sensibly use a converted-to-C-string
|
||||||
-1, or the byte length of v (if it's a byte array).
|
address here and don't have access to the JS string (IF ANY)
|
||||||
*/
|
which the user passed in.*/
|
||||||
const __flexiString = function(v,n){
|
''+argv[1]
|
||||||
if('string'===typeof v){
|
).toLowerCase());
|
||||||
n = -1;
|
};
|
||||||
}else if(util.isSQLableTypedArray(v)){
|
const __ccv2 = wasm.xWrap(
|
||||||
n = v.byteLength;
|
'sqlite3_create_collation_v2', 'int',
|
||||||
v = util.typedArrayToString(v);
|
'sqlite3*','string','int','*','*','*'
|
||||||
}else if(Array.isArray(v)){
|
/* int(*xCompare)(void*,int,const void*,int,const void*) */
|
||||||
v = v.join("");
|
/* void(*xDestroy(void*) */
|
||||||
n = -1;
|
);
|
||||||
|
if(0){
|
||||||
|
// Problem: we cannot, due to xWrap() arg-passing limitations,
|
||||||
|
// currently easily/efficiently get a per-collation distinct
|
||||||
|
// key for purposes of creating distinct FuncPtrAdapter contexts.
|
||||||
|
new wasm.xWrap.FuncPtrAdapter({
|
||||||
|
/* int(*xCompare)(void*,int,const void*,int,const void*) */
|
||||||
|
name: 'xCompare',
|
||||||
|
signature: 'i(pipip)',
|
||||||
|
bindScope: 'context',
|
||||||
|
contextKey: __collationContextKey
|
||||||
|
}),
|
||||||
|
new wasm.xWrap.FuncPtrAdapter({
|
||||||
|
/* void(*xDestroy(void*) */
|
||||||
|
name: 'xDestroy',
|
||||||
|
signature: 'v(p)',
|
||||||
|
bindScope: 'context',
|
||||||
|
contextKey: __collationContextKey
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return [v, n];
|
|
||||||
};
|
/**
|
||||||
|
Works exactly like C's sqlite3_create_collation_v2() except that:
|
||||||
|
|
||||||
|
1) It accepts JS functions for its function-pointer arguments,
|
||||||
|
for which it will install WASM-bound proxies. The bindings
|
||||||
|
are "permanent," in that they will stay in the WASM environment
|
||||||
|
until it shuts down unless the client somehow finds and removes
|
||||||
|
them.
|
||||||
|
|
||||||
|
2) It returns capi.SQLITE_FORMAT if the 3rd argument is not
|
||||||
|
capi.SQLITE_UTF8. No other encodings are supported.
|
||||||
|
|
||||||
|
Returns 0 on success, non-0 on error, in which case the error
|
||||||
|
state of pDb (of type `sqlite3*` or argument-convertible to it)
|
||||||
|
may contain more information.
|
||||||
|
*/
|
||||||
|
capi.sqlite3_create_collation_v2 = function(pDb,zName,eTextRep,pArg,xCompare,xDestroy){
|
||||||
|
if(6!==arguments.length) return __dbArgcMismatch(pDb, 'sqlite3_create_collation_v2', 6);
|
||||||
|
else if(capi.SQLITE_UTF8!==eTextRep){
|
||||||
|
return util.sqlite3_wasm_db_error(
|
||||||
|
pDb, capi.SQLITE_FORMAT, "SQLITE_UTF8 is the only supported encoding."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let rc, pfCompare, pfDestroy;
|
||||||
|
try{
|
||||||
|
if(xCompare instanceof Function){
|
||||||
|
pfCompare = wasm.installFunction(xCompare, 'i(pipip)');
|
||||||
|
}
|
||||||
|
if(xDestroy instanceof Function){
|
||||||
|
pfDestroy = wasm.installFunction(xDestroy, 'v(p)');
|
||||||
|
}
|
||||||
|
rc = __ccv2(pDb, zName, eTextRep, pArg,
|
||||||
|
pfCompare || xCompare, pfDestroy || xDestroy);
|
||||||
|
}catch(e){
|
||||||
|
if(pfCompare) wasm.uninstallFunction(pfCompare);
|
||||||
|
if(pfDestroy) wasm.uninstallFunction(pfDestroy);
|
||||||
|
rc = util.sqlite3_wasm_db_error(pDb, e);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
};
|
||||||
|
|
||||||
|
capi.sqlite3_create_collation = (pDb,zName,eTextRep,pArg,xCompare)=>{
|
||||||
|
return (5===arguments.length)
|
||||||
|
? capi.sqlite3_create_collation_v2(pDb,zName,eTextRep,pArg,xCompare,0)
|
||||||
|
: __dbArgcMismatch(pDb, 'sqlite3_create_collation', 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
}/*sqlite3_create_collation() and friends*/
|
||||||
|
|
||||||
if(1){/* Special-case handling of sqlite3_exec() */
|
if(1){/* Special-case handling of sqlite3_exec() */
|
||||||
const __exec = wasm.xWrap("sqlite3_exec", "int",
|
const __exec = wasm.xWrap("sqlite3_exec", "int",
|
||||||
["sqlite3*", "flexible-string", "*", "*", "**"]);
|
["sqlite3*", "string:flexible",
|
||||||
|
new wasm.xWrap.FuncPtrAdapter({
|
||||||
|
signature: 'i(pipp)',
|
||||||
|
bindScope: 'transient'
|
||||||
|
}), "*", "**"]);
|
||||||
/* Documented in the api object's initializer. */
|
/* Documented in the api object's initializer. */
|
||||||
capi.sqlite3_exec = function f(pDb, sql, callback, pVoid, pErrMsg){
|
capi.sqlite3_exec = function f(pDb, sql, callback, pVoid, pErrMsg){
|
||||||
if(f.length!==arguments.length){
|
if(f.length!==arguments.length){
|
||||||
return __dbArgcMismatch(pDb,"sqlite3_exec",f.length);
|
return __dbArgcMismatch(pDb,"sqlite3_exec",f.length);
|
||||||
}else if('function' !== typeof callback){
|
}else if(!(callback instanceof Function)){
|
||||||
return __exec(pDb, sql, callback, pVoid, pErrMsg);
|
return __exec(pDb, sql, callback, pVoid, pErrMsg);
|
||||||
}
|
}
|
||||||
/* Wrap the callback in a WASM-bound function and convert the callback's
|
/* Wrap the callback in a WASM-bound function and convert the callback's
|
||||||
@@ -204,8 +564,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
try {
|
try {
|
||||||
let aVals = [], aNames = [], i = 0, offset = 0;
|
let aVals = [], aNames = [], i = 0, offset = 0;
|
||||||
for( ; i < nCols; offset += (wasm.ptrSizeof * ++i) ){
|
for( ; i < nCols; offset += (wasm.ptrSizeof * ++i) ){
|
||||||
aVals.push( wasm.cstringToJs(wasm.getPtrValue(pColVals + offset)) );
|
aVals.push( wasm.cstrToJs(wasm.peekPtr(pColVals + offset)) );
|
||||||
aNames.push( wasm.cstringToJs(wasm.getPtrValue(pColNames + offset)) );
|
aNames.push( wasm.cstrToJs(wasm.peekPtr(pColNames + offset)) );
|
||||||
}
|
}
|
||||||
rc = callback(pVoid, nCols, aVals, aNames) | 0;
|
rc = callback(pVoid, nCols, aVals, aNames) | 0;
|
||||||
/* The first 2 args of the callback are useless for JS but
|
/* The first 2 args of the callback are useless for JS but
|
||||||
@@ -219,15 +579,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
};
|
};
|
||||||
let pFunc, rc;
|
let rc;
|
||||||
try{
|
try{
|
||||||
pFunc = wasm.installFunction("ipipp", cbwrap);
|
rc = __exec(pDb, sql, cbwrap, pVoid, pErrMsg);
|
||||||
rc = __exec(pDb, sql, pFunc, pVoid, pErrMsg);
|
|
||||||
}catch(e){
|
}catch(e){
|
||||||
rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
|
rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
|
||||||
"Error running exec(): "+e.message);
|
"Error running exec(): "+e);
|
||||||
}finally{
|
|
||||||
if(pFunc) wasm.uninstallFunction(pFunc);
|
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
};
|
};
|
||||||
@@ -249,132 +606,31 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
"*"/*xInverse*/, "*"/*xDestroy*/]
|
"*"/*xInverse*/, "*"/*xDestroy*/]
|
||||||
);
|
);
|
||||||
|
|
||||||
const __udfSetResult = function(pCtx, val){
|
|
||||||
//console.warn("udfSetResult",typeof val, val);
|
|
||||||
switch(typeof val) {
|
|
||||||
case 'undefined':
|
|
||||||
/* Assume that the client already called sqlite3_result_xxx(). */
|
|
||||||
break;
|
|
||||||
case 'boolean':
|
|
||||||
capi.sqlite3_result_int(pCtx, val ? 1 : 0);
|
|
||||||
break;
|
|
||||||
case 'bigint':
|
|
||||||
if(wasm.bigIntEnabled){
|
|
||||||
if(util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val);
|
|
||||||
else toss3("BigInt value",val.toString(),"is too BigInt for int64.");
|
|
||||||
}else if(util.bigIntFits32(val)){
|
|
||||||
capi.sqlite3_result_int(pCtx, Number(val));
|
|
||||||
}else if(util.bigIntFitsDouble(val)){
|
|
||||||
capi.sqlite3_result_double(pCtx, Number(val));
|
|
||||||
}else{
|
|
||||||
toss3("BigInt value",val.toString(),"is too BigInt.");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'number': {
|
|
||||||
(util.isInt32(val)
|
|
||||||
? capi.sqlite3_result_int
|
|
||||||
: capi.sqlite3_result_double)(pCtx, val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'string':
|
|
||||||
capi.sqlite3_result_text(pCtx, val, -1, capi.SQLITE_TRANSIENT);
|
|
||||||
break;
|
|
||||||
case 'object':
|
|
||||||
if(null===val/*yes, typeof null === 'object'*/) {
|
|
||||||
capi.sqlite3_result_null(pCtx);
|
|
||||||
break;
|
|
||||||
}else if(util.isBindableTypedArray(val)){
|
|
||||||
const pBlob = wasm.allocFromTypedArray(val);
|
|
||||||
capi.sqlite3_result_blob(
|
|
||||||
pCtx, pBlob, val.byteLength,
|
|
||||||
wasm.exports[sqlite3.config.deallocExportName]
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// else fall through
|
|
||||||
default:
|
|
||||||
toss3("Don't not how to handle this UDF result value:",(typeof val), val);
|
|
||||||
};
|
|
||||||
}/*__udfSetResult()*/;
|
|
||||||
|
|
||||||
const __udfConvertArgs = function(argc, pArgv){
|
|
||||||
let i, pVal, valType, arg;
|
|
||||||
const tgt = [];
|
|
||||||
for(i = 0; i < argc; ++i){
|
|
||||||
pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i));
|
|
||||||
/**
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
valType = capi.sqlite3_value_type(pVal);
|
|
||||||
switch(valType){
|
|
||||||
case capi.SQLITE_INTEGER:
|
|
||||||
if(wasm.bigIntEnabled){
|
|
||||||
arg = capi.sqlite3_value_int64(pVal);
|
|
||||||
if(util.bigIntFitsDouble(arg)) arg = Number(arg);
|
|
||||||
}
|
|
||||||
else arg = capi.sqlite3_value_double(pVal)/*yes, double, for larger integers*/;
|
|
||||||
break;
|
|
||||||
case capi.SQLITE_FLOAT:
|
|
||||||
arg = capi.sqlite3_value_double(pVal);
|
|
||||||
break;
|
|
||||||
case capi.SQLITE_TEXT:
|
|
||||||
arg = capi.sqlite3_value_text(pVal);
|
|
||||||
break;
|
|
||||||
case capi.SQLITE_BLOB:{
|
|
||||||
const n = capi.sqlite3_value_bytes(pVal);
|
|
||||||
const pBlob = capi.sqlite3_value_blob(pVal);
|
|
||||||
if(n && !pBlob) sqlite3.WasmAllocError.toss(
|
|
||||||
"Cannot allocate memory for blob argument of",n,"byte(s)"
|
|
||||||
);
|
|
||||||
arg = n ? wasm.heap8u().slice(pBlob, pBlob + Number(n)) : null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case capi.SQLITE_NULL:
|
|
||||||
arg = null; break;
|
|
||||||
default:
|
|
||||||
toss3("Unhandled sqlite3_value_type()",valType,
|
|
||||||
"is possibly indicative of incorrect",
|
|
||||||
"pointer size assumption.");
|
|
||||||
}
|
|
||||||
tgt.push(arg);
|
|
||||||
}
|
|
||||||
return tgt;
|
|
||||||
}/*__udfConvertArgs()*/;
|
|
||||||
|
|
||||||
const __udfSetError = (pCtx, e)=>{
|
|
||||||
if(e instanceof sqlite3.WasmAllocError){
|
|
||||||
capi.sqlite3_result_error_nomem(pCtx);
|
|
||||||
}else{
|
|
||||||
const msg = ('string'===typeof e) ? e : e.message;
|
|
||||||
capi.sqlite3_result_error(pCtx, msg, -1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const __xFunc = function(callback){
|
const __xFunc = function(callback){
|
||||||
return function(pCtx, argc, pArgv){
|
return function(pCtx, argc, pArgv){
|
||||||
try{ __udfSetResult(pCtx, callback(pCtx, ...__udfConvertArgs(argc, pArgv))) }
|
try{
|
||||||
catch(e){
|
capi.sqlite3_result_js(
|
||||||
|
pCtx,
|
||||||
|
callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv))
|
||||||
|
);
|
||||||
|
}catch(e){
|
||||||
//console.error('xFunc() caught:',e);
|
//console.error('xFunc() caught:',e);
|
||||||
__udfSetError(pCtx, e);
|
capi.sqlite3_result_error_js(pCtx, e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const __xInverseAndStep = function(callback){
|
const __xInverseAndStep = function(callback){
|
||||||
return function(pCtx, argc, pArgv){
|
return function(pCtx, argc, pArgv){
|
||||||
try{ callback(pCtx, ...__udfConvertArgs(argc, pArgv)) }
|
try{ callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)) }
|
||||||
catch(e){ __udfSetError(pCtx, e) }
|
catch(e){ capi.sqlite3_result_error_js(pCtx, e) }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const __xFinalAndValue = function(callback){
|
const __xFinalAndValue = function(callback){
|
||||||
return function(pCtx){
|
return function(pCtx){
|
||||||
try{ __udfSetResult(pCtx, callback(pCtx)) }
|
try{ capi.sqlite3_result_js(pCtx, callback(pCtx)) }
|
||||||
catch(e){ __udfSetError(pCtx, e) }
|
catch(e){ capi.sqlite3_result_error_js(pCtx, e) }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -480,66 +736,53 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
return rc;
|
return rc;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
A helper for UDFs implemented in JS and bound to WASM by the
|
A _deprecated_ alias for capi.sqlite3_result_js() which
|
||||||
client. Given a JS value, udfSetResult(pCtx,X) calls one of the
|
predates the addition of that function in the public API.
|
||||||
sqlite3_result_xyz(pCtx,...) routines, depending on X's data
|
|
||||||
type:
|
|
||||||
|
|
||||||
- `null`: sqlite3_result_null()
|
|
||||||
- `boolean`: sqlite3_result_int()
|
|
||||||
- `number`: sqlite3_result_int() or sqlite3_result_double()
|
|
||||||
- `string`: sqlite3_result_text()
|
|
||||||
- Uint8Array or Int8Array: sqlite3_result_blob()
|
|
||||||
- `undefined`: indicates that the UDF called one of the
|
|
||||||
`sqlite3_result_xyz()` routines on its own, making this
|
|
||||||
function a no-op. Results are _undefined_ if this function is
|
|
||||||
passed the `undefined` value but did _not_ call one of the
|
|
||||||
`sqlite3_result_xyz()` routines.
|
|
||||||
|
|
||||||
Anything else triggers sqlite3_result_error().
|
|
||||||
*/
|
*/
|
||||||
capi.sqlite3_create_function_v2.udfSetResult =
|
capi.sqlite3_create_function_v2.udfSetResult =
|
||||||
capi.sqlite3_create_function.udfSetResult =
|
capi.sqlite3_create_function.udfSetResult =
|
||||||
capi.sqlite3_create_window_function.udfSetResult = __udfSetResult;
|
capi.sqlite3_create_window_function.udfSetResult = capi.sqlite3_result_js;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A helper for UDFs implemented in JS and bound to WASM by the
|
A _deprecated_ alias for capi.sqlite3_values_to_js() which
|
||||||
client. When passed the
|
predates the addition of that function in the public API.
|
||||||
(argc,argv) values from the UDF-related functions which receive
|
|
||||||
them (xFunc, xStep, xInverse), it creates a JS array
|
|
||||||
representing those arguments, converting each to JS in a manner
|
|
||||||
appropriate to its data type: numeric, text, blob
|
|
||||||
(Uint8Array), or null.
|
|
||||||
|
|
||||||
Results are undefined if it's passed anything other than those
|
|
||||||
two arguments from those specific contexts.
|
|
||||||
|
|
||||||
Thus an argc of 4 will result in a length-4 array containing
|
|
||||||
the converted values from the corresponding argv.
|
|
||||||
|
|
||||||
The conversion will throw only on allocation error or an internal
|
|
||||||
error.
|
|
||||||
*/
|
*/
|
||||||
capi.sqlite3_create_function_v2.udfConvertArgs =
|
capi.sqlite3_create_function_v2.udfConvertArgs =
|
||||||
capi.sqlite3_create_function.udfConvertArgs =
|
capi.sqlite3_create_function.udfConvertArgs =
|
||||||
capi.sqlite3_create_window_function.udfConvertArgs = __udfConvertArgs;
|
capi.sqlite3_create_window_function.udfConvertArgs = capi.sqlite3_values_to_js;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A helper for UDFs implemented in JS and bound to WASM by the
|
A _deprecated_ alias for capi.sqlite3_result_error_js() which
|
||||||
client. It expects to be a passed `(sqlite3_context*, Error)`
|
predates the addition of that function in the public API.
|
||||||
(an exception object or message string). And it sets the
|
|
||||||
current UDF's result to sqlite3_result_error_nomem() or
|
|
||||||
sqlite3_result_error(), depending on whether the 2nd argument
|
|
||||||
is a sqlite3.WasmAllocError object or not.
|
|
||||||
*/
|
*/
|
||||||
capi.sqlite3_create_function_v2.udfSetError =
|
capi.sqlite3_create_function_v2.udfSetError =
|
||||||
capi.sqlite3_create_function.udfSetError =
|
capi.sqlite3_create_function.udfSetError =
|
||||||
capi.sqlite3_create_window_function.udfSetError = __udfSetError;
|
capi.sqlite3_create_window_function.udfSetError = capi.sqlite3_result_error_js;
|
||||||
|
|
||||||
}/*sqlite3_create_function_v2() and sqlite3_create_window_function() proxies*/;
|
}/*sqlite3_create_function_v2() and sqlite3_create_window_function() proxies*/;
|
||||||
|
|
||||||
if(1){/* Special-case handling of sqlite3_prepare_v2() and
|
if(1){/* Special-case handling of sqlite3_prepare_v2() and
|
||||||
sqlite3_prepare_v3() */
|
sqlite3_prepare_v3() */
|
||||||
|
/**
|
||||||
|
Helper for string:flexible conversions which require a
|
||||||
|
byte-length counterpart argument. Passed a value and its
|
||||||
|
ostensible length, this function returns [V,N], where V
|
||||||
|
is either v or a transformed copy of v and N is either n,
|
||||||
|
-1, or the byte length of v (if it's a byte array).
|
||||||
|
*/
|
||||||
|
const __flexiString = (v,n)=>{
|
||||||
|
if('string'===typeof v){
|
||||||
|
n = -1;
|
||||||
|
}else if(util.isSQLableTypedArray(v)){
|
||||||
|
n = v.byteLength;
|
||||||
|
v = util.typedArrayToString(v);
|
||||||
|
}else if(Array.isArray(v)){
|
||||||
|
v = v.join("");
|
||||||
|
n = -1;
|
||||||
|
}
|
||||||
|
return [v, n];
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Scope-local holder of the two impls of sqlite3_prepare_v2/v3().
|
Scope-local holder of the two impls of sqlite3_prepare_v2/v3().
|
||||||
*/
|
*/
|
||||||
@@ -570,7 +813,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
"int", ["sqlite3*", "*", "int", "int",
|
"int", ["sqlite3*", "*", "int", "int",
|
||||||
"**", "**"]);
|
"**", "**"]);
|
||||||
|
|
||||||
/* Documented in the api object's initializer. */
|
/* Documented in the capi object's initializer. */
|
||||||
capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){
|
capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){
|
||||||
if(f.length!==arguments.length){
|
if(f.length!==arguments.length){
|
||||||
return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",f.length);
|
return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",f.length);
|
||||||
@@ -587,7 +830,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Documented in the api object's initializer. */
|
/* Documented in the capi object's initializer. */
|
||||||
capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail){
|
capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail){
|
||||||
return (f.length===arguments.length)
|
return (f.length===arguments.length)
|
||||||
? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail)
|
? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail)
|
||||||
@@ -601,15 +844,22 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
toss("Maintenance required: increase sqlite3_wasm_enum_json()'s",
|
toss("Maintenance required: increase sqlite3_wasm_enum_json()'s",
|
||||||
"static buffer size!");
|
"static buffer size!");
|
||||||
}
|
}
|
||||||
wasm.ctype = JSON.parse(wasm.cstringToJs(cJson));
|
wasm.ctype = JSON.parse(wasm.cstrToJs(cJson));
|
||||||
//console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
|
//console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
|
||||||
for(const t of ['access', 'blobFinalizers', 'dataTypes',
|
const defineGroups = ['access', 'authorizer',
|
||||||
'encodings', 'fcntl', 'flock', 'ioCap',
|
'blobFinalizers', 'dataTypes',
|
||||||
'limits',
|
'dbConfig', 'dbStatus',
|
||||||
'openFlags', 'prepareFlags', 'resultCodes',
|
'encodings', 'fcntl', 'flock', 'ioCap',
|
||||||
'serialize', 'syncFlags', 'trace', 'udfFlags',
|
'limits', 'openFlags',
|
||||||
'version'
|
'prepareFlags', 'resultCodes',
|
||||||
]){
|
'serialize', 'sqlite3Status',
|
||||||
|
'stmtStatus', 'syncFlags',
|
||||||
|
'trace', 'txnState', 'udfFlags',
|
||||||
|
'version' ];
|
||||||
|
if(wasm.bigIntEnabled){
|
||||||
|
defineGroups.push('vtab');
|
||||||
|
}
|
||||||
|
for(const t of defineGroups){
|
||||||
for(const e of Object.entries(wasm.ctype[t])){
|
for(const e of Object.entries(wasm.ctype[t])){
|
||||||
// ^^^ [k,v] there triggers a buggy code transformation via
|
// ^^^ [k,v] there triggers a buggy code transformation via
|
||||||
// one of the Emscripten-driven optimizers.
|
// one of the Emscripten-driven optimizers.
|
||||||
@@ -629,19 +879,37 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
capi.sqlite3_js_rc_str = (rc)=>__rcMap[rc];
|
capi.sqlite3_js_rc_str = (rc)=>__rcMap[rc];
|
||||||
/* Bind all registered C-side structs... */
|
/* Bind all registered C-side structs... */
|
||||||
const notThese = Object.assign(Object.create(null),{
|
const notThese = Object.assign(Object.create(null),{
|
||||||
// Structs NOT to register
|
// For each struct to NOT register, map its name to true:
|
||||||
WasmTestStruct: true
|
WasmTestStruct: true,
|
||||||
|
/* We unregister the kvvfs VFS from Worker threads below. */
|
||||||
|
sqlite3_kvvfs_methods: !util.isUIThread(),
|
||||||
|
/* sqlite3_index_info and friends require int64: */
|
||||||
|
sqlite3_index_info: !wasm.bigIntEnabled,
|
||||||
|
sqlite3_index_constraint: !wasm.bigIntEnabled,
|
||||||
|
sqlite3_index_orderby: !wasm.bigIntEnabled,
|
||||||
|
sqlite3_index_constraint_usage: !wasm.bigIntEnabled
|
||||||
});
|
});
|
||||||
if(!util.isUIThread()){
|
|
||||||
/* We remove the kvvfs VFS from Worker threads below. */
|
|
||||||
notThese.sqlite3_kvvfs_methods = true;
|
|
||||||
}
|
|
||||||
for(const s of wasm.ctype.structs){
|
for(const s of wasm.ctype.structs){
|
||||||
if(!notThese[s.name]){
|
if(!notThese[s.name]){
|
||||||
capi[s.name] = sqlite3.StructBinder(s);
|
capi[s.name] = sqlite3.StructBinder(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}/*end C constant imports*/
|
if(capi.sqlite3_index_info){
|
||||||
|
/* Move these inner structs into sqlite3_index_info. Binding
|
||||||
|
** them to WASM requires that we create global-scope structs to
|
||||||
|
** model them with, but those are no longer needed after we've
|
||||||
|
** passed them to StructBinder. */
|
||||||
|
for(const k of ['sqlite3_index_constraint',
|
||||||
|
'sqlite3_index_orderby',
|
||||||
|
'sqlite3_index_constraint_usage']){
|
||||||
|
capi.sqlite3_index_info[k] = capi[k];
|
||||||
|
delete capi[k];
|
||||||
|
}
|
||||||
|
capi.sqlite3_vtab_config =
|
||||||
|
(pDb, op, arg=0)=>wasm.exports.sqlite3_wasm_vtab_config(
|
||||||
|
wasm.xWrap.argAdapter('sqlite3*')(pDb), op, arg);
|
||||||
|
}/* end vtab-related setup */
|
||||||
|
}/*end C constant and struct imports*/
|
||||||
|
|
||||||
const pKvvfs = capi.sqlite3_vfs_find("kvvfs");
|
const pKvvfs = capi.sqlite3_vfs_find("kvvfs");
|
||||||
if( pKvvfs ){/* kvvfs-specific glue */
|
if( pKvvfs ){/* kvvfs-specific glue */
|
||||||
@@ -652,11 +920,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
delete capi.sqlite3_kvvfs_methods;
|
delete capi.sqlite3_kvvfs_methods;
|
||||||
|
|
||||||
const kvvfsMakeKey = wasm.exports.sqlite3_wasm_kvvfsMakeKeyOnPstack,
|
const kvvfsMakeKey = wasm.exports.sqlite3_wasm_kvvfsMakeKeyOnPstack,
|
||||||
pstack = wasm.pstack,
|
pstack = wasm.pstack;
|
||||||
pAllocRaw = wasm.exports.sqlite3_wasm_pstack_alloc;
|
|
||||||
|
|
||||||
const kvvfsStorage = (zClass)=>
|
const kvvfsStorage = (zClass)=>
|
||||||
((115/*=='s'*/===wasm.getMemValue(zClass))
|
((115/*=='s'*/===wasm.peek(zClass))
|
||||||
? sessionStorage : localStorage);
|
? sessionStorage : localStorage);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -672,7 +939,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
try {
|
try {
|
||||||
const zXKey = kvvfsMakeKey(zClass,zKey);
|
const zXKey = kvvfsMakeKey(zClass,zKey);
|
||||||
if(!zXKey) return -3/*OOM*/;
|
if(!zXKey) return -3/*OOM*/;
|
||||||
const jKey = wasm.cstringToJs(zXKey);
|
const jKey = wasm.cstrToJs(zXKey);
|
||||||
const jV = kvvfsStorage(zClass).getItem(jKey);
|
const jV = kvvfsStorage(zClass).getItem(jKey);
|
||||||
if(!jV) return -1;
|
if(!jV) return -1;
|
||||||
const nV = jV.length /* Note that we are relying 100% on v being
|
const nV = jV.length /* Note that we are relying 100% on v being
|
||||||
@@ -680,13 +947,13 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
C-string's byte length. */;
|
C-string's byte length. */;
|
||||||
if(nBuf<=0) return nV;
|
if(nBuf<=0) return nV;
|
||||||
else if(1===nBuf){
|
else if(1===nBuf){
|
||||||
wasm.setMemValue(zBuf, 0);
|
wasm.poke(zBuf, 0);
|
||||||
return nV;
|
return nV;
|
||||||
}
|
}
|
||||||
const zV = wasm.scopedAllocCString(jV);
|
const zV = wasm.scopedAllocCString(jV);
|
||||||
if(nBuf > nV + 1) nBuf = nV + 1;
|
if(nBuf > nV + 1) nBuf = nV + 1;
|
||||||
wasm.heap8u().copyWithin(zBuf, zV, zV + nBuf - 1);
|
wasm.heap8u().copyWithin(zBuf, zV, zV + nBuf - 1);
|
||||||
wasm.setMemValue(zBuf + nBuf - 1, 0);
|
wasm.poke(zBuf + nBuf - 1, 0);
|
||||||
return nBuf - 1;
|
return nBuf - 1;
|
||||||
}catch(e){
|
}catch(e){
|
||||||
console.error("kvstorageRead()",e);
|
console.error("kvstorageRead()",e);
|
||||||
@@ -701,8 +968,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
try {
|
try {
|
||||||
const zXKey = kvvfsMakeKey(zClass,zKey);
|
const zXKey = kvvfsMakeKey(zClass,zKey);
|
||||||
if(!zXKey) return 1/*OOM*/;
|
if(!zXKey) return 1/*OOM*/;
|
||||||
const jKey = wasm.cstringToJs(zXKey);
|
const jKey = wasm.cstrToJs(zXKey);
|
||||||
kvvfsStorage(zClass).setItem(jKey, wasm.cstringToJs(zData));
|
kvvfsStorage(zClass).setItem(jKey, wasm.cstrToJs(zData));
|
||||||
return 0;
|
return 0;
|
||||||
}catch(e){
|
}catch(e){
|
||||||
console.error("kvstorageWrite()",e);
|
console.error("kvstorageWrite()",e);
|
||||||
@@ -716,7 +983,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
try {
|
try {
|
||||||
const zXKey = kvvfsMakeKey(zClass,zKey);
|
const zXKey = kvvfsMakeKey(zClass,zKey);
|
||||||
if(!zXKey) return 1/*OOM*/;
|
if(!zXKey) return 1/*OOM*/;
|
||||||
kvvfsStorage(zClass).removeItem(wasm.cstringToJs(zXKey));
|
kvvfsStorage(zClass).removeItem(wasm.cstrToJs(zXKey));
|
||||||
return 0;
|
return 0;
|
||||||
}catch(e){
|
}catch(e){
|
||||||
console.error("kvstorageDelete()",e);
|
console.error("kvstorageDelete()",e);
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
if(capi.SQLITE_TRACE_STMT===t){
|
if(capi.SQLITE_TRACE_STMT===t){
|
||||||
// x == SQL, p == sqlite3_stmt*
|
// x == SQL, p == sqlite3_stmt*
|
||||||
console.log("SQL TRACE #"+(++this.counter),
|
console.log("SQL TRACE #"+(++this.counter),
|
||||||
wasm.cstringToJs(x));
|
wasm.cstrToJs(x));
|
||||||
}
|
}
|
||||||
}.bind({counter: 0}));
|
}.bind({counter: 0}));
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
console.error("Invalid DB ctor args",opt,arguments);
|
console.error("Invalid DB ctor args",opt,arguments);
|
||||||
toss3("Invalid arguments for DB constructor.");
|
toss3("Invalid arguments for DB constructor.");
|
||||||
}
|
}
|
||||||
let fnJs = ('number'===typeof fn) ? wasm.cstringToJs(fn) : fn;
|
let fnJs = ('number'===typeof fn) ? wasm.cstrToJs(fn) : fn;
|
||||||
const vfsCheck = ctor._name2vfs[fnJs];
|
const vfsCheck = ctor._name2vfs[fnJs];
|
||||||
if(vfsCheck){
|
if(vfsCheck){
|
||||||
vfsName = vfsCheck.vfs;
|
vfsName = vfsCheck.vfs;
|
||||||
@@ -156,7 +156,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
try {
|
try {
|
||||||
const pPtr = wasm.pstack.allocPtr() /* output (sqlite3**) arg */;
|
const pPtr = wasm.pstack.allocPtr() /* output (sqlite3**) arg */;
|
||||||
let rc = capi.sqlite3_open_v2(fn, pPtr, oflags, vfsName || 0);
|
let rc = capi.sqlite3_open_v2(fn, pPtr, oflags, vfsName || 0);
|
||||||
pDb = wasm.getPtrValue(pPtr);
|
pDb = wasm.peekPtr(pPtr);
|
||||||
checkSqlite3Rc(pDb, rc);
|
checkSqlite3Rc(pDb, rc);
|
||||||
if(flagsStr.indexOf('t')>=0){
|
if(flagsStr.indexOf('t')>=0){
|
||||||
capi.sqlite3_trace_v2(pDb, capi.SQLITE_TRACE_STMT,
|
capi.sqlite3_trace_v2(pDb, capi.SQLITE_TRACE_STMT,
|
||||||
@@ -192,13 +192,13 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
/**
|
/**
|
||||||
Sets SQL which should be exec()'d on a DB instance after it is
|
Sets SQL which should be exec()'d on a DB instance after it is
|
||||||
opened with the given VFS pointer. The SQL may be any type
|
opened with the given VFS pointer. The SQL may be any type
|
||||||
supported by the "flexible-string" function argument
|
supported by the "string:flexible" function argument conversion.
|
||||||
conversion. Alternately, the 2nd argument may be a function, in
|
Alternately, the 2nd argument may be a function, in which case it
|
||||||
which case it is called with (theOo1DbObject,sqlite3Namespace) at
|
is called with (theOo1DbObject,sqlite3Namespace) at the end of
|
||||||
the end of the DB() constructor. The function must throw on
|
the DB() constructor. The function must throw on error, in which
|
||||||
error, in which case the db is closed and the exception is
|
case the db is closed and the exception is propagated. This
|
||||||
propagated. This function is intended only for use by DB
|
function is intended only for use by DB subclasses or sqlite3_vfs
|
||||||
subclasses or sqlite3_vfs implementations.
|
implementations.
|
||||||
*/
|
*/
|
||||||
dbCtorHelper.setVfsPostOpenSql = function(pVfs, sql){
|
dbCtorHelper.setVfsPostOpenSql = function(pVfs, sql){
|
||||||
__vfsPostOpenSql[pVfs] = sql;
|
__vfsPostOpenSql[pVfs] = sql;
|
||||||
@@ -473,6 +473,15 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
return rc;
|
return rc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Internal impl of the DB.selectArrays() and
|
||||||
|
selectObjects() methods.
|
||||||
|
*/
|
||||||
|
const __selectAll =
|
||||||
|
(db, sql, bind, rowMode)=>db.exec({
|
||||||
|
sql, bind, rowMode, returnValue: 'resultRows'
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Expects to be given a DB instance or an `sqlite3*` pointer (may
|
Expects to be given a DB instance or an `sqlite3*` pointer (may
|
||||||
be null) and an sqlite3 API result code. If the result code is
|
be null) and an sqlite3 API result code. If the result code is
|
||||||
@@ -511,10 +520,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
db is closed but before auxiliary state like this.filename is
|
db is closed but before auxiliary state like this.filename is
|
||||||
cleared.
|
cleared.
|
||||||
|
|
||||||
Both onclose handlers are passed this object. If this db is not
|
Both onclose handlers are passed this object, with the onclose
|
||||||
opened, neither of the handlers are called. Any exceptions the
|
object as their "this," noting that the db will have been
|
||||||
handlers throw are ignored because "destructors must not
|
closed when onclose.after is called. If this db is not opened
|
||||||
throw."
|
when close() is called, neither of the handlers are called. Any
|
||||||
|
exceptions the handlers throw are ignored because "destructors
|
||||||
|
must not throw."
|
||||||
|
|
||||||
Note that garbage collection of a db handle, if it happens at
|
Note that garbage collection of a db handle, if it happens at
|
||||||
all, will never trigger close(), so onclose handlers are not a
|
all, will never trigger close(), so onclose handlers are not a
|
||||||
@@ -591,7 +602,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
);
|
);
|
||||||
if(pVfs){
|
if(pVfs){
|
||||||
const v = new capi.sqlite3_vfs(pVfs);
|
const v = new capi.sqlite3_vfs(pVfs);
|
||||||
try{ rc = wasm.cstringToJs(v.$zName) }
|
try{ rc = wasm.cstrToJs(v.$zName) }
|
||||||
finally { v.dispose() }
|
finally { v.dispose() }
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
@@ -625,7 +636,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
try{
|
try{
|
||||||
ppStmt = wasm.pstack.alloc(8)/* output (sqlite3_stmt**) arg */;
|
ppStmt = wasm.pstack.alloc(8)/* output (sqlite3_stmt**) arg */;
|
||||||
DB.checkRc(this, capi.sqlite3_prepare_v2(this.pointer, sql, -1, ppStmt, null));
|
DB.checkRc(this, capi.sqlite3_prepare_v2(this.pointer, sql, -1, ppStmt, null));
|
||||||
pStmt = wasm.getPtrValue(ppStmt);
|
pStmt = wasm.peekPtr(ppStmt);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
wasm.pstack.restore(stack);
|
wasm.pstack.restore(stack);
|
||||||
@@ -805,8 +816,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
const pSqlEnd = pSql + sqlByteLen;
|
const pSqlEnd = pSql + sqlByteLen;
|
||||||
if(isTA) wasm.heap8().set(arg.sql, pSql);
|
if(isTA) wasm.heap8().set(arg.sql, pSql);
|
||||||
else wasm.jstrcpy(arg.sql, wasm.heap8(), pSql, sqlByteLen, false);
|
else wasm.jstrcpy(arg.sql, wasm.heap8(), pSql, sqlByteLen, false);
|
||||||
wasm.setMemValue(pSql + sqlByteLen, 0/*NUL terminator*/);
|
wasm.poke(pSql + sqlByteLen, 0/*NUL terminator*/);
|
||||||
while(pSql && wasm.getMemValue(pSql, 'i8')
|
while(pSql && wasm.peek(pSql, 'i8')
|
||||||
/* Maintenance reminder:^^^ _must_ be 'i8' or else we
|
/* Maintenance reminder:^^^ _must_ be 'i8' or else we
|
||||||
will very likely cause an endless loop. What that's
|
will very likely cause an endless loop. What that's
|
||||||
doing is checking for a terminating NUL byte. If we
|
doing is checking for a terminating NUL byte. If we
|
||||||
@@ -814,13 +825,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
around the NUL terminator, and get stuck in and
|
around the NUL terminator, and get stuck in and
|
||||||
endless loop at the end of the SQL, endlessly
|
endless loop at the end of the SQL, endlessly
|
||||||
re-preparing an empty statement. */ ){
|
re-preparing an empty statement. */ ){
|
||||||
wasm.setPtrValue(ppStmt, 0);
|
wasm.pokePtr([ppStmt, pzTail], 0);
|
||||||
wasm.setPtrValue(pzTail, 0);
|
|
||||||
DB.checkRc(this, capi.sqlite3_prepare_v3(
|
DB.checkRc(this, capi.sqlite3_prepare_v3(
|
||||||
this.pointer, pSql, sqlByteLen, 0, ppStmt, pzTail
|
this.pointer, pSql, sqlByteLen, 0, ppStmt, pzTail
|
||||||
));
|
));
|
||||||
const pStmt = wasm.getPtrValue(ppStmt);
|
const pStmt = wasm.peekPtr(ppStmt);
|
||||||
pSql = wasm.getPtrValue(pzTail);
|
pSql = wasm.peekPtr(pzTail);
|
||||||
sqlByteLen = pSqlEnd - pSql;
|
sqlByteLen = pSqlEnd - pSql;
|
||||||
if(!pStmt) continue;
|
if(!pStmt) continue;
|
||||||
if(Array.isArray(opt.saveSql)){
|
if(Array.isArray(opt.saveSql)){
|
||||||
@@ -1098,6 +1108,26 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
return __selectFirstRow(this, sql, bind, {});
|
return __selectFirstRow(this, sql, bind, {});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
Runs the given SQL and returns an array of all results, with
|
||||||
|
each row represented as an array, as per the 'array' `rowMode`
|
||||||
|
option to `exec()`. An empty result set resolves
|
||||||
|
to an empty array. The second argument, if any, is treated as
|
||||||
|
the 'bind' option to a call to exec().
|
||||||
|
*/
|
||||||
|
selectArrays: function(sql,bind){
|
||||||
|
return __selectAll(this, sql, bind, 'array');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
Works identically to selectArrays() except that each value
|
||||||
|
in the returned array is an object, as per the 'object' `rowMode`
|
||||||
|
option to `exec()`.
|
||||||
|
*/
|
||||||
|
selectObjects: function(sql,bind){
|
||||||
|
return __selectAll(this, sql, bind, 'object');
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns the number of currently-opened Stmt handles for this db
|
Returns the number of currently-opened Stmt handles for this db
|
||||||
handle, or 0 if this DB instance is closed.
|
handle, or 0 if this DB instance is closed.
|
||||||
|
|||||||
@@ -185,28 +185,49 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
/**
|
/**
|
||||||
Constructs this object with a message depending on its arguments:
|
Constructs this object with a message depending on its arguments:
|
||||||
|
|
||||||
- If it's passed only a single integer argument, it is assumed
|
If its first argument is an integer, it is assumed to be
|
||||||
to be an sqlite3 C API result code. The message becomes the
|
an SQLITE_... result code and it is passed to
|
||||||
result of sqlite3.capi.sqlite3_js_rc_str() or (if that returns
|
sqlite3.capi.sqlite3_js_rc_str() to stringify it.
|
||||||
falsy) a synthesized string which contains that integer.
|
|
||||||
|
|
||||||
- If passed 2 arguments and the 2nd is a object, it behaves
|
If called with exactly 2 arguments and the 2nd is an object,
|
||||||
like the Error(string,object) constructor except that the first
|
that object is treated as the 2nd argument to the parent
|
||||||
argument is subject to the is-integer semantics from the
|
constructor.
|
||||||
previous point.
|
|
||||||
|
|
||||||
- Else all arguments are concatenated with a space between each
|
The exception's message is created by concatenating its
|
||||||
one, using args.join(' '), to create the error message.
|
arguments with a space between each, except for the
|
||||||
|
two-args-with-an-objec form and that the first argument will
|
||||||
|
get coerced to a string, as described above, if it's an
|
||||||
|
integer.
|
||||||
|
|
||||||
|
If passed an integer first argument, the error object's
|
||||||
|
`resultCode` member will be set to the given integer value,
|
||||||
|
else it will be set to capi.SQLITE_ERROR.
|
||||||
*/
|
*/
|
||||||
constructor(...args){
|
constructor(...args){
|
||||||
if(1===args.length && __isInt(args[0])){
|
let rc;
|
||||||
super(__rcStr(args[0]));
|
if(args.length){
|
||||||
}else if(2===args.length && 'object'===typeof args[1]){
|
if(__isInt(args[0])){
|
||||||
if(__isInt(args[0])) super(__rcStr(args[0]), args[1]);
|
rc = args[0];
|
||||||
else super(...args);
|
if(1===args.length){
|
||||||
}else{
|
super(__rcStr(args[0]));
|
||||||
super(args.join(' '));
|
}else{
|
||||||
|
const rcStr = __rcStr(rc);
|
||||||
|
if('object'===typeof args[1]){
|
||||||
|
super(rcStr,args[1]);
|
||||||
|
}else{
|
||||||
|
args[0] = rcStr+':';
|
||||||
|
super(args.join(' '));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(2===args.length && 'object'===typeof args[1]){
|
||||||
|
super(...args);
|
||||||
|
}else{
|
||||||
|
super(args.join(' '));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
this.resultCode = rc || capi.SQLITE_ERROR;
|
||||||
this.name = 'SQLite3Error';
|
this.name = 'SQLite3Error';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -348,13 +369,13 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
/**
|
/**
|
||||||
If v is-a Array, its join("") result is returned. If
|
If v is-a Array, its join("") result is returned. If
|
||||||
isSQLableTypedArray(v) is true then typedArrayToString(v) is
|
isSQLableTypedArray(v) is true then typedArrayToString(v) is
|
||||||
returned. If it looks like a WASM pointer, wasm.cstringToJs(v) is
|
returned. If it looks like a WASM pointer, wasm.cstrToJs(v) is
|
||||||
returned. Else v is returned as-is.
|
returned. Else v is returned as-is.
|
||||||
*/
|
*/
|
||||||
const flexibleString = function(v){
|
const flexibleString = function(v){
|
||||||
if(isSQLableTypedArray(v)) return typedArrayToString(v);
|
if(isSQLableTypedArray(v)) return typedArrayToString(v);
|
||||||
else if(Array.isArray(v)) return v.join("");
|
else if(Array.isArray(v)) return v.join("");
|
||||||
else if(wasm.isPtr(v)) v = wasm.cstringToJs(v);
|
else if(wasm.isPtr(v)) v = wasm.cstrToJs(v);
|
||||||
return v;
|
return v;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -565,7 +586,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
|
|
||||||
It returns its result and compiled statement as documented in
|
It returns its result and compiled statement as documented in
|
||||||
the C API. Fetching the output pointers (5th and 6th
|
the C API. Fetching the output pointers (5th and 6th
|
||||||
parameters) requires using `capi.wasm.getMemValue()` (or
|
parameters) requires using `capi.wasm.peek()` (or
|
||||||
equivalent) and the `pzTail` will point to an address relative to
|
equivalent) and the `pzTail` will point to an address relative to
|
||||||
the `sqlAsPointer` value.
|
the `sqlAsPointer` value.
|
||||||
|
|
||||||
@@ -602,7 +623,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
If the callback is not a JS function then this binding performs
|
If the callback is not a JS function then this binding performs
|
||||||
no translation of the callback, but the sql argument is still
|
no translation of the callback, but the sql argument is still
|
||||||
converted to a WASM string for the call using the
|
converted to a WASM string for the call using the
|
||||||
"flexible-string" argument converter.
|
"string:flexible" argument converter.
|
||||||
*/
|
*/
|
||||||
sqlite3_exec: (pDb, sql, callback, pVoid, pErrMsg)=>{}/*installed later*/,
|
sqlite3_exec: (pDb, sql, callback, pVoid, pErrMsg)=>{}/*installed later*/,
|
||||||
|
|
||||||
@@ -857,171 +878,6 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
) ? !!capi.sqlite3_compileoption_used(optName) : false;
|
) ? !!capi.sqlite3_compileoption_used(optName) : false;
|
||||||
}/*compileOptionUsed()*/;
|
}/*compileOptionUsed()*/;
|
||||||
|
|
||||||
/**
|
|
||||||
Signatures for the WASM-exported C-side functions. Each entry
|
|
||||||
is an array with 2+ elements:
|
|
||||||
|
|
||||||
[ "c-side name",
|
|
||||||
"result type" (wasm.xWrap() syntax),
|
|
||||||
[arg types in xWrap() syntax]
|
|
||||||
// ^^^ this needn't strictly be an array: it can be subsequent
|
|
||||||
// elements instead: [x,y,z] is equivalent to x,y,z
|
|
||||||
]
|
|
||||||
|
|
||||||
Note that support for the API-specific data types in the
|
|
||||||
result/argument type strings gets plugged in at a later phase in
|
|
||||||
the API initialization process.
|
|
||||||
*/
|
|
||||||
wasm.bindingSignatures = [
|
|
||||||
// Please keep these sorted by function name!
|
|
||||||
["sqlite3_aggregate_context","void*", "sqlite3_context*", "int"],
|
|
||||||
["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*"
|
|
||||||
/* TODO: we should arguably write a custom wrapper which knows
|
|
||||||
how to handle Blob, TypedArrays, and JS strings. */
|
|
||||||
],
|
|
||||||
["sqlite3_bind_double","int", "sqlite3_stmt*", "int", "f64"],
|
|
||||||
["sqlite3_bind_int","int", "sqlite3_stmt*", "int", "int"],
|
|
||||||
["sqlite3_bind_null",undefined, "sqlite3_stmt*", "int"],
|
|
||||||
["sqlite3_bind_parameter_count", "int", "sqlite3_stmt*"],
|
|
||||||
["sqlite3_bind_parameter_index","int", "sqlite3_stmt*", "string"],
|
|
||||||
["sqlite3_bind_text","int", "sqlite3_stmt*", "int", "string", "int", "int"
|
|
||||||
/* We should arguably create a hand-written binding of
|
|
||||||
bind_text() which does more flexible text conversion, along
|
|
||||||
the lines of sqlite3_prepare_v3(). The slightly problematic
|
|
||||||
part is the final argument (text destructor). */
|
|
||||||
],
|
|
||||||
["sqlite3_busy_timeout","int", "sqlite3*", "int"],
|
|
||||||
["sqlite3_close_v2", "int", "sqlite3*"],
|
|
||||||
["sqlite3_changes", "int", "sqlite3*"],
|
|
||||||
["sqlite3_clear_bindings","int", "sqlite3_stmt*"],
|
|
||||||
["sqlite3_column_blob","*", "sqlite3_stmt*", "int"],
|
|
||||||
["sqlite3_column_bytes","int", "sqlite3_stmt*", "int"],
|
|
||||||
["sqlite3_column_count", "int", "sqlite3_stmt*"],
|
|
||||||
["sqlite3_column_double","f64", "sqlite3_stmt*", "int"],
|
|
||||||
["sqlite3_column_int","int", "sqlite3_stmt*", "int"],
|
|
||||||
["sqlite3_column_name","string", "sqlite3_stmt*", "int"],
|
|
||||||
["sqlite3_column_text","string", "sqlite3_stmt*", "int"],
|
|
||||||
["sqlite3_column_type","int", "sqlite3_stmt*", "int"],
|
|
||||||
["sqlite3_compileoption_get", "string", "int"],
|
|
||||||
["sqlite3_compileoption_used", "int", "string"],
|
|
||||||
/* sqlite3_create_function(), sqlite3_create_function_v2(), and
|
|
||||||
sqlite3_create_window_function() use hand-written bindings to
|
|
||||||
simplify handling of their function-type arguments. */
|
|
||||||
["sqlite3_data_count", "int", "sqlite3_stmt*"],
|
|
||||||
["sqlite3_db_filename", "string", "sqlite3*", "string"],
|
|
||||||
["sqlite3_db_handle", "sqlite3*", "sqlite3_stmt*"],
|
|
||||||
["sqlite3_db_name", "string", "sqlite3*", "int"],
|
|
||||||
["sqlite3_deserialize", "int", "sqlite3*", "string", "*", "i64", "i64", "int"]
|
|
||||||
/* Careful! Short version: de/serialize() are problematic because they
|
|
||||||
might use a different allocator than the user for managing the
|
|
||||||
deserialized block. de/serialize() are ONLY safe to use with
|
|
||||||
sqlite3_malloc(), sqlite3_free(), and its 64-bit variants. */,
|
|
||||||
["sqlite3_errmsg", "string", "sqlite3*"],
|
|
||||||
["sqlite3_error_offset", "int", "sqlite3*"],
|
|
||||||
["sqlite3_errstr", "string", "int"],
|
|
||||||
/*["sqlite3_exec", "int", "sqlite3*", "string", "*", "*", "**"
|
|
||||||
Handled seperately to perform translation of the callback
|
|
||||||
into a WASM-usable one. ],*/
|
|
||||||
["sqlite3_expanded_sql", "string", "sqlite3_stmt*"],
|
|
||||||
["sqlite3_extended_errcode", "int", "sqlite3*"],
|
|
||||||
["sqlite3_extended_result_codes", "int", "sqlite3*", "int"],
|
|
||||||
["sqlite3_file_control", "int", "sqlite3*", "string", "int", "*"],
|
|
||||||
["sqlite3_finalize", "int", "sqlite3_stmt*"],
|
|
||||||
["sqlite3_free", undefined,"*"],
|
|
||||||
["sqlite3_initialize", undefined],
|
|
||||||
/*["sqlite3_interrupt", undefined, "sqlite3*"
|
|
||||||
^^^ we cannot actually currently support this because JS is
|
|
||||||
single-threaded and we don't have a portable way to access a DB
|
|
||||||
from 2 SharedWorkers concurrently. ],*/
|
|
||||||
["sqlite3_libversion", "string"],
|
|
||||||
["sqlite3_libversion_number", "int"],
|
|
||||||
["sqlite3_malloc", "*","int"],
|
|
||||||
["sqlite3_open", "int", "string", "*"],
|
|
||||||
["sqlite3_open_v2", "int", "string", "*", "int", "string"],
|
|
||||||
/* sqlite3_prepare_v2() and sqlite3_prepare_v3() are handled
|
|
||||||
separately due to us requiring two different sets of semantics
|
|
||||||
for those, depending on how their SQL argument is provided. */
|
|
||||||
/* sqlite3_randomness() uses a hand-written wrapper to extend
|
|
||||||
the range of supported argument types. */
|
|
||||||
["sqlite3_realloc", "*","*","int"],
|
|
||||||
["sqlite3_reset", "int", "sqlite3_stmt*"],
|
|
||||||
["sqlite3_result_blob",undefined, "sqlite3_context*", "*", "int", "*"],
|
|
||||||
["sqlite3_result_double",undefined, "sqlite3_context*", "f64"],
|
|
||||||
["sqlite3_result_error",undefined, "sqlite3_context*", "string", "int"],
|
|
||||||
["sqlite3_result_error_code", undefined, "sqlite3_context*", "int"],
|
|
||||||
["sqlite3_result_error_nomem", undefined, "sqlite3_context*"],
|
|
||||||
["sqlite3_result_error_toobig", undefined, "sqlite3_context*"],
|
|
||||||
["sqlite3_result_int",undefined, "sqlite3_context*", "int"],
|
|
||||||
["sqlite3_result_null",undefined, "sqlite3_context*"],
|
|
||||||
["sqlite3_result_text",undefined, "sqlite3_context*", "string", "int", "*"],
|
|
||||||
["sqlite3_result_zeroblob", undefined, "sqlite3_context*", "int"],
|
|
||||||
["sqlite3_serialize","*", "sqlite3*", "string", "*", "int"],
|
|
||||||
["sqlite3_shutdown", undefined],
|
|
||||||
["sqlite3_sourceid", "string"],
|
|
||||||
["sqlite3_sql", "string", "sqlite3_stmt*"],
|
|
||||||
["sqlite3_step", "int", "sqlite3_stmt*"],
|
|
||||||
["sqlite3_strglob", "int", "string","string"],
|
|
||||||
["sqlite3_strlike", "int", "string","string","int"],
|
|
||||||
["sqlite3_trace_v2", "int", "sqlite3*", "int", "*", "*"],
|
|
||||||
["sqlite3_total_changes", "int", "sqlite3*"],
|
|
||||||
/* Note sqlite3_uri_...() has very specific requirements
|
|
||||||
for their first C-string arguments, so we cannot perform
|
|
||||||
any type conversion on those. */
|
|
||||||
["sqlite3_uri_boolean", "int", "sqlite3_filename", "string", "int"],
|
|
||||||
["sqlite3_uri_key", "string", "sqlite3_filename", "int"],
|
|
||||||
["sqlite3_uri_parameter", "string", "sqlite3_filename", "string"],
|
|
||||||
["sqlite3_user_data","void*", "sqlite3_context*"],
|
|
||||||
["sqlite3_value_blob", "*", "sqlite3_value*"],
|
|
||||||
["sqlite3_value_bytes","int", "sqlite3_value*"],
|
|
||||||
["sqlite3_value_double","f64", "sqlite3_value*"],
|
|
||||||
["sqlite3_value_int","int", "sqlite3_value*"],
|
|
||||||
["sqlite3_value_text", "string", "sqlite3_value*"],
|
|
||||||
["sqlite3_value_type", "int", "sqlite3_value*"],
|
|
||||||
["sqlite3_vfs_find", "*", "string"],
|
|
||||||
["sqlite3_vfs_register", "int", "sqlite3_vfs*", "int"],
|
|
||||||
["sqlite3_vfs_unregister", "int", "sqlite3_vfs*"]
|
|
||||||
]/*wasm.bindingSignatures*/;
|
|
||||||
|
|
||||||
if(false && wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){
|
|
||||||
/* ^^^ "the problem" is that this is an option feature and the
|
|
||||||
build-time function-export list does not currently take
|
|
||||||
optional features into account. */
|
|
||||||
wasm.bindingSignatures.push(["sqlite3_normalized_sql", "string", "sqlite3_stmt*"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Functions which require BigInt (int64) support are separated from
|
|
||||||
the others because we need to conditionally bind them or apply
|
|
||||||
dummy impls, depending on the capabilities of the environment.
|
|
||||||
*/
|
|
||||||
wasm.bindingSignatures.int64 = [
|
|
||||||
["sqlite3_bind_int64","int", ["sqlite3_stmt*", "int", "i64"]],
|
|
||||||
["sqlite3_changes64","i64", ["sqlite3*"]],
|
|
||||||
["sqlite3_column_int64","i64", ["sqlite3_stmt*", "int"]],
|
|
||||||
["sqlite3_malloc64", "*","i64"],
|
|
||||||
["sqlite3_msize", "i64", "*"],
|
|
||||||
["sqlite3_realloc64", "*","*", "i64"],
|
|
||||||
["sqlite3_result_int64",undefined, "*", "i64"],
|
|
||||||
["sqlite3_result_zeroblob64", "int", "*", "i64"],
|
|
||||||
["sqlite3_total_changes64", "i64", ["sqlite3*"]],
|
|
||||||
["sqlite3_uri_int64", "i64", ["sqlite3_filename", "string", "i64"]],
|
|
||||||
["sqlite3_value_int64","i64", "sqlite3_value*"],
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
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 = [
|
|
||||||
["sqlite3_wasm_db_reset", "int", "sqlite3*"],
|
|
||||||
["sqlite3_wasm_db_vfs", "sqlite3_vfs*", "sqlite3*","string"],
|
|
||||||
["sqlite3_wasm_vfs_create_file", "int",
|
|
||||||
"sqlite3_vfs*","string","*", "int"],
|
|
||||||
["sqlite3_wasm_vfs_unlink", "int", "sqlite3_vfs*","string"]
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
sqlite3.wasm.pstack (pseudo-stack) holds a special-case
|
sqlite3.wasm.pstack (pseudo-stack) holds a special-case
|
||||||
stack-style allocator intended only for use with _small_ data of
|
stack-style allocator intended only for use with _small_ data of
|
||||||
@@ -1051,7 +907,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
space managed by Emscripten's stack-management, so does not
|
space managed by Emscripten's stack-management, so does not
|
||||||
collide with Emscripten-provided stack allocation APIs. The
|
collide with Emscripten-provided stack allocation APIs. The
|
||||||
memory lives in the WASM heap and can be used with routines such
|
memory lives in the WASM heap and can be used with routines such
|
||||||
as wasm.setMemValue() and any wasm.heap8u().slice().
|
as wasm.poke() and any wasm.heap8u().slice().
|
||||||
*/
|
*/
|
||||||
wasm.pstack = Object.assign(Object.create(null),{
|
wasm.pstack = Object.assign(Object.create(null),{
|
||||||
/**
|
/**
|
||||||
@@ -1110,7 +966,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
|
|
||||||
When a returned pointers will refer to a 64-bit value, e.g. a
|
When a returned pointers will refer to a 64-bit value, e.g. a
|
||||||
double or int64, and that value must be written or fetched,
|
double or int64, and that value must be written or fetched,
|
||||||
e.g. using wasm.setMemValue() or wasm.getMemValue(), it is
|
e.g. using wasm.poke() or wasm.peek(), it is
|
||||||
important that the pointer in question be aligned to an 8-byte
|
important that the pointer in question be aligned to an 8-byte
|
||||||
boundary or else it will not be fetched or written properly and
|
boundary or else it will not be fetched or written properly and
|
||||||
will corrupt or read neighboring memory.
|
will corrupt or read neighboring memory.
|
||||||
@@ -1297,7 +1153,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
let pVfs = capi.sqlite3_vfs_find(0);
|
let pVfs = capi.sqlite3_vfs_find(0);
|
||||||
while(pVfs){
|
while(pVfs){
|
||||||
const oVfs = new capi.sqlite3_vfs(pVfs);
|
const oVfs = new capi.sqlite3_vfs(pVfs);
|
||||||
rc.push(wasm.cstringToJs(oVfs.$zName));
|
rc.push(wasm.cstrToJs(oVfs.$zName));
|
||||||
pVfs = oVfs.$pNext;
|
pVfs = oVfs.$pNext;
|
||||||
oVfs.dispose();
|
oVfs.dispose();
|
||||||
}
|
}
|
||||||
@@ -1343,8 +1199,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
toss3("Database serialization failed with code",
|
toss3("Database serialization failed with code",
|
||||||
sqlite3.capi.sqlite3_js_rc_str(rc));
|
sqlite3.capi.sqlite3_js_rc_str(rc));
|
||||||
}
|
}
|
||||||
pOut = wasm.getPtrValue(ppOut);
|
pOut = wasm.peekPtr(ppOut);
|
||||||
const nOut = wasm.getMemValue(pSize, 'i64');
|
const nOut = wasm.peek(pSize, 'i64');
|
||||||
rc = nOut
|
rc = nOut
|
||||||
? wasm.heap8u().slice(pOut, pOut + Number(nOut))
|
? wasm.heap8u().slice(pOut, pOut + Number(nOut))
|
||||||
: new Uint8Array();
|
: new Uint8Array();
|
||||||
@@ -1538,6 +1394,265 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
|
|
||||||
}/* main-window-only bits */
|
}/* main-window-only bits */
|
||||||
|
|
||||||
|
/**
|
||||||
|
Wraps all known variants of the C-side variadic
|
||||||
|
sqlite3_db_config().
|
||||||
|
|
||||||
|
Full docs: https://sqlite.org/c3ref/db_config.html
|
||||||
|
|
||||||
|
Returns capi.SQLITE_MISUSE if op is not a valid operation ID.
|
||||||
|
*/
|
||||||
|
capi.sqlite3_db_config = function f(pDb, op, ...args){
|
||||||
|
if(!this.s){
|
||||||
|
this.s = wasm.xWrap('sqlite3_wasm_db_config_s','int',
|
||||||
|
['sqlite3*', 'int', 'string:static']
|
||||||
|
/* MAINDBNAME requires a static string */);
|
||||||
|
this.pii = wasm.xWrap('sqlite3_wasm_db_config_pii', 'int',
|
||||||
|
['sqlite3*', 'int', '*','int', 'int']);
|
||||||
|
this.ip = wasm.xWrap('sqlite3_wasm_db_config_ip','int',
|
||||||
|
['sqlite3*', 'int', 'int','*']);
|
||||||
|
}
|
||||||
|
const c = capi;
|
||||||
|
switch(op){
|
||||||
|
case c.SQLITE_DBCONFIG_ENABLE_FKEY:
|
||||||
|
case c.SQLITE_DBCONFIG_ENABLE_TRIGGER:
|
||||||
|
case c.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER:
|
||||||
|
case c.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION:
|
||||||
|
case c.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE:
|
||||||
|
case c.SQLITE_DBCONFIG_ENABLE_QPSG:
|
||||||
|
case c.SQLITE_DBCONFIG_TRIGGER_EQP:
|
||||||
|
case c.SQLITE_DBCONFIG_RESET_DATABASE:
|
||||||
|
case c.SQLITE_DBCONFIG_DEFENSIVE:
|
||||||
|
case c.SQLITE_DBCONFIG_WRITABLE_SCHEMA:
|
||||||
|
case c.SQLITE_DBCONFIG_LEGACY_ALTER_TABLE:
|
||||||
|
case c.SQLITE_DBCONFIG_DQS_DML:
|
||||||
|
case c.SQLITE_DBCONFIG_DQS_DDL:
|
||||||
|
case c.SQLITE_DBCONFIG_ENABLE_VIEW:
|
||||||
|
case c.SQLITE_DBCONFIG_LEGACY_FILE_FORMAT:
|
||||||
|
case c.SQLITE_DBCONFIG_TRUSTED_SCHEMA:
|
||||||
|
return this.ip(pDb, op, args[0], args[1] || 0);
|
||||||
|
case c.SQLITE_DBCONFIG_LOOKASIDE:
|
||||||
|
return this.pii(pDb, op, args[0], args[1], args[2]);
|
||||||
|
case c.SQLITE_DBCONFIG_MAINDBNAME:
|
||||||
|
return this.s(pDb, op, args[0]);
|
||||||
|
default:
|
||||||
|
return c.SQLITE_MISUSE;
|
||||||
|
}
|
||||||
|
}.bind(Object.create(null));
|
||||||
|
|
||||||
|
/**
|
||||||
|
Given a (sqlite3_value*), this function attempts to convert it
|
||||||
|
to an equivalent JS value with as much fidelity as feasible and
|
||||||
|
return it.
|
||||||
|
|
||||||
|
By default it throws if it cannot determine any sensible
|
||||||
|
conversion. If passed a falsy second argument, it instead returns
|
||||||
|
`undefined` if no suitable conversion is found. Note that there
|
||||||
|
is no conversion from SQL to JS which results in the `undefined`
|
||||||
|
value, so `undefined` has an unambiguous meaning here. It will
|
||||||
|
always throw a WasmAllocError if allocating memory for a
|
||||||
|
conversion fails.
|
||||||
|
|
||||||
|
Caveats:
|
||||||
|
|
||||||
|
- It does not support sqlite3_value_to_pointer() conversions
|
||||||
|
because those require a type name string which this function
|
||||||
|
does not have and cannot sensibly be given at the level of the
|
||||||
|
API where this is used (e.g. automatically converting UDF
|
||||||
|
arguments). Clients using sqlite3_value_to_pointer(), and its
|
||||||
|
related APIs, will need to manage those themselves.
|
||||||
|
*/
|
||||||
|
capi.sqlite3_value_to_js = function(pVal,throwIfCannotConvert=true){
|
||||||
|
let arg;
|
||||||
|
const valType = capi.sqlite3_value_type(pVal);
|
||||||
|
switch(valType){
|
||||||
|
case capi.SQLITE_INTEGER:
|
||||||
|
if(wasm.bigIntEnabled){
|
||||||
|
arg = capi.sqlite3_value_int64(pVal);
|
||||||
|
if(util.bigIntFitsDouble(arg)) arg = Number(arg);
|
||||||
|
}
|
||||||
|
else arg = capi.sqlite3_value_double(pVal)/*yes, double, for larger integers*/;
|
||||||
|
break;
|
||||||
|
case capi.SQLITE_FLOAT:
|
||||||
|
arg = capi.sqlite3_value_double(pVal);
|
||||||
|
break;
|
||||||
|
case capi.SQLITE_TEXT:
|
||||||
|
arg = capi.sqlite3_value_text(pVal);
|
||||||
|
break;
|
||||||
|
case capi.SQLITE_BLOB:{
|
||||||
|
const n = capi.sqlite3_value_bytes(pVal);
|
||||||
|
const pBlob = capi.sqlite3_value_blob(pVal);
|
||||||
|
if(n && !pBlob) sqlite3.WasmAllocError.toss(
|
||||||
|
"Cannot allocate memory for blob argument of",n,"byte(s)"
|
||||||
|
);
|
||||||
|
arg = n ? wasm.heap8u().slice(pBlob, pBlob + Number(n)) : null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case capi.SQLITE_NULL:
|
||||||
|
arg = null; break;
|
||||||
|
default:
|
||||||
|
if(throwIfCannotConvert){
|
||||||
|
toss3(capi.SQLITE_MISMATCH,
|
||||||
|
"Unhandled sqlite3_value_type():",valType);
|
||||||
|
}
|
||||||
|
arg = undefined;
|
||||||
|
}
|
||||||
|
return arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Requires a C-style array of `sqlite3_value*` objects and the
|
||||||
|
number of entries in that array. Returns a JS array containing
|
||||||
|
the results of passing each C array entry to
|
||||||
|
sqlite3_value_to_js(). The 3rd argument to this function is
|
||||||
|
passed on as the 2nd argument to that one.
|
||||||
|
*/
|
||||||
|
capi.sqlite3_values_to_js = function(argc,pArgv,throwIfCannotConvert=true){
|
||||||
|
let i;
|
||||||
|
const tgt = [];
|
||||||
|
for(i = 0; i < argc; ++i){
|
||||||
|
/**
|
||||||
|
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
|
||||||
|
WasmAllocError, or sqlite3_result_error(). In the latter case,
|
||||||
|
the second arugment is coerced to a string to create the error
|
||||||
|
message.
|
||||||
|
|
||||||
|
The first argument is a (sqlite3_context*). Returns void.
|
||||||
|
Does not throw.
|
||||||
|
*/
|
||||||
|
capi.sqlite3_result_error_js = function(pCtx,e){
|
||||||
|
if(e instanceof WasmAllocError){
|
||||||
|
capi.sqlite3_result_error_nomem(pCtx);
|
||||||
|
}else{
|
||||||
|
/* Maintenance reminder: ''+e, rather than e.message,
|
||||||
|
will prefix e.message with e.name, so it includes
|
||||||
|
the exception's type name in the result. */;
|
||||||
|
capi.sqlite3_result_error(pCtx, ''+e, -1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
This function passes its 2nd argument to one of the
|
||||||
|
sqlite3_result_xyz() routines, depending on the type of that
|
||||||
|
argument:
|
||||||
|
|
||||||
|
- If (val instanceof Error), this function passes it to
|
||||||
|
sqlite3_result_error_js().
|
||||||
|
- `null`: `sqlite3_result_null()`
|
||||||
|
- `boolean`: `sqlite3_result_int()` with a value of 0 or 1.
|
||||||
|
- `number`: `sqlite3_result_int()`, `sqlite3_result_int64()`, or
|
||||||
|
`sqlite3_result_double()`, depending on the range of the number
|
||||||
|
and whether or not int64 support is enabled.
|
||||||
|
- `bigint`: similar to `number` but will trigger an error if the
|
||||||
|
value is too big to store in an int64.
|
||||||
|
- `string`: `sqlite3_result_text()`
|
||||||
|
- Uint8Array or Int8Array: `sqlite3_result_blob()`
|
||||||
|
- `undefined`: is a no-op provided to simplify certain use cases.
|
||||||
|
|
||||||
|
Anything else triggers `sqlite3_result_error()` with a
|
||||||
|
description of the problem.
|
||||||
|
|
||||||
|
The first argument to this function is a `(sqlite3_context*)`.
|
||||||
|
Returns void. Does not throw.
|
||||||
|
*/
|
||||||
|
capi.sqlite3_result_js = function(pCtx,val){
|
||||||
|
if(val instanceof Error){
|
||||||
|
capi.sqlite3_result_error_js(pCtx, val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
switch(typeof val) {
|
||||||
|
case 'undefined':
|
||||||
|
/* This is a no-op. This routine originated in the create_function()
|
||||||
|
family of APIs and in that context, passing in undefined indicated
|
||||||
|
that the caller was responsible for calling sqlite3_result_xxx()
|
||||||
|
(if needed). */
|
||||||
|
break;
|
||||||
|
case 'boolean':
|
||||||
|
capi.sqlite3_result_int(pCtx, val ? 1 : 0);
|
||||||
|
break;
|
||||||
|
case 'bigint':
|
||||||
|
if(util.bigIntFits32(val)){
|
||||||
|
capi.sqlite3_result_int(pCtx, Number(val));
|
||||||
|
}else if(util.bigIntFitsDouble(val)){
|
||||||
|
capi.sqlite3_result_double(pCtx, Number(val));
|
||||||
|
}else if(wasm.bigIntEnabled){
|
||||||
|
if(util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val);
|
||||||
|
else toss3("BigInt value",val.toString(),"is too BigInt for int64.");
|
||||||
|
}else{
|
||||||
|
toss3("BigInt value",val.toString(),"is too BigInt.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'number': {
|
||||||
|
let f;
|
||||||
|
if(util.isInt32(val)){
|
||||||
|
f = capi.sqlite3_result_int;
|
||||||
|
}else if(wasm.bigIntEnabled
|
||||||
|
&& Number.isInteger(val)
|
||||||
|
&& util.bigIntFits64(BigInt(val))){
|
||||||
|
f = capi.sqlite3_result_int64;
|
||||||
|
}else{
|
||||||
|
f = capi.sqlite3_result_double;
|
||||||
|
}
|
||||||
|
f(pCtx, val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'string':
|
||||||
|
capi.sqlite3_result_text(pCtx, val, -1, capi.SQLITE_TRANSIENT);
|
||||||
|
break;
|
||||||
|
case 'object':
|
||||||
|
if(null===val/*yes, typeof null === 'object'*/) {
|
||||||
|
capi.sqlite3_result_null(pCtx);
|
||||||
|
break;
|
||||||
|
}else if(util.isBindableTypedArray(val)){
|
||||||
|
const pBlob = wasm.allocFromTypedArray(val);
|
||||||
|
capi.sqlite3_result_blob(
|
||||||
|
pCtx, pBlob, val.byteLength,
|
||||||
|
wasm.exports[sqlite3.config.deallocExportName]
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// else fall through
|
||||||
|
default:
|
||||||
|
toss3("Don't not how to handle this UDF result value:",(typeof val), val);
|
||||||
|
}
|
||||||
|
}catch(e){
|
||||||
|
capi.sqlite3_result_error_js(pCtx, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the result sqlite3_column_value(pStmt,iCol) passed to
|
||||||
|
sqlite3_value_to_js(). The 3rd argument of this function is
|
||||||
|
ignored by this function except to pass it on as the second
|
||||||
|
argument of sqlite3_value_to_js(). If the sqlite3_column_value()
|
||||||
|
returns NULL (e.g. because the column index is out of range),
|
||||||
|
this function returns `undefined`, regardless of the 3rd
|
||||||
|
argument. 3rd argument is falsy and conversion fails, `undefined`
|
||||||
|
will be returned.
|
||||||
|
|
||||||
|
Note that sqlite3_column_value() returns an "unprotected" value
|
||||||
|
object, but in a single-threaded environment (like this one)
|
||||||
|
there is no distinction between protected and unprotected values.
|
||||||
|
*/
|
||||||
|
capi.sqlite3_column_js = function(pStmt, iCol, throwIfCannotConvert=true){
|
||||||
|
const v = capi.sqlite3_column_value(pStmt, iCol);
|
||||||
|
return (0===v) ? undefined : capi.sqlite3_value_to_js(v, throwIfCannotConvert);
|
||||||
|
};
|
||||||
|
|
||||||
/* The remainder of the API will be set up in later steps. */
|
/* The remainder of the API will be set up in later steps. */
|
||||||
const sqlite3 = {
|
const sqlite3 = {
|
||||||
@@ -1600,7 +1715,10 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
some initializers. Retain them when running in test mode
|
some initializers. Retain them when running in test mode
|
||||||
so that we can add tests for them. */
|
so that we can add tests for them. */
|
||||||
delete sqlite3.util;
|
delete sqlite3.util;
|
||||||
delete sqlite3.VfsHelper;
|
/* It's conceivable that we might want to expose
|
||||||
|
StructBinder to client-side code, but it's only useful if
|
||||||
|
clients build their own sqlite3.wasm which contains their
|
||||||
|
one C struct types. */
|
||||||
delete sqlite3.StructBinder;
|
delete sqlite3.StructBinder;
|
||||||
}
|
}
|
||||||
return sqlite3;
|
return sqlite3;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
A Worker which manages asynchronous OPFS handles on behalf of a
|
A Worker which manages asynchronous OPFS handles on behalf of a
|
||||||
synchronous API which controls it via a combination of Worker
|
synchronous API which controls it via a combination of Worker
|
||||||
messages, SharedArrayBuffer, and Atomics. It is the asynchronous
|
messages, SharedArrayBuffer, and Atomics. It is the asynchronous
|
||||||
counterpart of the API defined in sqlite3-api-opfs.js.
|
counterpart of the API defined in sqlite3-vfs-opfs.js.
|
||||||
|
|
||||||
Highly indebted to:
|
Highly indebted to:
|
||||||
|
|
||||||
@@ -343,16 +343,6 @@ const installAsyncProxy = function(self){
|
|||||||
const affirmNotRO = function(opName,fh){
|
const affirmNotRO = function(opName,fh){
|
||||||
if(fh.readOnly) toss(opName+"(): File is read-only: "+fh.filenameAbs);
|
if(fh.readOnly) toss(opName+"(): File is read-only: "+fh.filenameAbs);
|
||||||
};
|
};
|
||||||
const affirmLocked = function(opName,fh){
|
|
||||||
//if(!fh.syncHandle) toss(opName+"(): File does not have a lock: "+fh.filenameAbs);
|
|
||||||
/**
|
|
||||||
Currently a no-op, as speedtest1 triggers xRead() without a
|
|
||||||
lock (that seems like a bug but it's currently uninvestigated).
|
|
||||||
This means, however, that some OPFS VFS routines may trigger
|
|
||||||
acquisition of a lock but never let it go until xUnlock() is
|
|
||||||
called (which it likely won't be if xLock() was not called).
|
|
||||||
*/
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
We track 2 different timers: the "metrics" timer records how much
|
We track 2 different timers: the "metrics" timer records how much
|
||||||
@@ -393,7 +383,6 @@ const installAsyncProxy = function(self){
|
|||||||
*/
|
*/
|
||||||
let flagAsyncShutdown = false;
|
let flagAsyncShutdown = false;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Asynchronous wrappers for sqlite3_vfs and sqlite3_io_methods
|
Asynchronous wrappers for sqlite3_vfs and sqlite3_io_methods
|
||||||
methods, as well as helpers like mkdir(). Maintenance reminder:
|
methods, as well as helpers like mkdir(). Maintenance reminder:
|
||||||
@@ -427,11 +416,11 @@ const installAsyncProxy = function(self){
|
|||||||
},
|
},
|
||||||
xAccess: async (filename)=>{
|
xAccess: async (filename)=>{
|
||||||
mTimeStart('xAccess');
|
mTimeStart('xAccess');
|
||||||
/* OPFS cannot support the full range of xAccess() queries sqlite3
|
/* OPFS cannot support the full range of xAccess() queries
|
||||||
calls for. We can essentially just tell if the file is
|
sqlite3 calls for. We can essentially just tell if the file
|
||||||
accessible, but if it is it's automatically writable (unless
|
is accessible, but if it is then it's automatically writable
|
||||||
it's locked, which we cannot(?) know without trying to open
|
(unless it's locked, which we cannot(?) know without trying
|
||||||
it). OPFS does not have the notion of read-only.
|
to open it). OPFS does not have the notion of read-only.
|
||||||
|
|
||||||
The return semantics of this function differ from sqlite3's
|
The return semantics of this function differ from sqlite3's
|
||||||
xAccess semantics because we are limited in what we can
|
xAccess semantics because we are limited in what we can
|
||||||
@@ -519,7 +508,6 @@ const installAsyncProxy = function(self){
|
|||||||
let rc = 0;
|
let rc = 0;
|
||||||
wTimeStart('xFileSize');
|
wTimeStart('xFileSize');
|
||||||
try{
|
try{
|
||||||
affirmLocked('xFileSize',fh);
|
|
||||||
const sz = await (await getSyncHandle(fh,'xFileSize')).getSize();
|
const sz = await (await getSyncHandle(fh,'xFileSize')).getSize();
|
||||||
state.s11n.serialize(Number(sz));
|
state.s11n.serialize(Number(sz));
|
||||||
}catch(e){
|
}catch(e){
|
||||||
@@ -615,7 +603,6 @@ const installAsyncProxy = function(self){
|
|||||||
let rc = 0, nRead;
|
let rc = 0, nRead;
|
||||||
const fh = __openFiles[fid];
|
const fh = __openFiles[fid];
|
||||||
try{
|
try{
|
||||||
affirmLocked('xRead',fh);
|
|
||||||
wTimeStart('xRead');
|
wTimeStart('xRead');
|
||||||
nRead = (await getSyncHandle(fh,'xRead')).read(
|
nRead = (await getSyncHandle(fh,'xRead')).read(
|
||||||
fh.sabView.subarray(0, n),
|
fh.sabView.subarray(0, n),
|
||||||
@@ -659,7 +646,6 @@ const installAsyncProxy = function(self){
|
|||||||
const fh = __openFiles[fid];
|
const fh = __openFiles[fid];
|
||||||
wTimeStart('xTruncate');
|
wTimeStart('xTruncate');
|
||||||
try{
|
try{
|
||||||
affirmLocked('xTruncate',fh);
|
|
||||||
affirmNotRO('xTruncate', fh);
|
affirmNotRO('xTruncate', fh);
|
||||||
await (await getSyncHandle(fh,'xTruncate')).truncate(size);
|
await (await getSyncHandle(fh,'xTruncate')).truncate(size);
|
||||||
}catch(e){
|
}catch(e){
|
||||||
@@ -696,7 +682,6 @@ const installAsyncProxy = function(self){
|
|||||||
const fh = __openFiles[fid];
|
const fh = __openFiles[fid];
|
||||||
wTimeStart('xWrite');
|
wTimeStart('xWrite');
|
||||||
try{
|
try{
|
||||||
affirmLocked('xWrite',fh);
|
|
||||||
affirmNotRO('xWrite', fh);
|
affirmNotRO('xWrite', fh);
|
||||||
rc = (
|
rc = (
|
||||||
n === (await getSyncHandle(fh,'xWrite'))
|
n === (await getSyncHandle(fh,'xWrite'))
|
||||||
|
|||||||
714
ext/wasm/api/sqlite3-v-helper.js
Normal file
714
ext/wasm/api/sqlite3-v-helper.js
Normal file
@@ -0,0 +1,714 @@
|
|||||||
|
/*
|
||||||
|
** 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';
|
||||||
|
self.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);
|
||||||
|
|
||||||
|
sqlite3.vfs = vfs;
|
||||||
|
sqlite3.vtab = vtab;
|
||||||
|
|
||||||
|
const sii = capi.sqlite3_index_info;
|
||||||
|
/**
|
||||||
|
If n is >=0 and less than this.$nConstraint, this function
|
||||||
|
returns either a WASM pointer to the 0-based nth entry of
|
||||||
|
this.$aConstraint (if passed a truthy 2nd argument) or an
|
||||||
|
sqlite3_index_info.sqlite3_index_constraint object wrapping that
|
||||||
|
address (if passed a falsy value or no 2nd argument). Returns a
|
||||||
|
falsy value if n is out of range.
|
||||||
|
*/
|
||||||
|
sii.prototype.nthConstraint = function(n, asPtr=false){
|
||||||
|
if(n<0 || n>=this.$nConstraint) return false;
|
||||||
|
const ptr = this.$aConstraint + (
|
||||||
|
sii.sqlite3_index_constraint.structInfo.sizeof * n
|
||||||
|
);
|
||||||
|
return asPtr ? ptr : new sii.sqlite3_index_constraint(ptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Works identically to nthConstraint() but returns state from
|
||||||
|
this.$aConstraintUsage, so returns an
|
||||||
|
sqlite3_index_info.sqlite3_index_constraint_usage instance
|
||||||
|
if passed no 2nd argument or a falsy 2nd argument.
|
||||||
|
*/
|
||||||
|
sii.prototype.nthConstraintUsage = function(n, asPtr=false){
|
||||||
|
if(n<0 || n>=this.$nConstraint) return false;
|
||||||
|
const ptr = this.$aConstraintUsage + (
|
||||||
|
sii.sqlite3_index_constraint_usage.structInfo.sizeof * n
|
||||||
|
);
|
||||||
|
return asPtr ? ptr : new sii.sqlite3_index_constraint_usage(ptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
If n is >=0 and less than this.$nOrderBy, this function
|
||||||
|
returns either a WASM pointer to the 0-based nth entry of
|
||||||
|
this.$aOrderBy (if passed a truthy 2nd argument) or an
|
||||||
|
sqlite3_index_info.sqlite3_index_orderby object wrapping that
|
||||||
|
address (if passed a falsy value or no 2nd argument). Returns a
|
||||||
|
falsy value if n is out of range.
|
||||||
|
*/
|
||||||
|
sii.prototype.nthOrderBy = function(n, asPtr=false){
|
||||||
|
if(n<0 || n>=this.$nOrderBy) return false;
|
||||||
|
const ptr = this.$aOrderBy + (
|
||||||
|
sii.sqlite3_index_orderby.structInfo.sizeof * n
|
||||||
|
);
|
||||||
|
return asPtr ? ptr : new sii.sqlite3_index_orderby(ptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Installs a StructBinder-bound function pointer member of the
|
||||||
|
given name and function in the given StructType target object.
|
||||||
|
|
||||||
|
It creates a WASM proxy for the given function and arranges for
|
||||||
|
that proxy to be cleaned up when tgt.dispose() is called. Throws
|
||||||
|
on the slightest hint of error, e.g. tgt is-not-a StructType,
|
||||||
|
name does not map to a struct-bound member, etc.
|
||||||
|
|
||||||
|
As a special case, if the given function is a pointer, then
|
||||||
|
`wasm.functionEntry()` is used to validate that it is a known
|
||||||
|
function. If so, it is used as-is with no extra level of proxying
|
||||||
|
or cleanup, else an exception is thrown. It is legal to pass a
|
||||||
|
value of 0, indicating a NULL pointer, with the caveat that 0
|
||||||
|
_is_ a legal function pointer in WASM but it will not be accepted
|
||||||
|
as such _here_. (Justification: the function at address zero must
|
||||||
|
be one which initially came from the WASM module, not a method we
|
||||||
|
want to bind to a virtual table or VFS.)
|
||||||
|
|
||||||
|
This function returns a proxy for itself which is bound to tgt
|
||||||
|
and takes 2 args (name,func). That function returns the same
|
||||||
|
thing as this one, permitting calls to be chained.
|
||||||
|
|
||||||
|
If called with only 1 arg, it has no side effects but returns a
|
||||||
|
func with the same signature as described above.
|
||||||
|
|
||||||
|
ACHTUNG: because we cannot generically know how to transform JS
|
||||||
|
exceptions into result codes, the installed functions do no
|
||||||
|
automatic catching of exceptions. It is critical, to avoid
|
||||||
|
undefined behavior in the C layer, that methods mapped via
|
||||||
|
this function do not throw. The exception, as it were, to that
|
||||||
|
rule is...
|
||||||
|
|
||||||
|
If applyArgcCheck is true then each JS function (as opposed to
|
||||||
|
function pointers) gets wrapped in a proxy which asserts that it
|
||||||
|
is passed the expected number of arguments, throwing if the
|
||||||
|
argument count does not match expectations. That is only intended
|
||||||
|
for dev-time usage for sanity checking, and will leave the C
|
||||||
|
environment in an undefined state.
|
||||||
|
*/
|
||||||
|
const installMethod = function callee(
|
||||||
|
tgt, name, func, applyArgcCheck = callee.installMethodArgcCheck
|
||||||
|
){
|
||||||
|
if(!(tgt instanceof sqlite3.StructBinder.StructType)){
|
||||||
|
toss("Usage error: target object is-not-a StructType.");
|
||||||
|
}else if(!(func instanceof Function) && !wasm.isPtr(func)){
|
||||||
|
toss("Usage errror: expecting a Function or WASM pointer to one.");
|
||||||
|
}
|
||||||
|
if(1===arguments.length){
|
||||||
|
return (n,f)=>callee(tgt, n, f, applyArgcCheck);
|
||||||
|
}
|
||||||
|
if(!callee.argcProxy){
|
||||||
|
callee.argcProxy = function(tgt, funcName, func,sig){
|
||||||
|
return function(...args){
|
||||||
|
if(func.length!==arguments.length){
|
||||||
|
toss("Argument mismatch for",
|
||||||
|
tgt.structInfo.name+"::"+funcName
|
||||||
|
+": Native signature is:",sig);
|
||||||
|
}
|
||||||
|
return func.apply(this, args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/* An ondispose() callback for use with
|
||||||
|
sqlite3.StructBinder-created types. */
|
||||||
|
callee.removeFuncList = function(){
|
||||||
|
if(this.ondispose.__removeFuncList){
|
||||||
|
this.ondispose.__removeFuncList.forEach(
|
||||||
|
(v,ndx)=>{
|
||||||
|
if('number'===typeof v){
|
||||||
|
try{wasm.uninstallFunction(v)}
|
||||||
|
catch(e){/*ignore*/}
|
||||||
|
}
|
||||||
|
/* else it's a descriptive label for the next number in
|
||||||
|
the list. */
|
||||||
|
}
|
||||||
|
);
|
||||||
|
delete this.ondispose.__removeFuncList;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}/*static init*/
|
||||||
|
const sigN = tgt.memberSignature(name);
|
||||||
|
if(sigN.length<2){
|
||||||
|
toss("Member",name,"does not have a function pointer signature:",sigN);
|
||||||
|
}
|
||||||
|
const memKey = tgt.memberKey(name);
|
||||||
|
const fProxy = (applyArgcCheck && !wasm.isPtr(func))
|
||||||
|
/** This middle-man proxy is only for use during development, to
|
||||||
|
confirm that we always pass the proper number of
|
||||||
|
arguments. We know that the C-level code will always use the
|
||||||
|
correct argument count. */
|
||||||
|
? callee.argcProxy(tgt, memKey, func, sigN)
|
||||||
|
: func;
|
||||||
|
if(wasm.isPtr(fProxy)){
|
||||||
|
if(fProxy && !wasm.functionEntry(fProxy)){
|
||||||
|
toss("Pointer",fProxy,"is not a WASM function table entry.");
|
||||||
|
}
|
||||||
|
tgt[memKey] = fProxy;
|
||||||
|
}else{
|
||||||
|
const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true));
|
||||||
|
tgt[memKey] = pFunc;
|
||||||
|
if(!tgt.ondispose || !tgt.ondispose.__removeFuncList){
|
||||||
|
tgt.addOnDispose('ondispose.__removeFuncList handler',
|
||||||
|
callee.removeFuncList);
|
||||||
|
tgt.ondispose.__removeFuncList = [];
|
||||||
|
}
|
||||||
|
tgt.ondispose.__removeFuncList.push(memKey, pFunc);
|
||||||
|
}
|
||||||
|
return (n,f)=>callee(tgt, n, f, applyArgcCheck);
|
||||||
|
}/*installMethod*/;
|
||||||
|
installMethod.installMethodArgcCheck = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Installs methods into the given StructType-type instance. Each
|
||||||
|
entry in the given methods object must map to a known member of
|
||||||
|
the given StructType, else an exception will be triggered. See
|
||||||
|
installMethod() for more details, including the semantics of the
|
||||||
|
3rd argument.
|
||||||
|
|
||||||
|
As an exception to the above, if any two or more methods in the
|
||||||
|
2nd argument are the exact same function, installMethod() is
|
||||||
|
_not_ called for the 2nd and subsequent instances, and instead
|
||||||
|
those instances get assigned the same method pointer which is
|
||||||
|
created for the first instance. This optimization is primarily to
|
||||||
|
accommodate special handling of sqlite3_module::xConnect and
|
||||||
|
xCreate methods.
|
||||||
|
|
||||||
|
On success, returns its first argument. Throws on error.
|
||||||
|
*/
|
||||||
|
const installMethods = function(
|
||||||
|
structInstance, methods, applyArgcCheck = installMethod.installMethodArgcCheck
|
||||||
|
){
|
||||||
|
const seen = new Map /* map of <Function, memberName> */;
|
||||||
|
for(const k of Object.keys(methods)){
|
||||||
|
const m = methods[k];
|
||||||
|
const prior = seen.get(m);
|
||||||
|
if(prior){
|
||||||
|
const mkey = structInstance.memberKey(k);
|
||||||
|
structInstance[mkey] = structInstance[structInstance.memberKey(prior)];
|
||||||
|
}else{
|
||||||
|
installMethod(structInstance, k, m, applyArgcCheck);
|
||||||
|
seen.set(m, k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return structInstance;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Equivalent to calling installMethod(this,...arguments) with a
|
||||||
|
first argument of this object. If called with 1 or 2 arguments
|
||||||
|
and the first is an object, it's instead equivalent to calling
|
||||||
|
installMethods(this,...arguments).
|
||||||
|
*/
|
||||||
|
sqlite3.StructBinder.StructType.prototype.installMethod = function callee(
|
||||||
|
name, func, applyArgcCheck = installMethod.installMethodArgcCheck
|
||||||
|
){
|
||||||
|
return (arguments.length < 3 && name && 'object'===typeof name)
|
||||||
|
? installMethods(this, ...arguments)
|
||||||
|
: installMethod(this, ...arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Equivalent to calling installMethods() with a first argument
|
||||||
|
of this object.
|
||||||
|
*/
|
||||||
|
sqlite3.StructBinder.StructType.prototype.installMethods = function(
|
||||||
|
methods, applyArgcCheck = installMethod.installMethodArgcCheck
|
||||||
|
){
|
||||||
|
return installMethods(this, methods, applyArgcCheck);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Uses sqlite3_vfs_register() to register this
|
||||||
|
sqlite3.capi.sqlite3_vfs. This object must have already been
|
||||||
|
filled out properly. If the first argument is truthy, the VFS is
|
||||||
|
registered as the default VFS, else it is not.
|
||||||
|
|
||||||
|
On success, returns this object. Throws on error.
|
||||||
|
*/
|
||||||
|
capi.sqlite3_vfs.prototype.registerVfs = function(asDefault=false){
|
||||||
|
if(!(this instanceof sqlite3.capi.sqlite3_vfs)){
|
||||||
|
toss("Expecting a sqlite3_vfs-type argument.");
|
||||||
|
}
|
||||||
|
const rc = capi.sqlite3_vfs_register(this, asDefault ? 1 : 0);
|
||||||
|
if(rc){
|
||||||
|
toss("sqlite3_vfs_register(",this,") failed with rc",rc);
|
||||||
|
}
|
||||||
|
if(this.pointer !== capi.sqlite3_vfs_find(this.$zName)){
|
||||||
|
toss("BUG: sqlite3_vfs_find(vfs.$zName) failed for just-installed VFS",
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
A wrapper for installMethods() or registerVfs() to reduce
|
||||||
|
installation of a VFS and/or its I/O methods to a single
|
||||||
|
call.
|
||||||
|
|
||||||
|
Accepts an object which contains the properties "io" and/or
|
||||||
|
"vfs", each of which is itself an object with following properties:
|
||||||
|
|
||||||
|
- `struct`: an sqlite3.StructType-type struct. This must be a
|
||||||
|
populated (except for the methods) object of type
|
||||||
|
sqlite3_io_methods (for the "io" entry) or sqlite3_vfs (for the
|
||||||
|
"vfs" entry).
|
||||||
|
|
||||||
|
- `methods`: an object mapping sqlite3_io_methods method names
|
||||||
|
(e.g. 'xClose') to JS implementations of those methods. The JS
|
||||||
|
implementations must be call-compatible with their native
|
||||||
|
counterparts.
|
||||||
|
|
||||||
|
For each of those object, this function passes its (`struct`,
|
||||||
|
`methods`, (optional) `applyArgcCheck`) properties to
|
||||||
|
installMethods().
|
||||||
|
|
||||||
|
If the `vfs` entry is set then:
|
||||||
|
|
||||||
|
- Its `struct` property's registerVfs() is called. The
|
||||||
|
`vfs` entry may optionally have an `asDefault` property, which
|
||||||
|
gets passed as the argument to registerVfs().
|
||||||
|
|
||||||
|
- If `struct.$zName` is falsy and the entry has a string-type
|
||||||
|
`name` property, `struct.$zName` is set to the C-string form of
|
||||||
|
that `name` value before registerVfs() is called.
|
||||||
|
|
||||||
|
On success returns this object. Throws on error.
|
||||||
|
*/
|
||||||
|
vfs.installVfs = function(opt){
|
||||||
|
let count = 0;
|
||||||
|
const propList = ['io','vfs'];
|
||||||
|
for(const key of propList){
|
||||||
|
const o = opt[key];
|
||||||
|
if(o){
|
||||||
|
++count;
|
||||||
|
installMethods(o.struct, o.methods, !!o.applyArgcCheck);
|
||||||
|
if('vfs'===key){
|
||||||
|
if(!o.struct.$zName && 'string'===typeof o.name){
|
||||||
|
o.struct.addOnDispose(
|
||||||
|
o.struct.$zName = wasm.allocCString(o.name)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
o.struct.registerVfs(!!o.asDefault);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!count) toss("Misuse: installVfs() options object requires at least",
|
||||||
|
"one of:", propList);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Internal factory function for xVtab and xCursor impls.
|
||||||
|
*/
|
||||||
|
const __xWrapFactory = function(methodName,StructType){
|
||||||
|
return function(ptr,removeMapping=false){
|
||||||
|
if(0===arguments.length) ptr = new StructType;
|
||||||
|
if(ptr instanceof StructType){
|
||||||
|
//T.assert(!this.has(ptr.pointer));
|
||||||
|
this.set(ptr.pointer, ptr);
|
||||||
|
return ptr;
|
||||||
|
}else if(!wasm.isPtr(ptr)){
|
||||||
|
sqlite3.SQLite3Error.toss("Invalid argument to",methodName+"()");
|
||||||
|
}
|
||||||
|
let rc = this.get(ptr);
|
||||||
|
if(removeMapping) this.delete(ptr);
|
||||||
|
return rc;
|
||||||
|
}.bind(new Map);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
A factory function which implements a simple lifetime manager for
|
||||||
|
mappings between C struct pointers and their JS-level wrappers.
|
||||||
|
The first argument must be the logical name of the manager
|
||||||
|
(e.g. 'xVtab' or 'xCursor'), which is only used for error
|
||||||
|
reporting. The second must be the capi.XYZ struct-type value,
|
||||||
|
e.g. capi.sqlite3_vtab or capi.sqlite3_vtab_cursor.
|
||||||
|
|
||||||
|
Returns an object with 4 methods: create(), get(), unget(), and
|
||||||
|
dispose(), plus a StructType member with the value of the 2nd
|
||||||
|
argument. The methods are documented in the body of this
|
||||||
|
function.
|
||||||
|
*/
|
||||||
|
const StructPtrMapper = function(name, StructType){
|
||||||
|
const __xWrap = __xWrapFactory(name,StructType);
|
||||||
|
/**
|
||||||
|
This object houses a small API for managing mappings of (`T*`)
|
||||||
|
to StructType<T> objects, specifically within the lifetime
|
||||||
|
requirements of sqlite3_module methods.
|
||||||
|
*/
|
||||||
|
return Object.assign(Object.create(null),{
|
||||||
|
/** The StructType object for this object's API. */
|
||||||
|
StructType,
|
||||||
|
/**
|
||||||
|
Creates a new StructType object, writes its `pointer`
|
||||||
|
value to the given output pointer, and returns that
|
||||||
|
object. Its intended usage depends on StructType:
|
||||||
|
|
||||||
|
sqlite3_vtab: to be called from sqlite3_module::xConnect()
|
||||||
|
or xCreate() implementations.
|
||||||
|
|
||||||
|
sqlite3_vtab_cursor: to be called from xOpen().
|
||||||
|
|
||||||
|
This will throw if allocation of the StructType instance
|
||||||
|
fails or if ppOut is not a pointer-type value.
|
||||||
|
*/
|
||||||
|
create: (ppOut)=>{
|
||||||
|
const rc = __xWrap();
|
||||||
|
wasm.pokePtr(ppOut, rc.pointer);
|
||||||
|
return rc;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
Returns the StructType object previously mapped to the
|
||||||
|
given pointer using create(). Its intended usage depends
|
||||||
|
on StructType:
|
||||||
|
|
||||||
|
sqlite3_vtab: to be called from sqlite3_module methods which
|
||||||
|
take a (sqlite3_vtab*) pointer _except_ for
|
||||||
|
xDestroy()/xDisconnect(), in which case unget() or dispose().
|
||||||
|
|
||||||
|
sqlite3_vtab_cursor: to be called from any sqlite3_module methods
|
||||||
|
which take a `sqlite3_vtab_cursor*` argument except xClose(),
|
||||||
|
in which case use unget() or dispose().
|
||||||
|
|
||||||
|
Rule to remember: _never_ call dispose() on an instance
|
||||||
|
returned by this function.
|
||||||
|
*/
|
||||||
|
get: (pCObj)=>__xWrap(pCObj),
|
||||||
|
/**
|
||||||
|
Identical to get() but also disconnects the mapping between the
|
||||||
|
given pointer and the returned StructType object, such that
|
||||||
|
future calls to this function or get() with the same pointer
|
||||||
|
will return the undefined value. Its intended usage depends
|
||||||
|
on StructType:
|
||||||
|
|
||||||
|
sqlite3_vtab: to be called from sqlite3_module::xDisconnect() or
|
||||||
|
xDestroy() implementations or in error handling of a failed
|
||||||
|
xCreate() or xConnect().
|
||||||
|
|
||||||
|
sqlite3_vtab_cursor: to be called from xClose() or during
|
||||||
|
cleanup in a failed xOpen().
|
||||||
|
|
||||||
|
Calling this method obligates the caller to call dispose() on
|
||||||
|
the returned object when they're done with it.
|
||||||
|
*/
|
||||||
|
unget: (pCObj)=>__xWrap(pCObj,true),
|
||||||
|
/**
|
||||||
|
Works like unget() plus it calls dispose() on the
|
||||||
|
StructType object.
|
||||||
|
*/
|
||||||
|
dispose: (pCObj)=>{
|
||||||
|
const o = __xWrap(pCObj,true);
|
||||||
|
if(o) o.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
A lifetime-management object for mapping `sqlite3_vtab*`
|
||||||
|
instances in sqlite3_module methods to capi.sqlite3_vtab
|
||||||
|
objects.
|
||||||
|
|
||||||
|
The API docs are in the API-internal StructPtrMapper().
|
||||||
|
*/
|
||||||
|
vtab.xVtab = StructPtrMapper('xVtab', capi.sqlite3_vtab);
|
||||||
|
|
||||||
|
/**
|
||||||
|
A lifetime-management object for mapping `sqlite3_vtab_cursor*`
|
||||||
|
instances in sqlite3_module methods to capi.sqlite3_vtab_cursor
|
||||||
|
objects.
|
||||||
|
|
||||||
|
The API docs are in the API-internal StructPtrMapper().
|
||||||
|
*/
|
||||||
|
vtab.xCursor = StructPtrMapper('xCursor', capi.sqlite3_vtab_cursor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Convenience form of creating an sqlite3_index_info wrapper,
|
||||||
|
intended for use in xBestIndex implementations. Note that the
|
||||||
|
caller is expected to call dispose() on the returned object
|
||||||
|
before returning. Though not _strictly_ required, as that object
|
||||||
|
does not own the pIdxInfo memory, it is nonetheless good form.
|
||||||
|
*/
|
||||||
|
vtab.xIndexInfo = (pIdxInfo)=>new capi.sqlite3_index_info(pIdxInfo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Given an error object, this function returns
|
||||||
|
sqlite3.capi.SQLITE_NOMEM if (e instanceof
|
||||||
|
sqlite3.WasmAllocError), else it returns its
|
||||||
|
second argument. Its intended usage is in the methods
|
||||||
|
of a sqlite3_vfs or sqlite3_module:
|
||||||
|
|
||||||
|
```
|
||||||
|
try{
|
||||||
|
let rc = ...
|
||||||
|
return rc;
|
||||||
|
}catch(e){
|
||||||
|
return sqlite3.vtab.exceptionToRc(e, sqlite3.capi.SQLITE_XYZ);
|
||||||
|
// where SQLITE_XYZ is some call-appropriate result code.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
/**vfs.exceptionToRc = vtab.exceptionToRc =
|
||||||
|
(e, defaultRc=capi.SQLITE_ERROR)=>(
|
||||||
|
(e instanceof sqlite3.WasmAllocError)
|
||||||
|
? capi.SQLITE_NOMEM
|
||||||
|
: defaultRc
|
||||||
|
);*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
Given an sqlite3_module method name and error object, this
|
||||||
|
function returns sqlite3.capi.SQLITE_NOMEM if (e instanceof
|
||||||
|
sqlite3.WasmAllocError), else it returns its second argument. Its
|
||||||
|
intended usage is in the methods of a sqlite3_vfs or
|
||||||
|
sqlite3_module:
|
||||||
|
|
||||||
|
```
|
||||||
|
try{
|
||||||
|
let rc = ...
|
||||||
|
return rc;
|
||||||
|
}catch(e){
|
||||||
|
return sqlite3.vtab.xError(
|
||||||
|
'xColumn', e, sqlite3.capi.SQLITE_XYZ);
|
||||||
|
// where SQLITE_XYZ is some call-appropriate result code.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If no 3rd argument is provided, its default depends on
|
||||||
|
the error type:
|
||||||
|
|
||||||
|
- An sqlite3.WasmAllocError always resolves to capi.SQLITE_NOMEM.
|
||||||
|
|
||||||
|
- If err is an SQLite3Error then its `resultCode` property
|
||||||
|
is used.
|
||||||
|
|
||||||
|
- If all else fails, capi.SQLITE_ERROR is used.
|
||||||
|
|
||||||
|
If xError.errorReporter is a function, it is called in
|
||||||
|
order to report the error, else the error is not reported.
|
||||||
|
If that function throws, that exception is ignored.
|
||||||
|
*/
|
||||||
|
vtab.xError = function f(methodName, err, defaultRc){
|
||||||
|
if(f.errorReporter instanceof Function){
|
||||||
|
try{f.errorReporter("sqlite3_module::"+methodName+"(): "+err.message);}
|
||||||
|
catch(e){/*ignored*/}
|
||||||
|
}
|
||||||
|
let rc;
|
||||||
|
if(err instanceof sqlite3.WasmAllocError) rc = capi.SQLITE_NOMEM;
|
||||||
|
else if(arguments.length>2) rc = defaultRc;
|
||||||
|
else if(err instanceof sqlite3.SQLite3Error) rc = err.resultCode;
|
||||||
|
return rc || capi.SQLITE_ERROR;
|
||||||
|
};
|
||||||
|
vtab.xError.errorReporter = 1 ? console.error.bind(console) : false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
"The problem" with this is that it introduces an outer function with
|
||||||
|
a different arity than the passed-in method callback. That means we
|
||||||
|
cannot do argc validation on these. Additionally, some methods (namely
|
||||||
|
xConnect) may have call-specific error handling. It would be a shame to
|
||||||
|
hard-coded that per-method support in this function.
|
||||||
|
*/
|
||||||
|
/** vtab.methodCatcher = function(methodName, method, defaultErrRc=capi.SQLITE_ERROR){
|
||||||
|
return function(...args){
|
||||||
|
try { method(...args); }
|
||||||
|
}catch(e){ return vtab.xError(methodName, e, defaultRc) }
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
A helper for sqlite3_vtab::xRowid() and xUpdate()
|
||||||
|
implementations. It must be passed the final argument to one of
|
||||||
|
those methods (an output pointer to an int64 row ID) and the
|
||||||
|
value to store at the output pointer's address. Returns the same
|
||||||
|
as wasm.poke() and will throw if the 1st or 2nd arguments
|
||||||
|
are invalid for that function.
|
||||||
|
|
||||||
|
Example xRowid impl:
|
||||||
|
|
||||||
|
```
|
||||||
|
const xRowid = (pCursor, ppRowid64)=>{
|
||||||
|
const c = vtab.xCursor(pCursor);
|
||||||
|
vtab.xRowid(ppRowid64, c.myRowId);
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
vtab.xRowid = (ppRowid64, value)=>wasm.poke(ppRowid64, value, 'i64');
|
||||||
|
|
||||||
|
/**
|
||||||
|
A helper to initialize and set up an sqlite3_module object for
|
||||||
|
later installation into individual databases using
|
||||||
|
sqlite3_create_module(). Requires an object with the following
|
||||||
|
properties:
|
||||||
|
|
||||||
|
- `methods`: an object containing a mapping of properties with
|
||||||
|
the C-side names of the sqlite3_module methods, e.g. xCreate,
|
||||||
|
xBestIndex, etc., to JS implementations for those functions.
|
||||||
|
Certain special-case handling is performed, as described below.
|
||||||
|
|
||||||
|
- `catchExceptions` (default=false): if truthy, the given methods
|
||||||
|
are not mapped as-is, but are instead wrapped inside wrappers
|
||||||
|
which translate exceptions into result codes of SQLITE_ERROR or
|
||||||
|
SQLITE_NOMEM, depending on whether the exception is an
|
||||||
|
sqlite3.WasmAllocError. In the case of the xConnect and xCreate
|
||||||
|
methods, the exception handler also sets the output error
|
||||||
|
string to the exception's error string.
|
||||||
|
|
||||||
|
- OPTIONAL `struct`: a sqlite3.capi.sqlite3_module() instance. If
|
||||||
|
not set, one will be created automatically. If the current
|
||||||
|
"this" is-a sqlite3_module then it is unconditionally used in
|
||||||
|
place of `struct`.
|
||||||
|
|
||||||
|
- OPTIONAL `iVersion`: if set, it must be an integer value and it
|
||||||
|
gets assigned to the `$iVersion` member of the struct object.
|
||||||
|
If it's _not_ set, and the passed-in `struct` object's `$iVersion`
|
||||||
|
is 0 (the default) then this function attempts to define a value
|
||||||
|
for that property based on the list of methods it has.
|
||||||
|
|
||||||
|
If `catchExceptions` is false, it is up to the client to ensure
|
||||||
|
that no exceptions escape the methods, as doing so would move
|
||||||
|
them through the C API, leading to undefined
|
||||||
|
behavior. (vtab.xError() is intended to assist in reporting
|
||||||
|
such exceptions.)
|
||||||
|
|
||||||
|
Certain methods may refer to the same implementation. To simplify
|
||||||
|
the definition of such methods:
|
||||||
|
|
||||||
|
- If `methods.xConnect` is `true` then the value of
|
||||||
|
`methods.xCreate` is used in its place, and vice versa. sqlite
|
||||||
|
treats xConnect/xCreate functions specially if they are exactly
|
||||||
|
the same function (same pointer value).
|
||||||
|
|
||||||
|
- If `methods.xDisconnect` is true then the value of
|
||||||
|
`methods.xDestroy` is used in its place, and vice versa.
|
||||||
|
|
||||||
|
This is to facilitate creation of those methods inline in the
|
||||||
|
passed-in object without requiring the client to explicitly get a
|
||||||
|
reference to one of them in order to assign it to the other
|
||||||
|
one.
|
||||||
|
|
||||||
|
The `catchExceptions`-installed handlers will account for
|
||||||
|
identical references to the above functions and will install the
|
||||||
|
same wrapper function for both.
|
||||||
|
|
||||||
|
The given methods are expected to return integer values, as
|
||||||
|
expected by the C API. If `catchExceptions` is truthy, the return
|
||||||
|
value of the wrapped function will be used as-is and will be
|
||||||
|
translated to 0 if the function returns a falsy value (e.g. if it
|
||||||
|
does not have an explicit return). If `catchExceptions` is _not_
|
||||||
|
active, the method implementations must explicitly return integer
|
||||||
|
values.
|
||||||
|
|
||||||
|
Throws on error. On success, returns the sqlite3_module object
|
||||||
|
(`this` or `opt.struct` or a new sqlite3_module instance,
|
||||||
|
depending on how it's called).
|
||||||
|
*/
|
||||||
|
vtab.setupModule = function(opt){
|
||||||
|
let createdMod = false;
|
||||||
|
const mod = (this instanceof capi.sqlite3_module)
|
||||||
|
? this : (opt.struct || (createdMod = new capi.sqlite3_module()));
|
||||||
|
try{
|
||||||
|
const methods = opt.methods || toss("Missing 'methods' object.");
|
||||||
|
for(const e of Object.entries({
|
||||||
|
// -----^ ==> [k,v] triggers a broken code transformation in
|
||||||
|
// some versions of the emsdk toolchain.
|
||||||
|
xConnect: 'xCreate', xDisconnect: 'xDestroy'
|
||||||
|
})){
|
||||||
|
// Remap X=true to X=Y for certain X/Y combinations
|
||||||
|
const k = e[0], v = e[1];
|
||||||
|
if(true === methods[k]) methods[k] = methods[v];
|
||||||
|
else if(true === methods[v]) methods[v] = methods[k];
|
||||||
|
}
|
||||||
|
if(opt.catchExceptions){
|
||||||
|
const fwrap = function(methodName, func){
|
||||||
|
if(['xConnect','xCreate'].indexOf(methodName) >= 0){
|
||||||
|
return function(pDb, pAux, argc, argv, ppVtab, pzErr){
|
||||||
|
try{return func(...arguments) || 0}
|
||||||
|
catch(e){
|
||||||
|
if(!(e instanceof sqlite3.WasmAllocError)){
|
||||||
|
wasm.dealloc(wasm.peekPtr(pzErr));
|
||||||
|
wasm.pokePtr(pzErr, wasm.allocCString(e.message));
|
||||||
|
}
|
||||||
|
return vtab.xError(methodName, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}else{
|
||||||
|
return function(...args){
|
||||||
|
try{return func(...args) || 0}
|
||||||
|
catch(e){
|
||||||
|
return vtab.xError(methodName, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const mnames = [
|
||||||
|
'xCreate', 'xConnect', 'xBestIndex', 'xDisconnect',
|
||||||
|
'xDestroy', 'xOpen', 'xClose', 'xFilter', 'xNext',
|
||||||
|
'xEof', 'xColumn', 'xRowid', 'xUpdate',
|
||||||
|
'xBegin', 'xSync', 'xCommit', 'xRollback',
|
||||||
|
'xFindFunction', 'xRename', 'xSavepoint', 'xRelease',
|
||||||
|
'xRollbackTo', 'xShadowName'
|
||||||
|
];
|
||||||
|
const remethods = Object.create(null);
|
||||||
|
for(const k of mnames){
|
||||||
|
const m = methods[k];
|
||||||
|
if(!(m instanceof Function)) continue;
|
||||||
|
else if('xConnect'===k && methods.xCreate===m){
|
||||||
|
remethods[k] = methods.xCreate;
|
||||||
|
}else if('xCreate'===k && methods.xConnect===m){
|
||||||
|
remethods[k] = methods.xConnect;
|
||||||
|
}else{
|
||||||
|
remethods[k] = fwrap(k, m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
installMethods(mod, remethods, false);
|
||||||
|
}else{
|
||||||
|
// No automatic exception handling. Trust the client
|
||||||
|
// to not throw.
|
||||||
|
installMethods(
|
||||||
|
mod, methods, !!opt.applyArgcCheck/*undocumented option*/
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if(0===mod.$iVersion){
|
||||||
|
let v;
|
||||||
|
if('number'===typeof opt.iVersion) v = opt.iVersion;
|
||||||
|
else if(mod.$xShadowName) v = 3;
|
||||||
|
else if(mod.$xSavePoint || mod.$xRelease || mod.$xRollbackTo) v = 2;
|
||||||
|
else v = 1;
|
||||||
|
mod.$iVersion = v;
|
||||||
|
}
|
||||||
|
}catch(e){
|
||||||
|
if(createdMod) createdMod.dispose();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
return mod;
|
||||||
|
}/*setupModule()*/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Equivalent to calling vtab.setupModule() with this sqlite3_module
|
||||||
|
object as the call's `this`.
|
||||||
|
*/
|
||||||
|
capi.sqlite3_module.prototype.setupModule = function(opt){
|
||||||
|
return vtab.setupModule.call(this, opt);
|
||||||
|
};
|
||||||
|
}/*sqlite3ApiBootstrap.initializers.push()*/);
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
/*
|
|
||||||
** 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 sqlite.VfsHelper, an object which exists
|
|
||||||
to assist in the creation of JavaScript implementations of
|
|
||||||
sqlite3_vfs. It is NOT part of the public API, and is an
|
|
||||||
internal implemenation detail for use in this project's
|
|
||||||
own development of VFSes. It may be exposed to clients
|
|
||||||
at some point, provided there is value in doing so.
|
|
||||||
*/
|
|
||||||
'use strict';
|
|
||||||
self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|
||||||
const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss;
|
|
||||||
const vh = Object.create(null);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Does nothing more than holds a permanent reference to each
|
|
||||||
argument. This is useful in some cases to ensure that, e.g., a
|
|
||||||
custom sqlite3_io_methods instance does not get
|
|
||||||
garbage-collected.
|
|
||||||
|
|
||||||
Returns this object.
|
|
||||||
*/
|
|
||||||
vh.holdReference = function(...args){
|
|
||||||
for(const v of args) this.refs.add(v);
|
|
||||||
return vh;
|
|
||||||
}.bind({refs: new Set});
|
|
||||||
|
|
||||||
/**
|
|
||||||
Installs a StructBinder-bound function pointer member of the
|
|
||||||
given name and function in the given StructType target object.
|
|
||||||
It creates a WASM proxy for the given function and arranges for
|
|
||||||
that proxy to be cleaned up when tgt.dispose() is called. Throws
|
|
||||||
on the slightest hint of error, e.g. tgt is-not-a StructType,
|
|
||||||
name does not map to a struct-bound member, etc.
|
|
||||||
|
|
||||||
If applyArgcCheck is true then each method gets wrapped in a
|
|
||||||
proxy which asserts that it is passed the expected number of
|
|
||||||
arguments, throwing if the argument count does not match
|
|
||||||
expectations. That is only recommended for dev-time usage for
|
|
||||||
sanity checking. Once a VFS implementation is known to be
|
|
||||||
working, it is a given that the C API will never call it with the
|
|
||||||
wrong argument count.
|
|
||||||
|
|
||||||
Returns a proxy for this function which is bound to tgt and takes
|
|
||||||
2 args (name,func). That function returns the same thing,
|
|
||||||
permitting calls to be chained.
|
|
||||||
|
|
||||||
If called with only 1 arg, it has no side effects but returns a
|
|
||||||
func with the same signature as described above.
|
|
||||||
|
|
||||||
If tgt.ondispose is set before this is called then it _must_
|
|
||||||
be an array, to which this function will append entries.
|
|
||||||
*/
|
|
||||||
vh.installMethod = function callee(tgt, name, func,
|
|
||||||
applyArgcCheck=callee.installMethodArgcCheck){
|
|
||||||
if(!(tgt instanceof sqlite3.StructBinder.StructType)){
|
|
||||||
toss("Usage error: target object is-not-a StructType.");
|
|
||||||
}
|
|
||||||
if(1===arguments.length){
|
|
||||||
return (n,f)=>callee(tgt, n, f, applyArgcCheck);
|
|
||||||
}
|
|
||||||
if(!callee.argcProxy){
|
|
||||||
callee.argcProxy = function(func,sig){
|
|
||||||
return function(...args){
|
|
||||||
if(func.length!==arguments.length){
|
|
||||||
toss("Argument mismatch. Native signature is:",sig);
|
|
||||||
}
|
|
||||||
return func.apply(this, args);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/* An ondispose() callback for use with
|
|
||||||
sqlite3.StructBinder-created types. */
|
|
||||||
callee.removeFuncList = function(){
|
|
||||||
if(this.ondispose.__removeFuncList){
|
|
||||||
this.ondispose.__removeFuncList.forEach(
|
|
||||||
(v,ndx)=>{
|
|
||||||
if('number'===typeof v){
|
|
||||||
try{wasm.uninstallFunction(v)}
|
|
||||||
catch(e){/*ignore*/}
|
|
||||||
}
|
|
||||||
/* else it's a descriptive label for the next number in
|
|
||||||
the list. */
|
|
||||||
}
|
|
||||||
);
|
|
||||||
delete this.ondispose.__removeFuncList;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}/*static init*/
|
|
||||||
const sigN = tgt.memberSignature(name);
|
|
||||||
if(sigN.length<2){
|
|
||||||
toss("Member",name," is not a function pointer. Signature =",sigN);
|
|
||||||
}
|
|
||||||
const memKey = tgt.memberKey(name);
|
|
||||||
const fProxy = applyArgcCheck
|
|
||||||
/** This middle-man proxy is only for use during development, to
|
|
||||||
confirm that we always pass the proper number of
|
|
||||||
arguments. We know that the C-level code will always use the
|
|
||||||
correct argument count. */
|
|
||||||
? callee.argcProxy(func, sigN)
|
|
||||||
: func;
|
|
||||||
const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true));
|
|
||||||
tgt[memKey] = pFunc;
|
|
||||||
if(!tgt.ondispose) tgt.ondispose = [];
|
|
||||||
if(!tgt.ondispose.__removeFuncList){
|
|
||||||
tgt.ondispose.push('ondispose.__removeFuncList handler',
|
|
||||||
callee.removeFuncList);
|
|
||||||
tgt.ondispose.__removeFuncList = [];
|
|
||||||
}
|
|
||||||
tgt.ondispose.__removeFuncList.push(memKey, pFunc);
|
|
||||||
return (n,f)=>callee(tgt, n, f, applyArgcCheck);
|
|
||||||
}/*installMethod*/;
|
|
||||||
vh.installMethod.installMethodArgcCheck = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Installs methods into the given StructType-type object. Each
|
|
||||||
entry in the given methods object must map to a known member of
|
|
||||||
the given StructType, else an exception will be triggered.
|
|
||||||
See installMethod() for more details, including the semantics
|
|
||||||
of the 3rd argument.
|
|
||||||
|
|
||||||
On success, passes its first argument to holdRefence() and
|
|
||||||
returns this object. Throws on error.
|
|
||||||
*/
|
|
||||||
vh.installMethods = function(structType, methods,
|
|
||||||
applyArgcCheck=vh.installMethod.installMethodArgcCheck){
|
|
||||||
for(const k of Object.keys(methods)){
|
|
||||||
vh.installMethod(structType, k, methods[k], applyArgcCheck);
|
|
||||||
}
|
|
||||||
return vh.holdReference(structType);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
Uses sqlite3_vfs_register() to register the
|
|
||||||
sqlite3.capi.sqlite3_vfs-type vfs, which must have already been
|
|
||||||
filled out properly. If the 2nd argument is truthy, the VFS is
|
|
||||||
registered as the default VFS, else it is not.
|
|
||||||
|
|
||||||
On success, passes its first argument to this.holdReference() and
|
|
||||||
returns this object. Throws on error.
|
|
||||||
*/
|
|
||||||
vh.registerVfs = function(vfs, asDefault=false){
|
|
||||||
if(!(vfs instanceof sqlite3.capi.sqlite3_vfs)){
|
|
||||||
toss("Expecting a sqlite3_vfs-type argument.");
|
|
||||||
}
|
|
||||||
const rc = capi.sqlite3_vfs_register(vfs.pointer, asDefault ? 1 : 0);
|
|
||||||
if(rc){
|
|
||||||
toss("sqlite3_vfs_register(",vfs,") failed with rc",rc);
|
|
||||||
}
|
|
||||||
if(vfs.pointer !== capi.sqlite3_vfs_find(vfs.$zName)){
|
|
||||||
toss("BUG: sqlite3_vfs_find(vfs.$zName) failed for just-installed VFS",
|
|
||||||
vfs);
|
|
||||||
}
|
|
||||||
return vh.holdReference(vfs);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
A wrapper for installMethods() or registerVfs() to reduce
|
|
||||||
installation of a VFS and/or its I/O methods to a single
|
|
||||||
call.
|
|
||||||
|
|
||||||
Accepts an object which contains the properties "io" and/or
|
|
||||||
"vfs", each of which is itself an object with following properties:
|
|
||||||
|
|
||||||
- `struct`: an sqlite3.StructType-type struct. This must be a
|
|
||||||
populated (except for the methods) object of type
|
|
||||||
sqlite3_io_methods (for the "io" entry) or sqlite3_vfs (for the
|
|
||||||
"vfs" entry).
|
|
||||||
|
|
||||||
- `methods`: an object mapping sqlite3_io_methods method names
|
|
||||||
(e.g. 'xClose') to JS implementations of those methods.
|
|
||||||
|
|
||||||
For each of those object, this function passes its (`struct`,
|
|
||||||
`methods`, (optional) `applyArgcCheck`) properties to
|
|
||||||
this.installMethods().
|
|
||||||
|
|
||||||
If the `vfs` entry is set then:
|
|
||||||
|
|
||||||
- Its `struct` property is passed to this.registerVfs(). The
|
|
||||||
`vfs` entry may optionally have an `asDefault` property, which
|
|
||||||
gets passed as the 2nd argument to registerVfs().
|
|
||||||
|
|
||||||
- If `struct.$zName` is falsy and the entry has a string-type
|
|
||||||
`name` property, `struct.$zName` is set to the C-string form of
|
|
||||||
that `name` value before registerVfs() is called.
|
|
||||||
|
|
||||||
On success returns this object. Throws on error.
|
|
||||||
*/
|
|
||||||
vh.installVfs = function(opt){
|
|
||||||
let count = 0;
|
|
||||||
const propList = ['io','vfs'];
|
|
||||||
for(const key of propList){
|
|
||||||
const o = opt[key];
|
|
||||||
if(o){
|
|
||||||
++count;
|
|
||||||
this.installMethods(o.struct, o.methods, !!o.applyArgcCheck);
|
|
||||||
if('vfs'===key){
|
|
||||||
if(!o.struct.$zName && 'string'===typeof o.name){
|
|
||||||
o.struct.$zName = wasm.allocCString(o.name);
|
|
||||||
/* Note that we leak that C-string. */
|
|
||||||
}
|
|
||||||
this.registerVfs(o.struct, !!o.asDefault);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!count) toss("Misuse: installVfs() options object requires at least",
|
|
||||||
"one of:", propList);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
sqlite3.VfsHelper = vh;
|
|
||||||
}/*sqlite3ApiBootstrap.initializers.push()*/);
|
|
||||||
@@ -678,7 +678,7 @@ const installOpfsVfs = function callee(options){
|
|||||||
given pFile is open.
|
given pFile is open.
|
||||||
*/
|
*/
|
||||||
const f = __openFiles[pFile];
|
const f = __openFiles[pFile];
|
||||||
wasm.setMemValue(pOut, f.lockType ? 1 : 0, 'i32');
|
wasm.poke(pOut, f.lockType ? 1 : 0, 'i32');
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
xClose: function(pFile){
|
xClose: function(pFile){
|
||||||
@@ -711,7 +711,7 @@ const installOpfsVfs = function callee(options){
|
|||||||
if(0==rc){
|
if(0==rc){
|
||||||
try {
|
try {
|
||||||
const sz = state.s11n.deserialize()[0];
|
const sz = state.s11n.deserialize()[0];
|
||||||
wasm.setMemValue(pSz64, sz, 'i64');
|
wasm.poke(pSz64, sz, 'i64');
|
||||||
}catch(e){
|
}catch(e){
|
||||||
error("Unexpected error reading xFileSize() result:",e);
|
error("Unexpected error reading xFileSize() result:",e);
|
||||||
rc = state.sq3Codes.SQLITE_IOERR;
|
rc = state.sq3Codes.SQLITE_IOERR;
|
||||||
@@ -803,27 +803,27 @@ const installOpfsVfs = function callee(options){
|
|||||||
const vfsSyncWrappers = {
|
const vfsSyncWrappers = {
|
||||||
xAccess: function(pVfs,zName,flags,pOut){
|
xAccess: function(pVfs,zName,flags,pOut){
|
||||||
mTimeStart('xAccess');
|
mTimeStart('xAccess');
|
||||||
const rc = opRun('xAccess', wasm.cstringToJs(zName));
|
const rc = opRun('xAccess', wasm.cstrToJs(zName));
|
||||||
wasm.setMemValue( pOut, (rc ? 0 : 1), 'i32' );
|
wasm.poke( pOut, (rc ? 0 : 1), 'i32' );
|
||||||
mTimeEnd();
|
mTimeEnd();
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
xCurrentTime: function(pVfs,pOut){
|
xCurrentTime: function(pVfs,pOut){
|
||||||
/* If it turns out that we need to adjust for timezone, see:
|
/* If it turns out that we need to adjust for timezone, see:
|
||||||
https://stackoverflow.com/a/11760121/1458521 */
|
https://stackoverflow.com/a/11760121/1458521 */
|
||||||
wasm.setMemValue(pOut, 2440587.5 + (new Date().getTime()/86400000),
|
wasm.poke(pOut, 2440587.5 + (new Date().getTime()/86400000),
|
||||||
'double');
|
'double');
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
xCurrentTimeInt64: function(pVfs,pOut){
|
xCurrentTimeInt64: function(pVfs,pOut){
|
||||||
// TODO: confirm that this calculation is correct
|
// TODO: confirm that this calculation is correct
|
||||||
wasm.setMemValue(pOut, (2440587.5 * 86400000) + new Date().getTime(),
|
wasm.poke(pOut, (2440587.5 * 86400000) + new Date().getTime(),
|
||||||
'i64');
|
'i64');
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
xDelete: function(pVfs, zName, doSyncDir){
|
xDelete: function(pVfs, zName, doSyncDir){
|
||||||
mTimeStart('xDelete');
|
mTimeStart('xDelete');
|
||||||
opRun('xDelete', wasm.cstringToJs(zName), doSyncDir, false);
|
opRun('xDelete', wasm.cstrToJs(zName), doSyncDir, false);
|
||||||
/* We're ignoring errors because we cannot yet differentiate
|
/* We're ignoring errors because we cannot yet differentiate
|
||||||
between harmless and non-harmless failures. */
|
between harmless and non-harmless failures. */
|
||||||
mTimeEnd();
|
mTimeEnd();
|
||||||
@@ -855,7 +855,7 @@ const installOpfsVfs = function callee(options){
|
|||||||
C-string here. */
|
C-string here. */
|
||||||
opfsFlags |= state.opfsFlags.OPFS_UNLOCK_ASAP;
|
opfsFlags |= state.opfsFlags.OPFS_UNLOCK_ASAP;
|
||||||
}
|
}
|
||||||
zName = wasm.cstringToJs(zName);
|
zName = wasm.cstrToJs(zName);
|
||||||
}
|
}
|
||||||
const fh = Object.create(null);
|
const fh = Object.create(null);
|
||||||
fh.fid = pFile;
|
fh.fid = pFile;
|
||||||
@@ -867,7 +867,7 @@ const installOpfsVfs = function callee(options){
|
|||||||
/* Recall that sqlite3_vfs::xClose() will be called, even on
|
/* Recall that sqlite3_vfs::xClose() will be called, even on
|
||||||
error, unless pFile->pMethods is NULL. */
|
error, unless pFile->pMethods is NULL. */
|
||||||
if(fh.readOnly){
|
if(fh.readOnly){
|
||||||
wasm.setMemValue(pOutFlags, capi.SQLITE_OPEN_READONLY, 'i32');
|
wasm.poke(pOutFlags, capi.SQLITE_OPEN_READONLY, 'i32');
|
||||||
}
|
}
|
||||||
__openFiles[pFile] = fh;
|
__openFiles[pFile] = fh;
|
||||||
fh.sabView = state.sabFileBufView;
|
fh.sabView = state.sabFileBufView;
|
||||||
@@ -1156,11 +1156,8 @@ const installOpfsVfs = function callee(options){
|
|||||||
opt.vfs = opfsVfs.$zName;
|
opt.vfs = opfsVfs.$zName;
|
||||||
sqlite3.oo1.DB.dbCtorHelper.call(this, opt);
|
sqlite3.oo1.DB.dbCtorHelper.call(this, opt);
|
||||||
};
|
};
|
||||||
sqlite3.oo1.OpfsDb =
|
|
||||||
opfsUtil.OpfsDb /* sqlite3.opfs.OpfsDb => deprecated name -
|
|
||||||
will be phased out Real Soon */ =
|
|
||||||
OpfsDb;
|
|
||||||
OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype);
|
OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype);
|
||||||
|
sqlite3.oo1.OpfsDb = OpfsDb;
|
||||||
sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenSql(
|
sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenSql(
|
||||||
opfsVfs.pointer,
|
opfsVfs.pointer,
|
||||||
function(oo1Db, sqlite3){
|
function(oo1Db, sqlite3){
|
||||||
@@ -1185,7 +1182,7 @@ const installOpfsVfs = function callee(options){
|
|||||||
], 0, 0, 0);
|
], 0, 0, 0);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}/*extend sqlite3.oo1*/
|
||||||
|
|
||||||
const sanityCheck = function(){
|
const sanityCheck = function(){
|
||||||
const scope = wasm.scopedAllocPush();
|
const scope = wasm.scopedAllocPush();
|
||||||
@@ -1205,7 +1202,7 @@ const installOpfsVfs = function callee(options){
|
|||||||
log("deserialize() says:",rc);
|
log("deserialize() says:",rc);
|
||||||
if("This is ä string."!==rc[0]) toss("String d13n error.");
|
if("This is ä string."!==rc[0]) toss("String d13n error.");
|
||||||
vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut);
|
vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut);
|
||||||
rc = wasm.getMemValue(pOut,'i32');
|
rc = wasm.peek(pOut,'i32');
|
||||||
log("xAccess(",dbFile,") exists ?=",rc);
|
log("xAccess(",dbFile,") exists ?=",rc);
|
||||||
rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile,
|
rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile,
|
||||||
fid, openFlags, pOut);
|
fid, openFlags, pOut);
|
||||||
@@ -1216,22 +1213,22 @@ const installOpfsVfs = function callee(options){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut);
|
vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut);
|
||||||
rc = wasm.getMemValue(pOut,'i32');
|
rc = wasm.peek(pOut,'i32');
|
||||||
if(!rc) toss("xAccess() failed to detect file.");
|
if(!rc) toss("xAccess() failed to detect file.");
|
||||||
rc = ioSyncWrappers.xSync(sq3File.pointer, 0);
|
rc = ioSyncWrappers.xSync(sq3File.pointer, 0);
|
||||||
if(rc) toss('sync failed w/ rc',rc);
|
if(rc) toss('sync failed w/ rc',rc);
|
||||||
rc = ioSyncWrappers.xTruncate(sq3File.pointer, 1024);
|
rc = ioSyncWrappers.xTruncate(sq3File.pointer, 1024);
|
||||||
if(rc) toss('truncate failed w/ rc',rc);
|
if(rc) toss('truncate failed w/ rc',rc);
|
||||||
wasm.setMemValue(pOut,0,'i64');
|
wasm.poke(pOut,0,'i64');
|
||||||
rc = ioSyncWrappers.xFileSize(sq3File.pointer, pOut);
|
rc = ioSyncWrappers.xFileSize(sq3File.pointer, pOut);
|
||||||
if(rc) toss('xFileSize failed w/ rc',rc);
|
if(rc) toss('xFileSize failed w/ rc',rc);
|
||||||
log("xFileSize says:",wasm.getMemValue(pOut, 'i64'));
|
log("xFileSize says:",wasm.peek(pOut, 'i64'));
|
||||||
rc = ioSyncWrappers.xWrite(sq3File.pointer, zDbFile, 10, 1);
|
rc = ioSyncWrappers.xWrite(sq3File.pointer, zDbFile, 10, 1);
|
||||||
if(rc) toss("xWrite() failed!");
|
if(rc) toss("xWrite() failed!");
|
||||||
const readBuf = wasm.scopedAlloc(16);
|
const readBuf = wasm.scopedAlloc(16);
|
||||||
rc = ioSyncWrappers.xRead(sq3File.pointer, readBuf, 6, 2);
|
rc = ioSyncWrappers.xRead(sq3File.pointer, readBuf, 6, 2);
|
||||||
wasm.setMemValue(readBuf+6,0);
|
wasm.poke(readBuf+6,0);
|
||||||
let jRead = wasm.cstringToJs(readBuf);
|
let jRead = wasm.cstrToJs(readBuf);
|
||||||
log("xRead() got:",jRead);
|
log("xRead() got:",jRead);
|
||||||
if("sanity"!==jRead) toss("Unexpected xRead() value.");
|
if("sanity"!==jRead) toss("Unexpected xRead() value.");
|
||||||
if(vfsSyncWrappers.xSleep){
|
if(vfsSyncWrappers.xSleep){
|
||||||
@@ -1244,7 +1241,7 @@ const installOpfsVfs = function callee(options){
|
|||||||
log("Deleting file:",dbFile);
|
log("Deleting file:",dbFile);
|
||||||
vfsSyncWrappers.xDelete(opfsVfs.pointer, zDbFile, 0x1234);
|
vfsSyncWrappers.xDelete(opfsVfs.pointer, zDbFile, 0x1234);
|
||||||
vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut);
|
vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut);
|
||||||
rc = wasm.getMemValue(pOut,'i32');
|
rc = wasm.peek(pOut,'i32');
|
||||||
if(rc) toss("Expecting 0 from xAccess(",dbFile,") after xDelete().");
|
if(rc) toss("Expecting 0 from xAccess(",dbFile,") after xDelete().");
|
||||||
warn("End of OPFS sanity checks.");
|
warn("End of OPFS sanity checks.");
|
||||||
}finally{
|
}finally{
|
||||||
@@ -1271,7 +1268,7 @@ const installOpfsVfs = function callee(options){
|
|||||||
and has finished initializing, so the real work can
|
and has finished initializing, so the real work can
|
||||||
begin...*/
|
begin...*/
|
||||||
try {
|
try {
|
||||||
sqlite3.VfsHelper.installVfs({
|
sqlite3.vfs.installVfs({
|
||||||
io: {struct: opfsIoMethods, methods: ioSyncWrappers},
|
io: {struct: opfsIoMethods, methods: ioSyncWrappers},
|
||||||
vfs: {struct: opfsVfs, methods: vfsSyncWrappers}
|
vfs: {struct: opfsVfs, methods: vfsSyncWrappers}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -368,7 +368,7 @@ void sqlite3_wasm_test_struct(WasmTestStruct * s){
|
|||||||
*/
|
*/
|
||||||
SQLITE_WASM_KEEP
|
SQLITE_WASM_KEEP
|
||||||
const char * sqlite3_wasm_enum_json(void){
|
const char * sqlite3_wasm_enum_json(void){
|
||||||
static char aBuffer[1024 * 12] = {0} /* where the JSON goes */;
|
static char aBuffer[1024 * 20] = {0} /* where the JSON goes */;
|
||||||
int n = 0, nChildren = 0, nStruct = 0
|
int n = 0, nChildren = 0, nStruct = 0
|
||||||
/* output counters for figuring out where commas go */;
|
/* output counters for figuring out where commas go */;
|
||||||
char * zPos = &aBuffer[1] /* skip first byte for now to help protect
|
char * zPos = &aBuffer[1] /* skip first byte for now to help protect
|
||||||
@@ -410,6 +410,12 @@ const char * sqlite3_wasm_enum_json(void){
|
|||||||
DefInt(SQLITE_ACCESS_READ)/*docs say this is unused*/;
|
DefInt(SQLITE_ACCESS_READ)/*docs say this is unused*/;
|
||||||
} _DefGroup;
|
} _DefGroup;
|
||||||
|
|
||||||
|
/* TODO? Authorizer... */
|
||||||
|
DefGroup(authorizer){
|
||||||
|
DefInt(SQLITE_DENY);
|
||||||
|
DefInt(SQLITE_IGNORE);
|
||||||
|
} _DefGroup;
|
||||||
|
|
||||||
DefGroup(blobFinalizers) {
|
DefGroup(blobFinalizers) {
|
||||||
/* SQLITE_STATIC/TRANSIENT need to be handled explicitly as
|
/* SQLITE_STATIC/TRANSIENT need to be handled explicitly as
|
||||||
** integers to avoid casting-related warnings. */
|
** integers to avoid casting-related warnings. */
|
||||||
@@ -424,6 +430,45 @@ const char * sqlite3_wasm_enum_json(void){
|
|||||||
DefInt(SQLITE_NULL);
|
DefInt(SQLITE_NULL);
|
||||||
} _DefGroup;
|
} _DefGroup;
|
||||||
|
|
||||||
|
DefGroup(dbConfig){
|
||||||
|
DefInt(SQLITE_DBCONFIG_MAINDBNAME);
|
||||||
|
DefInt(SQLITE_DBCONFIG_LOOKASIDE);
|
||||||
|
DefInt(SQLITE_DBCONFIG_ENABLE_FKEY);
|
||||||
|
DefInt(SQLITE_DBCONFIG_ENABLE_TRIGGER);
|
||||||
|
DefInt(SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER);
|
||||||
|
DefInt(SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION);
|
||||||
|
DefInt(SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE);
|
||||||
|
DefInt(SQLITE_DBCONFIG_ENABLE_QPSG);
|
||||||
|
DefInt(SQLITE_DBCONFIG_TRIGGER_EQP);
|
||||||
|
DefInt(SQLITE_DBCONFIG_RESET_DATABASE);
|
||||||
|
DefInt(SQLITE_DBCONFIG_DEFENSIVE);
|
||||||
|
DefInt(SQLITE_DBCONFIG_WRITABLE_SCHEMA);
|
||||||
|
DefInt(SQLITE_DBCONFIG_LEGACY_ALTER_TABLE);
|
||||||
|
DefInt(SQLITE_DBCONFIG_DQS_DML);
|
||||||
|
DefInt(SQLITE_DBCONFIG_DQS_DDL);
|
||||||
|
DefInt(SQLITE_DBCONFIG_ENABLE_VIEW);
|
||||||
|
DefInt(SQLITE_DBCONFIG_LEGACY_FILE_FORMAT);
|
||||||
|
DefInt(SQLITE_DBCONFIG_TRUSTED_SCHEMA);
|
||||||
|
DefInt(SQLITE_DBCONFIG_MAX);
|
||||||
|
} _DefGroup;
|
||||||
|
|
||||||
|
DefGroup(dbStatus){
|
||||||
|
DefInt(SQLITE_DBSTATUS_LOOKASIDE_USED);
|
||||||
|
DefInt(SQLITE_DBSTATUS_CACHE_USED);
|
||||||
|
DefInt(SQLITE_DBSTATUS_SCHEMA_USED);
|
||||||
|
DefInt(SQLITE_DBSTATUS_STMT_USED);
|
||||||
|
DefInt(SQLITE_DBSTATUS_LOOKASIDE_HIT);
|
||||||
|
DefInt(SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE);
|
||||||
|
DefInt(SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL);
|
||||||
|
DefInt(SQLITE_DBSTATUS_CACHE_HIT);
|
||||||
|
DefInt(SQLITE_DBSTATUS_CACHE_MISS);
|
||||||
|
DefInt(SQLITE_DBSTATUS_CACHE_WRITE);
|
||||||
|
DefInt(SQLITE_DBSTATUS_DEFERRED_FKS);
|
||||||
|
DefInt(SQLITE_DBSTATUS_CACHE_USED_SHARED);
|
||||||
|
DefInt(SQLITE_DBSTATUS_CACHE_SPILL);
|
||||||
|
DefInt(SQLITE_DBSTATUS_MAX);
|
||||||
|
} _DefGroup;
|
||||||
|
|
||||||
DefGroup(encodings) {
|
DefGroup(encodings) {
|
||||||
/* Noting that the wasm binding only aims to support UTF-8. */
|
/* Noting that the wasm binding only aims to support UTF-8. */
|
||||||
DefInt(SQLITE_UTF8);
|
DefInt(SQLITE_UTF8);
|
||||||
@@ -505,6 +550,30 @@ const char * sqlite3_wasm_enum_json(void){
|
|||||||
|
|
||||||
DefGroup(limits) {
|
DefGroup(limits) {
|
||||||
DefInt(SQLITE_MAX_ALLOCATION_SIZE);
|
DefInt(SQLITE_MAX_ALLOCATION_SIZE);
|
||||||
|
DefInt(SQLITE_LIMIT_LENGTH);
|
||||||
|
DefInt(SQLITE_MAX_LENGTH);
|
||||||
|
DefInt(SQLITE_LIMIT_SQL_LENGTH);
|
||||||
|
DefInt(SQLITE_MAX_SQL_LENGTH);
|
||||||
|
DefInt(SQLITE_LIMIT_COLUMN);
|
||||||
|
DefInt(SQLITE_MAX_COLUMN);
|
||||||
|
DefInt(SQLITE_LIMIT_EXPR_DEPTH);
|
||||||
|
DefInt(SQLITE_MAX_EXPR_DEPTH);
|
||||||
|
DefInt(SQLITE_LIMIT_COMPOUND_SELECT);
|
||||||
|
DefInt(SQLITE_MAX_COMPOUND_SELECT);
|
||||||
|
DefInt(SQLITE_LIMIT_VDBE_OP);
|
||||||
|
DefInt(SQLITE_MAX_VDBE_OP);
|
||||||
|
DefInt(SQLITE_LIMIT_FUNCTION_ARG);
|
||||||
|
DefInt(SQLITE_MAX_FUNCTION_ARG);
|
||||||
|
DefInt(SQLITE_LIMIT_ATTACHED);
|
||||||
|
DefInt(SQLITE_MAX_ATTACHED);
|
||||||
|
DefInt(SQLITE_LIMIT_LIKE_PATTERN_LENGTH);
|
||||||
|
DefInt(SQLITE_MAX_LIKE_PATTERN_LENGTH);
|
||||||
|
DefInt(SQLITE_LIMIT_VARIABLE_NUMBER);
|
||||||
|
DefInt(SQLITE_MAX_VARIABLE_NUMBER);
|
||||||
|
DefInt(SQLITE_LIMIT_TRIGGER_DEPTH);
|
||||||
|
DefInt(SQLITE_MAX_TRIGGER_DEPTH);
|
||||||
|
DefInt(SQLITE_LIMIT_WORKER_THREADS);
|
||||||
|
DefInt(SQLITE_MAX_WORKER_THREADS);
|
||||||
} _DefGroup;
|
} _DefGroup;
|
||||||
|
|
||||||
DefGroup(openFlags) {
|
DefGroup(openFlags) {
|
||||||
@@ -657,6 +726,31 @@ const char * sqlite3_wasm_enum_json(void){
|
|||||||
DefInt(SQLITE_DESERIALIZE_RESIZEABLE);
|
DefInt(SQLITE_DESERIALIZE_RESIZEABLE);
|
||||||
} _DefGroup;
|
} _DefGroup;
|
||||||
|
|
||||||
|
DefGroup(sqlite3Status){
|
||||||
|
DefInt(SQLITE_STATUS_MEMORY_USED);
|
||||||
|
DefInt(SQLITE_STATUS_PAGECACHE_USED);
|
||||||
|
DefInt(SQLITE_STATUS_PAGECACHE_OVERFLOW);
|
||||||
|
//DefInt(SQLITE_STATUS_SCRATCH_USED) /* NOT USED */;
|
||||||
|
//DefInt(SQLITE_STATUS_SCRATCH_OVERFLOW) /* NOT USED */;
|
||||||
|
DefInt(SQLITE_STATUS_MALLOC_SIZE);
|
||||||
|
DefInt(SQLITE_STATUS_PARSER_STACK);
|
||||||
|
DefInt(SQLITE_STATUS_PAGECACHE_SIZE);
|
||||||
|
//DefInt(SQLITE_STATUS_SCRATCH_SIZE) /* NOT USED */;
|
||||||
|
DefInt(SQLITE_STATUS_MALLOC_COUNT);
|
||||||
|
} _DefGroup;
|
||||||
|
|
||||||
|
DefGroup(stmtStatus){
|
||||||
|
DefInt(SQLITE_STMTSTATUS_FULLSCAN_STEP);
|
||||||
|
DefInt(SQLITE_STMTSTATUS_SORT);
|
||||||
|
DefInt(SQLITE_STMTSTATUS_AUTOINDEX);
|
||||||
|
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) {
|
DefGroup(syncFlags) {
|
||||||
DefInt(SQLITE_SYNC_NORMAL);
|
DefInt(SQLITE_SYNC_NORMAL);
|
||||||
DefInt(SQLITE_SYNC_FULL);
|
DefInt(SQLITE_SYNC_FULL);
|
||||||
@@ -670,6 +764,12 @@ const char * sqlite3_wasm_enum_json(void){
|
|||||||
DefInt(SQLITE_TRACE_CLOSE);
|
DefInt(SQLITE_TRACE_CLOSE);
|
||||||
} _DefGroup;
|
} _DefGroup;
|
||||||
|
|
||||||
|
DefGroup(txnState){
|
||||||
|
DefInt(SQLITE_TXN_NONE);
|
||||||
|
DefInt(SQLITE_TXN_READ);
|
||||||
|
DefInt(SQLITE_TXN_WRITE);
|
||||||
|
} _DefGroup;
|
||||||
|
|
||||||
DefGroup(udfFlags) {
|
DefGroup(udfFlags) {
|
||||||
DefInt(SQLITE_DETERMINISTIC);
|
DefInt(SQLITE_DETERMINISTIC);
|
||||||
DefInt(SQLITE_DIRECTONLY);
|
DefInt(SQLITE_DIRECTONLY);
|
||||||
@@ -681,7 +781,36 @@ const char * sqlite3_wasm_enum_json(void){
|
|||||||
DefStr(SQLITE_VERSION);
|
DefStr(SQLITE_VERSION);
|
||||||
DefStr(SQLITE_SOURCE_ID);
|
DefStr(SQLITE_SOURCE_ID);
|
||||||
} _DefGroup;
|
} _DefGroup;
|
||||||
|
|
||||||
|
DefGroup(vtab) {
|
||||||
|
DefInt(SQLITE_INDEX_SCAN_UNIQUE);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_EQ);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_GT);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_LE);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_LT);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_GE);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_MATCH);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_LIKE);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_GLOB);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_REGEXP);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_NE);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_ISNOT);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_ISNOTNULL);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_ISNULL);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_IS);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_LIMIT);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_OFFSET);
|
||||||
|
DefInt(SQLITE_INDEX_CONSTRAINT_FUNCTION);
|
||||||
|
DefInt(SQLITE_VTAB_CONSTRAINT_SUPPORT);
|
||||||
|
DefInt(SQLITE_VTAB_INNOCUOUS);
|
||||||
|
DefInt(SQLITE_VTAB_DIRECTONLY);
|
||||||
|
DefInt(SQLITE_ROLLBACK);
|
||||||
|
//DefInt(SQLITE_IGNORE); // Also used by sqlite3_authorizer() callback
|
||||||
|
DefInt(SQLITE_FAIL);
|
||||||
|
//DefInt(SQLITE_ABORT); // Also an error code
|
||||||
|
DefInt(SQLITE_REPLACE);
|
||||||
|
} _DefGroup;
|
||||||
|
|
||||||
#undef DefGroup
|
#undef DefGroup
|
||||||
#undef DefStr
|
#undef DefStr
|
||||||
#undef DefInt
|
#undef DefInt
|
||||||
@@ -793,6 +922,128 @@ const char * sqlite3_wasm_enum_json(void){
|
|||||||
} _StructBinder;
|
} _StructBinder;
|
||||||
#undef CurrentStruct
|
#undef CurrentStruct
|
||||||
|
|
||||||
|
|
||||||
|
#define CurrentStruct sqlite3_vtab
|
||||||
|
StructBinder {
|
||||||
|
M(pModule, "p");
|
||||||
|
M(nRef, "i");
|
||||||
|
M(zErrMsg, "p");
|
||||||
|
} _StructBinder;
|
||||||
|
#undef CurrentStruct
|
||||||
|
|
||||||
|
#define CurrentStruct sqlite3_vtab_cursor
|
||||||
|
StructBinder {
|
||||||
|
M(pVtab, "p");
|
||||||
|
} _StructBinder;
|
||||||
|
#undef CurrentStruct
|
||||||
|
|
||||||
|
#define CurrentStruct sqlite3_module
|
||||||
|
StructBinder {
|
||||||
|
M(iVersion, "i");
|
||||||
|
M(xCreate, "i(ppippp)");
|
||||||
|
M(xConnect, "i(ppippp)");
|
||||||
|
M(xBestIndex, "i(pp)");
|
||||||
|
M(xDisconnect, "i(p)");
|
||||||
|
M(xDestroy, "i(p)");
|
||||||
|
M(xOpen, "i(pp)");
|
||||||
|
M(xClose, "i(p)");
|
||||||
|
M(xFilter, "i(pisip)");
|
||||||
|
M(xNext, "i(p)");
|
||||||
|
M(xEof, "i(p)");
|
||||||
|
M(xColumn, "i(ppi)");
|
||||||
|
M(xRowid, "i(pp)");
|
||||||
|
M(xUpdate, "i(pipp)");
|
||||||
|
M(xBegin, "i(p)");
|
||||||
|
M(xSync, "i(p)");
|
||||||
|
M(xCommit, "i(p)");
|
||||||
|
M(xRollback, "i(p)");
|
||||||
|
M(xFindFunction, "i(pispp)");
|
||||||
|
M(xRename, "i(ps)");
|
||||||
|
// ^^^ v1. v2+ follows...
|
||||||
|
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 {
|
||||||
|
int iColumn;
|
||||||
|
unsigned char op;
|
||||||
|
unsigned char usable;
|
||||||
|
int iTermOffset;
|
||||||
|
} sqlite3_index_constraint;
|
||||||
|
typedef struct {
|
||||||
|
int iColumn;
|
||||||
|
unsigned char desc;
|
||||||
|
} sqlite3_index_orderby;
|
||||||
|
typedef struct {
|
||||||
|
int argvIndex;
|
||||||
|
unsigned char omit;
|
||||||
|
} sqlite3_index_constraint_usage;
|
||||||
|
{ /* Validate that the above struct sizeof()s match
|
||||||
|
** expectations. We could improve upon this by
|
||||||
|
** checking the offsetof() for each member. */
|
||||||
|
const sqlite3_index_info siiCheck;
|
||||||
|
#define IndexSzCheck(T,M) \
|
||||||
|
(sizeof(T) == sizeof(*siiCheck.M))
|
||||||
|
if(!IndexSzCheck(sqlite3_index_constraint,aConstraint)
|
||||||
|
|| !IndexSzCheck(sqlite3_index_orderby,aOrderBy)
|
||||||
|
|| !IndexSzCheck(sqlite3_index_constraint_usage,aConstraintUsage)){
|
||||||
|
assert(!"sizeof mismatch in sqlite3_index_... struct(s)");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#undef IndexSzCheck
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CurrentStruct sqlite3_index_constraint
|
||||||
|
StructBinder {
|
||||||
|
M(iColumn, "i");
|
||||||
|
M(op, "C");
|
||||||
|
M(usable, "C");
|
||||||
|
M(iTermOffset, "i");
|
||||||
|
} _StructBinder;
|
||||||
|
#undef CurrentStruct
|
||||||
|
|
||||||
|
#define CurrentStruct sqlite3_index_orderby
|
||||||
|
StructBinder {
|
||||||
|
M(iColumn, "i");
|
||||||
|
M(desc, "C");
|
||||||
|
} _StructBinder;
|
||||||
|
#undef CurrentStruct
|
||||||
|
|
||||||
|
#define CurrentStruct sqlite3_index_constraint_usage
|
||||||
|
StructBinder {
|
||||||
|
M(argvIndex, "i");
|
||||||
|
M(omit, "C");
|
||||||
|
} _StructBinder;
|
||||||
|
#undef CurrentStruct
|
||||||
|
|
||||||
|
#define CurrentStruct sqlite3_index_info
|
||||||
|
StructBinder {
|
||||||
|
M(nConstraint, "i");
|
||||||
|
M(aConstraint, "p");
|
||||||
|
M(nOrderBy, "i");
|
||||||
|
M(aOrderBy, "p");
|
||||||
|
M(aConstraintUsage, "p");
|
||||||
|
M(idxNum, "i");
|
||||||
|
M(idxStr, "p");
|
||||||
|
M(needToFreeIdxStr, "i");
|
||||||
|
M(orderByConsumed, "i");
|
||||||
|
M(estimatedCost, "d");
|
||||||
|
M(estimatedRows, "j");
|
||||||
|
M(idxFlags, "i");
|
||||||
|
M(colUsed, "j");
|
||||||
|
} _StructBinder;
|
||||||
|
#undef CurrentStruct
|
||||||
|
|
||||||
#if SQLITE_WASM_TESTS
|
#if SQLITE_WASM_TESTS
|
||||||
#define CurrentStruct WasmTestStruct
|
#define CurrentStruct WasmTestStruct
|
||||||
StructBinder {
|
StructBinder {
|
||||||
@@ -864,7 +1115,11 @@ sqlite3_vfs * sqlite3_wasm_db_vfs(sqlite3 *pDb, const char *zDbName){
|
|||||||
**
|
**
|
||||||
** This function resets the given db pointer's database as described at
|
** This function resets the given db pointer's database as described at
|
||||||
**
|
**
|
||||||
** https://www.sqlite.org/c3ref/c_dbconfig_defensive.html#sqlitedbconfigresetdatabase
|
** https://sqlite.org/c3ref/c_dbconfig_defensive.html#sqlitedbconfigresetdatabase
|
||||||
|
**
|
||||||
|
** But beware: virtual tables destroyed that way do not have their
|
||||||
|
** xDestroy() called, so will leak if they require that function for
|
||||||
|
** proper cleanup.
|
||||||
**
|
**
|
||||||
** Returns 0 on success, an SQLITE_xxx code on error. Returns
|
** Returns 0 on success, an SQLITE_xxx code on error. Returns
|
||||||
** SQLITE_MISUSE if pDb is NULL.
|
** SQLITE_MISUSE if pDb is NULL.
|
||||||
@@ -873,6 +1128,7 @@ SQLITE_WASM_KEEP
|
|||||||
int sqlite3_wasm_db_reset(sqlite3 *pDb){
|
int sqlite3_wasm_db_reset(sqlite3 *pDb){
|
||||||
int rc = SQLITE_MISUSE;
|
int rc = SQLITE_MISUSE;
|
||||||
if( pDb ){
|
if( pDb ){
|
||||||
|
sqlite3_table_column_metadata(pDb, "main", 0, 0, 0, 0, 0, 0, 0);
|
||||||
rc = sqlite3_db_config(pDb, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0);
|
rc = sqlite3_db_config(pDb, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0);
|
||||||
if( 0==rc ){
|
if( 0==rc ){
|
||||||
rc = sqlite3_exec(pDb, "VACUUM", 0, 0, 0);
|
rc = sqlite3_exec(pDb, "VACUUM", 0, 0, 0);
|
||||||
@@ -1109,6 +1365,93 @@ sqlite3_kvvfs_methods * sqlite3_wasm_kvvfs_methods(void){
|
|||||||
return &sqlite3KvvfsMethods;
|
return &sqlite3KvvfsMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function is NOT part of the sqlite3 public API. It is strictly
|
||||||
|
** for use by the sqlite project's own JS/WASM bindings.
|
||||||
|
**
|
||||||
|
** This is a proxy for the variadic sqlite3_vtab_config() which passes
|
||||||
|
** its argument on, or not, to sqlite3_vtab_config(), depending on the
|
||||||
|
** value of its 2nd argument. Returns the result of
|
||||||
|
** sqlite3_vtab_config(), or SQLITE_MISUSE if the 2nd arg is not a
|
||||||
|
** valid value.
|
||||||
|
*/
|
||||||
|
SQLITE_WASM_KEEP
|
||||||
|
int sqlite3_wasm_vtab_config(sqlite3 *pDb, int op, int arg){
|
||||||
|
switch(op){
|
||||||
|
case SQLITE_VTAB_DIRECTONLY:
|
||||||
|
case SQLITE_VTAB_INNOCUOUS:
|
||||||
|
return sqlite3_vtab_config(pDb, op);
|
||||||
|
case SQLITE_VTAB_CONSTRAINT_SUPPORT:
|
||||||
|
return sqlite3_vtab_config(pDb, op, arg);
|
||||||
|
default:
|
||||||
|
return SQLITE_MISUSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function is NOT part of the sqlite3 public API. It is strictly
|
||||||
|
** for use by the sqlite project's own JS/WASM bindings.
|
||||||
|
**
|
||||||
|
** Wrapper for the variants of sqlite3_db_config() which take
|
||||||
|
** (int,int*) variadic args.
|
||||||
|
*/
|
||||||
|
SQLITE_WASM_KEEP
|
||||||
|
int sqlite3_wasm_db_config_ip(sqlite3 *pDb, int op, int arg1, int* pArg2){
|
||||||
|
switch(op){
|
||||||
|
case SQLITE_DBCONFIG_ENABLE_FKEY:
|
||||||
|
case SQLITE_DBCONFIG_ENABLE_TRIGGER:
|
||||||
|
case SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER:
|
||||||
|
case SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION:
|
||||||
|
case SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE:
|
||||||
|
case SQLITE_DBCONFIG_ENABLE_QPSG:
|
||||||
|
case SQLITE_DBCONFIG_TRIGGER_EQP:
|
||||||
|
case SQLITE_DBCONFIG_RESET_DATABASE:
|
||||||
|
case SQLITE_DBCONFIG_DEFENSIVE:
|
||||||
|
case SQLITE_DBCONFIG_WRITABLE_SCHEMA:
|
||||||
|
case SQLITE_DBCONFIG_LEGACY_ALTER_TABLE:
|
||||||
|
case SQLITE_DBCONFIG_DQS_DML:
|
||||||
|
case SQLITE_DBCONFIG_DQS_DDL:
|
||||||
|
case SQLITE_DBCONFIG_ENABLE_VIEW:
|
||||||
|
case SQLITE_DBCONFIG_LEGACY_FILE_FORMAT:
|
||||||
|
case SQLITE_DBCONFIG_TRUSTED_SCHEMA:
|
||||||
|
return sqlite3_db_config(pDb, op, arg1, pArg2);
|
||||||
|
default: return SQLITE_MISUSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function is NOT part of the sqlite3 public API. It is strictly
|
||||||
|
** for use by the sqlite project's own JS/WASM bindings.
|
||||||
|
**
|
||||||
|
** Wrapper for the variants of sqlite3_db_config() which take
|
||||||
|
** (void*,int,int) variadic args.
|
||||||
|
*/
|
||||||
|
SQLITE_WASM_KEEP
|
||||||
|
int sqlite3_wasm_db_config_pii(sqlite3 *pDb, int op, void * pArg1, int arg2, int arg3){
|
||||||
|
switch(op){
|
||||||
|
case SQLITE_DBCONFIG_LOOKASIDE:
|
||||||
|
return sqlite3_db_config(pDb, op, pArg1, arg2, arg3);
|
||||||
|
default: return SQLITE_MISUSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function is NOT part of the sqlite3 public API. It is strictly
|
||||||
|
** for use by the sqlite project's own JS/WASM bindings.
|
||||||
|
**
|
||||||
|
** Wrapper for the variants of sqlite3_db_config() which take
|
||||||
|
** (const char *) variadic args.
|
||||||
|
*/
|
||||||
|
SQLITE_WASM_KEEP
|
||||||
|
int sqlite3_wasm_db_config_s(sqlite3 *pDb, int op, const char *zArg){
|
||||||
|
switch(op){
|
||||||
|
case SQLITE_DBCONFIG_MAINDBNAME:
|
||||||
|
return sqlite3_db_config(pDb, op, zArg);
|
||||||
|
default: return SQLITE_MISUSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#if defined(__EMSCRIPTEN__) && defined(SQLITE_ENABLE_WASMFS)
|
#if defined(__EMSCRIPTEN__) && defined(SQLITE_ENABLE_WASMFS)
|
||||||
#include <emscripten/wasmfs.h>
|
#include <emscripten/wasmfs.h>
|
||||||
|
|
||||||
|
|||||||
@@ -227,20 +227,20 @@
|
|||||||
const pSqlEnd = pSqlBegin + sqlByteLen;
|
const pSqlEnd = pSqlBegin + sqlByteLen;
|
||||||
t = performance.now();
|
t = performance.now();
|
||||||
wasm.heap8().set(sql, pSql);
|
wasm.heap8().set(sql, pSql);
|
||||||
wasm.setMemValue(pSql + sqlByteLen, 0);
|
wasm.poke(pSql + sqlByteLen, 0);
|
||||||
metrics.strcpy = performance.now() - t;
|
metrics.strcpy = performance.now() - t;
|
||||||
let breaker = 0;
|
let breaker = 0;
|
||||||
while(pSql && wasm.getMemValue(pSql,'i8')){
|
while(pSql && wasm.peek(pSql,'i8')){
|
||||||
wasm.setPtrValue(ppStmt, 0);
|
wasm.pokePtr(ppStmt, 0);
|
||||||
wasm.setPtrValue(pzTail, 0);
|
wasm.pokePtr(pzTail, 0);
|
||||||
t = performance.now();
|
t = performance.now();
|
||||||
let rc = capi.sqlite3_prepare_v3(
|
let rc = capi.sqlite3_prepare_v3(
|
||||||
db.handle, pSql, sqlByteLen, 0, ppStmt, pzTail
|
db.handle, pSql, sqlByteLen, 0, ppStmt, pzTail
|
||||||
);
|
);
|
||||||
metrics.prepTotal += performance.now() - t;
|
metrics.prepTotal += performance.now() - t;
|
||||||
checkSqliteRc(db.handle, rc);
|
checkSqliteRc(db.handle, rc);
|
||||||
pStmt = wasm.getPtrValue(ppStmt);
|
pStmt = wasm.peekPtr(ppStmt);
|
||||||
pSql = wasm.getPtrValue(pzTail);
|
pSql = wasm.peekPtr(pzTail);
|
||||||
sqlByteLen = pSqlEnd - pSql;
|
sqlByteLen = pSqlEnd - pSql;
|
||||||
if(!pStmt) continue/*empty statement*/;
|
if(!pStmt) continue/*empty statement*/;
|
||||||
++metrics.stmtCount;
|
++metrics.stmtCount;
|
||||||
@@ -495,7 +495,7 @@
|
|||||||
const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE;
|
const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE;
|
||||||
const ppDb = wasm.scopedAllocPtr();
|
const ppDb = wasm.scopedAllocPtr();
|
||||||
const rc = capi.sqlite3_open_v2(d.filename, ppDb, oFlags, null);
|
const rc = capi.sqlite3_open_v2(d.filename, ppDb, oFlags, null);
|
||||||
pDb = wasm.getPtrValue(ppDb)
|
pDb = wasm.peekPtr(ppDb)
|
||||||
if(rc) toss("sqlite3_open_v2() failed with code",rc);
|
if(rc) toss("sqlite3_open_v2() failed with code",rc);
|
||||||
capi.sqlite3_exec(pDb, "PRAGMA cache_size="+cacheSize, 0, 0, 0);
|
capi.sqlite3_exec(pDb, "PRAGMA cache_size="+cacheSize, 0, 0, 0);
|
||||||
this.logHtml(dbId,"cache_size =",cacheSize);
|
this.logHtml(dbId,"cache_size =",cacheSize);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* emcscript-related styling, used during the module load/intialization processes... */
|
/* emscripten-related styling, used during the module load/intialization processes... */
|
||||||
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
|
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
|
||||||
div.emscripten { text-align: center; }
|
div.emscripten { text-align: center; }
|
||||||
div.emscripten_border { border: 1px solid black; }
|
div.emscripten_border { border: 1px solid black; }
|
||||||
|
|||||||
@@ -61,3 +61,9 @@ span.labeled-input {
|
|||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
}
|
}
|
||||||
label[for] { cursor: pointer }
|
label[for] { cursor: pointer }
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
border-radius: 0.25em;
|
||||||
|
padding: 0.15em 0.25em;
|
||||||
|
}
|
||||||
|
h1:first-of-type {margin: 0 0 0.5em 0;}
|
||||||
|
|||||||
@@ -316,13 +316,11 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
|
|
||||||
Throws if passed an invalid n.
|
Throws if passed an invalid n.
|
||||||
|
|
||||||
Pedantic side note: the name "heap" is a bit of a misnomer. In an
|
Pedantic side note: the name "heap" is a bit of a misnomer. In a
|
||||||
Emscripten environment, the memory managed via the stack
|
WASM environment, the stack and heap memory are all accessed via
|
||||||
allocation API is in the same Memory object as the heap (which
|
the same view(s) of the memory.
|
||||||
makes sense because otherwise arbitrary pointer X would be
|
|
||||||
ambiguous: is it in the heap or the stack?).
|
|
||||||
*/
|
*/
|
||||||
target.heapForSize = function(n,unsigned = false){
|
target.heapForSize = function(n,unsigned = true){
|
||||||
let ctor;
|
let ctor;
|
||||||
const c = (cache.memory && cache.heapSize === cache.memory.buffer.byteLength)
|
const c = (cache.memory && cache.heapSize === cache.memory.buffer.byteLength)
|
||||||
? cache : heapWrappers();
|
? cache : heapWrappers();
|
||||||
@@ -496,6 +494,70 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
e: { f: func }
|
e: { f: func }
|
||||||
})).exports['f'];
|
})).exports['f'];
|
||||||
}/*jsFuncToWasm()*/;
|
}/*jsFuncToWasm()*/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Documented as target.installFunction() except for the 3rd
|
||||||
|
argument: if truthy, the newly-created function pointer
|
||||||
|
is stashed in the current scoped-alloc scope and will be
|
||||||
|
cleaned up at the matching scopedAllocPop(), else it
|
||||||
|
is not stashed there.
|
||||||
|
*/
|
||||||
|
const __installFunction = function f(func, sig, scoped){
|
||||||
|
if(scoped && !cache.scopedAlloc.length){
|
||||||
|
toss("No scopedAllocPush() scope is active.");
|
||||||
|
}
|
||||||
|
if('string'===typeof func){
|
||||||
|
const x = sig;
|
||||||
|
sig = func;
|
||||||
|
func = x;
|
||||||
|
}
|
||||||
|
if('string'!==typeof sig || !(func instanceof Function)){
|
||||||
|
toss("Invalid arguments: expecting (function,signature) "+
|
||||||
|
"or (signature,function).");
|
||||||
|
}
|
||||||
|
const ft = target.functionTable();
|
||||||
|
const oldLen = ft.length;
|
||||||
|
let ptr;
|
||||||
|
while(cache.freeFuncIndexes.length){
|
||||||
|
ptr = cache.freeFuncIndexes.pop();
|
||||||
|
if(ft.get(ptr)){ /* Table was modified via a different API */
|
||||||
|
ptr = null;
|
||||||
|
continue;
|
||||||
|
}else{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!ptr){
|
||||||
|
ptr = oldLen;
|
||||||
|
ft.grow(1);
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
/*this will only work if func is a WASM-exported function*/
|
||||||
|
ft.set(ptr, func);
|
||||||
|
if(scoped){
|
||||||
|
cache.scopedAlloc[cache.scopedAlloc.length-1].push(ptr);
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}catch(e){
|
||||||
|
if(!(e instanceof TypeError)){
|
||||||
|
if(ptr===oldLen) cache.freeFuncIndexes.push(oldLen);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// It's not a WASM-exported function, so compile one...
|
||||||
|
try {
|
||||||
|
const fptr = target.jsFuncToWasm(func, sig);
|
||||||
|
ft.set(ptr, fptr);
|
||||||
|
if(scoped){
|
||||||
|
cache.scopedAlloc[cache.scopedAlloc.length-1].push(ptr);
|
||||||
|
}
|
||||||
|
}catch(e){
|
||||||
|
if(ptr===oldLen) cache.freeFuncIndexes.push(oldLen);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Expects a JS function and signature, exactly as for
|
Expects a JS function and signature, exactly as for
|
||||||
@@ -528,50 +590,19 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
|
|
||||||
https://github.com/emscripten-core/emscripten/issues/17323
|
https://github.com/emscripten-core/emscripten/issues/17323
|
||||||
*/
|
*/
|
||||||
target.installFunction = function f(func, sig){
|
target.installFunction = (func, sig)=>__installFunction(func, sig, false);
|
||||||
if(2!==arguments.length){
|
|
||||||
toss("installFunction() requires exactly 2 arguments");
|
/**
|
||||||
}
|
EXPERIMENTAL! DO NOT USE IN CLIENT CODE!
|
||||||
if('string'===typeof func){
|
|
||||||
const x = sig;
|
Works exactly like installFunction() but requires that a
|
||||||
sig = func;
|
scopedAllocPush() is active and uninstalls the given function
|
||||||
func = x;
|
when that alloc scope is popped via scopedAllocPop().
|
||||||
}
|
This is used for implementing JS/WASM function bindings which
|
||||||
const ft = target.functionTable();
|
should only persist for the life of a call into a single
|
||||||
const oldLen = ft.length;
|
C-side function.
|
||||||
let ptr;
|
*/
|
||||||
while(cache.freeFuncIndexes.length){
|
target.scopedInstallFunction = (func, sig)=>__installFunction(func, sig, true);
|
||||||
ptr = cache.freeFuncIndexes.pop();
|
|
||||||
if(ft.get(ptr)){ /* Table was modified via a different API */
|
|
||||||
ptr = null;
|
|
||||||
continue;
|
|
||||||
}else{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!ptr){
|
|
||||||
ptr = oldLen;
|
|
||||||
ft.grow(1);
|
|
||||||
}
|
|
||||||
try{
|
|
||||||
/*this will only work if func is a WASM-exported function*/
|
|
||||||
ft.set(ptr, func);
|
|
||||||
return ptr;
|
|
||||||
}catch(e){
|
|
||||||
if(!(e instanceof TypeError)){
|
|
||||||
if(ptr===oldLen) cache.freeFuncIndexes.push(oldLen);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// It's not a WASM-exported function, so compile one...
|
|
||||||
try {
|
|
||||||
ft.set(ptr, target.jsFuncToWasm(func, sig));
|
|
||||||
}catch(e){
|
|
||||||
if(ptr===oldLen) cache.freeFuncIndexes.push(oldLen);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
return ptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Requires a pointer value previously returned from
|
Requires a pointer value previously returned from
|
||||||
@@ -599,19 +630,24 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
type triggers an exception if this.bigIntEnabled is
|
type triggers an exception if this.bigIntEnabled is
|
||||||
falsy). Throws if given an invalid type.
|
falsy). Throws if given an invalid type.
|
||||||
|
|
||||||
|
If the first argument is an array, it is treated as an array of
|
||||||
|
addresses and the result is an array of the values from each of
|
||||||
|
those address, using the same 2nd argument for determining the
|
||||||
|
value type to fetch.
|
||||||
|
|
||||||
As a special case, if type ends with a `*`, it is considered to
|
As a special case, if type ends with a `*`, it is considered to
|
||||||
be a pointer type and is treated as the WASM numeric type
|
be a pointer type and is treated as the WASM numeric type
|
||||||
appropriate for the pointer size (`i32`).
|
appropriate for the pointer size (`i32`).
|
||||||
|
|
||||||
While likely not obvious, this routine and its setMemValue()
|
While likely not obvious, this routine and its poke()
|
||||||
counterpart are how pointer-to-value _output_ parameters
|
counterpart are how pointer-to-value _output_ parameters
|
||||||
in WASM-compiled C code can be interacted with:
|
in WASM-compiled C code can be interacted with:
|
||||||
|
|
||||||
```
|
```
|
||||||
const ptr = alloc(4);
|
const ptr = alloc(4);
|
||||||
setMemValue(ptr, 0, 'i32'); // clear the ptr's value
|
poke(ptr, 0, 'i32'); // clear the ptr's value
|
||||||
aCFuncWithOutputPtrToInt32Arg( ptr ); // e.g. void foo(int *x);
|
aCFuncWithOutputPtrToInt32Arg( ptr ); // e.g. void foo(int *x);
|
||||||
const result = getMemValue(ptr, 'i32'); // fetch ptr's value
|
const result = peek(ptr, 'i32'); // fetch ptr's value
|
||||||
dealloc(ptr);
|
dealloc(ptr);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -623,15 +659,15 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
const scope = scopedAllocPush();
|
const scope = scopedAllocPush();
|
||||||
try{
|
try{
|
||||||
const ptr = scopedAlloc(4);
|
const ptr = scopedAlloc(4);
|
||||||
setMemValue(ptr, 0, 'i32');
|
poke(ptr, 0, 'i32');
|
||||||
aCFuncWithOutputPtrArg( ptr );
|
aCFuncWithOutputPtrArg( ptr );
|
||||||
result = getMemValue(ptr, 'i32');
|
result = peek(ptr, 'i32');
|
||||||
}finally{
|
}finally{
|
||||||
scopedAllocPop(scope);
|
scopedAllocPop(scope);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
As a rule setMemValue() must be called to set (typically zero
|
As a rule poke() must be called to set (typically zero
|
||||||
out) the pointer's value, else it will contain an essentially
|
out) the pointer's value, else it will contain an essentially
|
||||||
random value.
|
random value.
|
||||||
|
|
||||||
@@ -639,70 +675,105 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
painful impact on performance. Rather than doing so, use
|
painful impact on performance. Rather than doing so, use
|
||||||
heapForSize() to fetch the heap object and read directly from it.
|
heapForSize() to fetch the heap object and read directly from it.
|
||||||
|
|
||||||
See: setMemValue()
|
See: poke()
|
||||||
*/
|
*/
|
||||||
target.getMemValue = function(ptr, type='i8'){
|
target.peek = function f(ptr, type='i8'){
|
||||||
if(type.endsWith('*')) type = ptrIR;
|
if(type.endsWith('*')) type = ptrIR;
|
||||||
const c = (cache.memory && cache.heapSize === cache.memory.buffer.byteLength)
|
const c = (cache.memory && cache.heapSize === cache.memory.buffer.byteLength)
|
||||||
? cache : heapWrappers();
|
? cache : heapWrappers();
|
||||||
switch(type){
|
const list = Array.isArray(ptr) ? [] : undefined;
|
||||||
case 'i1':
|
let rc;
|
||||||
case 'i8': return c.HEAP8[ptr>>0];
|
do{
|
||||||
case 'i16': return c.HEAP16[ptr>>1];
|
if(list) ptr = arguments[0].shift();
|
||||||
case 'i32': return c.HEAP32[ptr>>2];
|
switch(type){
|
||||||
case 'i64':
|
case 'i1':
|
||||||
if(target.bigIntEnabled) return BigInt(c.HEAP64[ptr>>3]);
|
case 'i8': rc = c.HEAP8[ptr>>0]; break;
|
||||||
break;
|
case 'i16': rc = c.HEAP16[ptr>>1]; break;
|
||||||
case 'float': case 'f32': return c.HEAP32F[ptr>>2];
|
case 'i32': rc = c.HEAP32[ptr>>2]; break;
|
||||||
case 'double': case 'f64': return Number(c.HEAP64F[ptr>>3]);
|
case 'float': case 'f32': rc = c.HEAP32F[ptr>>2]; break;
|
||||||
default: break;
|
case 'double': case 'f64': rc = Number(c.HEAP64F[ptr>>3]); break;
|
||||||
}
|
case 'i64':
|
||||||
toss('Invalid type for getMemValue():',type);
|
if(target.bigIntEnabled){
|
||||||
|
rc = BigInt(c.HEAP64[ptr>>3]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* fallthru */
|
||||||
|
default:
|
||||||
|
toss('Invalid type for peek():',type);
|
||||||
|
}
|
||||||
|
if(list) list.push(rc);
|
||||||
|
}while(list && arguments[0].length);
|
||||||
|
return list || rc;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The counterpart of getMemValue(), this sets a numeric value at
|
The counterpart of peek(), this sets a numeric value at
|
||||||
the given WASM heap address, using the type to define how many
|
the given WASM heap address, using the type to define how many
|
||||||
bytes are written. Throws if given an invalid type. See
|
bytes are written. Throws if given an invalid type. See
|
||||||
getMemValue() for details about the type argument. If the 3rd
|
peek() for details about the type argument. If the 3rd
|
||||||
argument ends with `*` then it is treated as a pointer type and
|
argument ends with `*` then it is treated as a pointer type and
|
||||||
this function behaves as if the 3rd argument were `i32`.
|
this function behaves as if the 3rd argument were `i32`.
|
||||||
|
|
||||||
This function returns itself.
|
If the first argument is an array, it is treated like a list
|
||||||
|
of pointers and the given value is written to each one.
|
||||||
|
|
||||||
|
Returns `this`. (Prior to 2022-12-09 it returns this function.)
|
||||||
|
|
||||||
ACHTUNG: calling this often, e.g. in a loop, can have a noticably
|
ACHTUNG: calling this often, e.g. in a loop, can have a noticably
|
||||||
painful impact on performance. Rather than doing so, use
|
painful impact on performance. Rather than doing so, use
|
||||||
heapForSize() to fetch the heap object and assign directly to it.
|
heapForSize() to fetch the heap object and assign directly to it
|
||||||
|
or use the heap's set() method.
|
||||||
*/
|
*/
|
||||||
target.setMemValue = function f(ptr, value, type='i8'){
|
target.poke = function(ptr, value, type='i8'){
|
||||||
if (type.endsWith('*')) type = ptrIR;
|
if (type.endsWith('*')) type = ptrIR;
|
||||||
const c = (cache.memory && cache.heapSize === cache.memory.buffer.byteLength)
|
const c = (cache.memory && cache.heapSize === cache.memory.buffer.byteLength)
|
||||||
? cache : heapWrappers();
|
? cache : heapWrappers();
|
||||||
switch (type) {
|
for(const p of (Array.isArray(ptr) ? ptr : [ptr])){
|
||||||
case 'i1':
|
switch (type) {
|
||||||
case 'i8': c.HEAP8[ptr>>0] = value; return f;
|
case 'i1':
|
||||||
case 'i16': c.HEAP16[ptr>>1] = value; return f;
|
case 'i8': c.HEAP8[p>>0] = value; continue;
|
||||||
case 'i32': c.HEAP32[ptr>>2] = value; return f;
|
case 'i16': c.HEAP16[p>>1] = value; continue;
|
||||||
case 'i64':
|
case 'i32': c.HEAP32[p>>2] = value; continue;
|
||||||
if(c.HEAP64){
|
case 'float': case 'f32': c.HEAP32F[p>>2] = value; continue;
|
||||||
c.HEAP64[ptr>>3] = BigInt(value);
|
case 'double': case 'f64': c.HEAP64F[p>>3] = value; continue;
|
||||||
return f;
|
case 'i64':
|
||||||
}
|
if(c.HEAP64){
|
||||||
break;
|
c.HEAP64[p>>3] = BigInt(value);
|
||||||
case 'float': case 'f32': c.HEAP32F[ptr>>2] = value; return f;
|
continue;
|
||||||
case 'double': case 'f64': c.HEAP64F[ptr>>3] = value; return f;
|
}
|
||||||
|
/* fallthru */
|
||||||
|
default:
|
||||||
|
toss('Invalid type for poke(): ' + type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
toss('Invalid type for setMemValue(): ' + type);
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Convenience form of peek() intended for fetching
|
||||||
|
pointer-to-pointer values. If passed a single non-array argument
|
||||||
|
it returns the value of that one pointer address. If passed
|
||||||
|
multiple arguments, or a single array of arguments, it returns an
|
||||||
|
array of their values.
|
||||||
|
*/
|
||||||
|
target.peekPtr = (...ptr)=>target.peek( (1===ptr.length ? ptr[0] : ptr), ptrIR );
|
||||||
|
|
||||||
/** Convenience form of getMemValue() intended for fetching
|
/**
|
||||||
pointer-to-pointer values. */
|
A variant of poke() intended for setting
|
||||||
target.getPtrValue = (ptr)=>target.getMemValue(ptr, ptrIR);
|
pointer-to-pointer values. Its differences from poke() are
|
||||||
|
that (1) it defaults to a value of 0, (2) it always writes
|
||||||
|
to the pointer-sized heap view, and (3) it returns `this`.
|
||||||
|
*/
|
||||||
|
target.pokePtr = (ptr, value=0)=>target.poke(ptr, value, ptrIR);
|
||||||
|
|
||||||
/** Convenience form of setMemValue() intended for setting
|
/** Deprecated alias for getMemValue() */
|
||||||
pointer-to-pointer values. */
|
target.getMemValue = target.peek;
|
||||||
target.setPtrValue = (ptr, value)=>target.setMemValue(ptr, value, ptrIR);
|
/** Deprecated alias for peekPtr() */
|
||||||
|
target.getPtrValue = target.peekPtr;
|
||||||
|
/** Deprecated alias for poke() */
|
||||||
|
target.setMemValue = target.poke;
|
||||||
|
/** Deprecated alias for pokePtr() */
|
||||||
|
target.setPtrValue = target.pokePtr;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns true if the given value appears to be legal for use as
|
Returns true if the given value appears to be legal for use as
|
||||||
@@ -725,11 +796,12 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
Expects ptr to be a pointer into the WASM heap memory which
|
Expects ptr to be a pointer into the WASM heap memory which
|
||||||
refers to a NUL-terminated C-style string encoded as UTF-8.
|
refers to a NUL-terminated C-style string encoded as UTF-8.
|
||||||
Returns the length, in bytes, of the string, as for `strlen(3)`.
|
Returns the length, in bytes, of the string, as for `strlen(3)`.
|
||||||
As a special case, if !ptr then it it returns `null`. Throws if
|
As a special case, if !ptr or if it's not a pointer then it
|
||||||
ptr is out of range for target.heap8u().
|
returns `null`. Throws if ptr is out of range for
|
||||||
|
target.heap8u().
|
||||||
*/
|
*/
|
||||||
target.cstrlen = function(ptr){
|
target.cstrlen = function(ptr){
|
||||||
if(!ptr) return null;
|
if(!ptr || !target.isPtr(ptr)) return null;
|
||||||
const h = heapWrappers().HEAP8U;
|
const h = heapWrappers().HEAP8U;
|
||||||
let pos = ptr;
|
let pos = ptr;
|
||||||
for( ; h[pos] !== 0; ++pos ){}
|
for( ; h[pos] !== 0; ++pos ){}
|
||||||
@@ -753,9 +825,9 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
refers to a NUL-terminated C-style string encoded as UTF-8. This
|
refers to a NUL-terminated C-style string encoded as UTF-8. This
|
||||||
function counts its byte length using cstrlen() then returns a
|
function counts its byte length using cstrlen() then returns a
|
||||||
JS-format string representing its contents. As a special case, if
|
JS-format string representing its contents. As a special case, if
|
||||||
ptr is falsy, `null` is returned.
|
ptr is falsy or not a pointer, `null` is returned.
|
||||||
*/
|
*/
|
||||||
target.cstringToJs = function(ptr){
|
target.cstrToJs = function(ptr){
|
||||||
const n = target.cstrlen(ptr);
|
const n = target.cstrlen(ptr);
|
||||||
return n ? __utf8Decode(heapWrappers().HEAP8U, ptr, ptr+n) : (null===n ? n : "");
|
return n ? __utf8Decode(heapWrappers().HEAP8U, ptr, ptr+n) : (null===n ? n : "");
|
||||||
};
|
};
|
||||||
@@ -942,10 +1014,19 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
const __allocCStr = function(jstr, returnWithLength, allocator, funcName){
|
const __allocCStr = function(jstr, returnWithLength, allocator, funcName){
|
||||||
__affirmAlloc(target, funcName);
|
__affirmAlloc(target, funcName);
|
||||||
if('string'!==typeof jstr) return null;
|
if('string'!==typeof jstr) return null;
|
||||||
const n = target.jstrlen(jstr),
|
if(0){/* older impl, possibly more widely compatible? */
|
||||||
ptr = allocator(n+1);
|
const n = target.jstrlen(jstr),
|
||||||
target.jstrcpy(jstr, target.heap8u(), ptr, n+1, true);
|
ptr = allocator(n+1);
|
||||||
return returnWithLength ? [ptr, n] : ptr;
|
target.jstrcpy(jstr, target.heap8u(), ptr, n+1, true);
|
||||||
|
return returnWithLength ? [ptr, n] : ptr;
|
||||||
|
}else{/* newer, (probably) faster and (certainly) simpler impl */
|
||||||
|
const u = cache.utf8Encoder.encode(jstr),
|
||||||
|
ptr = allocator(u.length+1),
|
||||||
|
heap = heapWrappers().HEAP8U;
|
||||||
|
heap.set(u, ptr);
|
||||||
|
heap[ptr + u.length] = 0;
|
||||||
|
return returnWithLength ? [ptr, u.length] : ptr;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1035,7 +1116,13 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
if(n<0) toss("Invalid state object for scopedAllocPop().");
|
if(n<0) toss("Invalid state object for scopedAllocPop().");
|
||||||
if(0===arguments.length) state = cache.scopedAlloc[n];
|
if(0===arguments.length) state = cache.scopedAlloc[n];
|
||||||
cache.scopedAlloc.splice(n,1);
|
cache.scopedAlloc.splice(n,1);
|
||||||
for(let p; (p = state.pop()); ) target.dealloc(p);
|
for(let p; (p = state.pop()); ){
|
||||||
|
if(target.functionEntry(p)){
|
||||||
|
//console.warn("scopedAllocPop() uninstalling transient function",p);
|
||||||
|
target.uninstallFunction(p);
|
||||||
|
}
|
||||||
|
else target.dealloc(p);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1081,40 +1168,67 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
|
|
||||||
// impl for allocMainArgv() and scopedAllocMainArgv().
|
// impl for allocMainArgv() and scopedAllocMainArgv().
|
||||||
const __allocMainArgv = function(isScoped, list){
|
const __allocMainArgv = function(isScoped, list){
|
||||||
if(!list.length) toss("Cannot allocate empty array.");
|
|
||||||
const pList = target[
|
const pList = target[
|
||||||
isScoped ? 'scopedAlloc' : 'alloc'
|
isScoped ? 'scopedAlloc' : 'alloc'
|
||||||
](list.length * target.ptrSizeof);
|
]((list.length + 1) * target.ptrSizeof);
|
||||||
let i = 0;
|
let i = 0;
|
||||||
list.forEach((e)=>{
|
list.forEach((e)=>{
|
||||||
target.setPtrValue(pList + (target.ptrSizeof * i++),
|
target.pokePtr(pList + (target.ptrSizeof * i++),
|
||||||
target[
|
target[
|
||||||
isScoped ? 'scopedAllocCString' : 'allocCString'
|
isScoped ? 'scopedAllocCString' : 'allocCString'
|
||||||
](""+e));
|
](""+e));
|
||||||
});
|
});
|
||||||
|
target.pokePtr(pList + (target.ptrSizeof * i), 0);
|
||||||
return pList;
|
return pList;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Creates an array, using scopedAlloc(), suitable for passing to a
|
Creates an array, using scopedAlloc(), suitable for passing to a
|
||||||
C-level main() routine. The input is a collection with a length
|
C-level main() routine. The input is a collection with a length
|
||||||
property and a forEach() method. A block of memory list.length
|
property and a forEach() method. A block of memory
|
||||||
entries long is allocated and each pointer-sized block of that
|
(list.length+1) entries long is allocated and each pointer-sized
|
||||||
memory is populated with a scopedAllocCString() conversion of the
|
block of that memory is populated with a scopedAllocCString()
|
||||||
(""+value) of each element. Returns a pointer to the start of the
|
conversion of the (""+value) of each element, with the exception
|
||||||
list, suitable for passing as the 2nd argument to a C-style
|
that the final entry is a NULL pointer. Returns a pointer to the
|
||||||
main() function.
|
start of the list, suitable for passing as the 2nd argument to a
|
||||||
|
C-style main() function.
|
||||||
|
|
||||||
Throws if list.length is falsy or scopedAllocPush() is not active.
|
Throws if scopedAllocPush() is not active.
|
||||||
|
|
||||||
|
Design note: the returned array is allocated with an extra NULL
|
||||||
|
pointer entry to accommodate certain APIs, but client code which
|
||||||
|
does not need that functionality should treat the returned array
|
||||||
|
as list.length entries long.
|
||||||
*/
|
*/
|
||||||
target.scopedAllocMainArgv = (list)=>__allocMainArgv(true, list);
|
target.scopedAllocMainArgv = (list)=>__allocMainArgv(true, list);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Identical to scopedAllocMainArgv() but uses alloc() instead of
|
Identical to scopedAllocMainArgv() but uses alloc() instead of
|
||||||
scopedAllocMainArgv
|
scopedAlloc().
|
||||||
*/
|
*/
|
||||||
target.allocMainArgv = (list)=>__allocMainArgv(false, list);
|
target.allocMainArgv = (list)=>__allocMainArgv(false, list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Expects to be given a C-style string array and its length. It
|
||||||
|
returns a JS array of strings and/or nulls: any entry in the
|
||||||
|
pArgv array which is NULL results in a null entry in the result
|
||||||
|
array. If argc is 0 then an empty array is returned.
|
||||||
|
|
||||||
|
Results are undefined if any entry in the first argc entries of
|
||||||
|
pArgv are neither 0 (NULL) nor legal UTF-format C strings.
|
||||||
|
|
||||||
|
To be clear, the expected C-style arguments to be passed to this
|
||||||
|
function are `(int, char **)` (optionally const-qualified).
|
||||||
|
*/
|
||||||
|
target.cArgvToJs = (argc, pArgv)=>{
|
||||||
|
const list = [];
|
||||||
|
for(let i = 0; i < argc; ++i){
|
||||||
|
const arg = target.peekPtr(pArgv + (target.ptrSizeof * i));
|
||||||
|
list.push( arg ? target.cstrToJs(arg) : null );
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Wraps function call func() in a scopedAllocPush() and
|
Wraps function call func() in a scopedAllocPush() and
|
||||||
scopedAllocPop() block, such that all calls to scopedAlloc() and
|
scopedAllocPop() block, such that all calls to scopedAlloc() and
|
||||||
@@ -1133,7 +1247,7 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
__affirmAlloc(target, method);
|
__affirmAlloc(target, method);
|
||||||
const pIr = safePtrSize ? 'i64' : ptrIR;
|
const pIr = safePtrSize ? 'i64' : ptrIR;
|
||||||
let m = target[method](howMany * (safePtrSize ? 8 : ptrSizeof));
|
let m = target[method](howMany * (safePtrSize ? 8 : ptrSizeof));
|
||||||
target.setMemValue(m, 0, pIr)
|
target.poke(m, 0, pIr)
|
||||||
if(1===howMany){
|
if(1===howMany){
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
@@ -1141,7 +1255,7 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
for(let i = 1; i < howMany; ++i){
|
for(let i = 1; i < howMany; ++i){
|
||||||
m += (safePtrSize ? 8 : ptrSizeof);
|
m += (safePtrSize ? 8 : ptrSizeof);
|
||||||
a[i] = m;
|
a[i] = m;
|
||||||
target.setMemValue(m, 0, pIr);
|
target.poke(m, 0, pIr);
|
||||||
}
|
}
|
||||||
return a;
|
return a;
|
||||||
};
|
};
|
||||||
@@ -1172,7 +1286,7 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
|
|
||||||
When one of the returned pointers will refer to a 64-bit value,
|
When one of the returned pointers will refer to a 64-bit value,
|
||||||
e.g. a double or int64, an that value must be written or fetched,
|
e.g. a double or int64, an that value must be written or fetched,
|
||||||
e.g. using setMemValue() or getMemValue(), it is important that
|
e.g. using poke() or peek(), it is important that
|
||||||
the pointer in question be aligned to an 8-byte boundary or else
|
the pointer in question be aligned to an 8-byte boundary or else
|
||||||
it will not be fetched or written properly and will corrupt or
|
it will not be fetched or written properly and will corrupt or
|
||||||
read neighboring memory. It is only safe to pass false when the
|
read neighboring memory. It is only safe to pass false when the
|
||||||
@@ -1228,32 +1342,39 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
State for use with xWrap()
|
State for use with xWrap()
|
||||||
*/
|
*/
|
||||||
cache.xWrap = Object.create(null);
|
cache.xWrap = Object.create(null);
|
||||||
const xcv = cache.xWrap.convert = Object.create(null);
|
cache.xWrap.convert = Object.create(null);
|
||||||
/** Map of type names to argument conversion functions. */
|
/** Map of type names to argument conversion functions. */
|
||||||
cache.xWrap.convert.arg = Object.create(null);
|
cache.xWrap.convert.arg = new Map;
|
||||||
/** Map of type names to return result conversion functions. */
|
/** Map of type names to return result conversion functions. */
|
||||||
cache.xWrap.convert.result = Object.create(null);
|
cache.xWrap.convert.result = new Map;
|
||||||
|
const xArg = cache.xWrap.convert.arg, xResult = cache.xWrap.convert.result;
|
||||||
|
|
||||||
if(target.bigIntEnabled){
|
if(target.bigIntEnabled){
|
||||||
xcv.arg.i64 = (i)=>BigInt(i);
|
xArg.set('i64', (i)=>BigInt(i));
|
||||||
}
|
}
|
||||||
xcv.arg.i32 = (i)=>(i | 0);
|
xArg.set('i32', (i)=>(i | 0));
|
||||||
xcv.arg.i16 = (i)=>((i | 0) & 0xFFFF);
|
xArg.set('i16', (i)=>((i | 0) & 0xFFFF));
|
||||||
xcv.arg.i8 = (i)=>((i | 0) & 0xFF);
|
xArg.set('i8', (i)=>((i | 0) & 0xFF));
|
||||||
xcv.arg.f32 = xcv.arg.float = (i)=>Number(i).valueOf();
|
xArg.set('f32', (i)=>Number(i).valueOf());
|
||||||
xcv.arg.f64 = xcv.arg.double = xcv.arg.f32;
|
xArg.set('float', xArg.get('f32'));
|
||||||
xcv.arg.int = xcv.arg.i32;
|
xArg.set('f64', xArg.get('f32'));
|
||||||
xcv.result['*'] = xcv.result['pointer'] = xcv.arg['**'] = xcv.arg[ptrIR];
|
xArg.set('double', xArg.get('f64'));
|
||||||
xcv.result['number'] = (v)=>Number(v);
|
xArg.set('int', xArg.get('i32'));
|
||||||
|
xResult.set('*', xArg.get(ptrIR));
|
||||||
|
xResult.set('pointer', xArg.get(ptrIR));
|
||||||
|
xArg.set('**', xArg.get(ptrIR));
|
||||||
|
xResult.set('number', (v)=>Number(v));
|
||||||
|
|
||||||
{ /* Copy certain xcv.arg[...] handlers to xcv.result[...] and
|
{ /* Copy certain xArg[...] handlers to xResult[...] and
|
||||||
add pointer-style variants of them. */
|
add pointer-style variants of them. */
|
||||||
const copyToResult = ['i8', 'i16', 'i32', 'int',
|
const copyToResult = ['i8', 'i16', 'i32', 'int',
|
||||||
'f32', 'float', 'f64', 'double'];
|
'f32', 'float', 'f64', 'double'];
|
||||||
if(target.bigIntEnabled) copyToResult.push('i64');
|
if(target.bigIntEnabled) copyToResult.push('i64');
|
||||||
|
const adaptPtr = xArg.get(ptrIR);
|
||||||
for(const t of copyToResult){
|
for(const t of copyToResult){
|
||||||
xcv.arg[t+'*'] = xcv.result[t+'*'] = xcv.arg[ptrIR];
|
xArg.set(t+'*', adaptPtr);
|
||||||
xcv.result[t] = xcv.arg[t] || toss("Missing arg converter:",t);
|
xResult.set(t+'*', adaptPtr);
|
||||||
|
xResult.set(t, (xArg.get(t) || toss("Missing arg converter:",t)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1273,23 +1394,28 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
Would that be too much magic concentrated in one place, ready to
|
Would that be too much magic concentrated in one place, ready to
|
||||||
backfire?
|
backfire?
|
||||||
*/
|
*/
|
||||||
xcv.arg.string = xcv.arg.utf8 = xcv.arg['pointer'] = xcv.arg['*']
|
xArg.set('string', function(v){
|
||||||
= function(v){
|
if('string'===typeof v) return target.scopedAllocCString(v);
|
||||||
if('string'===typeof v) return target.scopedAllocCString(v);
|
return v ? xArg.get(ptrIR)(v) : null;
|
||||||
return v ? xcv.arg[ptrIR](v) : null;
|
});
|
||||||
};
|
xArg.set('utf8', xArg.get('string'));
|
||||||
xcv.result.string = xcv.result.utf8 = (i)=>target.cstringToJs(i);
|
xArg.set('pointer', xArg.get('string'));
|
||||||
xcv.result['string:dealloc'] = xcv.result['utf8:dealloc'] = (i)=>{
|
xArg.set('*', xArg.get('string'));
|
||||||
try { return i ? target.cstringToJs(i) : null }
|
|
||||||
|
xResult.set('string', (i)=>target.cstrToJs(i));
|
||||||
|
xResult.set('utf8', xResult.get('string'));
|
||||||
|
xResult.set('string:dealloc', (i)=>{
|
||||||
|
try { return i ? target.cstrToJs(i) : null }
|
||||||
finally{ target.dealloc(i) }
|
finally{ target.dealloc(i) }
|
||||||
};
|
});
|
||||||
xcv.result.json = (i)=>JSON.parse(target.cstringToJs(i));
|
xResult.set('utf8:dealloc', xResult.get('string:dealloc'));
|
||||||
xcv.result['json:dealloc'] = (i)=>{
|
xResult.set('json', (i)=>JSON.parse(target.cstrToJs(i)));
|
||||||
try{ return i ? JSON.parse(target.cstringToJs(i)) : null }
|
xResult.set('json:dealloc', (i)=>{
|
||||||
|
try{ return i ? JSON.parse(target.cstrToJs(i)) : null }
|
||||||
finally{ target.dealloc(i) }
|
finally{ target.dealloc(i) }
|
||||||
}
|
});
|
||||||
xcv.result['void'] = (v)=>undefined;
|
xResult.set('void', (v)=>undefined);
|
||||||
xcv.result['null'] = (v)=>v;
|
xResult.set('null', (v)=>v);
|
||||||
|
|
||||||
if(0){
|
if(0){
|
||||||
/***
|
/***
|
||||||
@@ -1302,19 +1428,178 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
the value will always be treated like -1 (which has a useful
|
the value will always be treated like -1 (which has a useful
|
||||||
case in the sqlite3 bindings).
|
case in the sqlite3 bindings).
|
||||||
*/
|
*/
|
||||||
xcv.arg['func-ptr'] = function(v){
|
xArg.set('func-ptr', function(v){
|
||||||
if(!(v instanceof Function)) return xcv.arg[ptrIR];
|
if(!(v instanceof Function)) return xArg.get(ptrIR);
|
||||||
const f = target.jsFuncToWasm(v, WHAT_SIGNATURE);
|
const f = target.jsFuncToWasm(v, WHAT_SIGNATURE);
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
EXPERIMENTAL! DO NOT USE IN CLIENT CODE!
|
||||||
|
|
||||||
|
An attempt at adding function pointer conversion support to
|
||||||
|
xWrap(). This type is recognized by xWrap() as a proxy for
|
||||||
|
converting a JS function to a C-side function, either
|
||||||
|
permanently, for the duration of a single call into the C layer,
|
||||||
|
or semi-contextual, where it may keep track of a single binding
|
||||||
|
for a given context and uninstall the binding if it's replaced.
|
||||||
|
|
||||||
|
Requires an options object with these properties:
|
||||||
|
|
||||||
|
- name (optional): string describing the function binding. This
|
||||||
|
is solely for debugging and error-reporting purposes. If not
|
||||||
|
provided, an empty string is assumed.
|
||||||
|
|
||||||
|
- signature: an function signature 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
|
||||||
|
instance. If it's called with a different function pointer,
|
||||||
|
it uninstalls the previous one after converting the new
|
||||||
|
value. This is only useful for use with "global" functions
|
||||||
|
which do not rely on any state other than this function
|
||||||
|
pointer. If the being-converted function pointer is intended
|
||||||
|
to be mapped to some sort of state object (e.g. an sqlite3*)
|
||||||
|
then "context" (see below) is the proper mode.
|
||||||
|
|
||||||
|
- context: similar to singleton mode but for a given "context",
|
||||||
|
where the context is a key provided by the user and possibly
|
||||||
|
dependent on a small amount of call-time context. This mode
|
||||||
|
is the default if bindScope is _not_ set but a property named
|
||||||
|
contextKey (described below) is.
|
||||||
|
|
||||||
|
FIXME: the contextKey definition is only useful for very basic
|
||||||
|
contexts and breaks down with dynamic ones like
|
||||||
|
sqlite3_create_collation().
|
||||||
|
|
||||||
|
- contextKey (function): only used if bindScope is not set or is
|
||||||
|
'context'. This function gets passed (argIndex,argv), where
|
||||||
|
argIndex is the index of this function pointer in its
|
||||||
|
_wrapping_ function's arguments and argv is the _current_
|
||||||
|
being-xWrap()-processed args array. All arguments to the left
|
||||||
|
of argIndex will have been processed by xWrap() by the time
|
||||||
|
this is called. argv[argIndex] will be the value the user
|
||||||
|
passed in to the xWrap()'d function for the argument this
|
||||||
|
FuncPtrAdapter is mapped to. Arguments to the right of
|
||||||
|
argv[argIndex] will not yet have been converted before this is
|
||||||
|
called. The function must return a key which uniquely
|
||||||
|
identifies this function mapping context for _this_
|
||||||
|
FuncPtrAdapter instance (other instances are not considered),
|
||||||
|
taking into account that C functions often take some sort of
|
||||||
|
state object as one or more of their arguments. As an example,
|
||||||
|
if the xWrap()'d function takes `(int,T*,functionPtr,X*)` and
|
||||||
|
this FuncPtrAdapter is the argv[2]nd arg, contextKey(2,argv)
|
||||||
|
might return 'T@'+argv[1], or even just argv[1]. Note,
|
||||||
|
however, that the (X*) argument will not yet have been
|
||||||
|
processed by the time this is called and should not be used as
|
||||||
|
part of that key. Similarly, C-string-type keys should not be
|
||||||
|
used as part of keys because they are normally transient in
|
||||||
|
this environment.
|
||||||
|
|
||||||
|
The constructor only saves the above state for later, and does
|
||||||
|
not actually bind any functions. Its convertArg() methor is
|
||||||
|
called via xWrap() to perform any bindings.
|
||||||
|
|
||||||
|
Caveats:
|
||||||
|
|
||||||
|
- singleton is globally singleton. This type does not currently
|
||||||
|
have enough context to apply, e.g., a different singleton for
|
||||||
|
each (sqlite3*) db handle.
|
||||||
|
*/
|
||||||
|
xArg.FuncPtrAdapter = function ctor(opt) {
|
||||||
|
if(!(this instanceof xArg.FuncPtrAdapter)){
|
||||||
|
toss("FuncPtrAdapter can only be used as a constructor. Use 'new'.");
|
||||||
|
}
|
||||||
|
this.signature = opt.signature;
|
||||||
|
if(!opt.bindScope && (opt.contextKey instanceof Function)){
|
||||||
|
opt.bindScope = 'context';
|
||||||
|
}else if(ctor.bindScopes.indexOf(opt.bindScope)<0){
|
||||||
|
toss("Invalid options.bindScope ("+opt.bindMod+") for FuncPtrAdapter. "+
|
||||||
|
"Expecting one of: ("+ctor.bindScopes.join(', ')+')');
|
||||||
|
}
|
||||||
|
this.bindScope = opt.bindScope;
|
||||||
|
this.name = opt.name || '';
|
||||||
|
if(opt.contextKey) this.contextKey = opt.contextKey /*else inherit one*/;
|
||||||
|
this.isTransient = 'transient'===this.bindScope;
|
||||||
|
this.isContext = 'context'===this.bindScope;
|
||||||
|
if( ('singleton'===this.bindScope) ){
|
||||||
|
this.singleton = [];
|
||||||
|
}else{
|
||||||
|
this.singleton = undefined;
|
||||||
|
}
|
||||||
|
//console.warn("FuncPtrAdapter()",opt,this);
|
||||||
|
};
|
||||||
|
xArg.FuncPtrAdapter.bindScopes = [
|
||||||
|
'transient', 'context', 'singleton'
|
||||||
|
];
|
||||||
|
xArg.FuncPtrAdapter.prototype = {
|
||||||
|
contextKey: function(argIndex,argv){
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
contextMap: function(key){
|
||||||
|
const cm = (this.__cmap || (this.__cmap = new Map));
|
||||||
|
let rc = cm.get(key);
|
||||||
|
if(undefined===rc) cm.set(key, (rc = []));
|
||||||
|
return rc;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
Gets called via xWrap() to "convert" v to a WASM-bound function
|
||||||
|
pointer. If v is one of (a pointer, null, undefined) then
|
||||||
|
(v||0) is returned, otherwise v must be a Function, for which
|
||||||
|
it creates (if needed) a WASM function binding and returns the
|
||||||
|
WASM pointer to that binding. It will remember the binding for
|
||||||
|
at least the next call, to avoid recreating the function
|
||||||
|
unnecessarily.
|
||||||
|
*/
|
||||||
|
convertArg: function(v,argIndex,argv){
|
||||||
|
//console.warn("FuncPtrAdapter.convertArg()",this.signature,this.transient,v);
|
||||||
|
let pair = this.singleton;
|
||||||
|
if(!pair && this.isContext){
|
||||||
|
pair = this.contextMap(this.contextKey(argIndex, argv));
|
||||||
|
}
|
||||||
|
if(pair && pair[0]===v) return pair[1];
|
||||||
|
if(v instanceof Function){
|
||||||
|
const fp = __installFunction(v, this.signature, this.isTransient);
|
||||||
|
if(pair){
|
||||||
|
if(pair[1]){
|
||||||
|
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]){
|
||||||
|
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 "+(this.name ? this.name+' ' : '')+
|
||||||
|
"function matching signature "+
|
||||||
|
this.signature+".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}/*FuncPtrAdapter.prototype*/;
|
||||||
|
|
||||||
const __xArgAdapterCheck =
|
const __xArgAdapterCheck =
|
||||||
(t)=>xcv.arg[t] || toss("Argument adapter not found:",t);
|
(t)=>xArg.get(t) || toss("Argument adapter not found:",t);
|
||||||
|
|
||||||
const __xResultAdapterCheck =
|
const __xResultAdapterCheck =
|
||||||
(t)=>xcv.result[t] || toss("Result adapter not found:",t);
|
(t)=>xResult.get(t) || toss("Result adapter not found:",t);
|
||||||
|
|
||||||
cache.xWrap.convertArg = (t,v)=>__xArgAdapterCheck(t)(v);
|
cache.xWrap.convertArg = (t,...args)=>__xArgAdapterCheck(t)(...args);
|
||||||
cache.xWrap.convertResult =
|
cache.xWrap.convertResult =
|
||||||
(t,v)=>(null===t ? v : (t ? __xResultAdapterCheck(t)(v) : undefined));
|
(t,v)=>(null===t ? v : (t ? __xResultAdapterCheck(t)(v) : undefined));
|
||||||
|
|
||||||
@@ -1423,7 +1708,7 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
target.xWrap.resultAdapter('string:my_free',(i)=>{
|
target.xWrap.resultAdapter('string:my_free',(i)=>{
|
||||||
try { return i ? target.cstringToJs(i) : null }
|
try { return i ? target.cstrToJs(i) : null }
|
||||||
finally{ target.exports.my_free(i) }
|
finally{ target.exports.my_free(i) }
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
@@ -1451,8 +1736,8 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
- Figure out how/whether we can (semi-)transparently handle
|
- Figure out how/whether we can (semi-)transparently handle
|
||||||
pointer-type _output_ arguments. Those currently require
|
pointer-type _output_ arguments. Those currently require
|
||||||
explicit handling by allocating pointers, assigning them before
|
explicit handling by allocating pointers, assigning them before
|
||||||
the call using setMemValue(), and fetching them with
|
the call using poke(), and fetching them with
|
||||||
getMemValue() after the call. We may be able to automate some
|
peek() after the call. We may be able to automate some
|
||||||
or all of that.
|
or all of that.
|
||||||
|
|
||||||
- Figure out whether it makes sense to extend the arg adapter
|
- Figure out whether it makes sense to extend the arg adapter
|
||||||
@@ -1478,19 +1763,23 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
}
|
}
|
||||||
/*Verify the arg type conversions are valid...*/;
|
/*Verify the arg type conversions are valid...*/;
|
||||||
if(undefined!==resultType && null!==resultType) __xResultAdapterCheck(resultType);
|
if(undefined!==resultType && null!==resultType) __xResultAdapterCheck(resultType);
|
||||||
argTypes.forEach(__xArgAdapterCheck);
|
for(const t of argTypes){
|
||||||
|
if(t instanceof xArg.FuncPtrAdapter) xArg.set(t, (...args)=>t.convertArg(...args));
|
||||||
|
else __xArgAdapterCheck(t);
|
||||||
|
}
|
||||||
|
const cxw = cache.xWrap;
|
||||||
if(0===xf.length){
|
if(0===xf.length){
|
||||||
// No args to convert, so we can create a simpler wrapper...
|
// No args to convert, so we can create a simpler wrapper...
|
||||||
return (...args)=>(args.length
|
return (...args)=>(args.length
|
||||||
? __argcMismatch(fname, xf.length)
|
? __argcMismatch(fname, xf.length)
|
||||||
: cache.xWrap.convertResult(resultType, xf.call(null)));
|
: cxw.convertResult(resultType, xf.call(null)));
|
||||||
}
|
}
|
||||||
return function(...args){
|
return function(...args){
|
||||||
if(args.length!==xf.length) __argcMismatch(fname, xf.length);
|
if(args.length!==xf.length) __argcMismatch(fname, xf.length);
|
||||||
const scope = target.scopedAllocPush();
|
const scope = target.scopedAllocPush();
|
||||||
try{
|
try{
|
||||||
const rc = xf.apply(null,args.map((v,i)=>cache.xWrap.convertArg(argTypes[i], v)));
|
for(const i in args) args[i] = cxw.convertArg(argTypes[i], args[i], i, args);
|
||||||
return cache.xWrap.convertResult(resultType, rc);
|
return cxw.convertResult(resultType, xf.apply(null,args));
|
||||||
}finally{
|
}finally{
|
||||||
target.scopedAllocPop(scope);
|
target.scopedAllocPop(scope);
|
||||||
}
|
}
|
||||||
@@ -1500,15 +1789,15 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
/** Internal impl for xWrap.resultAdapter() and argAdapter(). */
|
/** Internal impl for xWrap.resultAdapter() and argAdapter(). */
|
||||||
const __xAdapter = function(func, argc, typeName, adapter, modeName, xcvPart){
|
const __xAdapter = function(func, argc, typeName, adapter, modeName, xcvPart){
|
||||||
if('string'===typeof typeName){
|
if('string'===typeof typeName){
|
||||||
if(1===argc) return xcvPart[typeName];
|
if(1===argc) return xcvPart.get(typeName);
|
||||||
else if(2===argc){
|
else if(2===argc){
|
||||||
if(!adapter){
|
if(!adapter){
|
||||||
delete xcvPart[typeName];
|
delete xcvPart.get(typeName);
|
||||||
return func;
|
return func;
|
||||||
}else if(!(adapter instanceof Function)){
|
}else if(!(adapter instanceof Function)){
|
||||||
toss(modeName,"requires a function argument.");
|
toss(modeName,"requires a function argument.");
|
||||||
}
|
}
|
||||||
xcvPart[typeName] = adapter;
|
xcvPart.set(typeName, adapter);
|
||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1545,7 +1834,7 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
*/
|
*/
|
||||||
target.xWrap.resultAdapter = function f(typeName, adapter){
|
target.xWrap.resultAdapter = function f(typeName, adapter){
|
||||||
return __xAdapter(f, arguments.length, typeName, adapter,
|
return __xAdapter(f, arguments.length, typeName, adapter,
|
||||||
'resultAdapter()', xcv.result);
|
'resultAdapter()', xResult);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1575,9 +1864,11 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
*/
|
*/
|
||||||
target.xWrap.argAdapter = function f(typeName, adapter){
|
target.xWrap.argAdapter = function f(typeName, adapter){
|
||||||
return __xAdapter(f, arguments.length, typeName, adapter,
|
return __xAdapter(f, arguments.length, typeName, adapter,
|
||||||
'argAdapter()', xcv.arg);
|
'argAdapter()', xArg);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
target.xWrap.FuncPtrAdapter = xArg.FuncPtrAdapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Functions like xCall() but performs argument and result type
|
Functions like xCall() but performs argument and result type
|
||||||
conversions as for xWrap(). The first argument is the name of the
|
conversions as for xWrap(). The first argument is the name of the
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ dist-name := $(dist-name-prefix)-TEMP
|
|||||||
# date. Our general policy is that we want the smallest binaries for
|
# date. Our general policy is that we want the smallest binaries for
|
||||||
# dist zip files, so use the oz build unless there is a compelling
|
# dist zip files, so use the oz build unless there is a compelling
|
||||||
# reason not to.
|
# reason not to.
|
||||||
dist.build ?= oz
|
dist.build ?= qoz
|
||||||
|
|
||||||
dist-dir.top := $(dist-name)
|
dist-dir.top := $(dist-name)
|
||||||
dist-dir.jswasm := $(dist-dir.top)/$(notdir $(dir.dout))
|
dist-dir.jswasm := $(dist-dir.top)/$(notdir $(dir.dout))
|
||||||
@@ -63,7 +63,7 @@ dist.common.extras := \
|
|||||||
# $(dist.build) will depend on clean, having any deps on
|
# $(dist.build) will depend on clean, having any deps on
|
||||||
# $(dist-archive) which themselves may be cleaned up by the clean
|
# $(dist-archive) which themselves may be cleaned up by the clean
|
||||||
# target will lead to grief in parallel builds (-j #). Thus
|
# target will lead to grief in parallel builds (-j #). Thus
|
||||||
# $(dist-target)'s deps must be trimmed to non-generated files or
|
# dist's deps must be trimmed to non-generated files or
|
||||||
# files which are _not_ cleaned up by the clean target.
|
# files which are _not_ cleaned up by the clean target.
|
||||||
#
|
#
|
||||||
# Note that we require $(bin.version-info) in order to figure out the
|
# Note that we require $(bin.version-info) in order to figure out the
|
||||||
|
|||||||
@@ -10,9 +10,12 @@
|
|||||||
|
|
||||||
***********************************************************************
|
***********************************************************************
|
||||||
|
|
||||||
The Jaccwabyt API is documented in detail in an external file.
|
The Jaccwabyt API is documented in detail in an external file,
|
||||||
|
_possibly_ called jaccwabyt.md in the same directory as this file.
|
||||||
|
|
||||||
Project home: https://fossil.wanderinghorse.net/r/jaccwabyt
|
Project homes:
|
||||||
|
- https://fossil.wanderinghorse.net/r/jaccwabyt
|
||||||
|
- https://sqlite.org/src/dir/ext/wasm/jaccwabyt
|
||||||
|
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
@@ -61,7 +64,6 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
BigInt = self['BigInt'],
|
BigInt = self['BigInt'],
|
||||||
BigInt64Array = self['BigInt64Array'],
|
BigInt64Array = self['BigInt64Array'],
|
||||||
/* Undocumented (on purpose) config options: */
|
/* Undocumented (on purpose) config options: */
|
||||||
functionTable = config.functionTable/*EXPERIMENTAL, undocumented*/,
|
|
||||||
ptrSizeof = config.ptrSizeof || 4,
|
ptrSizeof = config.ptrSizeof || 4,
|
||||||
ptrIR = config.ptrIR || 'i32'
|
ptrIR = config.ptrIR || 'i32'
|
||||||
;
|
;
|
||||||
@@ -121,6 +123,7 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
at SIG s[0]. Throws for an unknown SIG. */
|
at SIG s[0]. Throws for an unknown SIG. */
|
||||||
const sigIR = function(s){
|
const sigIR = function(s){
|
||||||
switch(sigLetter(s)){
|
switch(sigLetter(s)){
|
||||||
|
case 'c': case 'C': return 'i8';
|
||||||
case 'i': return 'i32';
|
case 'i': return 'i32';
|
||||||
case 'p': case 'P': case 's': return ptrIR;
|
case 'p': case 'P': case 's': return ptrIR;
|
||||||
case 'j': return 'i64';
|
case 'j': return 'i64';
|
||||||
@@ -129,33 +132,9 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
}
|
}
|
||||||
toss("Unhandled signature IR:",s);
|
toss("Unhandled signature IR:",s);
|
||||||
};
|
};
|
||||||
/** Returns the sizeof value for the given SIG. Throws for an
|
|
||||||
unknown SIG. */
|
|
||||||
const sigSizeof = function(s){
|
|
||||||
switch(sigLetter(s)){
|
|
||||||
case 'i': return 4;
|
|
||||||
case 'p': case 'P': case 's': return ptrSizeof;
|
|
||||||
case 'j': return 8;
|
|
||||||
case 'f': return 4 /* C-side floats, not JS-side */;
|
|
||||||
case 'd': return 8;
|
|
||||||
}
|
|
||||||
toss("Unhandled signature sizeof:",s);
|
|
||||||
};
|
|
||||||
const affirmBigIntArray = BigInt64Array
|
const affirmBigIntArray = BigInt64Array
|
||||||
? ()=>true : ()=>toss('BigInt64Array is not available.');
|
? ()=>true : ()=>toss('BigInt64Array is not available.');
|
||||||
/** Returns the (signed) TypedArray associated with the type
|
|
||||||
described by the given SIG. Throws for an unknown SIG. */
|
|
||||||
/**********
|
|
||||||
const sigTypedArray = function(s){
|
|
||||||
switch(sigIR(s)) {
|
|
||||||
case 'i32': return Int32Array;
|
|
||||||
case 'i64': return affirmBigIntArray() && BigInt64Array;
|
|
||||||
case 'float': return Float32Array;
|
|
||||||
case 'double': return Float64Array;
|
|
||||||
}
|
|
||||||
toss("Unhandled signature TypedArray:",s);
|
|
||||||
};
|
|
||||||
**************/
|
|
||||||
/** Returns the name of a DataView getter method corresponding
|
/** Returns the name of a DataView getter method corresponding
|
||||||
to the given SIG. */
|
to the given SIG. */
|
||||||
const sigDVGetter = function(s){
|
const sigDVGetter = function(s){
|
||||||
@@ -168,6 +147,8 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'i': return 'getInt32';
|
case 'i': return 'getInt32';
|
||||||
|
case 'c': return 'getInt8';
|
||||||
|
case 'C': return 'getUint8';
|
||||||
case 'j': return affirmBigIntArray() && 'getBigInt64';
|
case 'j': return affirmBigIntArray() && 'getBigInt64';
|
||||||
case 'f': return 'getFloat32';
|
case 'f': return 'getFloat32';
|
||||||
case 'd': return 'getFloat64';
|
case 'd': return 'getFloat64';
|
||||||
@@ -186,6 +167,8 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'i': return 'setInt32';
|
case 'i': return 'setInt32';
|
||||||
|
case 'c': return 'setInt8';
|
||||||
|
case 'C': return 'setUint8';
|
||||||
case 'j': return affirmBigIntArray() && 'setBigInt64';
|
case 'j': return affirmBigIntArray() && 'setBigInt64';
|
||||||
case 'f': return 'setFloat32';
|
case 'f': return 'setFloat32';
|
||||||
case 'd': return 'setFloat64';
|
case 'd': return 'setFloat64';
|
||||||
@@ -199,7 +182,7 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
*/
|
*/
|
||||||
const sigDVSetWrapper = function(s){
|
const sigDVSetWrapper = function(s){
|
||||||
switch(sigLetter(s)) {
|
switch(sigLetter(s)) {
|
||||||
case 'i': case 'f': case 'd': return Number;
|
case 'i': case 'f': case 'c': case 'C': case 'd': return Number;
|
||||||
case 'j': return affirmBigIntArray() && BigInt;
|
case 'j': return affirmBigIntArray() && BigInt;
|
||||||
case 'p': case 'P': case 's':
|
case 'p': case 'P': case 's':
|
||||||
switch(ptrSizeof){
|
switch(ptrSizeof){
|
||||||
@@ -211,36 +194,14 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
toss("Unhandled DataView set wrapper for signature:",s);
|
toss("Unhandled DataView set wrapper for signature:",s);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Returns the given struct and member name in a form suitable for
|
||||||
|
debugging and error output. */
|
||||||
const sPropName = (s,k)=>s+'::'+k;
|
const sPropName = (s,k)=>s+'::'+k;
|
||||||
|
|
||||||
const __propThrowOnSet = function(structName,propName){
|
const __propThrowOnSet = function(structName,propName){
|
||||||
return ()=>toss(sPropName(structName,propName),"is read-only.");
|
return ()=>toss(sPropName(structName,propName),"is read-only.");
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
When C code passes a pointer of a bound struct to back into
|
|
||||||
a JS function via a function pointer struct member, it
|
|
||||||
arrives in JS as a number (pointer).
|
|
||||||
StructType.instanceForPointer(ptr) can be used to get the
|
|
||||||
instance associated with that pointer, and __ptrBacklinks
|
|
||||||
holds that mapping. WeakMap keys must be objects, so we
|
|
||||||
cannot use a weak map to map pointers to instances. We use
|
|
||||||
the StructType constructor as the WeakMap key, mapped to a
|
|
||||||
plain, prototype-less Object which maps the pointers to
|
|
||||||
struct instances. That arrangement gives us a
|
|
||||||
per-StructType type-safe way to resolve pointers.
|
|
||||||
*/
|
|
||||||
const __ptrBacklinks = new WeakMap();
|
|
||||||
/**
|
|
||||||
Similar to __ptrBacklinks but is scoped at the StructBinder
|
|
||||||
level and holds pointer-to-object mappings for all struct
|
|
||||||
instances created by any struct from any StructFactory
|
|
||||||
which this specific StructBinder has created. The intention
|
|
||||||
of this is to help implement more transparent handling of
|
|
||||||
pointer-type property resolution.
|
|
||||||
*/
|
|
||||||
const __ptrBacklinksGlobal = Object.create(null);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
In order to completely hide StructBinder-bound struct
|
In order to completely hide StructBinder-bound struct
|
||||||
pointers from JS code, we store them in a scope-local
|
pointers from JS code, we store them in a scope-local
|
||||||
@@ -261,17 +222,13 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
const __freeStruct = function(ctor, obj, m){
|
const __freeStruct = function(ctor, obj, m){
|
||||||
if(!m) m = __instancePointerMap.get(obj);
|
if(!m) m = __instancePointerMap.get(obj);
|
||||||
if(m) {
|
if(m) {
|
||||||
if(obj.ondispose instanceof Function){
|
__instancePointerMap.delete(obj);
|
||||||
try{obj.ondispose()}
|
if(Array.isArray(obj.ondispose)){
|
||||||
catch(e){
|
let x;
|
||||||
/*do not rethrow: destructors must not throw*/
|
while((x = obj.ondispose.shift())){
|
||||||
console.warn("ondispose() for",ctor.structName,'@',
|
|
||||||
m,'threw. NOT propagating it.',e);
|
|
||||||
}
|
|
||||||
}else if(Array.isArray(obj.ondispose)){
|
|
||||||
obj.ondispose.forEach(function(x){
|
|
||||||
try{
|
try{
|
||||||
if(x instanceof Function) x.call(obj);
|
if(x instanceof Function) x.call(obj);
|
||||||
|
else if(x instanceof StructType) x.dispose();
|
||||||
else if('number' === typeof x) dealloc(x);
|
else if('number' === typeof x) dealloc(x);
|
||||||
// else ignore. Strings are permitted to annotate entries
|
// else ignore. Strings are permitted to annotate entries
|
||||||
// to assist in debugging.
|
// to assist in debugging.
|
||||||
@@ -279,12 +236,16 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
console.warn("ondispose() for",ctor.structName,'@',
|
console.warn("ondispose() for",ctor.structName,'@',
|
||||||
m,'threw. NOT propagating it.',e);
|
m,'threw. NOT propagating it.',e);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
}else if(obj.ondispose instanceof Function){
|
||||||
|
try{obj.ondispose()}
|
||||||
|
catch(e){
|
||||||
|
/*do not rethrow: destructors must not throw*/
|
||||||
|
console.warn("ondispose() for",ctor.structName,'@',
|
||||||
|
m,'threw. NOT propagating it.',e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
delete obj.ondispose;
|
delete obj.ondispose;
|
||||||
delete __ptrBacklinks.get(ctor)[m];
|
|
||||||
delete __ptrBacklinksGlobal[m];
|
|
||||||
__instancePointerMap.delete(obj);
|
|
||||||
if(ctor.debugFlags.__flags.dealloc){
|
if(ctor.debugFlags.__flags.dealloc){
|
||||||
log("debug.dealloc:",(obj[xPtrPropName]?"EXTERNAL":""),
|
log("debug.dealloc:",(obj[xPtrPropName]?"EXTERNAL":""),
|
||||||
ctor.structName,"instance:",
|
ctor.structName,"instance:",
|
||||||
@@ -300,7 +261,7 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
iterable: false, value: v}};
|
iterable: false, value: v}};
|
||||||
|
|
||||||
/** Allocates obj's memory buffer based on the size defined in
|
/** Allocates obj's memory buffer based on the size defined in
|
||||||
DEF.sizeof. */
|
ctor.structInfo.sizeof. */
|
||||||
const __allocStruct = function(ctor, obj, m){
|
const __allocStruct = function(ctor, obj, m){
|
||||||
let fill = !m;
|
let fill = !m;
|
||||||
if(m) Object.defineProperty(obj, xPtrPropName, rop(m));
|
if(m) Object.defineProperty(obj, xPtrPropName, rop(m));
|
||||||
@@ -316,8 +277,6 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
}
|
}
|
||||||
if(fill) heap().fill(0, m, m + ctor.structInfo.sizeof);
|
if(fill) heap().fill(0, m, m + ctor.structInfo.sizeof);
|
||||||
__instancePointerMap.set(obj, m);
|
__instancePointerMap.set(obj, m);
|
||||||
__ptrBacklinks.get(ctor)[m] = obj;
|
|
||||||
__ptrBacklinksGlobal[m] = obj;
|
|
||||||
}catch(e){
|
}catch(e){
|
||||||
__freeStruct(ctor, obj, m);
|
__freeStruct(ctor, obj, m);
|
||||||
throw e;
|
throw e;
|
||||||
@@ -339,7 +298,7 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
if tossIfNotFound is true, else returns undefined if not
|
if tossIfNotFound is true, else returns undefined if not
|
||||||
found. The given name may be either the name of the
|
found. The given name may be either the name of the
|
||||||
structInfo.members key (faster) or the key as modified by the
|
structInfo.members key (faster) or the key as modified by the
|
||||||
memberPrefix/memberSuffix settings.
|
memberPrefix and memberSuffix settings.
|
||||||
*/
|
*/
|
||||||
const __lookupMember = function(structInfo, memberName, tossIfNotFound=true){
|
const __lookupMember = function(structInfo, memberName, tossIfNotFound=true){
|
||||||
let m = structInfo.members[memberName];
|
let m = structInfo.members[memberName];
|
||||||
@@ -361,21 +320,11 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
framework's native format or in Emscripten format.
|
framework's native format or in Emscripten format.
|
||||||
*/
|
*/
|
||||||
const __memberSignature = function f(obj,memberName,emscriptenFormat=false){
|
const __memberSignature = function f(obj,memberName,emscriptenFormat=false){
|
||||||
if(!f._) f._ = (x)=>x.replace(/[^vipPsjrd]/g,"").replace(/[pPs]/g,'i');
|
if(!f._) f._ = (x)=>x.replace(/[^vipPsjrdcC]/g,"").replace(/[pPscC]/g,'i');
|
||||||
const m = __lookupMember(obj.structInfo, memberName, true);
|
const m = __lookupMember(obj.structInfo, memberName, true);
|
||||||
return emscriptenFormat ? f._(m.signature) : m.signature;
|
return emscriptenFormat ? f._(m.signature) : m.signature;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
Returns the instanceForPointer() impl for the given
|
|
||||||
StructType constructor.
|
|
||||||
*/
|
|
||||||
const __instanceBacklinkFactory = function(ctor){
|
|
||||||
const b = Object.create(null);
|
|
||||||
__ptrBacklinks.set(ctor, b);
|
|
||||||
return (ptr)=>b[ptr];
|
|
||||||
};
|
|
||||||
|
|
||||||
const __ptrPropDescriptor = {
|
const __ptrPropDescriptor = {
|
||||||
configurable: false, enumerable: false,
|
configurable: false, enumerable: false,
|
||||||
get: function(){return __instancePointerMap.get(this)},
|
get: function(){return __instancePointerMap.get(this)},
|
||||||
@@ -388,7 +337,9 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
/** Impl of X.memberKeys() for StructType and struct ctors. */
|
/** Impl of X.memberKeys() for StructType and struct ctors. */
|
||||||
const __structMemberKeys = rop(function(){
|
const __structMemberKeys = rop(function(){
|
||||||
const a = [];
|
const a = [];
|
||||||
Object.keys(this.structInfo.members).forEach((k)=>a.push(this.memberKey(k)));
|
for(const k of Object.keys(this.structInfo.members)){
|
||||||
|
a.push(this.memberKey(k));
|
||||||
|
}
|
||||||
return a;
|
return a;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -454,15 +405,15 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
Adds value v to obj.ondispose, creating ondispose,
|
Adds value v to obj.ondispose, creating ondispose,
|
||||||
or converting it to an array, if needed.
|
or converting it to an array, if needed.
|
||||||
*/
|
*/
|
||||||
const __addOnDispose = function(obj, v){
|
const __addOnDispose = function(obj, ...v){
|
||||||
if(obj.ondispose){
|
if(obj.ondispose){
|
||||||
if(obj.ondispose instanceof Function){
|
if(!Array.isArray(obj.ondispose)){
|
||||||
obj.ondispose = [obj.ondispose];
|
obj.ondispose = [obj.ondispose];
|
||||||
}/*else assume it's an array*/
|
}
|
||||||
}else{
|
}else{
|
||||||
obj.ondispose = [];
|
obj.ondispose = [];
|
||||||
}
|
}
|
||||||
obj.ondispose.push(v);
|
obj.ondispose.push(...v);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -477,8 +428,9 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
const mem = alloc(u.length+1);
|
const mem = alloc(u.length+1);
|
||||||
if(!mem) toss("Allocation error while duplicating string:",str);
|
if(!mem) toss("Allocation error while duplicating string:",str);
|
||||||
const h = heap();
|
const h = heap();
|
||||||
let i = 0;
|
//let i = 0;
|
||||||
for( ; i < u.length; ++i ) h[mem + i] = u[i];
|
//for( ; i < u.length; ++i ) h[mem + i] = u[i];
|
||||||
|
h.set(u, mem);
|
||||||
h[mem + u.length] = 0;
|
h[mem + u.length] = 0;
|
||||||
//log("allocCString @",mem," =",u);
|
//log("allocCString @",mem," =",u);
|
||||||
return mem;
|
return mem;
|
||||||
@@ -490,6 +442,10 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
to free any prior memory, if appropriate. The newly-allocated
|
to free any prior memory, if appropriate. The newly-allocated
|
||||||
string is added to obj.ondispose so will be freed when the object
|
string is added to obj.ondispose so will be freed when the object
|
||||||
is disposed.
|
is disposed.
|
||||||
|
|
||||||
|
The given name may be either the name of the structInfo.members
|
||||||
|
key (faster) or the key as modified by the memberPrefix and
|
||||||
|
memberSuffix settings.
|
||||||
*/
|
*/
|
||||||
const __setMemberCString = function(obj, memberName, str){
|
const __setMemberCString = function(obj, memberName, str){
|
||||||
const m = __lookupMember(obj.structInfo, memberName, true);
|
const m = __lookupMember(obj.structInfo, memberName, true);
|
||||||
@@ -544,13 +500,19 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
return __setMemberCString(this, memberName, str);
|
return __setMemberCString(this, memberName, str);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
// Function-type non-Property inherited members
|
||||||
|
Object.assign(StructType.prototype,{
|
||||||
|
addOnDispose: function(...v){
|
||||||
|
__addOnDispose(this,...v);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
"Static" properties for StructType.
|
"Static" properties for StructType.
|
||||||
*/
|
*/
|
||||||
Object.defineProperties(StructType, {
|
Object.defineProperties(StructType, {
|
||||||
allocCString: rop(__allocCString),
|
allocCString: rop(__allocCString),
|
||||||
instanceForPointer: rop((ptr)=>__ptrBacklinksGlobal[ptr]),
|
|
||||||
isA: rop((v)=>v instanceof StructType),
|
isA: rop((v)=>v instanceof StructType),
|
||||||
hasExternalPointer: rop((v)=>(v instanceof StructType) && !!v[xPtrPropName]),
|
hasExternalPointer: rop((v)=>(v instanceof StructType) && !!v[xPtrPropName]),
|
||||||
memberKey: __memberKeyProp
|
memberKey: __memberKeyProp
|
||||||
@@ -570,7 +532,7 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
/*cache all available getters/setters/set-wrappers for
|
/*cache all available getters/setters/set-wrappers for
|
||||||
direct reuse in each accessor function. */
|
direct reuse in each accessor function. */
|
||||||
f._ = {getters: {}, setters: {}, sw:{}};
|
f._ = {getters: {}, setters: {}, sw:{}};
|
||||||
const a = ['i','p','P','s','f','d','v()'];
|
const a = ['i','c','C','p','P','s','f','d','v()'];
|
||||||
if(bigIntEnabled) a.push('j');
|
if(bigIntEnabled) a.push('j');
|
||||||
a.forEach(function(v){
|
a.forEach(function(v){
|
||||||
//const ir = sigIR(v);
|
//const ir = sigIR(v);
|
||||||
@@ -579,8 +541,8 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
f._.sw[v] = sigDVSetWrapper(v) /* BigInt or Number ctor to wrap around values
|
f._.sw[v] = sigDVSetWrapper(v) /* BigInt or Number ctor to wrap around values
|
||||||
for conversion */;
|
for conversion */;
|
||||||
});
|
});
|
||||||
const rxSig1 = /^[ipPsjfd]$/,
|
const rxSig1 = /^[ipPsjfdcC]$/,
|
||||||
rxSig2 = /^[vipPsjfd]\([ipPsjfd]*\)$/;
|
rxSig2 = /^[vipPsjfdcC]\([ipPsjfdcC]*\)$/;
|
||||||
f.sigCheck = function(obj, name, key,sig){
|
f.sigCheck = function(obj, name, key,sig){
|
||||||
if(Object.prototype.hasOwnProperty.call(obj, key)){
|
if(Object.prototype.hasOwnProperty.call(obj, key)){
|
||||||
toss(obj.structName,'already has a property named',key+'.');
|
toss(obj.structName,'already has a property named',key+'.');
|
||||||
@@ -594,7 +556,6 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
f.sigCheck(ctor.prototype, name, key, descr.signature);
|
f.sigCheck(ctor.prototype, name, key, descr.signature);
|
||||||
descr.key = key;
|
descr.key = key;
|
||||||
descr.name = name;
|
descr.name = name;
|
||||||
const sizeOf = sigSizeof(descr.signature);
|
|
||||||
const sigGlyph = sigLetter(descr.signature);
|
const sigGlyph = sigLetter(descr.signature);
|
||||||
const xPropName = sPropName(ctor.prototype.structName,key);
|
const xPropName = sPropName(ctor.prototype.structName,key);
|
||||||
const dbg = ctor.prototype.debugFlags.__flags;
|
const dbg = ctor.prototype.debugFlags.__flags;
|
||||||
@@ -610,16 +571,12 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
prop.get = function(){
|
prop.get = function(){
|
||||||
if(dbg.getter){
|
if(dbg.getter){
|
||||||
log("debug.getter:",f._.getters[sigGlyph],"for", sigIR(sigGlyph),
|
log("debug.getter:",f._.getters[sigGlyph],"for", sigIR(sigGlyph),
|
||||||
xPropName,'@', this.pointer,'+',descr.offset,'sz',sizeOf);
|
xPropName,'@', this.pointer,'+',descr.offset,'sz',descr.sizeof);
|
||||||
}
|
}
|
||||||
let rc = (
|
let rc = (
|
||||||
new DataView(heap().buffer, this.pointer + descr.offset, sizeOf)
|
new DataView(heap().buffer, this.pointer + descr.offset, descr.sizeof)
|
||||||
)[f._.getters[sigGlyph]](0, isLittleEndian);
|
)[f._.getters[sigGlyph]](0, isLittleEndian);
|
||||||
if(dbg.getter) log("debug.getter:",xPropName,"result =",rc);
|
if(dbg.getter) log("debug.getter:",xPropName,"result =",rc);
|
||||||
if(rc && isAutoPtrSig(descr.signature)){
|
|
||||||
rc = StructType.instanceForPointer(rc) || rc;
|
|
||||||
if(dbg.getter) log("debug.getter:",xPropName,"resolved =",rc);
|
|
||||||
}
|
|
||||||
return rc;
|
return rc;
|
||||||
};
|
};
|
||||||
if(descr.readOnly){
|
if(descr.readOnly){
|
||||||
@@ -628,7 +585,7 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
prop.set = function(v){
|
prop.set = function(v){
|
||||||
if(dbg.setter){
|
if(dbg.setter){
|
||||||
log("debug.setter:",f._.setters[sigGlyph],"for", sigIR(sigGlyph),
|
log("debug.setter:",f._.setters[sigGlyph],"for", sigIR(sigGlyph),
|
||||||
xPropName,'@', this.pointer,'+',descr.offset,'sz',sizeOf, v);
|
xPropName,'@', this.pointer,'+',descr.offset,'sz',descr.sizeof, v);
|
||||||
}
|
}
|
||||||
if(!this.pointer){
|
if(!this.pointer){
|
||||||
toss("Cannot set struct property on disposed instance.");
|
toss("Cannot set struct property on disposed instance.");
|
||||||
@@ -644,7 +601,7 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
toss("Invalid value for pointer-type",xPropName+'.');
|
toss("Invalid value for pointer-type",xPropName+'.');
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
new DataView(heap().buffer, this.pointer + descr.offset, sizeOf)
|
new DataView(heap().buffer, this.pointer + descr.offset, descr.sizeof)
|
||||||
)[f._.setters[sigGlyph]](0, f._.sw[sigGlyph](v), isLittleEndian);
|
)[f._.setters[sigGlyph]](0, f._.sw[sigGlyph](v), isLittleEndian);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -665,13 +622,25 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
if(!structName) toss("Struct name is required.");
|
if(!structName) toss("Struct name is required.");
|
||||||
let lastMember = false;
|
let lastMember = false;
|
||||||
Object.keys(structInfo.members).forEach((k)=>{
|
Object.keys(structInfo.members).forEach((k)=>{
|
||||||
|
// Sanity checks of sizeof/offset info...
|
||||||
const m = structInfo.members[k];
|
const m = structInfo.members[k];
|
||||||
if(!m.sizeof) toss(structName,"member",k,"is missing sizeof.");
|
if(!m.sizeof) toss(structName,"member",k,"is missing sizeof.");
|
||||||
else if(0!==(m.sizeof%4)){
|
else if(m.sizeof===1){
|
||||||
toss(structName,"member",k,"sizeof is not aligned.");
|
(m.signature === 'c' || m.signature === 'C') ||
|
||||||
}
|
toss("Unexpected sizeof==1 member",
|
||||||
else if(0!==(m.offset%4)){
|
sPropName(structInfo.name,k),
|
||||||
toss(structName,"member",k,"offset is not aligned.");
|
"with signature",m.signature);
|
||||||
|
}else{
|
||||||
|
// sizes and offsets of size-1 members may be odd values, but
|
||||||
|
// others may not.
|
||||||
|
if(0!==(m.sizeof%4)){
|
||||||
|
console.warn("Invalid struct member description =",m,"from",structInfo);
|
||||||
|
toss(structName,"member",k,"sizeof is not aligned. sizeof="+m.sizeof);
|
||||||
|
}
|
||||||
|
if(0!==(m.offset%4)){
|
||||||
|
console.warn("Invalid struct member description =",m,"from",structInfo);
|
||||||
|
toss(structName,"member",k,"offset is not aligned. offset="+m.offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(!lastMember || lastMember.offset < m.offset) lastMember = m;
|
if(!lastMember || lastMember.offset < m.offset) lastMember = m;
|
||||||
});
|
});
|
||||||
@@ -697,27 +666,9 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
};
|
};
|
||||||
Object.defineProperties(StructCtor,{
|
Object.defineProperties(StructCtor,{
|
||||||
debugFlags: debugFlags,
|
debugFlags: debugFlags,
|
||||||
disposeAll: rop(function(){
|
|
||||||
const map = __ptrBacklinks.get(StructCtor);
|
|
||||||
Object.keys(map).forEach(function(ptr){
|
|
||||||
const b = map[ptr];
|
|
||||||
if(b) __freeStruct(StructCtor, b, ptr);
|
|
||||||
});
|
|
||||||
__ptrBacklinks.set(StructCtor, Object.create(null));
|
|
||||||
return StructCtor;
|
|
||||||
}),
|
|
||||||
instanceForPointer: rop(__instanceBacklinkFactory(StructCtor)),
|
|
||||||
isA: rop((v)=>v instanceof StructCtor),
|
isA: rop((v)=>v instanceof StructCtor),
|
||||||
memberKey: __memberKeyProp,
|
memberKey: __memberKeyProp,
|
||||||
memberKeys: __structMemberKeys,
|
memberKeys: __structMemberKeys,
|
||||||
resolveToInstance: rop(function(v, throwIfNot=false){
|
|
||||||
if(!(v instanceof StructCtor)){
|
|
||||||
v = Number.isSafeInteger(v)
|
|
||||||
? StructCtor.instanceForPointer(v) : undefined;
|
|
||||||
}
|
|
||||||
if(!v && throwIfNot) toss("Value is-not-a",StructCtor.structName);
|
|
||||||
return v;
|
|
||||||
}),
|
|
||||||
methodInfoForKey: rop(function(mKey){
|
methodInfoForKey: rop(function(mKey){
|
||||||
}),
|
}),
|
||||||
structInfo: rop(structInfo),
|
structInfo: rop(structInfo),
|
||||||
@@ -735,7 +686,6 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
);
|
);
|
||||||
return StructCtor;
|
return StructCtor;
|
||||||
};
|
};
|
||||||
StructBinder.instanceForPointer = StructType.instanceForPointer;
|
|
||||||
StructBinder.StructType = StructType;
|
StructBinder.StructType = StructType;
|
||||||
StructBinder.config = config;
|
StructBinder.config = config;
|
||||||
StructBinder.allocCString = __allocCString;
|
StructBinder.allocCString = __allocCString;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ Jaccwabyt 🐇
|
|||||||
**Jaccwabyt**: _JavaScript ⇄ C Struct Communication via WASM Byte
|
**Jaccwabyt**: _JavaScript ⇄ C Struct Communication via WASM Byte
|
||||||
Arrays_
|
Arrays_
|
||||||
|
|
||||||
|
|
||||||
Welcome to Jaccwabyt, a JavaScript API which creates bindings for
|
Welcome to Jaccwabyt, a JavaScript API which creates bindings for
|
||||||
WASM-compiled C structs, defining them in such a way that changes to
|
WASM-compiled C structs, defining them in such a way that changes to
|
||||||
their state in JS are visible in C/WASM, and vice versa, permitting
|
their state in JS are visible in C/WASM, and vice versa, permitting
|
||||||
@@ -27,8 +26,28 @@ are based solely on feature compatibility tables provided at
|
|||||||
**Formalities:**
|
**Formalities:**
|
||||||
|
|
||||||
- Author: [Stephan Beal][sgb]
|
- Author: [Stephan Beal][sgb]
|
||||||
- License: Public Domain
|
- Project Homes:
|
||||||
- Project Home: <https://fossil.wanderinghorse.net/r/jaccwabyt>
|
- <https://fossil.wanderinghorse.net/r/jaccwabyt>\
|
||||||
|
Is the primary home but...
|
||||||
|
- <https://sqlite.org/src/dir/ext/wasm/jaccwabyt>\
|
||||||
|
... most development happens here.
|
||||||
|
|
||||||
|
The license for both this documentation and the software it documents
|
||||||
|
is the same as [sqlite3][], the project from which this spinoff
|
||||||
|
project was spawned:
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
> 2022-06-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.
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
<a name='overview'></a>
|
<a name='overview'></a>
|
||||||
Table of Contents
|
Table of Contents
|
||||||
@@ -205,7 +224,6 @@ simply look like:
|
|||||||
The StructBinder factory function returns a function which can then be
|
The StructBinder factory function returns a function which can then be
|
||||||
used to create bindings for our structs.
|
used to create bindings for our structs.
|
||||||
|
|
||||||
|
|
||||||
<a name='step-2'></a>
|
<a name='step-2'></a>
|
||||||
Step 2: Create a Struct Description
|
Step 2: Create a Struct Description
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
@@ -281,21 +299,29 @@ supported letters are:
|
|||||||
signature entry.
|
signature entry.
|
||||||
- **`f`** = `float` (4 bytes)
|
- **`f`** = `float` (4 bytes)
|
||||||
- **`d`** = `double` (8 bytes)
|
- **`d`** = `double` (8 bytes)
|
||||||
- **`p`** = `int32` (but see below!)
|
- **`c`** = `int8` (1 byte) char - see notes below!
|
||||||
|
- **`C`** = `uint8` (1 byte) unsigned char - see notes below!
|
||||||
|
- **`p`** = `int32` (see notes below!)
|
||||||
- **`P`** = Like `p` but with extra handling. Described below.
|
- **`P`** = Like `p` but with extra handling. Described below.
|
||||||
- **`s`** = like `int32` but is a _hint_ that it's a pointer to a string
|
- **`s`** = like `int32` but is a _hint_ that it's a pointer to a
|
||||||
so that _some_ (very limited) contexts may treat it as such, noting
|
string so that _some_ (very limited) contexts may treat it as such,
|
||||||
such algorithms must, for lack of information to the contrary,
|
noting that such algorithms must, for lack of information to the
|
||||||
assume both that the encoding is UTF-8 and that the pointer's member
|
contrary, assume both that the encoding is UTF-8 and that the
|
||||||
is NUL-terminated. If that is _not_ the case for a given string
|
pointer's member is NUL-terminated. If that is _not_ the case for a
|
||||||
member, do not use `s`: use `i` or `p` instead and do any string
|
given string member, do not use `s`: use `i` or `p` instead and do
|
||||||
handling yourself.
|
any string handling yourself.
|
||||||
|
|
||||||
Noting that:
|
Noting that:
|
||||||
|
|
||||||
- All of these types are numeric. Attempting to set any struct-bound
|
- **All of these types are numeric**. Attempting to set any
|
||||||
property to a non-numeric value will trigger an exception except in
|
struct-bound property to a non-numeric value will trigger an
|
||||||
cases explicitly noted otherwise.
|
exception except in cases explicitly noted otherwise.
|
||||||
|
- **"Char" types**: WASM does not define an `int8` type, nor does it
|
||||||
|
distinguish between signed and unsigned. This API treats `c` as
|
||||||
|
`int8` and `C` as `uint8` for purposes of getting and setting values
|
||||||
|
when using the `DataView` class. It is _not_ recommended that client
|
||||||
|
code use these types in new WASM-capable code, but they were added
|
||||||
|
for the sake of binding some immutable legacy code to WASM.
|
||||||
|
|
||||||
> Sidebar: Emscripten's public docs do not mention `p`, but their
|
> Sidebar: Emscripten's public docs do not mention `p`, but their
|
||||||
generated code includes `p` as an alias for `i`, presumably to mean
|
generated code includes `p` as an alias for `i`, presumably to mean
|
||||||
@@ -317,12 +343,12 @@ Signatures in the form `x(...)` denote function-pointer members and
|
|||||||
form `x()`. For function-type signatures, the strings are formulated
|
form `x()`. For function-type signatures, the strings are formulated
|
||||||
such that they can be passed to Emscripten's `addFunction()` after
|
such that they can be passed to Emscripten's `addFunction()` after
|
||||||
stripping out the `(` and `)` characters. For good measure, to match
|
stripping out the `(` and `)` characters. For good measure, to match
|
||||||
the public Emscripten docs, `p` should also be replaced with `i`. In
|
the public Emscripten docs, `p`, `c`, and `C`, should also be replaced
|
||||||
JavaScript that might look like:
|
with `i`. In JavaScript that might look like:
|
||||||
|
|
||||||
>
|
>
|
||||||
```
|
```
|
||||||
signature.replace(/[^vipPsjfd]/g,'').replace(/[pPs]/g,'i');
|
signature.replace(/[^vipPsjfdcC]/g,'').replace(/[pPscC]/g,'i');
|
||||||
```
|
```
|
||||||
|
|
||||||
<a name='step-2-pvsp'></a>
|
<a name='step-2-pvsp'></a>
|
||||||
@@ -337,12 +363,6 @@ special use of unsigned numbers). A capital `P` changes the semantics
|
|||||||
of plain member pointers (but not, as of this writing, function
|
of plain member pointers (but not, as of this writing, function
|
||||||
pointer members) as follows:
|
pointer members) as follows:
|
||||||
|
|
||||||
- When a `P`-type member is **fetched** via `myStruct.x` and its value is
|
|
||||||
a non-0 integer, [`StructBinder.instanceForPointer()`][StructBinder]
|
|
||||||
is used to try to map that pointer to a struct instance. If a match
|
|
||||||
is found, the "get" operation returns that instance instead of the
|
|
||||||
integer. If no match is found, it behaves exactly as for `p`, returning
|
|
||||||
the integer value.
|
|
||||||
- When a `P`-type member is **set** via `myStruct.x=y`, if
|
- When a `P`-type member is **set** via `myStruct.x=y`, if
|
||||||
[`(y instanceof StructType)`][StructType] then the value of `y.pointer` is
|
[`(y instanceof StructType)`][StructType] then the value of `y.pointer` is
|
||||||
stored in `myStruct.x`. If `y` is neither a number nor
|
stored in `myStruct.x`. If `y` is neither a number nor
|
||||||
@@ -388,14 +408,11 @@ It is important to understand that creating a new instance allocates
|
|||||||
memory on the WASM heap. We must not simply rely on garbage collection
|
memory on the WASM heap. We must not simply rely on garbage collection
|
||||||
to clean up the instances because doing so will not free up the WASM
|
to clean up the instances because doing so will not free up the WASM
|
||||||
heap memory. The correct way to free up that memory is to use the
|
heap memory. The correct way to free up that memory is to use the
|
||||||
object's `dispose()` method. Alternately, there is a "nuclear option":
|
object's `dispose()` method.
|
||||||
`MyBinder.disposeAll()` will free the memory allocated for _all_
|
|
||||||
instances which have not been manually disposed.
|
|
||||||
|
|
||||||
The following usage pattern offers one way to easily ensure proper
|
The following usage pattern offers one way to easily ensure proper
|
||||||
cleanup of struct instances:
|
cleanup of struct instances:
|
||||||
|
|
||||||
|
|
||||||
>
|
>
|
||||||
```javascript
|
```javascript
|
||||||
const my = new MyStruct();
|
const my = new MyStruct();
|
||||||
@@ -409,11 +426,6 @@ try {
|
|||||||
from the byte array. */
|
from the byte array. */
|
||||||
// Pass the struct to C code which takes a MyStruct pointer:
|
// Pass the struct to C code which takes a MyStruct pointer:
|
||||||
aCFunction( my.pointer );
|
aCFunction( my.pointer );
|
||||||
// Type-safely check if a pointer returned from C is a MyStruct:
|
|
||||||
const x = MyStruct.instanceForPointer( anotherCFunction() );
|
|
||||||
// If it is a MyStruct, x now refers to that object. Note, however,
|
|
||||||
// that this only works for instances created in JS, as the
|
|
||||||
// pointer mapping only exists in JS space.
|
|
||||||
} finally {
|
} finally {
|
||||||
my.dispose();
|
my.dispose();
|
||||||
}
|
}
|
||||||
@@ -426,6 +438,16 @@ to use `try`/`finally` without a `catch`, and doing so is an ideal
|
|||||||
match for the memory management requirements of Jaccwaby-bound struct
|
match for the memory management requirements of Jaccwaby-bound struct
|
||||||
instances.
|
instances.
|
||||||
|
|
||||||
|
It is often useful to wrap an existing instance of a C-side struct
|
||||||
|
without taking over ownership of its memory. That can be achieved by
|
||||||
|
simply passing a pointer to the constructor. For example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const m = new MyStruct( functionReturningASharedPtr() );
|
||||||
|
// calling m.dispose() will _not_ free the wrapped C-side instance
|
||||||
|
// but will trigger any ondispose handler.
|
||||||
|
```
|
||||||
|
|
||||||
Now that we have struct instances, there are a number of things we
|
Now that we have struct instances, there are a number of things we
|
||||||
can do with them, as covered in the rest of this document.
|
can do with them, as covered in the rest of this document.
|
||||||
|
|
||||||
@@ -549,20 +571,6 @@ The Struct Binder has the following members:
|
|||||||
any of its "significant" configuration values may have undefined
|
any of its "significant" configuration values may have undefined
|
||||||
results.
|
results.
|
||||||
|
|
||||||
- `instanceForPointer(pointer)`
|
|
||||||
Given a pointer value relative to `config.memory`, if that pointer
|
|
||||||
resolves to a struct of _any type_ generated via the same Struct
|
|
||||||
Binder, this returns the struct instance associated with it, or
|
|
||||||
`undefined` if no struct object is mapped to that pointer. This
|
|
||||||
differs from the struct-type-specific member of the same name in
|
|
||||||
that this one is not "type-safe": it does not know the type of the
|
|
||||||
returned object (if any) and may return a struct of any
|
|
||||||
[StructType][] for which this Struct Binder has created a
|
|
||||||
constructor. It cannot return instances created via a different
|
|
||||||
[StructBinderFactory][] because each factory can hypothetically have
|
|
||||||
a different memory heap.
|
|
||||||
|
|
||||||
|
|
||||||
<a name='api-structtype'></a>
|
<a name='api-structtype'></a>
|
||||||
API: Struct Type
|
API: Struct Type
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
@@ -582,6 +590,14 @@ only called by the [StructBinder][]-generated
|
|||||||
has the following "static" properties (^Which are accessible from
|
has the following "static" properties (^Which are accessible from
|
||||||
individual instances via `theInstance.constructor`.):
|
individual instances via `theInstance.constructor`.):
|
||||||
|
|
||||||
|
- `addOnDispose(...value)`\
|
||||||
|
If this object has no `ondispose` property, this function creates it
|
||||||
|
as an array and pushes the given value(s) onto it. If the object has
|
||||||
|
a function-typed `ondispose` property, this call replaces it with an
|
||||||
|
array and moves that function into the array. In all other cases,
|
||||||
|
`ondispose` is assumed to be an array and the argument(s) is/are
|
||||||
|
appended to it. Returns `this`.
|
||||||
|
|
||||||
- `allocCString(str)`
|
- `allocCString(str)`
|
||||||
Identical to the [StructBinder][] method of the same name.
|
Identical to the [StructBinder][] method of the same name.
|
||||||
|
|
||||||
@@ -591,9 +607,6 @@ individual instances via `theInstance.constructor`.):
|
|||||||
[struct's constructor][StructCtors]. If true, the memory is owned by
|
[struct's constructor][StructCtors]. If true, the memory is owned by
|
||||||
someone other than the object and must outlive the object.
|
someone other than the object and must outlive the object.
|
||||||
|
|
||||||
- `instanceForPointer(pointer)`
|
|
||||||
Works identically to the [StructBinder][] method of the same name.
|
|
||||||
|
|
||||||
- `isA(value)`
|
- `isA(value)`
|
||||||
Returns true if its argument is a StructType instance _from the same
|
Returns true if its argument is a StructType instance _from the same
|
||||||
[StructBinder][]_ as this StructType.
|
[StructBinder][]_ as this StructType.
|
||||||
@@ -619,14 +632,15 @@ legally be called on concrete struct instances unless noted otherwise:
|
|||||||
- If it is a function, it is called with the struct object as its `this`.
|
- If it is a function, it is called with the struct object as its `this`.
|
||||||
That method must not throw - if it does, the exception will be
|
That method must not throw - if it does, the exception will be
|
||||||
ignored.
|
ignored.
|
||||||
- If it is an array, it may contain functions, pointers, and/or JS
|
- If it is an array, it may contain functions, pointers, other
|
||||||
strings. If an entry is a function, it is called as described
|
[StructType] instances, and/or JS strings. If an entry is a
|
||||||
above. If it's a number, it's assumed to be a pointer and is
|
function, it is called as described above. If it's a number, it's
|
||||||
passed to the `dealloc()` function configured for the parent
|
assumed to be a pointer and is passed to the `dealloc()` function
|
||||||
[StructBinder][]. If it's a JS string, it's assumed to be a
|
configured for the parent [StructBinder][]. If it's a
|
||||||
helpful description of the next entry in the list and is simply
|
[StructType][] instance then its `dispose()` method is called. If
|
||||||
ignored. Strings are supported primarily for use as debugging
|
it's a JS string, it's assumed to be a helpful description of the
|
||||||
information.
|
next entry in the list and is simply ignored. Strings are
|
||||||
|
supported primarily for use as debugging information.
|
||||||
- Some struct APIs will manipulate the `ondispose` member, creating
|
- Some struct APIs will manipulate the `ondispose` member, creating
|
||||||
it as an array or converting it from a function to array as
|
it as an array or converting it from a function to array as
|
||||||
needed.
|
needed.
|
||||||
@@ -738,21 +752,6 @@ pointer can be taken over using something like
|
|||||||
|
|
||||||
These constructors have the following "static" members:
|
These constructors have the following "static" members:
|
||||||
|
|
||||||
- `disposeAll()`
|
|
||||||
For each instance of this struct, the equivalent of its `dispose()`
|
|
||||||
method is called. This frees all WASM-allocated memory associated
|
|
||||||
with _all_ instances and clears the `instanceForPointer()`
|
|
||||||
mappings. Returns `this`.
|
|
||||||
|
|
||||||
- `instanceForPointer(pointer)`
|
|
||||||
Given a pointer value (accessible via the `pointer` property of all
|
|
||||||
struct instances) which ostensibly refers to an instance of this
|
|
||||||
class, this returns the instance associated with it, or `undefined`
|
|
||||||
if no object _of this specific struct type_ is mapped to that
|
|
||||||
pointer. When C-side code calls back into JS code and passes a
|
|
||||||
pointer to an object, this function can be used to type-safely
|
|
||||||
"cast" that pointer back to its original object.
|
|
||||||
|
|
||||||
- `isA(value)`
|
- `isA(value)`
|
||||||
Returns true if its argument was created by this constructor.
|
Returns true if its argument was created by this constructor.
|
||||||
|
|
||||||
@@ -762,15 +761,6 @@ These constructors have the following "static" members:
|
|||||||
- `memberKeys(string)`
|
- `memberKeys(string)`
|
||||||
Works exactly as documented for [StructType][].
|
Works exactly as documented for [StructType][].
|
||||||
|
|
||||||
- `resolveToInstance(value [,throwIfNot=false])`
|
|
||||||
Works like `instanceForPointer()` but accepts either an instance
|
|
||||||
of this struct type or a pointer which resolves to one.
|
|
||||||
It returns an instance of this struct type on success.
|
|
||||||
By default it returns a falsy value if its argument is not,
|
|
||||||
or does not resolve to, an instance of this struct type,
|
|
||||||
but if passed a truthy second argument then it will throw
|
|
||||||
instead.
|
|
||||||
|
|
||||||
- `structInfo`
|
- `structInfo`
|
||||||
The structure description passed to [StructBinder][] when this
|
The structure description passed to [StructBinder][] when this
|
||||||
constructor was generated.
|
constructor was generated.
|
||||||
|
|||||||
@@ -192,6 +192,177 @@
|
|||||||
fossil-doc block so that this part can work without modification in
|
fossil-doc block so that this part can work without modification in
|
||||||
the wasm docs repo. */</script>
|
the wasm docs repo. */</script>
|
||||||
<script>(async function(){
|
<script>(async function(){
|
||||||
|
const apiLinks = Object.assign(Object.create(null),{
|
||||||
|
sqlite3_aggregate_context: 'www:/c3ref/aggregate_context.html',
|
||||||
|
sqlite3_bind_blob: 'www:/c3ref/bind_blob.html',
|
||||||
|
sqlite3_bind_double: 'www:/c3ref/bind_blob.html',
|
||||||
|
sqlite3_bind_int: 'www:/c3ref/bind_blob.html',
|
||||||
|
sqlite3_bind_int64: 'www:/c3ref/bind_blob.html',
|
||||||
|
sqlite3_bind_null: 'www:/c3ref/bind_blob.html',
|
||||||
|
sqlite3_bind_parameter_count: 'www:/c3ref/bind_parameter_count.html',
|
||||||
|
sqlite3_bind_parameter_index: 'www:/c3ref/bind_parameter_index.html',
|
||||||
|
sqlite3_bind_pointer: 'www:/c3ref/bind_blob.html',
|
||||||
|
sqlite3_bind_text: 'www:/c3ref/bind_blob.html',
|
||||||
|
sqlite3_busy_handler: 'www:/c3ref/busy_handler.html',
|
||||||
|
sqlite3_busy_timeout: 'www:/c3ref/busy_timeout.html',
|
||||||
|
sqlite3_changes: 'www:/c3ref/changes.html',
|
||||||
|
sqlite3_changes64: 'www:/c3ref/changes.html',
|
||||||
|
sqlite3_clear_bindings: 'www:/c3ref/clear_bindings.html',
|
||||||
|
sqlite3_close_v2: 'www:/c3ref/close.html',
|
||||||
|
sqlite3_collation_needed: 'www:/c3ref/collation_needed.html',
|
||||||
|
sqlite3_column_blob: 'www:/c3ref/column_blob.html',
|
||||||
|
sqlite3_column_bytes: 'www:/c3ref/column.html',
|
||||||
|
sqlite3_column_count: 'www:/c3ref/column_count.html',
|
||||||
|
sqlite3_column_double: 'www:/c3ref/column_blob.html',
|
||||||
|
sqlite3_column_int: 'www:/c3ref/column_blob.html',
|
||||||
|
sqlite3_column_int64: 'www:/c3ref/column_blob.html',
|
||||||
|
sqlite3_column_name: 'www:/c3ref/column_name.html',
|
||||||
|
sqlite3_column_text: 'www:/c3ref/column_blob.html',
|
||||||
|
sqlite3_column_type: 'www:/c3ref/column_blob.html',
|
||||||
|
sqlite3_column_value: 'www:/c3ref/column_blob.html',
|
||||||
|
sqlite3_compileoption_get: 'www:/c3ref/compileoption_get.html',
|
||||||
|
sqlite3_compileoption_used: 'www:/c3ref/compileoption_get.html',
|
||||||
|
sqlite3_complete: 'www:/c3ref/complete.html',
|
||||||
|
sqlite3_create_collation: 'www:/c3ref/create_collation.html',
|
||||||
|
sqlite3_create_collation_v2: 'www:/c3ref/create_collation.html',
|
||||||
|
sqlite3_create_function: 'wasm:/api-c-style.md#sqlite3_create_function',
|
||||||
|
sqlite3_create_function_v2: 'wasm:/api-c-style.md#sqlite3_create_function',
|
||||||
|
sqlite3_create_module: 'www:/c3ref/create_module.html',
|
||||||
|
sqlite3_create_module_v2: 'www:/c3ref/create_module.html',
|
||||||
|
sqlite3_create_window_function: 'wasm:/api-c-style.md#sqlite3_create_function',
|
||||||
|
sqlite3_db_config: 'wasm:/api-c-style.md#sqlite3_db_config',
|
||||||
|
sqlite3_data_count: 'www:/c3ref/data_count.html',
|
||||||
|
sqlite3_db_filename: 'www:/c3ref/db_filename.html',
|
||||||
|
sqlite3_db_handle: 'www:/c3ref/db_handle.html',
|
||||||
|
sqlite3_db_name: 'www:/c3ref/db_name.html',
|
||||||
|
sqlite3_db_status: 'www:/c3ref/db_status.html',
|
||||||
|
sqlite3_declare_vtab: 'www:/c3ref/declare_vtab.html',
|
||||||
|
sqlite3_deserialize: 'wasm:/api-c-style.md#sqlite3_deserialize',
|
||||||
|
sqlite3_drop_modules: 'www:/c3ref/drop_modules.html',
|
||||||
|
sqlite3_errcode: 'www:/c3ref/errcode.html',
|
||||||
|
sqlite3_errmsg: 'www:/c3ref/errcode.html',
|
||||||
|
sqlite3_error_offset: 'www:/c3ref/error_offset.html',
|
||||||
|
sqlite3_errstr: 'www:/c3ref/errcode.html',
|
||||||
|
sqlite3_exec: 'wasm:/api-c-style.md#sqlite3_exec',
|
||||||
|
sqlite3_expanded_sql: 'www:/c3ref/expanded_sql.html',
|
||||||
|
sqlite3_extended_errcode: 'www:/c3ref/errcode.html',
|
||||||
|
sqlite3_extended_result_codes: 'www:/c3ref/extended_result_codes.html',
|
||||||
|
sqlite3_file_control: 'www:/c3ref/file_control.html',
|
||||||
|
sqlite3_finalize: 'www:/c3ref/finalize.html',
|
||||||
|
sqlite3_free: 'www:/c3ref/free.html',
|
||||||
|
sqlite3_get_auxdata: 'www:/c3ref/get_auxdata.html',
|
||||||
|
sqlite3_initialize: 'www:/c3ref/initialize.html',
|
||||||
|
sqlite3_keyword_check: 'www:/c3ref/keyword_check.html',
|
||||||
|
sqlite3_keyword_count: 'www:/c3ref/keyword_check.html',
|
||||||
|
sqlite3_keyword_name: 'www:/c3ref/keyword_check.html',
|
||||||
|
sqlite3_last_insert_rowid: 'www:/c3ref/last_insert_rowid.html',
|
||||||
|
sqlite3_libversion: 'www:/c3ref/libversion.html',
|
||||||
|
sqlite3_libversion_number: 'www:/c3ref/libversion.html',
|
||||||
|
sqlite3_limit: 'www:/c3ref/limit.html',
|
||||||
|
sqlite3_malloc: 'www:/c3ref/free.html',
|
||||||
|
sqlite3_malloc64: 'www:/c3ref/free.html',
|
||||||
|
sqlite3_msize: 'www:/c3ref/free.html',
|
||||||
|
sqlite3_open: 'www:/c3ref/open.html',
|
||||||
|
sqlite3_open_v2: 'www:/c3ref/open.html',
|
||||||
|
sqlite3_overload_function: 'www:/c3ref/overload_function.html',
|
||||||
|
sqlite3_prepare_v2: 'wasm:/api-c-style.md#sqlite3_prepare_v2',
|
||||||
|
sqlite3_prepare_v3: 'wasm:/api-c-style.md#sqlite3_prepare_v2',
|
||||||
|
sqlite3_progress_handler: 'www:/c3ref/progress_handler.html',
|
||||||
|
sqlite3_randomness: 'wasm:/api-c-style.md#sqlite3_randomness',
|
||||||
|
sqlite3_realloc: 'www:/c3ref/free.html',
|
||||||
|
sqlite3_realloc64: 'www:/c3ref/free.html',
|
||||||
|
sqlite3_reset: 'www:/c3ref/reset.html',
|
||||||
|
sqlite3_result_blob: 'www:/c3ref/result_blob.html',
|
||||||
|
sqlite3_result_double: 'www:/c3ref/result_blob.html',
|
||||||
|
sqlite3_result_error: 'www:/c3ref/result_blob.html',
|
||||||
|
sqlite3_result_error_code: 'www:/c3ref/result_blob.html',
|
||||||
|
sqlite3_result_error_nomem: 'www:/c3ref/result_blob.html',
|
||||||
|
sqlite3_result_error_toobig: 'www:/c3ref/result_blob.html',
|
||||||
|
sqlite3_result_int: 'www:/c3ref/result_blob.html',
|
||||||
|
sqlite3_result_int64: 'www:/c3ref/result_blob.html',
|
||||||
|
sqlite3_result_null: 'www:/c3ref/result_blob.html',
|
||||||
|
sqlite3_result_pointer: 'www:/c3ref/result_blob.html',
|
||||||
|
sqlite3_result_subtype: 'www:/c3ref/result_subtype.html',
|
||||||
|
sqlite3_result_text: 'www:/c3ref/result_blob.html',
|
||||||
|
sqlite3_result_zeroblob: 'www:/c3ref/result_blob.html',
|
||||||
|
sqlite3_result_zeroblob64: 'www:/c3ref/result_blob.html',
|
||||||
|
sqlite3_serialize: 'www:/c3ref/serialize.html',
|
||||||
|
sqlite3_set_auxdata: 'www:/c3ref/set_auxdata.html',
|
||||||
|
sqlite3_set_last_insert_rowid: 'www:/c3ref/set_last_insert_rowid',
|
||||||
|
sqlite3_shutdown: 'www:/c3ref/initialize.html',
|
||||||
|
sqlite3_sourceid: 'www:/c3ref/libversion.html',
|
||||||
|
sqlite3_sql: 'www:/c3ref/expanded_sql.html',
|
||||||
|
sqlite3_status: 'www:/c3ref/status.html',
|
||||||
|
sqlite3_status64: 'www:/c3ref/status.html',
|
||||||
|
sqlite3_step: 'www:/c3ref/step.html',
|
||||||
|
sqlite3_stmt_isexplain: 'www:/c3ref/stmt_isexplain.html',
|
||||||
|
sqlite3_stmt_readonly: 'www:/c3ref/stmt_readonly.html',
|
||||||
|
sqlite3_stmt_status: 'www:/c3ref/stmt_status.html',
|
||||||
|
sqlite3_strglob: 'www:/c3ref/strglob.html',
|
||||||
|
sqlite3_stricmp: 'www:/c3ref/stricmp.html',
|
||||||
|
sqlite3_strlike: 'www:/c3ref/strlike.html',
|
||||||
|
sqlite3_strnicmp: 'www:/c3ref/strnicmp.html',
|
||||||
|
sqlite3_table_column_metadata: 'www:/c3ref/table_column_metadata.html',
|
||||||
|
sqlite3_total_changes: 'www:/c3ref/total_changes.html',
|
||||||
|
sqlite3_total_changes64: 'www:/c3ref/total_changes.html',
|
||||||
|
sqlite3_trace_v2: 'www:/c3ref/trace_v2.html',
|
||||||
|
sqlite3_txn_state: 'www:/c3ref/txn_state.html',
|
||||||
|
sqlite3_uri_boolean: 'www:/c3ref/uri_boolean.html',
|
||||||
|
sqlite3_uri_int64: 'www:/c3ref/uri_boolean.html',
|
||||||
|
sqlite3_uri_key: 'www:/c3ref/uri_boolean.html',
|
||||||
|
sqlite3_uri_parameter: 'www:/c3ref/uri_boolean.html',
|
||||||
|
sqlite3_user_data: 'www:/c3ref/user_data.html',
|
||||||
|
sqlite3_value_blob: 'www:/c3ref/value_blob.html',
|
||||||
|
sqlite3_value_bytes: 'www:/c3ref/value_blob.html',
|
||||||
|
sqlite3_value_double: 'www:/c3ref/value_blob.html',
|
||||||
|
sqlite3_value_dup: 'www:/c3ref/value_dup.html',
|
||||||
|
sqlite3_value_free: 'www:/c3ref/value_dup.html',
|
||||||
|
sqlite3_value_frombind: 'www:/c3ref/value_blob.html',
|
||||||
|
sqlite3_value_int: 'www:/c3ref/value_blob.html',
|
||||||
|
sqlite3_value_int64: 'www:/c3ref/value_blob.html',
|
||||||
|
sqlite3_value_nochange: 'www:/c3ref/value_blob.html',
|
||||||
|
sqlite3_value_numeric_type: 'www:/c3ref/value_blob.html',
|
||||||
|
sqlite3_value_pointer: 'www:/c3ref/value_blob.html',
|
||||||
|
sqlite3_value_subtype: 'www:/c3ref/value_subtype.html',
|
||||||
|
sqlite3_value_text: 'www:/c3ref/value_blob.html',
|
||||||
|
sqlite3_value_type: 'www:/c3ref/value_blob.html',
|
||||||
|
sqlite3_vfs_find: 'www:/c3ref/vfs_find.html',
|
||||||
|
sqlite3_vfs_register: 'www:/c3ref/vfs_find.html',
|
||||||
|
sqlite3_vfs_unregister: 'www:/c3ref/vfs_find.html',
|
||||||
|
sqlite3_vtab_collation: 'www:/c3ref/vtab_collation.html',
|
||||||
|
sqlite3_vtab_config: 'www:/c3ref/vtab_config.html',
|
||||||
|
sqlite3_vtab_distinct: 'www:/c3ref/vtab_distinct.html',
|
||||||
|
sqlite3_vtab_in: 'www:/c3ref/vtab_in.html',
|
||||||
|
sqlite3_vtab_in_first: 'www:/c3ref/vtab_in_first.html',
|
||||||
|
sqlite3_vtab_in_next: 'www:/c3ref/vtab_in_next.html',
|
||||||
|
sqlite3_vtab_nochange: 'www:/c3ref/vtab_nochange.html',
|
||||||
|
sqlite3_vtab_on_conflict: 'www:/c3ref/vtab_on_conflict.html',
|
||||||
|
sqlite3_vtab_rhs_value: 'www:/c3ref/vtab_rhs_value.html',
|
||||||
|
|
||||||
|
sqlite3_column_js: 'wasm:/api-c-style.md#sqlite3_column_js',
|
||||||
|
sqlite3_js_aggregate_context: 'wasm:/api-c-style.md#sqlite3_js_aggregate_context',
|
||||||
|
sqlite3_js_db_export: 'wasm:/api-c-style.md#sqlite3_js_db_export',
|
||||||
|
sqlite3_js_db_uses_vfs: 'wasm:/api-c-style.md#sqlite3_js_db_uses_vfs',
|
||||||
|
sqlite3_js_db_vfs: 'wasm:/api-c-style.md#sqlite3_js_db_vfs',
|
||||||
|
sqlite3_js_kvvfs_clear: 'wasm:/api-c-style.md#sqlite3_js_kvvfs',
|
||||||
|
sqlite3_js_kvvfs_size: 'wasm:/api-c-style.md#sqlite3_js_kvvfs',
|
||||||
|
sqlite3_js_rc_str: 'wasm:/api-c-style.md#sqlite3_js_rc_str',
|
||||||
|
sqlite3_js_vfs_create_file: 'wasm:/api-c-style.md#sqlite3_js_vfs_create_file',
|
||||||
|
sqlite3_js_vfs_list: 'wasm:/api-c-style.md#sqlite3_js_vfs_list',
|
||||||
|
sqlite3_result_error_js: 'wasm:/api-c-style.md#sqlite3_result_error_js',
|
||||||
|
sqlite3_result_js: 'wasm:/api-c-style.md#sqlite3_result_js',
|
||||||
|
sqlite3_value_to_js: 'wasm:/api-c-style.md#sqlite3_value_to_js',
|
||||||
|
sqlite3_values_to_js: 'wasm:/api-c-style.md#sqlite3_values_to_js',
|
||||||
|
|
||||||
|
xform: (v)=>{
|
||||||
|
if(v){
|
||||||
|
return v.replace('www:','https://sqlite.org')
|
||||||
|
.replace('wasm:','https://sqlite.org/wasm/doc/trunk');
|
||||||
|
}else{
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
const eNew = (tag,parent)=>{
|
const eNew = (tag,parent)=>{
|
||||||
const e = document.createElement(tag);
|
const e = document.createElement(tag);
|
||||||
if(parent) parent.appendChild(e);
|
if(parent) parent.appendChild(e);
|
||||||
@@ -202,6 +373,16 @@
|
|||||||
e.innerText = label;
|
e.innerText = label;
|
||||||
return e;
|
return e;
|
||||||
};
|
};
|
||||||
|
const eLink = (label,url,parent)=>{
|
||||||
|
const w = eNew('span',parent);
|
||||||
|
const e = eNew('a',w);
|
||||||
|
if(url){
|
||||||
|
e.href = url;
|
||||||
|
e.target = 'sqlite3-api-docs';
|
||||||
|
}
|
||||||
|
e.innerText = label;
|
||||||
|
return w;
|
||||||
|
};
|
||||||
const E = (sel)=>document.querySelector(sel);
|
const E = (sel)=>document.querySelector(sel);
|
||||||
const EAll = (sel)=>document.querySelectorAll(sel);
|
const EAll = (sel)=>document.querySelectorAll(sel);
|
||||||
const eFuncs = E('#list-functions'),
|
const eFuncs = E('#list-functions'),
|
||||||
@@ -211,9 +392,9 @@
|
|||||||
};
|
};
|
||||||
const renderFunc = function(name){
|
const renderFunc = function(name){
|
||||||
let lbl = name+'()';
|
let lbl = name+'()';
|
||||||
const e = eLi(lbl, eFuncs);;
|
const e = eLink(lbl, apiLinks.xform(apiLinks[name]), eFuncs);
|
||||||
if(name.startsWith('sqlite3_js')
|
if(name.indexOf('_js')>0
|
||||||
|| name.startsWith('sqlite3_wasm')){
|
|| name.indexOf('_wasm')>0){
|
||||||
e.classList.add('func-wasm');
|
e.classList.add('func-wasm');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -257,20 +438,28 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
/* sqlite3_...() and SQLITE_... */
|
/* sqlite3_...() and SQLITE_... */
|
||||||
const lists = {c: [], f: []};
|
const lists = {c: [/*constants*/], f: [/*functions*/],
|
||||||
for(const [k,v] of Object.entries(capi)){
|
s: [/*structs*/]};
|
||||||
if(k.startsWith('SQLITE_')) lists.c.push(k);
|
/* Exclude these from the function list... */
|
||||||
else if(k.startsWith('sqlite3_')) lists.f.push(k);
|
|
||||||
}
|
|
||||||
const excludeCapi = [
|
const excludeCapi = [
|
||||||
|
// WASMFS stuff:
|
||||||
'sqlite3_wasmfs_filename_is_persistent',
|
'sqlite3_wasmfs_filename_is_persistent',
|
||||||
'sqlite3_wasmfs_opfs_dir'
|
'sqlite3_wasmfs_opfs_dir'
|
||||||
];
|
];
|
||||||
|
for(const [k,v] of Object.entries(capi)){
|
||||||
|
if(k.startsWith('SQLITE_')){
|
||||||
|
lists.c.push(k);
|
||||||
|
}else if(k.startsWith('sqlite3_')){
|
||||||
|
if(excludeCapi.indexOf(k)>=0) continue;
|
||||||
|
if(v.structInfo){
|
||||||
|
// assume this is a StructType-type.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
lists.f.push(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
lists.c.sort().forEach(renderConst);
|
lists.c.sort().forEach(renderConst);
|
||||||
lists.f
|
lists.f.sort().forEach(renderFunc);
|
||||||
.filter((v)=>excludeCapi.indexOf(v)<0)
|
|
||||||
.sort()
|
|
||||||
.forEach(renderFunc);
|
|
||||||
lists.c = lists.f = null;
|
lists.c = lists.f = null;
|
||||||
|
|
||||||
renderX(E('#list-oo1'), sqlite3.oo1,
|
renderX(E('#list-oo1'), sqlite3.oo1,
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li>The easiest way to try different optimization levels is,
|
<li>The easiest way to try different optimization levels is,
|
||||||
from this directory:
|
from this directory:
|
||||||
<pre>$ rm -f speedtest1.js; make -e emcc_opt='-O2' speedtest1.js</pre>
|
<pre>$ rm -f jswasm/speedtest1.js; make -e emcc_opt='-O2' speedtest1</pre>
|
||||||
Then reload this page. -O2 seems to consistently produce the fastest results.
|
Then reload this page. -O2 seems to consistently produce the fastest results.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -40,9 +40,9 @@
|
|||||||
<script src="jswasm/speedtest1.js"></script>
|
<script src="jswasm/speedtest1.js"></script>
|
||||||
<script>(function(){
|
<script>(function(){
|
||||||
/**
|
/**
|
||||||
If this environment contains OPFS, this function initializes it and
|
If this environment contains WASMFS with OPFS, this function
|
||||||
returns the name of the dir on which OPFS is mounted, else it returns
|
initializes it and returns the name of the dir on which OPFS is
|
||||||
an empty string.
|
mounted, else it returns an empty string.
|
||||||
*/
|
*/
|
||||||
const wasmfsDir = function f(wasmUtil){
|
const wasmfsDir = function f(wasmUtil){
|
||||||
if(undefined !== f._) return f._;
|
if(undefined !== f._) return f._;
|
||||||
|
|||||||
@@ -7,11 +7,7 @@
|
|||||||
<link rel="stylesheet" href="../common/emscripten.css"/>
|
<link rel="stylesheet" href="../common/emscripten.css"/>
|
||||||
<link rel="stylesheet" href="../common/testing.css"/>
|
<link rel="stylesheet" href="../common/testing.css"/>
|
||||||
<title>sqlite3 tester #1: Worker thread</title>
|
<title>sqlite3 tester #1: Worker thread</title>
|
||||||
<style>
|
<style></style>
|
||||||
body {
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1 id='color-target'>sqlite3 tester #1: Worker thread</h1>
|
<h1 id='color-target'>sqlite3 tester #1: Worker thread</h1>
|
||||||
|
|||||||
@@ -13,14 +13,9 @@ ES6 Module in UI thread
|
|||||||
UI thread
|
UI thread
|
||||||
//#endif
|
//#endif
|
||||||
</title>
|
</title>
|
||||||
<style>
|
<style></style>
|
||||||
body {
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body><h1 id='color-target'></h1>
|
||||||
<h1 id='color-target'></h1>
|
|
||||||
<div>See <a href='tester1-worker.html' target='tester1-worker.html'>tester1-worker.html</a>
|
<div>See <a href='tester1-worker.html' target='tester1-worker.html'>tester1-worker.html</a>
|
||||||
for the Worker-thread variant.</div>
|
for the Worker-thread variant.</div>
|
||||||
<div class='input-wrapper'>
|
<div class='input-wrapper'>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
72
manifest
72
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Remove\sthe\sSQLITE_PREPARE_SAFEOPT\sflag.\s\sThe\sname\sis\sobsolete\sand\sit\sis\sat\sthe\nwrong\slevel.\s\sInstead\suse\sthe\sSF_UpdateFrom\sflags\son\sthe\sSelect\sobject.
|
C Merge\srecent\strunk\schanges\sinto\sthe\scoroutine-exp2\sbranch.
|
||||||
D 2022-12-09T18:26:15.981
|
D 2022-12-13T00:51:34.741
|
||||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||||
@@ -357,7 +357,7 @@ F ext/rbu/rbu7.test ae25f47b56f178197fc1098537a35a39176cc73d1629b03dc9d795929fc3
|
|||||||
F ext/rbu/rbu8.test b98a6fc58ead84a0e6ddee775b9702cd981f318d5d4fd1d4df0fa0c40db7251b
|
F ext/rbu/rbu8.test b98a6fc58ead84a0e6ddee775b9702cd981f318d5d4fd1d4df0fa0c40db7251b
|
||||||
F ext/rbu/rbu9.test 0e4d985e25620d61920597e8ea69c871c9e8c1f5a0be2ae9fa70bb641d74378c
|
F ext/rbu/rbu9.test 0e4d985e25620d61920597e8ea69c871c9e8c1f5a0be2ae9fa70bb641d74378c
|
||||||
F ext/rbu/rbuA.test b34a90cb495682c25b5fc03a9d5e7a4fc99541c29256f25e2e2a4f6542b4f5b3
|
F ext/rbu/rbuA.test b34a90cb495682c25b5fc03a9d5e7a4fc99541c29256f25e2e2a4f6542b4f5b3
|
||||||
F ext/rbu/rbuB.test 52b07158824c6927b7e25554ace92a695cdebfc296ae3d308ac386984aded9bc
|
F ext/rbu/rbuB.test 8d1f141711be8122739853d876af4306bc756d925499577f9b917ec1f1c5ae65
|
||||||
F ext/rbu/rbuC.test 80f1cc2fb74f44b1128fd0ed8eedab3a76fefeb72a947860e2869ef76fc8dc6b
|
F ext/rbu/rbuC.test 80f1cc2fb74f44b1128fd0ed8eedab3a76fefeb72a947860e2869ef76fc8dc6b
|
||||||
F ext/rbu/rbu_common.tcl 60d904133ff843fe72cc0514e9dd2486707181e6e0fbab20979da28c48d21de9
|
F ext/rbu/rbu_common.tcl 60d904133ff843fe72cc0514e9dd2486707181e6e0fbab20979da28c48d21de9
|
||||||
F ext/rbu/rbubusy.test f38ef557358564491b8a2ee70e4cad31e40fcea57a16f27bc56ba40a59bbde50
|
F ext/rbu/rbubusy.test f38ef557358564491b8a2ee70e4cad31e40fcea57a16f27bc56ba40a59bbde50
|
||||||
@@ -386,7 +386,7 @@ F ext/rbu/rbuvacuum.test 55e101e90168c2b31df6c9638fe73dc7f7cc666b6142266d1563697
|
|||||||
F ext/rbu/rbuvacuum2.test 2643b58f4d8d3573db0f93faae18805a35ab162b4c55ff6b656062ff432ed55b
|
F ext/rbu/rbuvacuum2.test 2643b58f4d8d3573db0f93faae18805a35ab162b4c55ff6b656062ff432ed55b
|
||||||
F ext/rbu/rbuvacuum3.test 8addd82e4b83b4c93fa47428eae4fd0dbf410f8512c186f38e348feb49ba03dc
|
F ext/rbu/rbuvacuum3.test 8addd82e4b83b4c93fa47428eae4fd0dbf410f8512c186f38e348feb49ba03dc
|
||||||
F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2f1dbccfd10
|
F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2f1dbccfd10
|
||||||
F ext/rbu/sqlite3rbu.c c4ba7901b2d3e0c7845f30840e3ffb35c6f999d6da0d80f121866491f982794c
|
F ext/rbu/sqlite3rbu.c 64d105c7c6c95272e7c12142cd021059214387a0d51e262bb0663285a2b91660
|
||||||
F ext/rbu/sqlite3rbu.h 02d981e2d39c151391759e1a400e29c7388730812957ac3db8dad7f6c9f9cfc8
|
F ext/rbu/sqlite3rbu.h 02d981e2d39c151391759e1a400e29c7388730812957ac3db8dad7f6c9f9cfc8
|
||||||
F ext/rbu/test_rbu.c ee6ede75147bc081fe9bc3931e6b206277418d14d3fbceea6fdc6216d9b47055
|
F ext/rbu/test_rbu.c ee6ede75147bc081fe9bc3931e6b206277418d14d3fbceea6fdc6216d9b47055
|
||||||
F ext/recover/dbdata.c 8f1f75d636431de69d7977ec50fc41bfdd0c48c510d5ee7eae0cbd4164e1429a
|
F ext/recover/dbdata.c 8f1f75d636431de69d7977ec50fc41bfdd0c48c510d5ee7eae0cbd4164e1429a
|
||||||
@@ -491,37 +491,37 @@ F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
|
|||||||
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
|
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
|
||||||
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
|
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
|
||||||
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
|
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
|
||||||
F ext/wasm/GNUmakefile bfa47f169468ca9db031105b0e336db29a88e93c3abd217d0bbb2b8731fa5413
|
F ext/wasm/GNUmakefile 32ad3deb3005a72d1955e28fa375e01e976cda01f0bc4ddd3fec93006fa3fa04
|
||||||
F ext/wasm/README-dist.txt 2d670b426fc7c613b90a7d2f2b05b433088fe65181abead970980f0a4a75ea20
|
F ext/wasm/README-dist.txt 2d670b426fc7c613b90a7d2f2b05b433088fe65181abead970980f0a4a75ea20
|
||||||
F ext/wasm/README.md ef39861aa21632fdbca0bdd469f78f0096f6449a720f3f39642594af503030e9
|
F ext/wasm/README.md ef39861aa21632fdbca0bdd469f78f0096f6449a720f3f39642594af503030e9
|
||||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 89af0612bad5c651f69e629c7e9689be6d3c8a92a9010da5dd90a87c1d86817a
|
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 3883604dfda98352ff7ea76b2092f406d8c1ebc576e16b8c6e470fa2b1724880
|
||||||
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
|
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
|
||||||
F ext/wasm/api/README.md 20a256f4aaae80035d2bb1c9e3e0a125570313a8d137d427471d7be10edde87a
|
F ext/wasm/api/README.md 17fb1e10335cc87e366dec496c5b17b061f3f75cdf216e825258de34d97a3e53
|
||||||
F ext/wasm/api/extern-post-js.c-pp.js 8923f76c3d2213159e12d641dc750523ead5c848185dc4996fae5cc12397f88d
|
F ext/wasm/api/extern-post-js.c-pp.js 8923f76c3d2213159e12d641dc750523ead5c848185dc4996fae5cc12397f88d
|
||||||
F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e7a4d11d43c5c8f41
|
F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e7a4d11d43c5c8f41
|
||||||
F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08902f15c34720ee4a1
|
F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08902f15c34720ee4a1
|
||||||
F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62
|
F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62
|
||||||
F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f
|
F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f
|
||||||
F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4
|
F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4
|
||||||
F ext/wasm/api/sqlite3-api-glue.js 6fe39964605fda3b699f69365eed565b5172d29cab2c49bc057a43f9a93f9f36
|
F ext/wasm/api/sqlite3-api-glue.js 1ff6deb11bd192c13cbd247e333a4739e2303aad3d1a9dc68defced2d7393375
|
||||||
F ext/wasm/api/sqlite3-api-oo1.js 91a7d7b9203fb0f031e6ba380a644a7f871e1798b388de399c01ed4087bac9e0
|
F ext/wasm/api/sqlite3-api-oo1.js 6d10849609231ccd46fa11b1d3fbbe0f45d9fe84c66a0b054601036540844300
|
||||||
F ext/wasm/api/sqlite3-api-prologue.js 697a5989ad52a9ba7bc60b5436589bd05885ee2201d84c38c5e9af3876af3ba4
|
F ext/wasm/api/sqlite3-api-prologue.js 39fbca8f25219c218d631433828ede53be8d518aa9f0da480758a3ea8abc1be8
|
||||||
F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f
|
F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f
|
||||||
F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3
|
F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3
|
||||||
F ext/wasm/api/sqlite3-opfs-async-proxy.js f79dd8d98ef3e0b55c10bb2bee7a3840fa967318e1f577c156aafc34664271d1
|
F ext/wasm/api/sqlite3-opfs-async-proxy.js 7795b84b66a7a8dedc791340709b310bb497c3c72a80bef364fa2a58e2ddae3f
|
||||||
F ext/wasm/api/sqlite3-vfs-helper.js 4ad4faf02e1524bf0296be8452c00b5708dce6faf649468d0377e26a0b299263
|
F ext/wasm/api/sqlite3-v-helper.js 6f6c3e390a72e08b0a5b16a0d567d7af3c04d172831853a29d72a6f1dd40ff24 w ext/wasm/api/sqlite3-vfs-helper.js
|
||||||
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 29d6487a26b2fb6a471cde52c37ffee7c27ed6a91914b308c247e0706f454ffb
|
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 66daf6fb6843bea615fe193109e1542efbeca24f560ee9da63375a910bb48115
|
||||||
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
||||||
F ext/wasm/api/sqlite3-wasm.c b0babf8435f31d21f28454fb81433aa538c68b23d0a4a251f0666fdec4e71f59
|
F ext/wasm/api/sqlite3-wasm.c 0d3d021c7f32d4422872cb1af8a163cd4cc63c0be314eb5cf6e56931260213c8
|
||||||
F ext/wasm/api/sqlite3-worker1-promiser.js 0c7a9826dbf82a5ed4e4f7bf7816e825a52aff253afbf3350431f5773faf0e4b
|
F ext/wasm/api/sqlite3-worker1-promiser.js 0c7a9826dbf82a5ed4e4f7bf7816e825a52aff253afbf3350431f5773faf0e4b
|
||||||
F ext/wasm/api/sqlite3-worker1.js 1e54ea3d540161bcfb2100368a2fc0cad871a207b8336afee1c445715851ec54
|
F ext/wasm/api/sqlite3-worker1.js 1e54ea3d540161bcfb2100368a2fc0cad871a207b8336afee1c445715851ec54
|
||||||
F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8
|
F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8
|
||||||
F ext/wasm/batch-runner.js 49609e89aaac9989d6c1ad3fae268e4878e1ad7bc5fd3e5c2f44959660780b2e
|
F ext/wasm/batch-runner.js 0dad6a02ad796f1003d3b7048947d275c4d6277f63767b8e685c27df8fdac93e
|
||||||
F ext/wasm/c-pp.c 92285f7bce67ed7b7020b40fde8ed0982c442b63dc33df9dfd4b658d4a6c0779
|
F ext/wasm/c-pp.c 92285f7bce67ed7b7020b40fde8ed0982c442b63dc33df9dfd4b658d4a6c0779
|
||||||
F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b
|
F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b
|
||||||
F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
|
F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15
|
||||||
F ext/wasm/common/testing.css 35889709547d89a6109ff83b25c11bbc91d8dd43aab8722e428655ca98880a06
|
F ext/wasm/common/testing.css 0ff15602a3ab2bad8aef2c3bd120c7ee3fd1c2054ad2ace7e214187ae68d926f
|
||||||
F ext/wasm/common/whwasmutil.js c1bc5715cd96728929cc31d788b16152ccbd6b2e111d2e88fbc9725247e67b4f
|
F ext/wasm/common/whwasmutil.js 85bdcefc3ae4b550231da1f94b196e15458828d7652b1f61f86d20873ff915ed
|
||||||
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
|
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
|
||||||
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
|
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
|
||||||
F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6
|
F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6
|
||||||
@@ -531,7 +531,7 @@ F ext/wasm/demo-worker1-promiser.html 1de7c248c7c2cfd4a5783d2aa154bce62d74c6de98
|
|||||||
F ext/wasm/demo-worker1-promiser.js b85a2bb1b918db4f09dfa24419241cb3edad7791389425c2505092e9b715017d
|
F ext/wasm/demo-worker1-promiser.js b85a2bb1b918db4f09dfa24419241cb3edad7791389425c2505092e9b715017d
|
||||||
F ext/wasm/demo-worker1.html 2c178c1890a2beb5a5fecb1453e796d067a4b8d3d2a04d65ca2eb1ab2c68ef5d
|
F ext/wasm/demo-worker1.html 2c178c1890a2beb5a5fecb1453e796d067a4b8d3d2a04d65ca2eb1ab2c68ef5d
|
||||||
F ext/wasm/demo-worker1.js a619adffc98b75b66c633b00f747b856449a134a9a0357909287d80a182d70fa
|
F ext/wasm/demo-worker1.js a619adffc98b75b66c633b00f747b856449a134a9a0357909287d80a182d70fa
|
||||||
F ext/wasm/dist.make 701694188a78c9a24bf44cdf529063f4b3a0e892adc1d20ed1619252738943f1
|
F ext/wasm/dist.make 5523b02e824db5ab8176e3eedc2e709fe1204d8f4d6e52e8321cdf6830114b72
|
||||||
F ext/wasm/fiddle.make 2812c44c9bafb5be9c8767963d1b9f374d77af7795fcaa06483c03e7059dea74
|
F ext/wasm/fiddle.make 2812c44c9bafb5be9c8767963d1b9f374d77af7795fcaa06483c03e7059dea74
|
||||||
F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
|
F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
|
||||||
F ext/wasm/fiddle/fiddle-worker.js b4a0c8ab6c0983218543ca771c45f6075449f63a1dcf290ae5a681b2cba8800d
|
F ext/wasm/fiddle/fiddle-worker.js b4a0c8ab6c0983218543ca771c45f6075449f63a1dcf290ae5a681b2cba8800d
|
||||||
@@ -539,23 +539,23 @@ F ext/wasm/fiddle/fiddle.js 974b995119ac443685d7d94d3b3c58c6a36540e9eb3fed7069d5
|
|||||||
F ext/wasm/fiddle/index.html 5daf54e8f3d7777cbb1ca4f93affe28858dbfff25841cb4ab81d694efed28ec2
|
F ext/wasm/fiddle/index.html 5daf54e8f3d7777cbb1ca4f93affe28858dbfff25841cb4ab81d694efed28ec2
|
||||||
F ext/wasm/index-dist.html c806b6005145b71d64240606e9c6e0bf56878ee8829c66fe7486cebf34b0e6b1
|
F ext/wasm/index-dist.html c806b6005145b71d64240606e9c6e0bf56878ee8829c66fe7486cebf34b0e6b1
|
||||||
F ext/wasm/index.html f151b7c7b5cfdc066567d556acd168e769efd4e982286dc5f849a5ee69ecd0ff
|
F ext/wasm/index.html f151b7c7b5cfdc066567d556acd168e769efd4e982286dc5f849a5ee69ecd0ff
|
||||||
F ext/wasm/jaccwabyt/jaccwabyt.js 95f573de1826474c9605dda620ee622fcb1673ae74f191eb324c0853aa4dcb66
|
F ext/wasm/jaccwabyt/jaccwabyt.js 06f2ef1ad640c26c593def3d960336e9bb789819b920516480895c38ed5f58fa
|
||||||
F ext/wasm/jaccwabyt/jaccwabyt.md 9aa6951b529a8b29f578ec8f0355713c39584c92cf1708f63ba0cf917cb5b68e
|
F ext/wasm/jaccwabyt/jaccwabyt.md 37911f00db12cbcca73aa1ed72594430365f30aafae2fa9c886961de74e5e0eb
|
||||||
F ext/wasm/module-symbols.html 980680c8acfa3c8ae6a5aa223512d1b8e78040ced20f8ba2c382129bc73ec028
|
F ext/wasm/module-symbols.html 650a5ad1bd54feb39eb627b06093612932317406825f3a727d5926016a46e502
|
||||||
F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06
|
F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06
|
||||||
F ext/wasm/scratchpad-wasmfs-main.js 4c140457f4d6da9d646a49addd91edb6e9ad1643c6c48e3258b5bce24725dc18
|
F ext/wasm/scratchpad-wasmfs-main.js 4c140457f4d6da9d646a49addd91edb6e9ad1643c6c48e3258b5bce24725dc18
|
||||||
F ext/wasm/speedtest1-wasmfs.html bc28eb29b69a73864b8d7aae428448f8b7e1de81d8bfb9bba99541322054dbd0
|
F ext/wasm/speedtest1-wasmfs.html bc28eb29b69a73864b8d7aae428448f8b7e1de81d8bfb9bba99541322054dbd0
|
||||||
F ext/wasm/speedtest1-worker.html e94cecebcbb9d187647025edd04d37af9789dfba98c2cc439b549b5ae8a8bc93
|
F ext/wasm/speedtest1-worker.html fe6b36a63de1012bb9fb4d2fb888b6de9c589c21b0aa3ae054459b0093e077bf
|
||||||
F ext/wasm/speedtest1-worker.js 13b57c4a41729678a1194014afec2bd5b94435dcfc8d1039dfa9a533ac819ee1
|
F ext/wasm/speedtest1-worker.js 13b57c4a41729678a1194014afec2bd5b94435dcfc8d1039dfa9a533ac819ee1
|
||||||
F ext/wasm/speedtest1.html e4c4e5c1c8ec1ad13c995e346e4216a1df152fd2c5cd17e0793b865b2f3c5000
|
F ext/wasm/speedtest1.html ff048b4a623aa192e83e143e48f1ce2a899846dd42c023fdedc8772b6e3f07da
|
||||||
F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
|
F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
|
||||||
F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
|
F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
|
||||||
F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
|
F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
|
||||||
F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555e685bce3da8c3f
|
F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555e685bce3da8c3f
|
||||||
F ext/wasm/test-opfs-vfs.js 44363db07b2a20e73b0eb1808de4400ca71b703af718d0fa6d962f15e73bf2ac
|
F ext/wasm/test-opfs-vfs.js 44363db07b2a20e73b0eb1808de4400ca71b703af718d0fa6d962f15e73bf2ac
|
||||||
F ext/wasm/tester1-worker.html 29b1d87f7d51f70d61645719fee657f3787fe939bb695f27034c75404e8f1e6f
|
F ext/wasm/tester1-worker.html d43f3c131d88f10d00aff3e328fed13c858d674ea2ff1ff90225506137f85aa9
|
||||||
F ext/wasm/tester1.c-pp.html 74aa9b31c75f12490653f814b53c3dd39f40cd3f70d6a53a716f4e8587107399
|
F ext/wasm/tester1.c-pp.html d34bef3d48e5cbc1c7c06882ad240fec49bf88f5f65696cc2c72c416933aa406
|
||||||
F ext/wasm/tester1.c-pp.js d096a8fadfd27caa680a4311b1d529551f8fe885a63dd27457c87b6008c64632
|
F ext/wasm/tester1.c-pp.js ee609a41cc1aabc971a6514b5d1b155f5f15d092ee015f5d03a204880532e62d
|
||||||
F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70
|
F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70
|
||||||
F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d
|
F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d
|
||||||
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
|
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
|
||||||
@@ -608,7 +608,7 @@ F src/insert.c 1b11a2e33ee52db93c02fddac67e39d00161d61b69fac2675b82f2aa68c1b61c
|
|||||||
F src/json.c 7749b98c62f691697c7ee536b570c744c0583cab4a89200fdd0fc2aa8cc8cbd6
|
F src/json.c 7749b98c62f691697c7ee536b570c744c0583cab4a89200fdd0fc2aa8cc8cbd6
|
||||||
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
|
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
|
||||||
F src/loadext.c 25663175950c5c4404b9377840b7b4c6fe5c53b415caf43634c62f442c02a9a7
|
F src/loadext.c 25663175950c5c4404b9377840b7b4c6fe5c53b415caf43634c62f442c02a9a7
|
||||||
F src/main.c 954490392b74fb215378af3c75a9e1f4f559f19cb1567e5d77f3fbbb63909b4d
|
F src/main.c 1f75553163a3349a3918d1aae072bc8e908bbb58c949d5fec9b90bf49ff3b73e
|
||||||
F src/malloc.c 47b82c5daad557d9b963e3873e99c22570fb470719082c6658bf64e3012f7d23
|
F src/malloc.c 47b82c5daad557d9b963e3873e99c22570fb470719082c6658bf64e3012f7d23
|
||||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||||
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
|
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
|
||||||
@@ -645,9 +645,9 @@ F src/printf.c e99ee9741e79ae3873458146f59644276657340385ade4e76a5f5d1c25793764
|
|||||||
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
|
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
|
||||||
F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633
|
F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633
|
||||||
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
||||||
F src/select.c d4df3eb74a1b0e1bd5a95525f2b571baeef3e3bedcc9d6401152dc0e0bdd7384
|
F src/select.c 4ad0aa2b21a94b118f6f6f1c9baf8e932d14227e8a9a5022b86e661049681c3a
|
||||||
F src/shell.c.in bcf8552c82f2c84650e39a6d638373569c2035942c0497b83eef197169e0305a
|
F src/shell.c.in 8d9dc02dd03f8fc93f3e3cdb17d8d16e8ddb985dddad213985c08186900a3ebb
|
||||||
F src/sqlite.h.in 1fe1836879ecbb2e28f00f44eb6092db09a2a06bf072af351c6c2466bd515496
|
F src/sqlite.h.in e752f82b9d71f1d42b259b1900e4b1caf0965e844d756cd5cc91cc2cf45ed925
|
||||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||||
F src/sqlite3ext.h c4b9fa7a7e2bcdf850cfeb4b8a91d5ec47b7a00033bc996fd2ee96cbf2741f5f
|
F src/sqlite3ext.h c4b9fa7a7e2bcdf850cfeb4b8a91d5ec47b7a00033bc996fd2ee96cbf2741f5f
|
||||||
F src/sqliteInt.h ef0c1af2d7b46ce89fe698ce5f1a26d19110780ac19ec1ef5a227f7e51e97de3
|
F src/sqliteInt.h ef0c1af2d7b46ce89fe698ce5f1a26d19110780ac19ec1ef5a227f7e51e97de3
|
||||||
@@ -863,7 +863,7 @@ F test/capi3c.test 54e2dc0c8fd7c34ad1590d1be6864397da2438c95a9f5aee2f8fbc60c112e
|
|||||||
F test/capi3d.test 8b778794af891b0dca3d900bd345fbc8ebd2aa2aae425a9dccdd10d5233dfbde
|
F test/capi3d.test 8b778794af891b0dca3d900bd345fbc8ebd2aa2aae425a9dccdd10d5233dfbde
|
||||||
F test/capi3e.test 3d49c01ef2a1a55f41d73cba2b23b5059ec460fe
|
F test/capi3e.test 3d49c01ef2a1a55f41d73cba2b23b5059ec460fe
|
||||||
F test/carray01.test d55d57bf66b1af1c7ac55fae66ff4910884a8f5d21a90a18797ce386212a2634
|
F test/carray01.test d55d57bf66b1af1c7ac55fae66ff4910884a8f5d21a90a18797ce386212a2634
|
||||||
F test/cast.test 336fa21989b5170ebcaf90c24266be22dd97b3e23d1fad5ecf6ad4efb04c4423
|
F test/cast.test 46a5963a216c2b14220557b8636b968d6de9d3292d79616becbf7109ca00e1ad
|
||||||
F test/cffault.test 9d6b20606afe712374952eec4f8fd74b1a8097ef
|
F test/cffault.test 9d6b20606afe712374952eec4f8fd74b1a8097ef
|
||||||
F test/changes.test 9dd8e597d84072122fc8a4fcdea837f4a54a461e6e536053ea984303e8ca937b
|
F test/changes.test 9dd8e597d84072122fc8a4fcdea837f4a54a461e6e536053ea984303e8ca937b
|
||||||
F test/changes2.test d222c0cbf5ab0ac4d7c180594e486c1bf20b2098d33e56ce33b8e12eba6823b9
|
F test/changes2.test d222c0cbf5ab0ac4d7c180594e486c1bf20b2098d33e56ce33b8e12eba6823b9
|
||||||
@@ -1819,7 +1819,7 @@ F test/vacuum6.test b137b04bf3392d3f5c3b8fda0ce85a6775a70ca112f6559f74ff52dc9ce0
|
|||||||
F test/vacuummem.test 4b30f5b95a9ff86e9d5c20741e50a898b2dc10b0962a3211571eb165357003fb
|
F test/vacuummem.test 4b30f5b95a9ff86e9d5c20741e50a898b2dc10b0962a3211571eb165357003fb
|
||||||
F test/varint.test bbce22cda8fc4d135bcc2b589574be8410614e62
|
F test/varint.test bbce22cda8fc4d135bcc2b589574be8410614e62
|
||||||
F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661
|
F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661
|
||||||
F test/view.test d16e49e89ada6137d1447777ef2a74574526a3b024e6733bf53ae2960da8c17c
|
F test/view.test a5662e9c7425d77b1e1eb4b07fb891fa15f20317013eddcb6f6bee009f2fccc9
|
||||||
F test/view2.test db32c8138b5b556f610b35dfddd38c5a58a292f07fda5281eedb0851b2672679
|
F test/view2.test db32c8138b5b556f610b35dfddd38c5a58a292f07fda5281eedb0851b2672679
|
||||||
F test/view3.test ad8a8290ee2b55ff6ce66c9ef1ce3f1e47926273a3814e1c425293e128a95456
|
F test/view3.test ad8a8290ee2b55ff6ce66c9ef1ce3f1e47926273a3814e1c425293e128a95456
|
||||||
F test/vt02.c 33ecddc0832d4cd24e9e9fa83d868981b1e049462f4ec9080710353f6479a534
|
F test/vt02.c 33ecddc0832d4cd24e9e9fa83d868981b1e049462f4ec9080710353f6479a534
|
||||||
@@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
|||||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||||
P d125d5afdf1b0a1c64fc64f180898099af07b8290ea9da49419df75d8b455f71
|
P 78723a9a7e72b42d28fc5645661da17f20cedcf864819b861800ad9340007be1 f0325359d5795237b79f90f21b42d7d942c7e918137cb0231d404365d3041e81
|
||||||
R bf6e5a558d8631cb7969118db6751e4e
|
R fab8476faef04e68db98cfe4c1810ebc
|
||||||
U drh
|
U drh
|
||||||
Z 2ad12df38bfb5c0313f6be39e7ec9c86
|
Z 1279761382706bd160c45650b0901daf
|
||||||
# Remove this line to create a well-formed Fossil manifest.
|
# Remove this line to create a well-formed Fossil manifest.
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
78723a9a7e72b42d28fc5645661da17f20cedcf864819b861800ad9340007be1
|
c43f433bcab29db0f1f8afd3948f5a4149e1f277c853c66f99c31f226d82bc0f
|
||||||
@@ -1566,6 +1566,7 @@ const char *sqlite3ErrName(int rc){
|
|||||||
case SQLITE_NOTICE_RECOVER_WAL: zName = "SQLITE_NOTICE_RECOVER_WAL";break;
|
case SQLITE_NOTICE_RECOVER_WAL: zName = "SQLITE_NOTICE_RECOVER_WAL";break;
|
||||||
case SQLITE_NOTICE_RECOVER_ROLLBACK:
|
case SQLITE_NOTICE_RECOVER_ROLLBACK:
|
||||||
zName = "SQLITE_NOTICE_RECOVER_ROLLBACK"; break;
|
zName = "SQLITE_NOTICE_RECOVER_ROLLBACK"; break;
|
||||||
|
case SQLITE_NOTICE_RBU: zName = "SQLITE_NOTICE_RBU"; break;
|
||||||
case SQLITE_WARNING: zName = "SQLITE_WARNING"; break;
|
case SQLITE_WARNING: zName = "SQLITE_WARNING"; break;
|
||||||
case SQLITE_WARNING_AUTOINDEX: zName = "SQLITE_WARNING_AUTOINDEX"; break;
|
case SQLITE_WARNING_AUTOINDEX: zName = "SQLITE_WARNING_AUTOINDEX"; break;
|
||||||
case SQLITE_DONE: zName = "SQLITE_DONE"; break;
|
case SQLITE_DONE: zName = "SQLITE_DONE"; break;
|
||||||
|
|||||||
29
src/select.c
29
src/select.c
@@ -2289,6 +2289,14 @@ int sqlite3ColumnsFromExprList(
|
|||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This bit, when added to the "aff" parameter of
|
||||||
|
** sqlite3SelectAddColumnTypeAndCollation() means that result set
|
||||||
|
** expressions of the form "CAST(expr AS NUMERIC)" should result in
|
||||||
|
** NONE affinity rather than NUMERIC affinity.
|
||||||
|
*/
|
||||||
|
#define SQLITE_AFF_FLAG1 0x10
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Add type and collation information to a column list based on
|
** Add type and collation information to a column list based on
|
||||||
** a SELECT statement.
|
** a SELECT statement.
|
||||||
@@ -2299,12 +2307,17 @@ int sqlite3ColumnsFromExprList(
|
|||||||
**
|
**
|
||||||
** This routine requires that all identifiers in the SELECT
|
** This routine requires that all identifiers in the SELECT
|
||||||
** statement be resolved.
|
** statement be resolved.
|
||||||
|
**
|
||||||
|
** The SQLITE_AFF_FLAG1 bit added to parameter aff means that a
|
||||||
|
** result set column of the form "CAST(expr AS NUMERIC)" should use
|
||||||
|
** NONE affinity rather than NUMERIC affinity. See the
|
||||||
|
** 2022-12-10 "reopen" of ticket https://sqlite.org/src/tktview/57c47526c3.
|
||||||
*/
|
*/
|
||||||
void sqlite3SelectAddColumnTypeAndCollation(
|
void sqlite3SelectAddColumnTypeAndCollation(
|
||||||
Parse *pParse, /* Parsing contexts */
|
Parse *pParse, /* Parsing contexts */
|
||||||
Table *pTab, /* Add column type information to this table */
|
Table *pTab, /* Add column type information to this table */
|
||||||
Select *pSelect, /* SELECT used to determine types and collations */
|
Select *pSelect, /* SELECT used to determine types and collations */
|
||||||
char aff /* Default affinity for columns */
|
char aff /* Default affinity. Maybe with SQLITE_AFF_FLAG1 too */
|
||||||
){
|
){
|
||||||
sqlite3 *db = pParse->db;
|
sqlite3 *db = pParse->db;
|
||||||
NameContext sNC;
|
NameContext sNC;
|
||||||
@@ -2318,6 +2331,7 @@ void sqlite3SelectAddColumnTypeAndCollation(
|
|||||||
assert( (pSelect->selFlags & SF_Resolved)!=0 );
|
assert( (pSelect->selFlags & SF_Resolved)!=0 );
|
||||||
assert( pTab->nCol==pSelect->pEList->nExpr || db->mallocFailed );
|
assert( pTab->nCol==pSelect->pEList->nExpr || db->mallocFailed );
|
||||||
if( db->mallocFailed ) return;
|
if( db->mallocFailed ) return;
|
||||||
|
while( pSelect->pPrior ) pSelect = pSelect->pPrior;
|
||||||
memset(&sNC, 0, sizeof(sNC));
|
memset(&sNC, 0, sizeof(sNC));
|
||||||
sNC.pSrcList = pSelect->pSrc;
|
sNC.pSrcList = pSelect->pSrc;
|
||||||
a = pSelect->pEList->a;
|
a = pSelect->pEList->a;
|
||||||
@@ -2329,6 +2343,12 @@ void sqlite3SelectAddColumnTypeAndCollation(
|
|||||||
zType = columnType(&sNC, p, 0, 0, 0);
|
zType = columnType(&sNC, p, 0, 0, 0);
|
||||||
/* pCol->szEst = ... // Column size est for SELECT tables never used */
|
/* pCol->szEst = ... // Column size est for SELECT tables never used */
|
||||||
pCol->affinity = sqlite3ExprAffinity(p);
|
pCol->affinity = sqlite3ExprAffinity(p);
|
||||||
|
if( pCol->affinity==SQLITE_AFF_NUMERIC
|
||||||
|
&& p->op==TK_CAST
|
||||||
|
&& (aff & SQLITE_AFF_FLAG1)!=0
|
||||||
|
){
|
||||||
|
pCol->affinity = SQLITE_AFF_NONE;
|
||||||
|
}
|
||||||
if( zType ){
|
if( zType ){
|
||||||
m = sqlite3Strlen30(zType);
|
m = sqlite3Strlen30(zType);
|
||||||
n = sqlite3Strlen30(pCol->zCnName);
|
n = sqlite3Strlen30(pCol->zCnName);
|
||||||
@@ -2341,7 +2361,10 @@ void sqlite3SelectAddColumnTypeAndCollation(
|
|||||||
pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL);
|
pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if( pCol->affinity<=SQLITE_AFF_NONE ) pCol->affinity = aff;
|
if( pCol->affinity<=SQLITE_AFF_NONE ){
|
||||||
|
assert( (SQLITE_AFF_FLAG1 & SQLITE_AFF_MASK)==0 );
|
||||||
|
pCol->affinity = aff & SQLITE_AFF_MASK;
|
||||||
|
}
|
||||||
pColl = sqlite3ExprCollSeq(pParse, p);
|
pColl = sqlite3ExprCollSeq(pParse, p);
|
||||||
if( pColl ){
|
if( pColl ){
|
||||||
assert( pTab->pIndex==0 );
|
assert( pTab->pIndex==0 );
|
||||||
@@ -6215,7 +6238,7 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
|
|||||||
if( pSel ){
|
if( pSel ){
|
||||||
while( pSel->pPrior ) pSel = pSel->pPrior;
|
while( pSel->pPrior ) pSel = pSel->pPrior;
|
||||||
sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel,
|
sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel,
|
||||||
SQLITE_AFF_NONE);
|
SQLITE_AFF_NONE|SQLITE_AFF_FLAG1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3125,6 +3125,7 @@ static void display_scanstats(
|
|||||||
int iId = 0;
|
int iId = 0;
|
||||||
int iPid = 0;
|
int iPid = 0;
|
||||||
const char *z = 0;
|
const char *z = 0;
|
||||||
|
const char *zName = 0;
|
||||||
char *zText = 0;
|
char *zText = 0;
|
||||||
double rEst = 0.0;
|
double rEst = 0.0;
|
||||||
|
|
||||||
@@ -3137,6 +3138,7 @@ static void display_scanstats(
|
|||||||
sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NCYCLE,f,(void*)&nCycle);
|
sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NCYCLE,f,(void*)&nCycle);
|
||||||
sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_SELECTID,f,(void*)&iId);
|
sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_SELECTID,f,(void*)&iId);
|
||||||
sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_PARENTID,f,(void*)&iPid);
|
sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_PARENTID,f,(void*)&iPid);
|
||||||
|
sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NAME,f,(void*)&zName);
|
||||||
|
|
||||||
zText = sqlite3_mprintf("%s", z);
|
zText = sqlite3_mprintf("%s", z);
|
||||||
if( nCycle>=0 || nLoop>=0 || nRow>=0 ){
|
if( nCycle>=0 || nLoop>=0 || nRow>=0 ){
|
||||||
@@ -3153,6 +3155,11 @@ static void display_scanstats(
|
|||||||
z = sqlite3_mprintf("%z%srows=%lld", z, z ? " " : "", nRow);
|
z = sqlite3_mprintf("%z%srows=%lld", z, z ? " " : "", nRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( zName && pArg->scanstatsOn>1 ){
|
||||||
|
double rpl = (double)nRow / (double)nLoop;
|
||||||
|
z = sqlite3_mprintf("%z rpl=%.1f est=%.1f", z, rpl, rEst);
|
||||||
|
}
|
||||||
|
|
||||||
zText = sqlite3_mprintf(
|
zText = sqlite3_mprintf(
|
||||||
"% *z (%z)", -1*(nWidth-scanStatsHeight(p, ii)*3), zText, z
|
"% *z (%z)", -1*(nWidth-scanStatsHeight(p, ii)*3), zText, z
|
||||||
);
|
);
|
||||||
@@ -4678,7 +4685,7 @@ static const char *(azHelp[]) = {
|
|||||||
".restore ?DB? FILE Restore content of DB (default \"main\") from FILE",
|
".restore ?DB? FILE Restore content of DB (default \"main\") from FILE",
|
||||||
".save ?OPTIONS? FILE Write database to FILE (an alias for .backup ...)",
|
".save ?OPTIONS? FILE Write database to FILE (an alias for .backup ...)",
|
||||||
#endif
|
#endif
|
||||||
".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off",
|
".scanstats on|off|est Turn sqlite3_stmt_scanstatus() metrics on or off",
|
||||||
".schema ?PATTERN? Show the CREATE statements matching PATTERN",
|
".schema ?PATTERN? Show the CREATE statements matching PATTERN",
|
||||||
" Options:",
|
" Options:",
|
||||||
" --indent Try to pretty-print the schema",
|
" --indent Try to pretty-print the schema",
|
||||||
@@ -9707,12 +9714,16 @@ static int do_meta_command(char *zLine, ShellState *p){
|
|||||||
|
|
||||||
if( c=='s' && cli_strncmp(azArg[0], "scanstats", n)==0 ){
|
if( c=='s' && cli_strncmp(azArg[0], "scanstats", n)==0 ){
|
||||||
if( nArg==2 ){
|
if( nArg==2 ){
|
||||||
p->scanstatsOn = (u8)booleanValue(azArg[1]);
|
if( cli_strcmp(azArg[1], "est")==0 ){
|
||||||
|
p->scanstatsOn = 2;
|
||||||
|
}else{
|
||||||
|
p->scanstatsOn = (u8)booleanValue(azArg[1]);
|
||||||
|
}
|
||||||
#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
|
#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||||
raw_printf(stderr, "Warning: .scanstats not available in this build.\n");
|
raw_printf(stderr, "Warning: .scanstats not available in this build.\n");
|
||||||
#endif
|
#endif
|
||||||
}else{
|
}else{
|
||||||
raw_printf(stderr, "Usage: .scanstats on|off\n");
|
raw_printf(stderr, "Usage: .scanstats on|off|est\n");
|
||||||
rc = 1;
|
rc = 1;
|
||||||
}
|
}
|
||||||
}else
|
}else
|
||||||
|
|||||||
@@ -563,6 +563,7 @@ int sqlite3_exec(
|
|||||||
#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8))
|
#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8))
|
||||||
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
|
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
|
||||||
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
|
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
|
||||||
|
#define SQLITE_NOTICE_RBU (SQLITE_NOTICE | (3<<8))
|
||||||
#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
|
#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
|
||||||
#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
|
#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
|
||||||
#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8))
|
#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8))
|
||||||
@@ -2184,7 +2185,7 @@ struct sqlite3_mem_methods {
|
|||||||
** configuration for a database connection can only be changed when that
|
** configuration for a database connection can only be changed when that
|
||||||
** connection is not currently using lookaside memory, or in other words
|
** connection is not currently using lookaside memory, or in other words
|
||||||
** when the "current value" returned by
|
** when the "current value" returned by
|
||||||
** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero.
|
** [sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero.
|
||||||
** Any attempt to change the lookaside memory configuration when lookaside
|
** Any attempt to change the lookaside memory configuration when lookaside
|
||||||
** memory is in use leaves the configuration unchanged and returns
|
** memory is in use leaves the configuration unchanged and returns
|
||||||
** [SQLITE_BUSY].)^</dd>
|
** [SQLITE_BUSY].)^</dd>
|
||||||
@@ -2334,8 +2335,12 @@ struct sqlite3_mem_methods {
|
|||||||
** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0);
|
** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0);
|
||||||
** </ol>
|
** </ol>
|
||||||
** Because resetting a database is destructive and irreversible, the
|
** Because resetting a database is destructive and irreversible, the
|
||||||
** process requires the use of this obscure API and multiple steps to help
|
** process requires the use of this obscure API and multiple steps to
|
||||||
** ensure that it does not happen by accident.
|
** help ensure that it does not happen by accident. Because this
|
||||||
|
** feature must be capable of resetting corrupt databases, and
|
||||||
|
** shutting down virtual tables may require access to that corrupt
|
||||||
|
** storage, the library must abandon any installed virtual tables
|
||||||
|
** without calling their xDestroy() methods.
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBCONFIG_DEFENSIVE]] <dt>SQLITE_DBCONFIG_DEFENSIVE</dt>
|
** [[SQLITE_DBCONFIG_DEFENSIVE]] <dt>SQLITE_DBCONFIG_DEFENSIVE</dt>
|
||||||
** <dd>The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the
|
** <dd>The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the
|
||||||
|
|||||||
@@ -483,5 +483,36 @@ do_execsql_test cast-9.0 {
|
|||||||
SELECT v1.c0 FROM v1, t0 WHERE v1.c0=0;
|
SELECT v1.c0 FROM v1, t0 WHERE v1.c0=0;
|
||||||
} {0.0}
|
} {0.0}
|
||||||
|
|
||||||
|
# Set the 2022-12-10 "reopen" of ticket [https://sqlite.org/src/tktview/57c47526c3]
|
||||||
|
#
|
||||||
|
do_execsql_test cast-9.1 {
|
||||||
|
CREATE TABLE dual(dummy TEXT);
|
||||||
|
INSERT INTO dual VALUES('X');
|
||||||
|
SELECT CAST(4 AS NUMERIC);
|
||||||
|
} {4}
|
||||||
|
do_execsql_test cast-9.2 {
|
||||||
|
SELECT CAST(4.0 AS NUMERIC);
|
||||||
|
} {4.0}
|
||||||
|
do_execsql_test cast-9.3 {
|
||||||
|
SELECT CAST(4.5 AS NUMERIC);
|
||||||
|
} {4.5}
|
||||||
|
do_execsql_test cast-9.4 {
|
||||||
|
SELECT x, typeof(x) FROM (SELECT CAST(4 AS NUMERIC) AS x) JOIN dual;
|
||||||
|
} {4 integer}
|
||||||
|
do_execsql_test cast-9.5 {
|
||||||
|
SELECT x, typeof(x) FROM dual CROSS JOIN (SELECT CAST(4 AS NUMERIC) AS x);
|
||||||
|
} {4 integer}
|
||||||
|
do_execsql_test cast-9.10 {
|
||||||
|
SELECT x, typeof(x) FROM (SELECT CAST(4.0 AS NUMERIC) AS x) JOIN dual;
|
||||||
|
} {4.0 real}
|
||||||
|
do_execsql_test cast-9.11 {
|
||||||
|
SELECT x, typeof(x) FROM dual CROSS JOIN (SELECT CAST(4.0 AS NUMERIC) AS x);
|
||||||
|
} {4.0 real}
|
||||||
|
do_execsql_test cast-9.12 {
|
||||||
|
SELECT x, typeof(x) FROM (SELECT CAST(4.5 AS NUMERIC) AS x) JOIN dual;
|
||||||
|
} {4.5 real}
|
||||||
|
do_execsql_test cast-9.13 {
|
||||||
|
SELECT x, typeof(x) FROM dual CROSS JOIN (SELECT CAST(4.5 AS NUMERIC) AS x);
|
||||||
|
} {4.5 real}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|||||||
@@ -775,5 +775,27 @@ do_catchsql_test view-29.1 {
|
|||||||
SELECT name FROM sqlite_schema ORDER BY name;
|
SELECT name FROM sqlite_schema ORDER BY name;
|
||||||
} {0 {t1 t2}}
|
} {0 {t1 t2}}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# 2022-12-11. https://sqlite.org/src/info/679ed6a2
|
||||||
|
#
|
||||||
|
reset_db
|
||||||
|
do_execsql_test view-30.0 {
|
||||||
|
CREATE TABLE t0(a INT, b TEXT);
|
||||||
|
|
||||||
|
INSERT INTO t0 VALUES(1,'one');
|
||||||
|
|
||||||
|
CREATE VIEW t1 AS SELECT a, b FROM t0 UNION ALL SELECT 2, 2;
|
||||||
|
CREATE VIEW t2(a,b) AS SELECT a, b FROM t0 UNION ALL SELECT 2, 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ifcapable schema_pragmas {
|
||||||
|
do_execsql_test view-30.1 {
|
||||||
|
PRAGMA table_info = t1;
|
||||||
|
} { 0 a INT 0 {} 0 1 b TEXT 0 {} 0 }
|
||||||
|
do_execsql_test view-30.2 {
|
||||||
|
PRAGMA table_info = t2;
|
||||||
|
} { 0 a INT 0 {} 0 1 b TEXT 0 {} 0 }
|
||||||
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user