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

Move the rest of testing1.js into tester1.js and eliminate the dependency on jaccwabyt_test.c. Extend the list of default config-related #defines in sqlite3-wasm.c and reorganize them for maintainability.

FossilOrigin-Name: 4e2a8aff2dd4b6e148f45184e2523ebe47815257eca97fa3d32bcbf9625f0def
This commit is contained in:
stephan
2022-10-13 16:48:35 +00:00
parent 921acff927
commit d92c652ac1
14 changed files with 938 additions and 1484 deletions

View File

@ -68,14 +68,10 @@ SQLITE_OPT = \
-DSQLITE_TEMP_STORE=3 \
-DSQLITE_OS_KV_OPTIONAL=1 \
'-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' \
-DSQLITE_USE_URI=1
#SQLITE_OPT += -DSQLITE_ENABLE_MEMSYS5
# ^^^ MEMSYS5 is hypothetically useful for non-Emscripten builds but
# requires adding more infrastructure and fixing one spot in the
# sqlite3 internals which calls malloc() early on.
# SQLITE_OMIT_LOAD_EXTENSION: if this is true, sqlite3_vfs::xDlOpen
# and friends may be NULL.
-DSQLITE_USE_URI=1 \
-DSQLITE_WASM_ENABLE_C_TESTS
# ^^^ most flags are set in sqlite3-wasm.c but we need them
# made explicit here for building speedtest1.c.
ifneq (,$(filter release,$(MAKECMDGOALS)))
emcc_opt ?= -Oz -flto
@ -128,8 +124,7 @@ else
$(info Development build. Use '$(MAKE) release' for a smaller release build.)
endif
EXPORTED_FUNCTIONS.api.in := $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api \
$(dir.jacc)/jaccwabyt_test.exports
EXPORTED_FUNCTIONS.api.in := $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api
EXPORTED_FUNCTIONS.api: $(EXPORTED_FUNCTIONS.api.in) $(MAKEFILE)
cat $(EXPORTED_FUNCTIONS.api.in) > $@
@ -294,12 +289,6 @@ sqlite3-wasm.o := $(dir.api)/sqlite3-wasm.o
$(sqlite3-wasm.o): emcc.cflags += $(SQLITE_OPT)
$(sqlite3-wasm.o): $(dir.top)/sqlite3.c
sqlite3-wasm.c := $(dir.api)/sqlite3-wasm.c
jaccwabyt_test.c := $(dir.jacc)/jaccwabyt_test.c
# ^^^ FIXME (how?): jaccwabyt_test.c is only needed for the test apps,
# so we don't really want to include it in release builds. However, we
# want to test the release builds with those apps, so we cannot simply
# elide that file in release builds. That component is critical to the
# VFS bindings so needs to be tested along with the core APIs.
########################################################################
# call-wasm-c-compile sets up build rules
# for $1.o. $1 must be the name of a C file (with extension).
@ -310,7 +299,7 @@ $$($(1).o): $$(MAKEFILE) $(1)
$$(emcc.bin) $$(emcc_opt_full) $$(emcc.flags) $$(emcc.cflags) -c $(1) -o $$@
CLEAN_FILES += $$($(1).o)
endef
$(foreach c,$(sqlite3-wasm.c) $(jaccwabyt_test.c),$(eval $(call call-wasm-c-compile,$(c))))
$(foreach c,$(sqlite3-wasm.c),$(eval $(call call-wasm-c-compile,$(c))))
$(eval $(call call-make-pre-js,sqlite3))
$(sqlite3.js): $(MAKEFILE) $(sqlite3.wasm.obj) \
EXPORTED_FUNCTIONS.api \
@ -401,7 +390,7 @@ speedtest1.wasm := $(subst .js,.wasm,$(speedtest1.js))
speedtest1.cflags := \
-I. -I.. -I$(dir.top) \
-DSQLITE_SPEEDTEST1_WASM
speedtest1.cs := $(speedtest1.c) $(sqlite3-wasm.c) $(jaccwabyt_test.c)
speedtest1.cs := $(speedtest1.c) $(sqlite3-wasm.c)
$(speedtest1.js): emcc.cflags+=
# speedtest1 notes re. sqlite3-wasm.o vs sqlite3-wasm.c: building against
# the latter (predictably) results in a slightly faster binary, but we're

View File

