1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Expose sqlite3_randomness() to WASM and add a custom binding for it which can populate a JS byte array. Add WhWasmUtil.isPtr().

FossilOrigin-Name: 333e67076b4bc967bb543ef8e265c63f6e3498c38ac121a7d1eff4a1d7a71c63
This commit is contained in:
stephan
2022-10-27 03:03:16 +00:00
parent 195687f1bf
commit de868175c3
7 changed files with 200 additions and 46 deletions

View File

@ -52,6 +52,7 @@ _sqlite3_open
_sqlite3_open_v2
_sqlite3_prepare_v2
_sqlite3_prepare_v3
_sqlite3_randomness
_sqlite3_realloc
_sqlite3_realloc64
_sqlite3_reset

View File

@ -45,7 +45,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
if(v && v.constructor && v instanceof StructBinder.StructType){
v = v.pointer;
}
return (v === (v | 0) /* v is a 32-bit integer */)
return wasm.isPtr(v)
? argPointer(v)
: toss("Invalid (object) type for StructType-type argument.");
});

View File

@ -215,6 +215,32 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
return (v && v.constructor && isInt32(v.constructor.BYTES_PER_ELEMENT)) ? v : false;
};
/** Internal helper to use in operations which need to distinguish
between TypedArrays which are backed by a SharedArrayBuffer
from those which are not. */
const __SAB = ('undefined'===typeof SharedArrayBuffer)
? function(){} : SharedArrayBuffer;
/** Returns true if the given TypedArray object is backed by a
SharedArrayBuffer, else false. */
const isSharedTypedArray = (aTypedArray)=>(aTypedArray.buffer instanceof __SAB);
/**
Returns either aTypedArray.slice(begin,end) (if
aTypedArray.buffer is a SharedArrayBuffer) or
aTypedArray.subarray(begin,end) (if it's not).
This distinction is important for APIs which don't like to
work on SABs, e.g. TextDecoder, and possibly for our
own APIs which work on memory ranges which "might" be
modified by other threads while it's working.
*/
const typedArrayPart = (aTypedArray, begin, end)=>{
return isSharedTypedArray(aTypedArray)
? aTypedArray.slice(begin, end)
: aTypedArray.subarray(begin, end);
};
/**
Returns true if v appears to be one of our bind()-able
TypedArray types: Uint8Array or Int8Array. Support for
@ -246,16 +272,16 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
const utf8Decoder = new TextDecoder('utf-8');
/** Internal helper to use in operations which need to distinguish
between SharedArrayBuffer heap memory and non-shared heap. */
const __SAB = ('undefined'===typeof SharedArrayBuffer)
? function(){} : SharedArrayBuffer;
const typedArrayToString = function(arrayBuffer, begin, end){
return utf8Decoder.decode(
(arrayBuffer.buffer instanceof __SAB)
? arrayBuffer.slice(begin, end)
: arrayBuffer.subarray(begin, end)
);
/**
Uses TextDecoder to decode the given half-open range of the
given TypedArray to a string. This differs from a simple
call to TextDecoder in that it accounts for whether the
first argument is based by a SharedArrayBuffer or not,
and can work more efficiently if it's not (TextDecoder
refuses to act upon an SAB).
*/
const typedArrayToString = function(typedArray, begin, end){
return utf8Decoder.decode(typedArrayPart(typedArray, begin,end));
};
/**
@ -502,11 +528,11 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
If the callback is a function, then for the duration of the
sqlite3_exec() call, it installs a WASM-bound function which
acts as a proxy for the given callback. That proxy will
also perform a conversion of the callback's arguments from
acts as a proxy for the given callback. That proxy will also
perform a conversion of the callback's arguments from
`(char**)` to JS arrays of strings. However, for API
consistency's sake it will still honor the C-level
callback parameter order and will call it like:
consistency's sake it will still honor the C-level callback
parameter order and will call it like:
`callback(pVoid, colCount, listOfValues, listOfColNames)`
@ -517,12 +543,27 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
*/
sqlite3_exec: (pDb, sql, callback, pVoid, pErrMsg)=>{}/*installed later*/,
/**
If passed a single argument which appears to be a byte-oriented
TypedArray (Int8Array or Uint8Array), this function treats that
TypedArray as an output target, fetches `theArray.byteLength`
bytes of randomness, and populates the whole array with it. As
a special case, if the array's length is 0, this function
behaves as if it were passed (0,0). When called this way, it
returns its argument, else it returns the `undefined` value.
If called with any other arguments, they are passed on as-is
to the C API. Results are undefined if passed any incompatible
values.
*/
sqlite3_randomness: (n, outPtr)=>{/*installed later*/},
/**
Various internal-use utilities are added here as needed. They
are bound to an object only so that we have access to them in
the differently-scoped steps of the API bootstrapping
process. At the end of the API setup process, this object gets
removed.
removed. These are NOT part of the public API.
*/
util:{
affirmBindableTypedArray, flexibleString,
@ -530,7 +571,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
isBindableTypedArray,
isInt32, isSQLableTypedArray, isTypedArray,
typedArrayToString,
isUIThread: ()=>'undefined'===typeof WorkerGlobalScope
isUIThread: ()=>'undefined'===typeof WorkerGlobalScope,
isSharedTypedArray,
typedArrayPart
},
/**
@ -617,7 +660,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
}/*wasm*/
}/*capi*/;
const wasm = capi.wasm;
const wasm = capi.wasm, util = capi.util;
/**
wasm.alloc()'s srcTypedArray.byteLength bytes,
@ -743,8 +786,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
// Please keep these sorted by function name!
["sqlite3_aggregate_context","void*", "sqlite3_context*", "int"],
["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*"
/* We should arguably write a custom wrapper which knows how
to handle Blob, TypedArrays, and JS strings. */
/* TODO: we should arguably write a custom wrapper which knows
how to handle Blob, TypedArrays, and JS strings. */
],
["sqlite3_bind_double","int", "sqlite3_stmt*", "int", "f64"],
["sqlite3_bind_int","int", "sqlite3_stmt*", "int", "int"],
@ -752,10 +795,10 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
["sqlite3_bind_parameter_count", "int", "sqlite3_stmt*"],
["sqlite3_bind_parameter_index","int", "sqlite3_stmt*", "string"],
["sqlite3_bind_text","int", "sqlite3_stmt*", "int", "string", "int", "int"
/* We should arguably create a hand-written binding
which does more flexible text conversion, along the lines of
sqlite3_prepare_v3(). The slightly problematic part is the
final argument (text destructor). */
/* We should arguably create a hand-written binding of
bind_text() which does more flexible text conversion, along
the lines of sqlite3_prepare_v3(). The slightly problematic
part is the final argument (text destructor). */
],
["sqlite3_close_v2", "int", "sqlite3*"],
["sqlite3_changes", "int", "sqlite3*"],
@ -770,15 +813,16 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
["sqlite3_column_type","int", "sqlite3_stmt*", "int"],
["sqlite3_compileoption_get", "string", "int"],
["sqlite3_compileoption_used", "int", "string"],
/* sqlite3_create_function_v2() is handled separate to simplify conversion
of its callback argument */
/* sqlite3_create_function(), sqlite3_create_function_v2(), and
sqlite3_create_window_function() use hand-written bindings to
simplify handling of their function-type arguments. */
["sqlite3_data_count", "int", "sqlite3_stmt*"],
["sqlite3_db_filename", "string", "sqlite3*", "string"],
["sqlite3_db_handle", "sqlite3*", "sqlite3_stmt*"],
["sqlite3_db_name", "string", "sqlite3*", "int"],
["sqlite3_deserialize", "int", "sqlite3*", "string", "*", "i64", "i64", "int"]
/* Careful! Short version: de/serialize() are problematic because they
might use a different allocator that the user for managing the
might use a different allocator than the user for managing the
deserialized block. de/serialize() are ONLY safe to use with
sqlite3_malloc(), sqlite3_free(), and its 64-bit variants. */,
["sqlite3_errmsg", "string", "sqlite3*"],
@ -806,6 +850,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
/* sqlite3_prepare_v2() and sqlite3_prepare_v3() are handled
separately due to us requiring two different sets of semantics
for those, depending on how their SQL argument is provided. */
/* sqlite3_randomness() uses a hand-written wrapper to extend
the range of supported argument types. */
["sqlite3_realloc", "*","*","int"],
["sqlite3_reset", "int", "sqlite3_stmt*"],
["sqlite3_result_blob",undefined, "*", "*", "int", "*"],
@ -1052,8 +1098,41 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
throw new SQLite3Error(...args);
};
capi.sqlite3_randomness = (...args)=>{
if(1===args.length && util.isTypedArray(args[0])
&& 1===args[0].BYTES_PER_ELEMENT){
const ta = args[0];
if(0===ta.byteLength){
wasm.exports.sqlite3_randomness(0,0);
return ta;
}
const stack = wasm.pstack.pointer;
try {
let n = ta.byteLength, offset = 0;
const r = wasm.exports.sqlite3_randomness;
const heap = wasm.heap8u();
const nAlloc = n < 512 ? n : 512;
const ptr = wasm.pstack.alloc(nAlloc);
do{
const j = (n>nAlloc ? nAlloc : n);
r(j, ptr);
ta.set(typedArrayPart(heap, ptr, ptr+j), offset);
n -= j;
offset += j;
} while(n > 0);
}catch(e){
console.error("Highly unexpected (and ignored!) "+
"exception in sqlite3_randomness():",e);
}finally{
wasm.pstack.restore(stack);
}
return ta;
}
capi.wasm.exports.sqlite3_randomness(...args);
};
/** State for sqlite3_wasmfs_opfs_dir(). */
let __persistentDir = undefined;
let __wasmfsOpfsDir = undefined;
/**
If the wasm environment has a WASMFS/OPFS-backed persistent
storage directory, its path is returned by this function. If it
@ -1068,26 +1147,26 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
Emscripten-managed virtual filesystem.
*/
capi.sqlite3_wasmfs_opfs_dir = function(){
if(undefined !== __persistentDir) return __persistentDir;
if(undefined !== __wasmfsOpfsDir) return __wasmfsOpfsDir;
// If we have no OPFS, there is no persistent dir
const pdir = config.wasmfsOpfsDir;
if(!pdir
|| !self.FileSystemHandle
|| !self.FileSystemDirectoryHandle
|| !self.FileSystemFileHandle){
return __persistentDir = "";
return __wasmfsOpfsDir = "";
}
try{
if(pdir && 0===wasm.xCallWrapped(
'sqlite3_wasm_init_wasmfs', 'i32', ['string'], pdir
)){
return __persistentDir = pdir;
return __wasmfsOpfsDir = pdir;
}else{
return __persistentDir = "";
return __wasmfsOpfsDir = "";
}
}catch(e){
// sqlite3_wasm_init_wasmfs() is not available
return __persistentDir = "";
return __wasmfsOpfsDir = "";
}
};

