mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-08 14:02:16 +03:00
Remove two features of jaccwabyt which were fundamentally flawed, along with approx. 250 lines of unit tests which heavily relied on them. Thankfully, none of the sqlite3.js-level code used those bits.
FossilOrigin-Name: a190abc307847174f36421eaa3f47ef349c6f84a2bb35857fa64f64bbe722708
This commit is contained in:
@@ -740,9 +740,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);
|
||||
@@ -752,8 +749,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){
|
||||
@@ -768,22 +764,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();
|
||||
@@ -813,10 +800,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;
|
||||
@@ -825,7 +810,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.");
|
||||
}
|
||||
@@ -849,7 +833,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'),
|
||||
@@ -857,279 +841,30 @@ 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;
|
||||
|
Reference in New Issue
Block a user