@ -592,9 +592,15 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
*/
capi.sqlite3_web_rc_str = (rc)=>__rcMap[rc];
/* Bind all registered C-side structs... */
const notThese = Object.assign(Object.create(null),{
// Structs NOT to register
WasmTestStruct: true
});
for(const s of wasm.ctype.structs){
if(!notThese[s.name]){
capi[s.name] = sqlite3.StructBinder(s);
}
}
}/*end C constant imports*/
if( util.isMainWindow()

View File

@ -806,12 +806,16 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
- (optionsObject)
In the final two cases, the function must be defined as the
'callback' property of the options object. In the final
`callback` property of the options object (optionally called
`xFunc` to align with the C API documentation). In the final
case, the function's name must be the 'name' property.
This can only be used to create scalar functions, not
aggregate or window functions. UDFs cannot be removed from
a DB handle after they're added.
This can currently only be used to create scalar functions, not
aggregate or window functions (requires only a bit of
refactoring to support aggregates and window functions).
UDFs cannot currently be removed from a DB handle after they're
added.
On success, returns this object. Throws on error.
@ -848,18 +852,22 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
- .deterministic = SQLITE_DETERMINISTIC
- .directOnly = SQLITE_DIRECTONLY
- .innocuous = SQLITE_INNOCUOUS
TODO: for the (optionsObject) form, accept callbacks for
aggregate and window functions.
*/
createFunction: function f(name, callback,opt){
createFunction: function f(name, callback, opt){
switch(arguments.length){
case 1: /* (optionsObject) */
opt = name;
name = opt.name;
callback = opt.callback;
callback = opt.xFunc || opt.callback;
break;
case 2: /* (name, callback|optionsObject) */
if(!(callback instanceof Function)){
opt = callback;
callback = opt.callback;
callback = opt.xFunc || opt.callback;
}
break;
default: break;

View File

@ -949,7 +949,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
return rc;
},
/**
A convenience wrapper for allocChunks() which sizes each chunks
A convenience wrapper for allocChunks() which sizes each chunk
as either 8 bytes (safePtrSize is truthy) or wasm.ptrSizeof (if
safePtrSize is falsy).

View File

@ -14,6 +14,16 @@
*/
#define SQLITE_WASM
#ifdef SQLITE_WASM_ENABLE_C_TESTS
/*
** Functions blocked off by SQLITE_WASM_TESTS are intended solely for
** use in unit/regression testing. They may be safely omitted from
** client-side builds.
*/
# define SQLITE_WASM_TESTS 1
#else
# define SQLITE_WASM_TESTS 0
#endif
/*
** Threading and file locking: JS is single-threaded. Each Worker
@ -32,34 +42,20 @@
** locking, and any similar future filesystems, threading and file
** locking support are unnecessary in the wasm build.
*/
/*
** Undefine any SQLITE_... config flags which we specifically do not
** want undefined. Please keep these alphabetized.
*/
#undef SQLITE_OMIT_DESERIALIZE
#ifndef SQLITE_DEFAULT_UNIX_VFS
# define SQLITE_DEFAULT_UNIX_VFS "unix-none"
#endif
#ifndef SQLITE_OMIT_DEPRECATED
# define SQLITE_OMIT_DEPRECATED
#endif
#ifndef SQLITE_OMIT_LOAD_EXTENSION
# define SQLITE_OMIT_LOAD_EXTENSION
#endif
#ifndef SQLITE_OMIT_SHARED_CACHE
# define SQLITE_OMIT_SHARED_CACHE
#endif
#ifndef SQLITE_OMIT_UTF16
# define SQLITE_OMIT_UTF16
#endif
#ifndef SQLITE_OS_KV_OPTIONAL
# define SQLITE_OS_KV_OPTIONAL 1
#endif
#ifndef SQLITE_TEMP_STORE
# define SQLITE_TEMP_STORE 3
#endif
#ifndef SQLITE_THREADSAFE
# define SQLITE_THREADSAFE 0
#endif
#ifndef SQLITE_OMIT_WAL
# define SQLITE_OMIT_WAL
#endif
/*
** Define any SQLITE_... config defaults we want if they aren't
** overridden by the builder. Please keep these alphabetized.
*/
/**********************************************************************/
/* SQLITE_DEFAULT_... */
#ifndef SQLITE_DEFAULT_CACHE_SIZE
/*
** The OPFS impls benefit tremendously from an increased cache size
@ -71,14 +67,78 @@
*/
# define SQLITE_DEFAULT_CACHE_SIZE -16777216
#endif
#if 0
/*
** TODO: experiment with this when back on the opfs-capable machine.
*/
#ifndef SQLITE_DEFAULT_PAGE_SIZE
#if 0 && !defined(SQLITE_DEFAULT_PAGE_SIZE)
/* TODO: experiment with this. */
# define SQLITE_DEFAULT_PAGE_SIZE 8192 /*4096*/
#endif
#ifndef SQLITE_DEFAULT_UNIX_VFS
# define SQLITE_DEFAULT_UNIX_VFS "unix-none"
#endif
/**********************************************************************/
/* SQLITE_ENABLE_... */
#ifndef SQLITE_ENABLE_BYTECODE_VTAB
# define SQLITE_ENABLE_BYTECODE_VTAB 1
#endif
#ifndef SQLITE_ENABLE_DBPAGE_VTAB
# define SQLITE_ENABLE_DBPAGE_VTAB 1
#endif
#ifndef SQLITE_ENABLE_DBSTAT_VTAB
# define SQLITE_ENABLE_DBSTAT_VTAB 1
#endif
#ifndef SQLITE_ENABLE_EXPLAIN_COMMENTS
# define SQLITE_ENABLE_EXPLAIN_COMMENTS 1
#endif
#ifndef SQLITE_ENABLE_FTS4
# define SQLITE_ENABLE_FTS4 1
#endif
#ifndef SQLITE_ENABLE_OFFSET_SQL_FUNC
# define SQLITE_ENABLE_OFFSET_SQL_FUNC 1
#endif
#ifndef SQLITE_ENABLE_RTREE
# define SQLITE_ENABLE_RTREE 1
#endif
#ifndef SQLITE_ENABLE_STMTVTAB
# define SQLITE_ENABLE_STMTVTAB 1
#endif
#ifndef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
# define SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
#endif
/**********************************************************************/
/* SQLITE_O... */
#ifndef SQLITE_OMIT_DEPRECATED
# define SQLITE_OMIT_DEPRECATED 1
#endif
#ifndef SQLITE_OMIT_LOAD_EXTENSION
# define SQLITE_OMIT_LOAD_EXTENSION 1
#endif
#ifndef SQLITE_OMIT_SHARED_CACHE
# define SQLITE_OMIT_SHARED_CACHE 1
#endif
#ifndef SQLITE_OMIT_UTF16
# define SQLITE_OMIT_UTF16 1
#endif
#ifndef SQLITE_OMIT_WAL
# define SQLITE_OMIT_WAL 1
#endif
#ifndef SQLITE_OS_KV_OPTIONAL
# define SQLITE_OS_KV_OPTIONAL 1
#endif
/**********************************************************************/
/* SQLITE_T... */
#ifndef SQLITE_TEMP_STORE
# define SQLITE_TEMP_STORE 3
#endif
#ifndef SQLITE_THREADSAFE
# define SQLITE_THREADSAFE 0
#endif
/**********************************************************************/
/* SQLITE_USE_... */
#ifndef SQLITE_USE_URI
# define SQLITE_USE_URI 1
#endif
#include <assert.h>
@ -89,11 +149,11 @@
#endif
/*
** SQLITE_WASM_KEEP is identical to EMSCRIPTEN_KEEPALIVE but is not
** Emscripten-specific. It explicitly marks functions for export into
** the target wasm file without requiring explicit listing of those
** functions in Emscripten's -sEXPORTED_FUNCTIONS=... list (or
** equivalent in other build platforms). Any function with neither
** SQLITE_WASM_KEEP is functionally identical to EMSCRIPTEN_KEEPALIVE
** but is not Emscripten-specific. It explicitly marks functions for
** export into the target wasm file without requiring explicit listing
** of those functions in Emscripten's -sEXPORTED_FUNCTIONS=... list
** (or equivalent in other build platforms). Any function with neither
** this attribute nor which is listed as an explicit export will not
** be exported from the wasm file (but may still be used internally
** within the wasm file).
@ -256,6 +316,28 @@ int sqlite3_wasm_db_error(sqlite3*db, int err_code, const char *zMsg){
return err_code;
}
#if SQLITE_WASM_TESTS
struct WasmTestStruct {
int v4;
void * ppV;
const char * cstr;
int64_t v8;
void (*xFunc)(void*);
};
typedef struct WasmTestStruct WasmTestStruct;
SQLITE_WASM_KEEP
void sqlite3_wasm_test_struct(WasmTestStruct * s){
if(s){
s->v4 *= 2;
s->v8 = s->v4 * 2;
s->ppV = s;
s->cstr = __FILE__;
if(s->xFunc) s->xFunc(s);
}
return;
}
#endif /* SQLITE_WASM_TESTS */
/*
** This function is NOT part of the sqlite3 public API. It is strictly
** for use by the sqlite project's own JS/WASM bindings. Unlike the
@ -692,6 +774,19 @@ const char * sqlite3_wasm_enum_json(void){
M(xDelete,"i(ss)");
M(nKeySize,"i");
} _StructBinder;
#undef CurrentStruct
#if SQLITE_WASM_TESTS
#define CurrentStruct WasmTestStruct
StructBinder {
M(v4,"i");
M(cstr,"s");
M(ppV,"p");
M(v8,"j");
M(xFunc,"v(p)");
} _StructBinder;
#undef CurrentStruct
#endif
} out( "]"/*structs*/);
@ -926,4 +1021,56 @@ int sqlite3_wasm_init_wasmfs(const char *zUnused){
}
#endif /* __EMSCRIPTEN__ && SQLITE_WASM_WASMFS */
#if SQLITE_WASM_TESTS
SQLITE_WASM_KEEP
int sqlite3_wasm_test_intptr(int * p){
return *p = *p * 2;
}
SQLITE_WASM_KEEP
int64_t sqlite3_wasm_test_int64_max(void){
return (int64_t)0x7fffffffffffffff;
}
SQLITE_WASM_KEEP
int64_t sqlite3_wasm_test_int64_min(void){
return ~sqlite3_wasm_test_int64_max();
}
SQLITE_WASM_KEEP
int64_t sqlite3_wasm_test_int64_times2(int64_t x){
return x * 2;
}
SQLITE_WASM_KEEP
void sqlite3_wasm_test_int64_minmax(int64_t * min, int64_t *max){
*max = sqlite3_wasm_test_int64_max();
*min = sqlite3_wasm_test_int64_min();
/*printf("minmax: min=%lld, max=%lld\n", *min, *max);*/
}
SQLITE_WASM_KEEP
int64_t sqlite3_wasm_test_int64ptr(int64_t * p){
/*printf("sqlite3_wasm_test_int64ptr( @%lld = 0x%llx )\n", (int64_t)p, *p);*/
return *p = *p * 2;
}
SQLITE_WASM_KEEP
void sqlite3_wasm_test_stack_overflow(int recurse){
if(recurse) sqlite3_wasm_test_stack_overflow(recurse);
}
/* For testing the 'string-free' whwasmutil.xWrap() conversion. */
SQLITE_WASM_KEEP
char * sqlite3_wasm_test_str_hello(int fail){
char * s = fail ? 0 : (char *)malloc(6);
if(s){
memcpy(s, "hello", 5);
s[5] = 0;
}
return s;
}
#endif /* SQLITE_WASM_TESTS */
#undef SQLITE_WASM_KEEP

View File

@ -2,7 +2,6 @@ body {
display: flex;
flex-direction: column;
flex-wrap: wrap;
white-space: break-spaces;
}
textarea {
font-family: monospace;

View File

@ -1255,12 +1255,13 @@ self.WhWasmUtilInstaller = function(target){
Would that be too much magic concentrated in one place, ready to
backfire?
*/
xcv.arg.string = xcv.arg['pointer'] = xcv.arg['*'] = function(v){
xcv.arg.string = xcv.arg.utf8 = xcv.arg['pointer'] = xcv.arg['*']
= function(v){
if('string'===typeof v) return target.scopedAllocCString(v);
return v ? xcv.arg[ptrIR](v) : null;
};
xcv.result.string = (i)=>target.cstringToJs(i);
xcv.result['string:free'] = (i)=>{
xcv.result.string = xcv.result.utf8 = (i)=>target.cstringToJs(i);
xcv.result['string:free'] = xcv.result['utf8:free'] = (i)=>{
try { return i ? target.cstringToJs(i) : null }
finally{ target.dealloc(i) }
};
@ -1374,31 +1375,33 @@ self.WhWasmUtilInstaller = function(target){
Non-numeric conversions include:
- `string` (args): has two different semantics in order to
accommodate various uses of certain C APIs (e.g. output-style
strings)...
- `string` or `utf8` (args): has two different semantics in order
to accommodate various uses of certain C APIs
(e.g. output-style strings)...
- If the arg is a string, it creates a _temporary_ C-string to
pass to the exported function, cleaning it up before the
wrapper returns. If a long-lived C-string pointer is
required, that requires client-side code to create the
string, then pass its pointer to the function.
- If the arg is a string, it creates a _temporary_
UTF-8-encoded C-string to pass to the exported function,
cleaning it up before the wrapper returns. If a long-lived
C-string pointer is required, that requires client-side code
to create the string, then pass its pointer to the function.
- Else the arg is assumed to be a pointer to a string the
client has already allocated and it's passed on as
a WASM pointer.
- `string` (results): treats the result value as a const C-string,
copies it to a JS string, and returns that JS string.
- `string` or `utf8` (results): treats the result value as a
const C-string, encoded as UTF-8, copies it to a JS string,
and returns that JS string.
- `string:free` (results): treats the result value as a non-const
C-string, ownership of which has just been transfered to the
caller. It copies the C-string to a JS string, frees the
C-string, and returns the JS string. If such a result value is
NULL, the JS result is `null`. Achtung: when using an API which
returns results from a specific allocator, e.g. `my_malloc()`,
this conversion _is not legal_. Instead, an equivalent conversion
which uses the appropriate deallocator is required. For example:
- `string:free` or `utf8:free) (results): treats the result value
as a non-const UTF-8 C-string, ownership of which has just been
transfered to the caller. It copies the C-string to a JS
string, frees the C-string, and returns the JS string. If such
a result value is NULL, the JS result is `null`. Achtung: when
using an API which returns results from a specific allocator,
e.g. `my_malloc()`, this conversion _is not legal_. Instead, an
equivalent conversion which uses the appropriate deallocator is
required. For example:
```js
target.xWrap.resultAdaptor('string:my_free',(i)=>{

View File

@ -41,6 +41,15 @@
</div>
<div>The tests and demos...
<ul id='test-list'>
<li>Core-most tests
<ul>
<li><a href='tester1.html'>tester1</a>: Core unit and
regression tests for the various APIs and surrounding
utility code.</li>
<li><a href='tester1.html'>tester1-worker</a>: same thing
but running in a Worker.</li>
</ul>
</li>
<li>High-level apps and demos...
<ul>
<li><a href='fiddle/fiddle.html'>fiddle</a> is an HTML front-end
@ -68,7 +77,6 @@
</li>
<li>The obligatory "misc." category...
<ul>
<li><a href='testing1.html'>testing1</a>: sanity tests of the core APIs and surrounding utility code.</li>
<li><a href='testing2.html'>testing2</a>: Worker-based test of OO API #1.</li>
<li><a href='testing-worker1-promiser.html'>testing-worker1-promiser</a>:
tests for the Promise-based wrapper of the Worker-based API.</li>

View File

@ -1,178 +0,0 @@
#include <assert.h>
#include <string.h> /* memset() */
#include <stddef.h> /* offsetof() */
#include <stdio.h> /* snprintf() */
#include <stdint.h> /* int64_t */
/*#include <stdlib.h>*/ /* malloc/free(), needed for emscripten exports. */
extern void * malloc(size_t);
extern void free(void *);
/*
** 2022-06-25
**
** 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.
**
***********************************************************************
**
** Utility functions for use with the emscripten/WASM bits. These
** functions ARE NOT part of the sqlite3 public API. They are strictly
** for internal use by the JS/WASM bindings.
**
** This file is intended to be WASM-compiled together with sqlite3.c,
** e.g.:
**
** emcc ... sqlite3.c wasm_util.c
*/
/*
** Experimenting with output parameters.
*/
int jaccwabyt_test_intptr(int * p){
if(1==((int)p)%3){
/* kludge to get emscripten to export malloc() and free() */;
free(malloc(0));
}
return *p = *p * 2;
}
int64_t jaccwabyt_test_int64_max(void){
return (int64_t)0x7fffffffffffffff;
}
int64_t jaccwabyt_test_int64_min(void){
return ~jaccwabyt_test_int64_max();
}
int64_t jaccwabyt_test_int64_times2(int64_t x){
return x * 2;
}
void jaccwabyt_test_int64_minmax(int64_t * min, int64_t *max){
*max = jaccwabyt_test_int64_max();
*min = jaccwabyt_test_int64_min();
/*printf("minmax: min=%lld, max=%lld\n", *min, *max);*/
}
int64_t jaccwabyt_test_int64ptr(int64_t * p){
/*printf("jaccwabyt_test_int64ptr( @%lld = 0x%llx )\n", (int64_t)p, *p);*/
return *p = *p * 2;
}
void jaccwabyt_test_stack_overflow(int recurse){
if(recurse) jaccwabyt_test_stack_overflow(recurse);
}
struct WasmTestStruct {
int v4;
void * ppV;
const char * cstr;
int64_t v8;
void (*xFunc)(void*);
};
typedef struct WasmTestStruct WasmTestStruct;
void jaccwabyt_test_struct(WasmTestStruct * s){
if(s){
s->v4 *= 2;
s->v8 = s->v4 * 2;
s->ppV = s;
s->cstr = __FILE__;
if(s->xFunc) s->xFunc(s);
}
return;
}
/** For testing the 'string-free' whwasmutil.xWrap() conversion. */
char * jaccwabyt_test_str_hello(int fail){
char * s = fail ? 0 : (char *)malloc(6);
if(s){
memcpy(s, "hello", 5);
s[5] = 0;
}
return s;
}
/*
** Returns a NUL-terminated string containing a JSON-format metadata
** regarding C structs, for use with the StructBinder API. The
** returned memory is static and is only written to the first time
** this is called.
*/
const char * jaccwabyt_test_ctype_json(void){
static char strBuf[1024 * 8] = {0};
int n = 0, structCount = 0, groupCount = 0;
char * pos = &strBuf[1] /* skip first byte for now to help protect
against a small race condition */;
char const * const zEnd = pos + sizeof(strBuf);
if(strBuf[0]) return strBuf;
/* Leave first strBuf[0] at 0 until the end to help guard against a
tiny race condition. If this is called twice concurrently, they
might end up both writing to strBuf, but they'll both write the
same thing, so that's okay. If we set byte 0 up front then the
2nd instance might return a partially-populated string. */
////////////////////////////////////////////////////////////////////
// First we need to build up our macro framework...
////////////////////////////////////////////////////////////////////
// Core output macros...
#define lenCheck assert(pos < zEnd - 100)
#define outf(format,...) \
pos += snprintf(pos, ((size_t)(zEnd - pos)), format, __VA_ARGS__); \
lenCheck
#define out(TXT) outf("%s",TXT)
#define CloseBrace(LEVEL) \
assert(LEVEL<5); memset(pos, '}', LEVEL); pos+=LEVEL; lenCheck
////////////////////////////////////////////////////////////////////
// Macros for emitting StructBinder descriptions...
#define StructBinder__(TYPE) \
n = 0; \
outf("%s{", (structCount++ ? ", " : "")); \
out("\"name\": \"" # TYPE "\","); \
outf("\"sizeof\": %d", (int)sizeof(TYPE)); \
out(",\"members\": {");
#define StructBinder_(T) StructBinder__(T)
// ^^^ indirection needed to expand CurrentStruct
#define StructBinder StructBinder_(CurrentStruct)
#define _StructBinder CloseBrace(2)
#define M(MEMBER,SIG) \
outf("%s\"%s\": " \
"{\"offset\":%d,\"sizeof\": %d,\"signature\":\"%s\"}", \
(n++ ? ", " : ""), #MEMBER, \
(int)offsetof(CurrentStruct,MEMBER), \
(int)sizeof(((CurrentStruct*)0)->MEMBER), \
SIG)
// End of macros
////////////////////////////////////////////////////////////////////
out("\"structs\": ["); {
#define CurrentStruct WasmTestStruct
StructBinder {
M(v4,"i");
M(cstr,"s");
M(ppV,"p");
M(v8,"j");
M(xFunc,"v(p)");
} _StructBinder;
#undef CurrentStruct
} out( "]"/*structs*/);
out("}"/*top-level object*/);
*pos = 0;
strBuf[0] = '{'/*end of the race-condition workaround*/;
return strBuf;
#undef DefGroup
#undef Def
#undef _DefGroup
#undef StructBinder
#undef StructBinder_
#undef StructBinder__
#undef M
#undef _StructBinder
#undef CurrentStruct
#undef CloseBrace
#undef out
#undef outf
#undef lenCheck
}

View File

@ -1,10 +0,0 @@
_jaccwabyt_test_intptr
_jaccwabyt_test_int64ptr
_jaccwabyt_test_int64_max
_jaccwabyt_test_int64_min
_jaccwabyt_test_int64_minmax
_jaccwabyt_test_int64_times2
_jaccwabyt_test_struct
_jaccwabyt_test_ctype_json
_jaccwabyt_test_stack_overflow
_jaccwabyt_test_str_hello

View File

@ -43,8 +43,8 @@
const isWorker = ()=>!isUIThread();
/* Predicate for tests/groups. */
const testIsTodo = ()=>false;
const haveJaccwabytTests = function(){
return !!wasm.exports.jaccwabyt_test_int64_max;
const haveWasmCTests = function(){
return !!wasm.exports.sqlite3_wasm_test_int64_max;
};
const mapToString = (v)=>{
switch(typeof v){
@ -283,6 +283,7 @@
// End of infrastructure setup. Now define the tests...
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
T.g('Basic sanity checks')
.t('Namespace object checks', function(sqlite3){
const wasmCtypes = wasm.ctype;
@ -310,13 +311,656 @@
.assert(e instanceof sqlite3.WasmAllocError);
}
})
////////////////////////////////////////////////////////////////////
.t('strglob/strlike', function(sqlite3){
T.assert(0===capi.sqlite3_strglob("*.txt", "foo.txt")).
assert(0!==capi.sqlite3_strglob("*.txt", "foo.xtx")).
assert(0===capi.sqlite3_strlike("%.txt", "foo.txt", 0)).
assert(0!==capi.sqlite3_strlike("%.txt", "foo.xtx", 0));
})
////////////////////////////////////////////////////////////////////
;/*end of basic sanity checks*/
////////////////////////////////////////////////////////////////////
T.g('C/WASM Utilities')
.t('sqlite3.capi.wasm', function(sqlite3){
const w = wasm;
const chr = (x)=>x.charCodeAt(0);
//log("heap getters...");
{
const li = [8, 16, 32];
if(w.bigIntEnabled) li.push(64);
for(const n of li){
const bpe = n/8;
const s = w.heapForSize(n,false);
T.assert(bpe===s.BYTES_PER_ELEMENT).
assert(w.heapForSize(s.constructor) === s);
const u = w.heapForSize(n,true);
T.assert(bpe===u.BYTES_PER_ELEMENT).
assert(s!==u).
assert(w.heapForSize(u.constructor) === u);
}
}
//log("jstrlen()...");
{
T.assert(3 === w.jstrlen("abc")).assert(4 === w.jstrlen("äbc"));
}
//log("jstrcpy()...");
{
const fillChar = 10;
let ua = new Uint8Array(8), rc,
refill = ()=>ua.fill(fillChar);
refill();
rc = w.jstrcpy("hello", ua);
T.assert(6===rc).assert(0===ua[5]).assert(chr('o')===ua[4]);
refill();
ua[5] = chr('!');
rc = w.jstrcpy("HELLO", ua, 0, -1, false);
T.assert(5===rc).assert(chr('!')===ua[5]).assert(chr('O')===ua[4]);
refill();
rc = w.jstrcpy("the end", ua, 4);
//log("rc,ua",rc,ua);
T.assert(4===rc).assert(0===ua[7]).
assert(chr('e')===ua[6]).assert(chr('t')===ua[4]);
refill();
rc = w.jstrcpy("the end", ua, 4, -1, false);
T.assert(4===rc).assert(chr(' ')===ua[7]).
assert(chr('e')===ua[6]).assert(chr('t')===ua[4]);
refill();
rc = w.jstrcpy("", ua, 0, 1, true);
//log("rc,ua",rc,ua);
T.assert(1===rc).assert(0===ua[0]);
refill();
rc = w.jstrcpy("x", ua, 0, 1, true);
//log("rc,ua",rc,ua);
T.assert(1===rc).assert(0===ua[0]);
refill();
rc = w.jstrcpy('äbä', ua, 0, 1, true);
T.assert(1===rc, 'Must not write partial multi-byte char.')
.assert(0===ua[0]);
refill();
rc = w.jstrcpy('äbä', ua, 0, 2, true);
T.assert(1===rc, 'Must not write partial multi-byte char.')
.assert(0===ua[0]);
refill();
rc = w.jstrcpy('äbä', ua, 0, 2, false);
T.assert(2===rc).assert(fillChar!==ua[1]).assert(fillChar===ua[2]);
}/*jstrcpy()*/
//log("cstrncpy()...");
{
const scope = w.scopedAllocPush();
try {
let cStr = w.scopedAllocCString("hello");
const n = w.cstrlen(cStr);
let cpy = w.scopedAlloc(n+10);
let rc = w.cstrncpy(cpy, cStr, n+10);
T.assert(n+1 === rc).
assert("hello" === w.cstringToJs(cpy)).
assert(chr('o') === w.getMemValue(cpy+n-1)).
assert(0 === w.getMemValue(cpy+n));
let cStr2 = w.scopedAllocCString("HI!!!");
rc = w.cstrncpy(cpy, cStr2, 3);
T.assert(3===rc).
assert("HI!lo" === w.cstringToJs(cpy)).
assert(chr('!') === w.getMemValue(cpy+2)).
assert(chr('l') === w.getMemValue(cpy+3));
}finally{
w.scopedAllocPop(scope);
}
}
//log("jstrToUintArray()...");
{
let a = w.jstrToUintArray("hello", false);
T.assert(5===a.byteLength).assert(chr('o')===a[4]);
a = w.jstrToUintArray("hello", true);
T.assert(6===a.byteLength).assert(chr('o')===a[4]).assert(0===a[5]);
a = w.jstrToUintArray("äbä", false);
T.assert(5===a.byteLength).assert(chr('b')===a[2]);
a = w.jstrToUintArray("äbä", true);
T.assert(6===a.byteLength).assert(chr('b')===a[2]).assert(0===a[5]);
}
//log("allocCString()...");
{
const cstr = w.allocCString("hällo, world");
const n = w.cstrlen(cstr);
T.assert(13 === n)
.assert(0===w.getMemValue(cstr+n))
.assert(chr('d')===w.getMemValue(cstr+n-1));
}
//log("scopedAlloc() and friends...");
{
const alloc = w.alloc, dealloc = w.dealloc;
w.alloc = w.dealloc = null;
T.assert(!w.scopedAlloc.level)
.mustThrowMatching(()=>w.scopedAlloc(1), /^No scopedAllocPush/)
.mustThrowMatching(()=>w.scopedAllocPush(), /missing alloc/);
w.alloc = alloc;
T.mustThrowMatching(()=>w.scopedAllocPush(), /missing alloc/);
w.dealloc = dealloc;
T.mustThrowMatching(()=>w.scopedAllocPop(), /^Invalid state/)
.mustThrowMatching(()=>w.scopedAlloc(1), /^No scopedAllocPush/)
.mustThrowMatching(()=>w.scopedAlloc.level=0, /read-only/);
const asc = w.scopedAllocPush();
let asc2;
try {
const p1 = w.scopedAlloc(16),
p2 = w.scopedAlloc(16);
T.assert(1===w.scopedAlloc.level)
.assert(Number.isFinite(p1))
.assert(Number.isFinite(p2))
.assert(asc[0] === p1)
.assert(asc[1]===p2);
asc2 = w.scopedAllocPush();
const p3 = w.scopedAlloc(16);
T.assert(2===w.scopedAlloc.level)
.assert(Number.isFinite(p3))
.assert(2===asc.length)
.assert(p3===asc2[0]);
const [z1, z2, z3] = w.scopedAllocPtr(3);
T.assert('number'===typeof z1).assert(z2>z1).assert(z3>z2)
.assert(0===w.getMemValue(z1,'i32'), 'allocPtr() must zero the targets')
.assert(0===w.getMemValue(z3,'i32'));
}finally{
// Pop them in "incorrect" order to make sure they behave:
w.scopedAllocPop(asc);
T.assert(0===asc.length);
T.mustThrowMatching(()=>w.scopedAllocPop(asc),
/^Invalid state object/);
if(asc2){
T.assert(2===asc2.length,'Should be p3 and z1');
w.scopedAllocPop(asc2);
T.assert(0===asc2.length);
T.mustThrowMatching(()=>w.scopedAllocPop(asc2),
/^Invalid state object/);
}
}
T.assert(0===w.scopedAlloc.level);
w.scopedAllocCall(function(){
T.assert(1===w.scopedAlloc.level);
const [cstr, n] = w.scopedAllocCString("hello, world", true);
T.assert(12 === n)
.assert(0===w.getMemValue(cstr+n))
.assert(chr('d')===w.getMemValue(cstr+n-1));
});
}/*scopedAlloc()*/
//log("xCall()...");
{
const pJson = w.xCall('sqlite3_wasm_enum_json');
T.assert(Number.isFinite(pJson)).assert(w.cstrlen(pJson)>300);
}
//log("xWrap()...");
{
T.mustThrowMatching(()=>w.xWrap('sqlite3_libversion',null,'i32'),
/requires 0 arg/).
assert(w.xWrap.resultAdapter('i32') instanceof Function).
assert(w.xWrap.argAdapter('i32') instanceof Function);
let fw = w.xWrap('sqlite3_libversion','utf8');
T.mustThrowMatching(()=>fw(1), /requires 0 arg/);
let rc = fw();
T.assert('string'===typeof rc).assert(rc.length>5);
rc = w.xCallWrapped('sqlite3_wasm_enum_json','*');
T.assert(rc>0 && Number.isFinite(rc));
rc = w.xCallWrapped('sqlite3_wasm_enum_json','utf8');
T.assert('string'===typeof rc).assert(rc.length>300);
if(haveWasmCTests()){
fw = w.xWrap('sqlite3_wasm_test_str_hello', 'utf8:free',['i32']);
rc = fw(0);
T.assert('hello'===rc);
rc = fw(1);
T.assert(null===rc);
if(w.bigIntEnabled){
w.xWrap.resultAdapter('thrice', (v)=>3n*BigInt(v));
w.xWrap.argAdapter('twice', (v)=>2n*BigInt(v));
fw = w.xWrap('sqlite3_wasm_test_int64_times2','thrice','twice');
rc = fw(1);
T.assert(12n===rc);
w.scopedAllocCall(function(){
let pI1 = w.scopedAlloc(8), pI2 = pI1+4;
w.setMemValue(pI1, 0,'*')(pI2, 0, '*');
let f = w.xWrap('sqlite3_wasm_test_int64_minmax',undefined,['i64*','i64*']);
let r1 = w.getMemValue(pI1, 'i64'), r2 = w.getMemValue(pI2, 'i64');
T.assert(!Number.isSafeInteger(r1)).assert(!Number.isSafeInteger(r2));
});
}
}
}
}/*WhWasmUtil*/)
////////////////////////////////////////////////////////////////////
.t('sqlite3.StructBinder (jaccwabyt)', function(sqlite3){
const S = sqlite3, W = S.capi.wasm;
const MyStructDef = {
sizeof: 16,
members: {
p4: {offset: 0, sizeof: 4, signature: "i"},
pP: {offset: 4, sizeof: 4, signature: "P"},
ro: {offset: 8, sizeof: 4, signature: "i", readOnly: true},
cstr: {offset: 12, sizeof: 4, signature: "s"}
}
};
if(W.bigIntEnabled){
const m = MyStructDef;
m.members.p8 = {offset: m.sizeof, sizeof: 8, signature: "j"};
m.sizeof += m.members.p8.sizeof;
}
const StructType = S.StructBinder.StructType;
const K = S.StructBinder('my_struct',MyStructDef);
T.mustThrowMatching(()=>K(), /via 'new'/).
mustThrowMatching(()=>new K('hi'), /^Invalid pointer/);
const k1 = new K(), k2 = new K();
try {
T.assert(k1.constructor === K).
assert(K.isA(k1)).
assert(k1 instanceof K).
assert(K.prototype.lookupMember('p4').key === '$p4').
assert(K.prototype.lookupMember('$p4').name === 'p4').
mustThrowMatching(()=>K.prototype.lookupMember('nope'), /not a mapped/).
assert(undefined === K.prototype.lookupMember('nope',false)).
assert(k1 instanceof StructType).
assert(StructType.isA(k1)).
assert(K.resolveToInstance(k1.pointer)===k1).
mustThrowMatching(()=>K.resolveToInstance(null,true), /is-not-a my_struct/).
assert(k1 === StructType.instanceForPointer(k1.pointer)).
mustThrowMatching(()=>k1.$ro = 1, /read-only/);
Object.keys(MyStructDef.members).forEach(function(key){
key = K.memberKey(key);
T.assert(0 == k1[key],
"Expecting allocation to zero the memory "+
"for "+key+" but got: "+k1[key]+
" from "+k1.memoryDump());
});
T.assert('number' === typeof k1.pointer).
mustThrowMatching(()=>k1.pointer = 1, /pointer/).
assert(K.instanceForPointer(k1.pointer) === k1);
k1.$p4 = 1; k1.$pP = 2;
T.assert(1 === k1.$p4).assert(2 === k1.$pP);
if(MyStructDef.members.$p8){
k1.$p8 = 1/*must not throw despite not being a BigInt*/;
k1.$p8 = BigInt(Number.MAX_SAFE_INTEGER * 2);
T.assert(BigInt(2 * Number.MAX_SAFE_INTEGER) === k1.$p8);
}
T.assert(!k1.ondispose);
k1.setMemberCString('cstr', "A C-string.");
T.assert(Array.isArray(k1.ondispose)).
assert(k1.ondispose[0] === k1.$cstr).
assert('number' === typeof k1.$cstr).
assert('A C-string.' === k1.memberToJsString('cstr'));
k1.$pP = k2;
T.assert(k1.$pP === k2);
k1.$pP = null/*null is special-cased to 0.*/;
T.assert(0===k1.$pP);
let ptr = k1.pointer;
k1.dispose();
T.assert(undefined === k1.pointer).
assert(undefined === K.instanceForPointer(ptr)).
mustThrowMatching(()=>{k1.$pP=1}, /disposed instance/);
const k3 = new K();
ptr = k3.pointer;
T.assert(k3 === K.instanceForPointer(ptr));
K.disposeAll();
T.assert(ptr).
assert(undefined === k2.pointer).
assert(undefined === k3.pointer).
assert(undefined === K.instanceForPointer(ptr));
}finally{
k1.dispose();
k2.dispose();
}
if(!W.bigIntEnabled){
log("Skipping WasmTestStruct tests: BigInt not enabled.");
return;
}
const WTStructDesc =
W.ctype.structs.filter((e)=>'WasmTestStruct'===e.name)[0];
const autoResolvePtr = true /* EXPERIMENTAL */;
if(autoResolvePtr){
WTStructDesc.members.ppV.signature = 'P';
}
const WTStruct = S.StructBinder(WTStructDesc);
//log(WTStruct.structName, WTStruct.structInfo);
const wts = new WTStruct();
//log("WTStruct.prototype keys:",Object.keys(WTStruct.prototype));
try{
T.assert(wts.constructor === WTStruct).
assert(WTStruct.memberKeys().indexOf('$ppV')>=0).
assert(wts.memberKeys().indexOf('$v8')>=0).
assert(!K.isA(wts)).
assert(WTStruct.isA(wts)).
assert(wts instanceof WTStruct).
assert(wts instanceof StructType).
assert(StructType.isA(wts)).
assert(wts === StructType.instanceForPointer(wts.pointer));
T.assert(wts.pointer>0).assert(0===wts.$v4).assert(0n===wts.$v8).
assert(0===wts.$ppV).assert(0===wts.$xFunc).
assert(WTStruct.instanceForPointer(wts.pointer) === wts);
const testFunc =
W.xGet('sqlite3_wasm_test_struct'/*name gets mangled in -O3 builds!*/);
let counter = 0;
//log("wts.pointer =",wts.pointer);
const wtsFunc = function(arg){
/*log("This from a JS function called from C, "+
"which itself was called from JS. arg =",arg);*/
++counter;
T.assert(WTStruct.instanceForPointer(arg) === wts);
if(3===counter){
tossQuietly("Testing exception propagation.");
}
}
wts.$v4 = 10; wts.$v8 = 20;
wts.$xFunc = W.installFunction(wtsFunc, wts.memberSignature('xFunc'))
T.assert(0===counter).assert(10 === wts.$v4).assert(20n === wts.$v8)
.assert(0 === wts.$ppV).assert('number' === typeof wts.$xFunc)
.assert(0 === wts.$cstr)
.assert(wts.memberIsString('$cstr'))
.assert(!wts.memberIsString('$v4'))
.assert(null === wts.memberToJsString('$cstr'))
.assert(W.functionEntry(wts.$xFunc) instanceof Function);
/* It might seem silly to assert that the values match
what we just set, but recall that all of those property
reads and writes are, via property interceptors,
actually marshaling their data to/from a raw memory
buffer, so merely reading them back is actually part of
testing the struct-wrapping API. */
testFunc(wts.pointer);
//log("wts.pointer, wts.$ppV",wts.pointer, wts.$ppV);
T.assert(1===counter).assert(20 === wts.$v4).assert(40n === wts.$v8)
.assert(autoResolvePtr ? (wts.$ppV === wts) : (wts.$ppV === wts.pointer))
.assert('string' === typeof wts.memberToJsString('cstr'))
.assert(wts.memberToJsString('cstr') === wts.memberToJsString('$cstr'))
.mustThrowMatching(()=>wts.memberToJsString('xFunc'),
/Invalid member type signature for C-string/)
;
testFunc(wts.pointer);
T.assert(2===counter).assert(40 === wts.$v4).assert(80n === wts.$v8)
.assert(autoResolvePtr ? (wts.$ppV === wts) : (wts.$ppV === wts.pointer));
/** The 3rd call to wtsFunc throw from JS, which is called
from C, which is called from JS. Let's ensure that
that exception propagates back here... */
T.mustThrowMatching(()=>testFunc(wts.pointer),/^Testing/);
W.uninstallFunction(wts.$xFunc);
wts.$xFunc = 0;
if(autoResolvePtr){
wts.$ppV = 0;
T.assert(!wts.$ppV);
WTStruct.debugFlags(0x03);
wts.$ppV = wts;
T.assert(wts === wts.$ppV)
WTStruct.debugFlags(0);
}
wts.setMemberCString('cstr', "A C-string.");
T.assert(Array.isArray(wts.ondispose)).
assert(wts.ondispose[0] === wts.$cstr).
assert('A C-string.' === wts.memberToJsString('cstr'));
const ptr = wts.pointer;
wts.dispose();
T.assert(ptr).assert(undefined === wts.pointer).
assert(undefined === WTStruct.instanceForPointer(ptr))
}finally{
wts.dispose();
}
}/*StructBinder*/)
////////////////////////////////////////////////////////////////////
.t('sqlite3.StructBinder part 2', function(sqlite3){
// https://www.sqlite.org/c3ref/vfs.html
// https://www.sqlite.org/c3ref/io_methods.html
const W = wasm;
const sqlite3_io_methods = capi.sqlite3_io_methods,
sqlite3_vfs = capi.sqlite3_vfs,
sqlite3_file = capi.sqlite3_file;
//log("struct sqlite3_file", sqlite3_file.memberKeys());
//log("struct sqlite3_vfs", sqlite3_vfs.memberKeys());
//log("struct sqlite3_io_methods", sqlite3_io_methods.memberKeys());
const installMethod = function callee(tgt, name, func){
if(1===arguments.length){
return (n,f)=>callee(tgt,n,f);
}
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);
}
};
callee.ondisposeRemoveFunc = function(){
if(this.__ondispose){
const who = this;
this.__ondispose.forEach(
(v)=>{
if('number'===typeof v){
try{capi.wasm.uninstallFunction(v)}
catch(e){/*ignore*/}
}else{/*wasm function wrapper property*/
delete who[v];
}
}
);
delete this.__ondispose;
}
};
}/*static init*/
const sigN = tgt.memberSignature(name),
memKey = tgt.memberKey(name);
//log("installMethod",tgt, name, sigN);
if(!tgt.__ondispose){
T.assert(undefined === tgt.ondispose);
tgt.ondispose = [callee.ondisposeRemoveFunc];
tgt.__ondispose = [];
}
const fProxy = callee.argcProxy(func, sigN);
const pFunc = capi.wasm.installFunction(fProxy, tgt.memberSignature(name, true));
tgt[memKey] = pFunc;
/**
ACHTUNG: function pointer IDs are from a different pool than
allocation IDs, starting at 1 and incrementing in steps of 1,
so if we set tgt[memKey] to those values, we'd very likely
later misinterpret them as plain old pointer addresses unless
unless we use some silly heuristic like "all values <5k are
presumably function pointers," or actually perform a function
lookup on every pointer to first see if it's a function. That
would likely work just fine, but would be kludgy.
It turns out that "all values less than X are functions" is
essentially how it works in wasm: a function pointer is
reported to the client as its index into the
__indirect_function_table.
So... once jaccwabyt can be told how to access the
function table, it could consider all pointer values less
than that table's size to be functions. As "real" pointer
values start much, much higher than the function table size,
that would likely work reasonably well. e.g. the object
pointer address for sqlite3's default VFS is (in this local
setup) 65104, whereas the function table has fewer than 600
entries.
*/
const wrapperKey = '$'+memKey;
tgt[wrapperKey] = fProxy;
tgt.__ondispose.push(pFunc, wrapperKey);
//log("tgt.__ondispose =",tgt.__ondispose);
return (n,f)=>callee(tgt, n, f);
}/*installMethod*/;
const installIOMethods = function instm(iom){
(iom instanceof capi.sqlite3_io_methods) || toss("Invalid argument type.");
if(!instm._requireFileArg){
instm._requireFileArg = function(arg,methodName){
arg = capi.sqlite3_file.resolveToInstance(arg);
if(!arg){
err("sqlite3_io_methods::xClose() was passed a non-sqlite3_file.");
}
return arg;
};
instm._methods = {
// https://sqlite.org/c3ref/io_methods.html
xClose: /*i(P)*/function(f){
/* int (*xClose)(sqlite3_file*) */
log("xClose(",f,")");
if(!(f = instm._requireFileArg(f,'xClose'))) return capi.SQLITE_MISUSE;
f.dispose(/*noting that f has externally-owned memory*/);
return 0;
},
xRead: /*i(Ppij)*/function(f,dest,n,offset){
/* int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) */
log("xRead(",arguments,")");
if(!(f = instm._requireFileArg(f))) return capi.SQLITE_MISUSE;
capi.wasm.heap8().fill(0, dest + offset, n);
return 0;
},
xWrite: /*i(Ppij)*/function(f,dest,n,offset){
/* int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst) */
log("xWrite(",arguments,")");
if(!(f=instm._requireFileArg(f,'xWrite'))) return capi.SQLITE_MISUSE;
return 0;
},
xTruncate: /*i(Pj)*/function(f){
/* int (*xTruncate)(sqlite3_file*, sqlite3_int64 size) */
log("xTruncate(",arguments,")");
if(!(f=instm._requireFileArg(f,'xTruncate'))) return capi.SQLITE_MISUSE;
return 0;
},
xSync: /*i(Pi)*/function(f){
/* int (*xSync)(sqlite3_file*, int flags) */
log("xSync(",arguments,")");
if(!(f=instm._requireFileArg(f,'xSync'))) return capi.SQLITE_MISUSE;
return 0;
},
xFileSize: /*i(Pp)*/function(f,pSz){
/* int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize) */
log("xFileSize(",arguments,")");
if(!(f=instm._requireFileArg(f,'xFileSize'))) return capi.SQLITE_MISUSE;
capi.wasm.setMemValue(pSz, 0/*file size*/);
return 0;
},
xLock: /*i(Pi)*/function(f){
/* int (*xLock)(sqlite3_file*, int) */
log("xLock(",arguments,")");
if(!(f=instm._requireFileArg(f,'xLock'))) return capi.SQLITE_MISUSE;
return 0;
},
xUnlock: /*i(Pi)*/function(f){
/* int (*xUnlock)(sqlite3_file*, int) */
log("xUnlock(",arguments,")");
if(!(f=instm._requireFileArg(f,'xUnlock'))) return capi.SQLITE_MISUSE;
return 0;
},
xCheckReservedLock: /*i(Pp)*/function(){
/* int (*xCheckReservedLock)(sqlite3_file*, int *pResOut) */
log("xCheckReservedLock(",arguments,")");
return 0;
},
xFileControl: /*i(Pip)*/function(){
/* int (*xFileControl)(sqlite3_file*, int op, void *pArg) */
log("xFileControl(",arguments,")");
return capi.SQLITE_NOTFOUND;
},
xSectorSize: /*i(P)*/function(){
/* int (*xSectorSize)(sqlite3_file*) */
log("xSectorSize(",arguments,")");
return 0/*???*/;
},
xDeviceCharacteristics:/*i(P)*/function(){
/* int (*xDeviceCharacteristics)(sqlite3_file*) */
log("xDeviceCharacteristics(",arguments,")");
return 0;
}
};
}/*static init*/
iom.$iVersion = 1;
Object.keys(instm._methods).forEach(
(k)=>installMethod(iom, k, instm._methods[k])
);
}/*installIOMethods()*/;
const iom = new sqlite3_io_methods, sfile = new sqlite3_file;
const err = console.error.bind(console);
try {
const IOM = sqlite3_io_methods, S3F = sqlite3_file;
//log("iom proto",iom,iom.constructor.prototype);
//log("sfile",sfile,sfile.constructor.prototype);
T.assert(0===sfile.$pMethods).assert(iom.pointer > 0);
//log("iom",iom);
sfile.$pMethods = iom.pointer;
T.assert(iom.pointer === sfile.$pMethods)
.assert(IOM.resolveToInstance(iom))
.assert(undefined ===IOM.resolveToInstance(sfile))
.mustThrow(()=>IOM.resolveToInstance(0,true))
.assert(S3F.resolveToInstance(sfile.pointer))
.assert(undefined===S3F.resolveToInstance(iom))
.assert(iom===IOM.resolveToInstance(sfile.$pMethods));
T.assert(0===iom.$iVersion);
installIOMethods(iom);
T.assert(1===iom.$iVersion);
//log("iom.__ondispose",iom.__ondispose);
T.assert(Array.isArray(iom.__ondispose)).assert(iom.__ondispose.length>10);
}finally{
iom.dispose();
T.assert(undefined === iom.__ondispose);
}
const dVfs = new sqlite3_vfs(capi.sqlite3_vfs_find(null));
try {
const SB = sqlite3.StructBinder;
T.assert(dVfs instanceof SB.StructType)
.assert(dVfs.pointer)
.assert('sqlite3_vfs' === dVfs.structName)
.assert(!!dVfs.structInfo)
.assert(SB.StructType.hasExternalPointer(dVfs))
.assert(dVfs.$iVersion>0)
.assert('number'===typeof dVfs.$zName)
.assert('number'===typeof dVfs.$xSleep)
.assert(capi.wasm.functionEntry(dVfs.$xOpen))
.assert(dVfs.memberIsString('zName'))
.assert(dVfs.memberIsString('$zName'))
.assert(!dVfs.memberIsString('pAppData'))
.mustThrowMatching(()=>dVfs.memberToJsString('xSleep'),
/Invalid member type signature for C-string/)
.mustThrowMatching(()=>dVfs.memberSignature('nope'), /nope is not a mapped/)
.assert('string' === typeof dVfs.memberToJsString('zName'))
.assert(dVfs.memberToJsString('zName')===dVfs.memberToJsString('$zName'))
;
//log("Default VFS: @",dVfs.pointer);
Object.keys(sqlite3_vfs.structInfo.members).forEach(function(mname){
const mk = sqlite3_vfs.memberKey(mname), mbr = sqlite3_vfs.structInfo.members[mname],
addr = dVfs[mk], prefix = 'defaultVfs.'+mname;
if(1===mbr.signature.length){
let sep = '?', val = undefined;
switch(mbr.signature[0]){
// TODO: move this into an accessor, e.g. getPreferredValue(member)
case 'i': case 'j': case 'f': case 'd': sep = '='; val = dVfs[mk]; break
case 'p': case 'P': sep = '@'; val = dVfs[mk]; break;
case 's': sep = '=';
val = dVfs.memberToJsString(mname);
break;
}
//log(prefix, sep, val);
}else{
//log(prefix," = funcptr @",addr, capi.wasm.functionEntry(addr));
}
});
}finally{
dVfs.dispose();
T.assert(undefined===dVfs.pointer);
}
}/*StructBinder part 2*/)
////////////////////////////////////////////////////////////////////
.t('sqlite3.capi.wasm.pstack', function(sqlite3){
const w = sqlite3.capi.wasm, P = w.pstack;
const isAllocErr = (e)=>e instanceof sqlite3.WasmAllocError;
@ -373,7 +1017,9 @@
P.restore(stack);
}
}/*pstack tests*/)
;/*end of basic sanity checks*/
////////////////////////////////////////////////////////////////////
;/*end of C/WASM utils checks*/
////////////////////////////////////////////////////////////////////////
T.g('sqlite3.oo1 sanity checks')
@ -389,6 +1035,8 @@
T.assert(capi.SQLITE_MISUSE === rc)
.assert(0 === capi.sqlite3_errmsg(db.pointer).indexOf("Invalid SQL"));
})
////////////////////////////////////////////////////////////////////
.t('DB.Stmt sanity checks', function(S){
let pId;
let st = this.db.prepare(
@ -436,6 +1084,8 @@
T.assert(!st.pointer)
.assert(0===this.db.openStatementCount());
})
////////////////////////////////////////////////////////////////////
.t('Table t', function(sqlite3){
const db = this.db;
let list = [];
@ -488,8 +1138,8 @@
db.selectValue("SELECT "+Number.MIN_SAFE_INTEGER)).
assert(Number.MAX_SAFE_INTEGER ===
db.selectValue("SELECT "+Number.MAX_SAFE_INTEGER));
if(capi.wasm.bigIntEnabled && haveJaccwabytTests()){
const mI = capi.wasm.xCall('jaccwabyt_test_int64_max');
if(capi.wasm.bigIntEnabled && haveWasmCTests()){
const mI = capi.wasm.xCall('sqlite3_wasm_test_int64_max');
const b = BigInt(Number.MAX_SAFE_INTEGER * 2);
T.assert(b === db.selectValue("SELECT "+b)).
assert(b === db.selectValue("SELECT ?", b)).
@ -521,6 +1171,7 @@
}
})
////////////////////////////////////////////////////////////////////
.t('Scalar UDFs', function(sqlite3){
const db = this.db;
db.createFunction("foo",(pCx,a,b)=>a+b);
@ -574,16 +1225,19 @@
T.assert(0x68==blobRc[0] && 0x69==blobRc[1]);
})
////////////////////////////////////////////////////////////////////
.t({
name: 'Aggregate UDFs (tests are TODO)',
predicate: testIsTodo
})
////////////////////////////////////////////////////////////////////
.t({
name: 'Window UDFs (tests are TODO)',
predicate: testIsTodo
})
////////////////////////////////////////////////////////////////////
.t("ATTACH", function(){
const db = this.db;
const resultRows = [];
@ -624,9 +1278,10 @@
T.mustThrow(()=>db.exec("select * from foo.bar"));
})
////////////////////////////////////////////////////////////////////
.t({
name: 'Jaccwabyt-specific int-pointer tests (if compiled in)',
predicate: haveJaccwabytTests,
name: 'C-side WASM tests (if compiled in)',
predicate: haveWasmCTests,
test: function(){
const w = wasm, db = this.db;
const stack = w.scopedAllocPush();
@ -636,7 +1291,7 @@
try{
ptrInt = w.scopedAlloc(4);
w.setMemValue(ptrInt,origValue, ptrValType);
const cf = w.xGet('jaccwabyt_test_intptr');
const cf = w.xGet('sqlite3_wasm_test_intptr');
const oldPtrInt = ptrInt;
//log('ptrInt',ptrInt);
//log('getMemValue(ptrInt)',w.getMemValue(ptrInt));
@ -658,14 +1313,14 @@
//log("getMemValue(pi64)",v64());
T.assert(v64() == o64);
//T.assert(o64 === w.getMemValue(pi64, ptrType64));
const cf64w = w.xGet('jaccwabyt_test_int64ptr');
const cf64w = w.xGet('sqlite3_wasm_test_int64ptr');
cf64w(pi64);
//log("getMemValue(pi64)",v64());
T.assert(v64() == BigInt(2 * o64));
cf64w(pi64);
T.assert(v64() == BigInt(4 * o64));
const biTimes2 = w.xGet('jaccwabyt_test_int64_times2');
const biTimes2 = w.xGet('sqlite3_wasm_test_int64_times2');
T.assert(BigInt(2 * o64) ===
biTimes2(BigInt(o64)/*explicit conv. required to avoid TypeError
in the call :/ */));
@ -676,13 +1331,13 @@
w.setMemValue(pMin, 0, ptrType64);
w.setMemValue(pMax, 0, ptrType64);
const minMaxI64 = [
w.xCall('jaccwabyt_test_int64_min'),
w.xCall('jaccwabyt_test_int64_max')
w.xCall('sqlite3_wasm_test_int64_min'),
w.xCall('sqlite3_wasm_test_int64_max')
];
T.assert(minMaxI64[0] < BigInt(Number.MIN_SAFE_INTEGER)).
assert(minMaxI64[1] > BigInt(Number.MAX_SAFE_INTEGER));
//log("int64_min/max() =",minMaxI64, typeof minMaxI64[0]);
w.xCall('jaccwabyt_test_int64_minmax', pMin, pMax);
w.xCall('sqlite3_wasm_test_int64_minmax', pMin, pMax);
T.assert(g64(pMin) === minMaxI64[0], "int64 mismatch").
assert(g64(pMax) === minMaxI64[1], "int64 mismatch");
//log("pMin",g64(pMin), "pMax",g64(pMax));
@ -733,8 +1388,8 @@
log("sqlite3 version:",capi.sqlite3_libversion(),
capi.sqlite3_sourceid());
log("BigInt/int64 support is",(wasm.bigIntEnabled ? "enabled" : "disabled"));
if(haveJaccwabytTests()){
log("Jaccwabyt test C code found. Jaccwabyt-specific low-level tests.");
if(haveWasmCTests()){
log("sqlite3_wasm_test_...() APIs are available.");
}
TestUtil.runTests(sqlite3);
});

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
C Optimize\sthe\sIS\sNULL\sand\sIS\sNOT\sNULL\soperators\sso\sthat\sthey\savoid\sloading\nlarge\sstrings\sor\sblobs\soff\sof\sdisk\sif\sall\sit\sneeds\sto\sknow\sis\swhether\sor\nnot\sthe\sstring\sor\sblob\sis\sNULL.
D 2022-10-13T15:09:44.252
C Move\sthe\srest\sof\stesting1.js\sinto\stester1.js\sand\seliminate\sthe\sdependency\son\sjaccwabyt_test.c.\sExtend\sthe\slist\sof\sdefault\sconfig-related\s#defines\sin\ssqlite3-wasm.c\sand\sreorganize\sthem\sfor\smaintainability.
D 2022-10-13T16:48:35.302
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -473,7 +473,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle 0e88c8cfc3719e4b7e74980d9da664c709e68acf863e48386cda376edfd3bfb0
F ext/wasm/GNUmakefile 8ab74ed186a15d956a21b28fa0800b84af2b8a289392ae2dff8126ff033bd3f9
F ext/wasm/GNUmakefile 4ec270532b921c7c4b437fbdb06f6a0ce41f3bd874395ce70dbc933c8553efa9
F ext/wasm/README.md 1e5b28158b74ab3ffc9d54fcbc020f0bbeb82c2ff8bbd904214c86c70e8a3066
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 89983a8d122c35a90c65ec667844b95a78bcd04f3198a99c1e0c8368c1a0b03a
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
@ -484,19 +484,19 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814
F ext/wasm/api/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1ecc5ba9e64ef90a8b
F ext/wasm/api/pre-js.js 2db711eb637991b383fc6b5c0f3df65ec48a7201e5730e304beba8de2d3f9b0b
F ext/wasm/api/sqlite3-api-cleanup.js 5d22d1d3818ecacb23bfa223d5970cd0617d8cdbb48c8bc4bbd463f05b021a99
F ext/wasm/api/sqlite3-api-glue.js 3b2a43c7b2ceb2b60f5f4a1afefbd93508c2fe0f2e667eea278afe570be4b606
F ext/wasm/api/sqlite3-api-oo1.js ac1e08d36bdfb5aa0a2d75b7d4c892fd51819d34c932370c3282810672bcc086
F ext/wasm/api/sqlite3-api-glue.js 842dc03783aecc951a543a209523343a6fda9af258fb0bee08e8cc2dc3c4d8ae
F ext/wasm/api/sqlite3-api-oo1.js 00f5cfce0989d2e08d7b21765d703c69234245d03a0cce8fcb32ccfcd53ffdbb
F ext/wasm/api/sqlite3-api-opfs.js 5a8ab3b76880c8ada8710ca9ba1ca5b160872edfd8bd5322e4f179a7f41cc616
F ext/wasm/api/sqlite3-api-prologue.js 5c56056810333974c971f6310c5c9698cf7ca8b06f6d2f1986cec819d0f5bbad
F ext/wasm/api/sqlite3-api-prologue.js b7c82a22d50658a48463fa646a23135273bc2cfa843aedda32627ff281c12e4d
F ext/wasm/api/sqlite3-api-worker1.js 7f4f46cb6b512a48572d7567233896e6a9c46570c44bdc3d13419730c7c221c8
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
F ext/wasm/api/sqlite3-wasm.c a321f12ceedac8611c1377ccfb5df0c0547bd9395f7fd7613827de365d994948
F ext/wasm/api/sqlite3-wasm.c 4c131945ced4b08a694d287abcdb066b896d961ef79ee5241805ecc37e83d63a
F ext/wasm/batch-runner.html cf1a410c92bad50fcec2ddc71390b4e9df63a6ea1bef12a5163a66a0af4d78d9
F ext/wasm/batch-runner.js 5bae81684728b6be157d1f92b39824153f0fd019345b39f2ab8930f7ee2a57d8
F ext/wasm/common/SqliteTestUtil.js 647bf014bd30bdd870a7e9001e251d12fc1c9ec9ce176a1004b838a4b33c5c05
F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
F ext/wasm/common/testing.css 12bd88c69eea1b61649ad9add3ff24f0fbc3b79a7616d74ab3997b7271cd8482
F ext/wasm/common/whwasmutil.js bc8522a071f4754af7b50f53807b95f691d2f9e44fc3b3e8c65dff6ef2485c0d
F ext/wasm/common/testing.css 53394885077edd3db22d2a0896192334dfc06fb3d1da0b646eb12a332d22f18e
F ext/wasm/common/whwasmutil.js 50d2ede0b0fa01c1d467e1801fab79f5e46bb02bcbd2b0232e4fdc6090a47818
F ext/wasm/demo-123-worker.html e50b51dc7271b9d3cc830cb7c2fba294d622f56b7acb199f7257d11195a63d49
F ext/wasm/demo-123.html 7c239c9951d1b113f9f532969ac039294cf1dcfee2b3ae0a2c1ed2b3d59f8dfa
F ext/wasm/demo-123.js d563cf9d725692ccd940c46df1c026d87863e0544942a2ba2015f17fba3f6f74
@ -507,11 +507,9 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695
F ext/wasm/fiddle/fiddle-worker.js 531859a471924a0ea48afa218e6877f0c164ca324d51e15843ed6ecc1c65c7ee
F ext/wasm/fiddle/fiddle.html 5daf54e8f3d7777cbb1ca4f93affe28858dbfff25841cb4ab81d694efed28ec2
F ext/wasm/fiddle/fiddle.js 974b995119ac443685d7d94d3b3c58c6a36540e9eb3fed7069d5653284071715
F ext/wasm/index.html 63b370619e4f849ac76f1baed435c05edc29dbb6795bc7c1c935561ff667dd27
F ext/wasm/index.html 9ae9f9629310ed3ee88901aa14e20195815583806af245da08c2ecb43ced2243
F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215
F ext/wasm/jaccwabyt/jaccwabyt.md 9aa6951b529a8b29f578ec8f0355713c39584c92cf1708f63ba0cf917cb5b68e
F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f
F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19
F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06
F ext/wasm/scratchpad-wasmfs-main.js 1aa32c1035cf1440a226a28fefcbb5762fbbcb020ccbe5895f8736d701695c63
F ext/wasm/speedtest1-wasmfs.html bc28eb29b69a73864b8d7aae428448f8b7e1de81d8bfb9bba99541322054dbd0
@ -528,11 +526,10 @@ F ext/wasm/test-opfs-vfs.html eb69dda21eb414b8f5e3f7c1cc0f774103cc9c0f87b2d28a33
F ext/wasm/test-opfs-vfs.js 56c3d725044c668fa7910451e96c1195d25ad95825f9ac79f747a7759d1973d0
F ext/wasm/tester1-worker.html 0af7a22025ff1da72a84765d64f8f221844a57c6e6e314acf3a30f176101fd3f
F ext/wasm/tester1.html fde0e0bdeaaa2c39877c749dc86a8c1c306f771c3d75b89a6289a5ed11243e9d
F ext/wasm/tester1.js d25cf7615bd39ab9ab4be2f231fecf1fff1378227e695083dc35adc4fdff668e
F ext/wasm/tester1.js d6a66fc36c7571b8050314b6d0e93e69cd6def88df14fb3ef2403e7aa244ec36
F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893
F ext/wasm/testing-worker1-promiser.js bd788e33c1807e0a6dda9c9a9d784bd3350ca49c9dd8ae2cc8719b506b6e013e
F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae
F ext/wasm/testing1.js 2034cf6972ab1506e75e41e532ca7cd3060e194c37edab3ff65d00d8a8af8ba2
F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3
F ext/wasm/testing2.js 88f40ef3cd8201bdadd120a711c36bbf0ce56cc0eab1d5e7debb71fed7822494
F ext/wasm/wasmfs.make 3cce1820006196de140f90f2da4b4ea657083fb5bfee7d125be43f7a85748c8f
@ -2034,9 +2031,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 043e76e6166da5cf8e213cce46aaccb1f910e1fdbdb5556576eafb81b3bc5faa 5e9c67ba18b701aabbb0546acdfc532c9e8f0d27fb0a2c899415a5c47096c90b
R c0d9c663bdd4b8318254b199c65d7e13
T +closed 5e9c67ba18b701aabbb0546acdfc532c9e8f0d27fb0a2c899415a5c47096c90b
U drh
Z 57cc74128fdf4ddaf1ca461d6df422e4
P cb94350185f555c333b628ee846c47bcc9df5f76bb82de569b8322f30dbbe1bc
R 32dab828066657d904ede486bb592456
U stephan
Z 3fd1b23c49b42c2df54d60ffd9b89d7f
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
cb94350185f555c333b628ee846c47bcc9df5f76bb82de569b8322f30dbbe1bc
4e2a8aff2dd4b6e148f45184e2523ebe47815257eca97fa3d32bcbf9625f0def