View File

@ -704,6 +704,23 @@ self.WhWasmUtilInstaller = function(target){
pointer-to-pointer values. */
target.setPtrValue = (ptr, value)=>target.setMemValue(ptr, value, ptrIR);
/**
Returns true if the given value appears to be legal for use as
a WASM pointer value. Its _range_ of values is not (cannot be)
validated except to ensure that it is a 32-bit integer with a
value of 0 or greater. Likewise, it cannot verify whether the
value actually refers to allocated memory in the WASM heap.
*/
target.isPtr32 = (ptr)=>('number'===typeof ptr && (ptr===(ptr|0)) && ptr>=0);
/**
isPtr() is an alias for isPtr32(). If/when 64-bit WASM pointer
support becomes widespread, it will become an alias for either
isPtr32() or the as-yet-hypothetical isPtr64(), depending on a
configuration option.
*/
target.isPtr = target.isPtr32;
/**
Expects ptr to be a pointer into the WASM heap memory which
refers to a NUL-terminated C-style string encoded as UTF-8.
@ -1229,7 +1246,8 @@ self.WhWasmUtilInstaller = function(target){
xcv.result['*'] = xcv.result['pointer'] = xcv.arg['**'] = xcv.arg[ptrIR];
xcv.result['number'] = (v)=>Number(v);
{
{ /* Copy certain xcv.arg[...] handlers to xcv.result[...] and
add pointer-style variants of them. */
const copyToResult = ['i8', 'i16', 'i32', 'int',
'f32', 'float', 'f64', 'double'];
if(target.bigIntEnabled) copyToResult.push('i64');

View File

@ -377,6 +377,19 @@
}
}
// isPtr32()
{
const ip = w.isPtr32;
T.assert(ip(0))
.assert(!ip(-1))
.assert(!ip(1.1))
.assert(!ip(0xffffffff))
.assert(ip(0x7fffffff))
.assert(!ip())
.assert(!ip(null)/*might change: under consideration*/)
;
}
//log("jstrlen()...");
{
T.assert(3 === w.jstrlen("abc")).assert(4 === w.jstrlen("äbc"));
@ -1056,6 +1069,49 @@
////////////////////////////////////////////////////////////////////
;/*end of C/WASM utils checks*/
T.g('sqlite3_randomness()')
.t('To memory buffer', function(sqlite3){
const stack = wasm.pstack.pointer;
try{
const n = 520;
const p = wasm.pstack.alloc(n);
T.assert(0===wasm.getMemValue(p))
.assert(0===wasm.getMemValue(p+n-1));
T.assert(undefined === capi.sqlite3_randomness(n - 10, p));
let j, check = 0;
const heap = wasm.heap8u();
for(j = 0; j < 10 && 0===check; ++j){
check += heap[p + j];
}
T.assert(check > 0);
check = 0;
// Ensure that the trailing bytes were not modified...
for(j = n - 10; j < n && 0===check; ++j){
check += heap[p + j];
}
T.assert(0===check);
}finally{
wasm.pstack.restore(stack);
}
})
.t('To byte array', function(sqlite3){
const ta = new Uint8Array(117);
let i, n = 0;
for(i=0; i<ta.byteLength && 0===n; ++i){
n += ta[i];
}
T.assert(0===n)
.assert(ta === capi.sqlite3_randomness(ta));
for(i=ta.byteLength-10; i<ta.byteLength && 0===n; ++i){
n += ta[i];
}
T.assert(n>0);
const t0 = new Uint8Array(0);
T.assert(t0 === capi.sqlite3_randomness(t0),
"0-length array is a special case");
})
;;/*end sqlite3_randomness() checks*/
////////////////////////////////////////////////////////////////////////
T.g('sqlite3.oo1')
.t('Create db', function(sqlite3){

View File

@ -1,5 +1,5 @@
C Disable\sthe\spush-down\soptimization\sfor\ssub-queries\sthat\sare\sINTERSECT,\sUNION\sor\sEXCEPT\scompounds.\sdbsqlfuzz\sa34f455c91ad75a0cf8cd9476841903f42930a7a.
D 2022-10-26T21:14:21.853
C Expose\ssqlite3_randomness()\sto\sWASM\sand\sadd\sa\scustom\sbinding\sfor\sit\swhich\scan\spopulate\sa\sJS\sbyte\sarray.\sAdd\sWhWasmUtil.isPtr().
D 2022-10-27T03:03:16.460
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -490,7 +490,7 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34ce
F ext/wasm/GNUmakefile 7a8c06f9bdbb791f8ef084ecd47e099da81e5797b9b1d60e33ac9a07eedd5dbd
F ext/wasm/README-dist.txt 2d670b426fc7c613b90a7d2f2b05b433088fe65181abead970980f0a4a75ea20
F ext/wasm/README.md 1e5b28158b74ab3ffc9d54fcbc020f0bbeb82c2ff8bbd904214c86c70e8a3066
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 36f413ab4dbb057d2dec938fb366ac0a4c5e85ba14660a8d672f0277602c0fc5
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api cfbe8efcb9d1444139d6c381eb54dfdd5e62cf1ddfe10ed8a38617149a664b9b
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
F ext/wasm/api/README.md 1350088aee90e959ad9a94fab1bb6bcb5e99d4d27f976db389050f54f2640c78
F ext/wasm/api/extern-post-js.js 926d192b72fa808378e5e7843721dc7ba3908c163a0260e06d8aa501c12f5469
@ -499,10 +499,10 @@ F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08
F ext/wasm/api/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1ecc5ba9e64ef90a8b
F ext/wasm/api/pre-js.js 151e0616614a49f3db19ed544fa13b38c87c108959fbcd4029ea8399a562d94f
F ext/wasm/api/sqlite3-api-cleanup.js 4d07a7524dc9b7b050acfde57163e839243ad2383bd7ee0de0178b1b3e988588
F ext/wasm/api/sqlite3-api-glue.js 6e4e472eb5afc732a695cd7c5ded6dee6ef8b480e61aa0d648a3fc9033c84745
F ext/wasm/api/sqlite3-api-glue.js f024dc2f41418ad203edf1228d7cf7934249c11ffcbb65d21f9bb69333d63d55
F ext/wasm/api/sqlite3-api-oo1.js 38004e18001396c078124769e14737a0ff703f98317279734020121af72efdd5
F ext/wasm/api/sqlite3-api-opfs.js 62da8b7cac30d4e7bb940762d2ac948b0aeb89704a5a290b74eb268ecbd1a64e
F ext/wasm/api/sqlite3-api-prologue.js fa00d55f927e5a4ec51cf2c80f6f0eaed2f4f5774341ecf3d63a0ea4c738f8f5
F ext/wasm/api/sqlite3-api-prologue.js 4706ca7fa125426019b8dd01f9e6a774021a2781874df8b9f435c69544383e79
F ext/wasm/api/sqlite3-api-worker1.js b2d650514ccc75f80dff666fd3ee68dc8fb4137bcd01caac2c62ff93a7ebf638
F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3
F ext/wasm/api/sqlite3-opfs-async-proxy.js f04cb1eb483c92bc61fe02749f7afcf17ec803968171aedd7d96faf428c26bcb
@ -515,7 +515,7 @@ F ext/wasm/batch-runner.js 5bae81684728b6be157d1f92b39824153f0fd019345b39f2ab893
F ext/wasm/common/SqliteTestUtil.js 647bf014bd30bdd870a7e9001e251d12fc1c9ec9ce176a1004b838a4b33c5c05
F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
F ext/wasm/common/testing.css 739b58c44511f642f16f57b701c84dc9ee412d8bc47b3d8a99d947babfa69d9d
F ext/wasm/common/whwasmutil.js 50d2ede0b0fa01c1d467e1801fab79f5e46bb02bcbd2b0232e4fdc6090a47818
F ext/wasm/common/whwasmutil.js 77930367c2a65cf6fd6f99ad3644ede33e4d20466f5e506eb87b8d101a0a7655
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6
@ -548,7 +548,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
F ext/wasm/test-opfs-vfs.js 48fc59110e8775bb43c9be25b6d634fc07ebadab7da8fbd44889e8129c6e2548
F ext/wasm/tester1-worker.html d02b9d38876b023854cf8955e77a40912f7e516956b4dbe1ec7f215faac273ee
F ext/wasm/tester1.html c6c47e5a8071eb09cb1301104435c8e44fbb5719c92411f5b2384a461f9793c5
F ext/wasm/tester1.js 21dad63165954a8a28dfa8eeab68a4881eb1d9b5c6bc9f2c83aa3ceea8c41fee
F ext/wasm/tester1.js 39dd4277944f79f475d78850ebbf48b0fe61b2caf4df2b5c5db6219f509d5c96
F ext/wasm/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd72273503ae7d5
F ext/wasm/wasmfs.make ee0004813e16c283ff633e08b482008d56adf9b7d42f6c5612f7ab002b924f69
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
@ -2052,8 +2052,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 a029dddff4f4ed7275538610cbd9cea658b905b72924860ec9cda9e76dabcfac
R b53abb7e437a17a51c11b8e565290cc0
U dan
Z f234279171f2df9582a4a9e673101d04
P 346a3b12b861ce7ba369e98cd336f79a1d4f7a7bb9acd7a4f63f37b391755bf5
R 5867ff413088cae7742d2b15dc451b3e
U stephan
Z c456212142f768a2abd7eb0069fb1a57
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
346a3b12b861ce7ba369e98cd336f79a1d4f7a7bb9acd7a4f63f37b391755bf5
333e67076b4bc967bb543ef8e265c63f6e3498c38ac121a7d1eff4a1d7a71c63