aboutsummaryrefslogtreecommitdiff
path: root/ext/wasm/api
diff options
context:
space:
mode:
authorstephan <stephan@noemail.net>2022-12-10 10:24:46 +0000
committerstephan <stephan@noemail.net>2022-12-10 10:24:46 +0000
commit5c99d91e53cd08058b3e6737945bd9a773289ec4 (patch)
tree98ea602c92e50530853fda733e4cf123b22d11fc /ext/wasm/api
parent8ccef8f27f93b29e3d1a634f928169c8744bc07d (diff)
downloadsqlite-5c99d91e53cd08058b3e6737945bd9a773289ec4.tar.gz
sqlite-5c99d91e53cd08058b3e6737945bd9a773289ec4.zip
Refactor the internal JS routines for converting UDF results and errors to JS into public APIs.
FossilOrigin-Name: 35d1d63c7d60119b64341c561294890812837d5432d1d7bed3ed88d6212fbfa0
Diffstat (limited to 'ext/wasm/api')
-rw-r--r--ext/wasm/api/sqlite3-api-glue.js144
-rw-r--r--ext/wasm/api/sqlite3-api-prologue.js141
2 files changed, 159 insertions, 126 deletions
diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js
index e98393df6..89420cbf5 100644
--- a/ext/wasm/api/sqlite3-api-glue.js
+++ b/ext/wasm/api/sqlite3-api-glue.js
@@ -324,102 +324,31 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
"*"/*xInverse*/, "*"/*xDestroy*/]
);
- const __udfSetResult = function(pCtx, val){
- //console.warn("udfSetResult",typeof val, val);
- switch(typeof val) {
- case 'undefined':
- /* Assume that the client already called sqlite3_result_xxx(). */
- break;
- case 'boolean':
- capi.sqlite3_result_int(pCtx, val ? 1 : 0);
- break;
- case 'bigint':
- if(wasm.bigIntEnabled){
- if(util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val);
- else toss3("BigInt value",val.toString(),"is too BigInt for int64.");
- }else if(util.bigIntFits32(val)){
- capi.sqlite3_result_int(pCtx, Number(val));
- }else if(util.bigIntFitsDouble(val)){
- capi.sqlite3_result_double(pCtx, Number(val));
- }else{
- toss3("BigInt value",val.toString(),"is too BigInt.");
- }
- break;
- case 'number': {
- (util.isInt32(val)
- ? capi.sqlite3_result_int
- : capi.sqlite3_result_double)(pCtx, val);
- break;
- }
- case 'string':
- capi.sqlite3_result_text(pCtx, val, -1, capi.SQLITE_TRANSIENT);
- break;
- case 'object':
- if(null===val/*yes, typeof null === 'object'*/) {
- capi.sqlite3_result_null(pCtx);
- break;
- }else if(util.isBindableTypedArray(val)){
- const pBlob = wasm.allocFromTypedArray(val);
- capi.sqlite3_result_blob(
- pCtx, pBlob, val.byteLength,
- wasm.exports[sqlite3.config.deallocExportName]
- );
- break;
- }
- // else fall through
- default:
- toss3("Don't not how to handle this UDF result value:",(typeof val), val);
- };
- }/*__udfSetResult()*/;
-
- const __udfConvertArgs = function(argc, pArgv){
- let i;
- const tgt = [];
- for(i = 0; i < argc; ++i){
- /**
- Curiously: despite ostensibly requiring 8-byte
- alignment, the pArgv array is parcelled into chunks of
- 4 bytes (1 pointer each). The values those point to
- have 8-byte alignment but the individual argv entries
- do not.
- */
- tgt.push(capi.sqlite3_value_to_js(
- wasm.peekPtr(pArgv + (wasm.ptrSizeof * i))
- ));
- }
- return tgt;
- }/*__udfConvertArgs()*/;
-
- const __udfSetError = (pCtx, e)=>{
- if(e instanceof sqlite3.WasmAllocError){
- capi.sqlite3_result_error_nomem(pCtx);
- }else{
- const msg = ('string'===typeof e) ? e : e.message;
- capi.sqlite3_result_error(pCtx, msg, -1);
- }
- };
-
const __xFunc = function(callback){
return function(pCtx, argc, pArgv){
- try{ __udfSetResult(pCtx, callback(pCtx, ...__udfConvertArgs(argc, pArgv))) }
- catch(e){
+ try{
+ capi.sqlite3_result_js(
+ pCtx,
+ callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv))
+ );
+ }catch(e){
//console.error('xFunc() caught:',e);
- __udfSetError(pCtx, e);
+ capi.sqlite3_result_error_js(pCtx, e);
}
};
};
const __xInverseAndStep = function(callback){
return function(pCtx, argc, pArgv){
- try{ callback(pCtx, ...__udfConvertArgs(argc, pArgv)) }
- catch(e){ __udfSetError(pCtx, e) }
+ try{ callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)) }
+ catch(e){ capi.sqlite3_result_error_js(pCtx, e) }
};
};
const __xFinalAndValue = function(callback){
return function(pCtx){
- try{ __udfSetResult(pCtx, callback(pCtx)) }
- catch(e){ __udfSetError(pCtx, e) }
+ try{ capi.sqlite3_result_js(pCtx, callback(pCtx)) }
+ catch(e){ capi.sqlite3_result_error_js(pCtx, e) }
};
};
@@ -525,61 +454,28 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
return rc;
};
/**
- A helper for UDFs implemented in JS and bound to WASM by the
- client. Given a JS value, udfSetResult(pCtx,X) calls one of the
- sqlite3_result_xyz(pCtx,...) routines, depending on X's data
- type:
-
- - `null`: sqlite3_result_null()
- - `boolean`: sqlite3_result_int()
- - `number`: sqlite3_result_int() or sqlite3_result_double()
- - `string`: sqlite3_result_text()
- - Uint8Array or Int8Array: sqlite3_result_blob()
- - `undefined`: indicates that the UDF called one of the
- `sqlite3_result_xyz()` routines on its own, making this
- function a no-op. Results are _undefined_ if this function is
- passed the `undefined` value but did _not_ call one of the
- `sqlite3_result_xyz()` routines.
-
- Anything else triggers sqlite3_result_error().
+ A _deprecated_ alias for capi.sqlite3_result_js() which
+ predates the addition of that function in the public API.
*/
capi.sqlite3_create_function_v2.udfSetResult =
capi.sqlite3_create_function.udfSetResult =
- capi.sqlite3_create_window_function.udfSetResult = __udfSetResult;
+ capi.sqlite3_create_window_function.udfSetResult = capi.sqlite3_result_js;
/**
- A helper for UDFs implemented in JS and bound to WASM by the
- client. When passed the
- (argc,argv) values from the UDF-related functions which receive
- them (xFunc, xStep, xInverse), it creates a JS array
- representing those arguments, converting each to JS in a manner
- appropriate to its data type: numeric, text, blob
- (Uint8Array), or null.
-
- Results are undefined if it's passed anything other than those
- two arguments from those specific contexts.
-
- Thus an argc of 4 will result in a length-4 array containing
- the converted values from the corresponding argv.
-
- The conversion will throw only on allocation error or an internal
- error.
+ A _deprecated_ alias for capi.sqlite3_values_to_js() which
+ predates the addition of that function in the public API.
*/
capi.sqlite3_create_function_v2.udfConvertArgs =
capi.sqlite3_create_function.udfConvertArgs =
- capi.sqlite3_create_window_function.udfConvertArgs = __udfConvertArgs;
+ capi.sqlite3_create_window_function.udfConvertArgs = capi.sqlite3_values_to_js;
/**
- A helper for UDFs implemented in JS and bound to WASM by the
- client. It expects to be a passed `(sqlite3_context*, Error)`
- (an exception object or message string). And it sets the
- current UDF's result to sqlite3_result_error_nomem() or
- sqlite3_result_error(), depending on whether the 2nd argument
- is a sqlite3.WasmAllocError object or not.
+ A _deprecated_ alias for capi.sqlite3_result_error_js() which
+ predates the addition of that function in the public API.
*/
capi.sqlite3_create_function_v2.udfSetError =
capi.sqlite3_create_function.udfSetError =
- capi.sqlite3_create_window_function.udfSetError = __udfSetError;
+ capi.sqlite3_create_window_function.udfSetError = capi.sqlite3_result_error_js;
}/*sqlite3_create_function_v2() and sqlite3_create_window_function() proxies*/;
diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js
index e28f459c2..219581801 100644
--- a/ext/wasm/api/sqlite3-api-prologue.js
+++ b/ext/wasm/api/sqlite3-api-prologue.js
@@ -1672,9 +1672,11 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
By default it throws if it cannot determine any sensible
conversion. If passed a falsy second argument, it instead returns
- `undefined` if no suitable conversion is found. Note that there
+ `undefined` if no suitable conversion is found. Note that there
is no conversion from SQL to JS which results in the `undefined`
- value, so `undefined` has an unambiguous meaning here.
+ value, so `undefined` has an unambiguous meaning here. It will
+ always throw a WasmAllocError if allocating memory for a
+ conversion fails.
Caveats:
@@ -1723,6 +1725,141 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
return arg;
};
+ /**
+ Requires a C-style array of `sqlite3_value*` objects and the
+ number of entries in that array. Returns a JS array containing
+ the results of passing each C array entry to
+ sqlite3_value_to_js(). The 3rd argument to this function is
+ passed on as the 2nd argument to that one.
+ */
+ capi.sqlite3_values_to_js = function(argc,pArgv,throwIfCannotConvert=true){
+ let i;
+ const tgt = [];
+ for(i = 0; i < argc; ++i){
+ /**
+ Curiously: despite ostensibly requiring 8-byte
+ alignment, the pArgv array is parcelled into chunks of
+ 4 bytes (1 pointer each). The values those point to
+ have 8-byte alignment but the individual argv entries
+ do not.
+ */
+ tgt.push(capi.sqlite3_value_to_js(
+ wasm.peekPtr(pArgv + (wasm.ptrSizeof * i))
+ ));
+ }
+ return tgt;
+ };
+
+ /**
+ Calls either sqlite3_result_error_nomem(), if e is-a
+ WasmAllocError, or sqlite3_result_error(). In the latter case,
+ the second arugment is coerced to a string to create the error
+ message.
+
+ The first argument is a (sqlite3_context*). Returns void.
+ Does not throw.
+ */
+ capi.sqlite3_result_error_js = function(pCtx,e){
+ if(e instanceof WasmAllocError){
+ capi.sqlite3_result_error_nomem(pCtx);
+ }else{
+ /* Maintenance reminder: ''+e, rather than e.message,
+ will prefix e.message with e.name, so it includes
+ the exception's type name in the result. */;
+ capi.sqlite3_result_error(pCtx, ''+e, -1);
+ }
+ };
+
+ /**
+ This function passes its 2nd argument to one of the
+ sqlite3_result_xyz() routines, depending on the type of that
+ argument:
+
+ - If (val instanceof Error), this function passes it to
+ sqlite3_result_error_js().
+ - `null`: `sqlite3_result_null()`
+ - `boolean`: `sqlite3_result_int()` with a value of 0 or 1.
+ - `number`: `sqlite3_result_int()`, `sqlite3_result_int64()`, or
+ `sqlite3_result_double()`, depending on the range of the number
+ and whether or not int64 support is enabled.
+ - `bigint`: similar to `number` but will trigger an error if the
+ value is too big to store in an int64.
+ - `string`: `sqlite3_result_text()`
+ - Uint8Array or Int8Array: `sqlite3_result_blob()`
+ - `undefined`: is a no-op provided to simplify certain use cases.
+
+ Anything else triggers `sqlite3_result_error()` with a
+ description of the problem.
+
+ The first argument to this function is a `(sqlite3_context*)`.
+ Returns void. Does not throw.
+ */
+ capi.sqlite3_result_js = function(pCtx,val){
+ if(val instanceof Error){
+ capi.sqlite3_result_error_js(pCtx, val);
+ return;
+ }
+ try{
+ switch(typeof val) {
+ case 'undefined':
+ /* This is a no-op. This routine originated in the create_function()
+ family of APIs and in that context, passing in undefined indicated
+ that the caller was responsible for calling sqlite3_result_xxx()
+ (if needed). */
+ break;
+ case 'boolean':
+ capi.sqlite3_result_int(pCtx, val ? 1 : 0);
+ break;
+ case 'bigint':
+ if(util.bigIntFits32(val)){
+ capi.sqlite3_result_int(pCtx, Number(val));
+ }else if(util.bigIntFitsDouble(val)){
+ capi.sqlite3_result_double(pCtx, Number(val));
+ }else if(wasm.bigIntEnabled){
+ if(util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val);
+ else toss3("BigInt value",val.toString(),"is too BigInt for int64.");
+ }else{
+ toss3("BigInt value",val.toString(),"is too BigInt.");
+ }
+ break;
+ case 'number': {
+ let f;
+ if(util.isInt32(val)){
+ f = capi.sqlite3_result_int;
+ }else if(wasm.bigIntEnabled
+ && Number.isInteger(val)
+ && util.bigIntFits64(BigInt(val))){
+ f = capi.sqlite3_result_int64;
+ }else{
+ f = capi.sqlite3_result_double;
+ }
+ f(pCtx, val);
+ break;
+ }
+ case 'string':
+ capi.sqlite3_result_text(pCtx, val, -1, capi.SQLITE_TRANSIENT);
+ break;
+ case 'object':
+ if(null===val/*yes, typeof null === 'object'*/) {
+ capi.sqlite3_result_null(pCtx);
+ break;
+ }else if(util.isBindableTypedArray(val)){
+ const pBlob = wasm.allocFromTypedArray(val);
+ capi.sqlite3_result_blob(
+ pCtx, pBlob, val.byteLength,
+ wasm.exports[sqlite3.config.deallocExportName]
+ );
+ break;
+ }
+ // else fall through
+ default:
+ toss3("Don't not how to handle this UDF result value:",(typeof val), val);
+ }
+ }catch(e){
+ capi.sqlite3_result_error_js(pCtx, e);
+ }
+ };
+
/* The remainder of the API will be set up in later steps. */
const sqlite3 = {
WasmAllocError: WasmAllocError,