aboutsummaryrefslogtreecommitdiff
path: root/ext/wasm/tester1.c-pp.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/wasm/tester1.c-pp.js')
-rw-r--r--ext/wasm/tester1.c-pp.js511
1 files changed, 229 insertions, 282 deletions
diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js
index 0aaee9769..2866d34f5 100644
--- a/ext/wasm/tester1.c-pp.js
+++ b/ext/wasm/tester1.c-pp.js
@@ -419,6 +419,7 @@ self.sqlite3InitModule = sqlite3InitModule;
////////////////////////////////////////////////////////////////////
T.g('C/WASM Utilities')
.t('sqlite3.wasm namespace', function(sqlite3){
+ // TODO: break this into smaller individual test functions.
const w = wasm;
const chr = (x)=>x.charCodeAt(0);
//log("heap getters...");
@@ -660,6 +661,26 @@ self.sqlite3InitModule = sqlite3InitModule;
T.assert(rc>0 && Number.isFinite(rc));
rc = w.xCallWrapped('sqlite3_wasm_enum_json','utf8');
T.assert('string'===typeof rc).assert(rc.length>300);
+
+
+ { // 'string:static' argAdapter() sanity checks...
+ let argAd = w.xWrap.argAdapter('string:static');
+ let p0 = argAd('foo'), p1 = argAd('bar');
+ T.assert(w.isPtr(p0) && w.isPtr(p1))
+ .assert(p0 !== p1)
+ .assert(p0 === argAd('foo'))
+ .assert(p1 === argAd('bar'));
+ }
+
+ // 'string:flexible' argAdapter() sanity checks...
+ w.scopedAllocCall(()=>{
+ const argAd = w.xWrap.argAdapter('string:flexible');
+ const cj = (v)=>w.cstringToJs(argAd(v));
+ T.assert('Hi' === cj('Hi'))
+ .assert('hi' === cj(['h','i']))
+ .assert('HI' === cj(new Uint8Array([72, 73])));
+ });
+
if(haveWasmCTests()){
if(!sqlite3.config.useStdAlloc){
fw = w.xWrap('sqlite3_wasm_test_str_hello', 'utf8:dealloc',['i32']);
@@ -720,9 +741,6 @@ self.sqlite3InitModule = sqlite3InitModule;
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);
@@ -732,8 +750,7 @@ self.sqlite3InitModule = sqlite3InitModule;
" from "+k1.memoryDump());
});
T.assert('number' === typeof k1.pointer).
- mustThrowMatching(()=>k1.pointer = 1, /pointer/).
- assert(K.instanceForPointer(k1.pointer) === k1);
+ mustThrowMatching(()=>k1.pointer = 1, /pointer/);
k1.$p4 = 1; k1.$pP = 2;
T.assert(1 === k1.$p4).assert(2 === k1.$pP);
if(MyStructDef.members.$p8){
@@ -748,22 +765,13 @@ self.sqlite3InitModule = sqlite3InitModule;
assert('number' === typeof k1.$cstr).
assert('A C-string.' === k1.memberToJsString('cstr'));
k1.$pP = k2;
- T.assert(k1.$pP === k2);
+ T.assert(k1.$pP === k2.pointer);
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();
@@ -793,10 +801,8 @@ self.sqlite3InitModule = sqlite3InitModule;
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);
+ assert(wts.pointer>0).assert(0===wts.$v4).assert(0n===wts.$v8).
+ assert(0===wts.$ppV).assert(0===wts.$xFunc);
const testFunc =
W.xGet('sqlite3_wasm_test_struct'/*name gets mangled in -O3 builds!*/);
let counter = 0;
@@ -805,7 +811,6 @@ self.sqlite3InitModule = sqlite3InitModule;
/*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.");
}
@@ -829,7 +834,7 @@ self.sqlite3InitModule = sqlite3InitModule;
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(wts.$ppV === wts.pointer)
.assert('string' === typeof wts.memberToJsString('cstr'))
.assert(wts.memberToJsString('cstr') === wts.memberToJsString('$cstr'))
.mustThrowMatching(()=>wts.memberToJsString('xFunc'),
@@ -837,280 +842,31 @@ self.sqlite3InitModule = sqlite3InitModule;
;
testFunc(wts.pointer);
T.assert(2===counter).assert(40 === wts.$v4).assert(80n === wts.$v8)
- .assert(autoResolvePtr ? (wts.$ppV === wts) : (wts.$ppV === wts.pointer));
+ .assert(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.$ppV = 0;
+ T.assert(!wts.$ppV);
+ //WTStruct.debugFlags(0x03);
+ wts.$ppV = wts;
+ T.assert(wts.pointer === wts.$ppV)
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))
+ T.assert(ptr).assert(undefined === wts.pointer);
}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 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{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 = 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;
- 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;
- 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(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, wasm.functionEntry(addr));
- }
- });
- }finally{
- dVfs.dispose();
- T.assert(undefined===dVfs.pointer);
- }
- }/*StructBinder part 2*/)
-
- ////////////////////////////////////////////////////////////////////
.t('sqlite3.wasm.pstack', function(sqlite3){
const P = wasm.pstack;
const isAllocErr = (e)=>e instanceof sqlite3.WasmAllocError;
@@ -1219,7 +975,7 @@ self.sqlite3InitModule = sqlite3InitModule;
.t('Create db', function(sqlite3){
const dbFile = '/tester1.db';
wasm.sqlite3_wasm_vfs_unlink(0, dbFile);
- const db = this.db = new sqlite3.oo1.DB(dbFile);
+ const db = this.db = new sqlite3.oo1.DB(dbFile, 0 ? 'ct' : 'c');
T.assert(Number.isInteger(db.pointer))
.mustThrowMatching(()=>db.pointer=1, /read-only/)
.assert(0===sqlite3.capi.sqlite3_extended_result_codes(db.pointer,1))
@@ -1442,6 +1198,29 @@ self.sqlite3InitModule = sqlite3InitModule;
rc = db.selectArray('select a, b from t where b=-1');
T.assert(undefined === rc);
})
+ ////////////////////////////////////////////////////////////////////////
+ .t('selectArrays/Objects()', function(sqlite3){
+ const db = this.db;
+ const sql = 'select a, b from t where a=? or b=? order by a';
+ let rc = db.selectArrays(sql, [1, 4]);
+ T.assert(Array.isArray(rc))
+ .assert(2===rc.length)
+ .assert(2===rc[0].length)
+ .assert(1===rc[0][0])
+ .assert(2===rc[0][1])
+ .assert(3===rc[1][0])
+ .assert(4===rc[1][1])
+ rc = db.selectArrays(sql, [99,99]);
+ T.assert(Array.isArray(rc)).assert(0===rc.length);
+ rc = db.selectObjects(sql, [1,4]);
+ T.assert(Array.isArray(rc))
+ .assert(2===rc.length)
+ .assert('object' === typeof rc[1])
+ .assert(1===rc[0].a)
+ .assert(2===rc[0].b)
+ .assert(3===rc[1].a)
+ .assert(4===rc[1].b);
+ })
////////////////////////////////////////////////////////////////////////
.t({
@@ -1748,10 +1527,178 @@ self.sqlite3InitModule = sqlite3InitModule;
T.mustThrow(()=>db.exec("select * from foo.bar"));
})
+ ////////////////////////////////////////////////////////////////////////
+ .t({
+ name: 'Custom virtual tables',
+ predicate: ()=>wasm.bigIntEnabled,
+ test: function(sqlite3){
+ warn("The vtab/module JS bindings are experimental and subject to change.");
+ const vth = sqlite3.VtabHelper;
+ const tmplCols = Object.assign(Object.create(null),{
+ A: 0, B: 1
+ });
+ /**
+ The vtab demonstrated here is a JS-ification of
+ ext/misc/templatevtab.c.
+ */
+ const tmplMethods = {
+ xConnect: function(pDb, pAux, argc, argv, ppVtab, pzErr){
+ try{
+ const rc = capi.sqlite3_declare_vtab(
+ pDb, "CREATE TABLE ignored(a,b)"
+ );
+ if(0===rc){
+ const t = vth.vtab2js();
+ wasm.setPtrValue(ppVtab, t.pointer);
+ T.assert(t === vth.vtab2js(wasm.getPtrValue(ppVtab)));
+ }
+ return rc;
+ }catch(e){
+ if(!(e instanceof sqlite3.WasmAllocError)){
+ wasm.setPtrValue(pzErr, wasm.allocCString(e.message));
+ }
+ return vth.xMethodError('xConnect',e);
+ }
+ },
+ xDisconnect: function(pVtab){
+ try {
+ const t = vth.vtab2js(pVtab, true);
+ t.dispose();
+ return 0;
+ }catch(e){
+ return vth.xMethodError('xDisconnect',e);
+ }
+ },
+ xOpen: function(pVtab, ppCursor){
+ try{
+ const t = vth.vtab2js(pVtab), c = vth.vcur2js();
+ T.assert(t instanceof capi.sqlite3_vtab);
+ T.assert(c instanceof capi.sqlite3_vtab_cursor);
+ wasm.setPtrValue(ppCursor, c.pointer);
+ c._rowId = 0;
+ return 0;
+ }catch(e){
+ return vth.xMethodError('xOpen',e);
+ }
+ },
+ xClose: function(pCursor){
+ try{
+ const c = vth.vcur2js(pCursor,true);
+ T.assert(c instanceof capi.sqlite3_vtab_cursor)
+ .assert(!vth.vcur2js(pCursor));
+ c.dispose();
+ return 0;
+ }catch(e){
+ return vth.xMethodError('xClose',e);
+ }
+ },
+ xNext: function(pCursor){
+ try{
+ const c = vth.vcur2js(pCursor);
+ ++c._rowId;
+ return 0;
+ }catch(e){
+ return vth.xMethodError('xNext',e);
+ }
+
+ },
+ xColumn: function(pCursor, pCtx, iCol){
+ try{
+ const c = vth.vcur2js(pCursor);
+ switch(iCol){
+ case tmplCols.A:
+ capi.sqlite3_result_int(pCtx, 1000 + c._rowId);
+ break;
+ case tmplCols.B:
+ capi.sqlite3_result_int(pCtx, 2000 + c._rowId);
+ break;
+ default: sqlite3.SQLite3Error.toss("Invalid column id",iCol);
+ }
+ return 0;
+ }catch(e){
+ return vth.xMethodError('xColumn',e);
+ }
+ },
+ xRowid: function(pCursor, ppRowid64){
+ try{
+ const c = vth.vcur2js(pCursor);
+ vth.setRowId(ppRowid64, c._rowId);
+ return 0;
+ }catch(e){
+ return vth.xMethodError('xRowid',e);
+ }
+ },
+ xEof: function(pCursor){
+ const c = vth.vcur2js(pCursor);
+ return c._rowId>=10;
+ },
+ xFilter: function(pCursor, idxNum, idxCStr,
+ argc, argv/* [sqlite3_value* ...] */){
+ try{
+ const c = vth.vcur2js(pCursor);
+ c._rowId = 0;
+ return 0;
+ }catch(e){
+ return vth.xMethodError('xFilter',e);
+ }
+ },
+ xBestIndex: function(pVtab, pIdxInfo){
+ try{
+ const t = vth.vtab2js(pVtab);
+ const pii = new capi.sqlite3_index_info(pIdxInfo);
+ pii.$estimatedRows = 10;
+ pii.$estimatedCost = 10.0;
+ pii.dispose();
+ return 0;
+ }catch(e){
+ return vth.xMethodError('xBestIndex',e);
+ }
+ }
+ };
+ /**
+ The vtab API places relevance on whether xCreate and
+ xConnect are exactly the same function (same pointer
+ address). Two JS-side references to the same method will
+ end up, without acrobatics to counter it, being compiled as
+ two different WASM-side bindings, i.e. two different
+ pointers.
+
+ In order to account for this, VtabHelper.installMethods()
+ checks for duplicate function entries and maps them to the
+ same WASM-compiled instance.
+ */
+ if(1){
+ tmplMethods.xCreate = tmplMethods.xConnect;
+ }
+
+ const tmplMod = new sqlite3.capi.sqlite3_module();
+ tmplMod.ondispose = [];
+ tmplMod.$iVersion = 0;
+ vth.installMethods(tmplMod, tmplMethods, true);
+ if(tmplMethods.xCreate){
+ T.assert(tmplMod.$xCreate)
+ .assert(tmplMod.$xCreate === tmplMod.$xConnect,
+ "installMethods() must avoid re-compiling identical functions");
+ tmplMod.$xCreate = 0;
+ }
+ let rc = capi.sqlite3_create_module(
+ this.db, "testvtab", tmplMod, 0
+ );
+ this.db.checkRc(rc);
+
+ const list = this.db.selectArrays(
+ "SELECT a,b FROM testvtab order by a"
+ );
+ T.assert(10===list.length)
+ .assert(1000===list[0][0])
+ .assert(2009===list[list.length-1][1])
+ }
+ })/*vtab sanity checks*/
+
////////////////////////////////////////////////////////////////////
.t({
- name: 'C-side WASM tests (if compiled in)',
- predicate: haveWasmCTests,
+ name: 'C-side WASM tests',
+ predicate: ()=>(haveWasmCTests() || "Not compiled in."),
test: function(){
const w = wasm, db = this.db;
const stack = w.scopedAllocPush();
@@ -1835,8 +1782,8 @@ self.sqlite3InitModule = sqlite3InitModule;
}/* jaccwabyt-specific tests */)
.t('Close db', function(){
- T.assert(this.db).assert(Number.isInteger(this.db.pointer));
- wasm.exports.sqlite3_wasm_db_reset(this.db.pointer);
+ T.assert(this.db).assert(wasm.isPtr(this.db.pointer));
+ wasm.sqlite3_wasm_db_reset(this.db);
this.db.close();
T.assert(!this.db.pointer);
})