js: implement a hand-written wrapper for sqlite3_create_function_v2() which converts, if necessary, JS-function-type args to WASM function wrappers. Replace DB.createFunction() impl with the new one.
FossilOrigin-Name: 435ab33384017967e46f52b70bee851a85a28808990a0e58dd5288f606b89c9c
This commit is contained in:
parent
9fdce595eb
commit
9892883e9e
@ -47,13 +47,9 @@ browser client:
|
|||||||
independent spinoff project, conceived for the sqlite3 project but
|
independent spinoff project, conceived for the sqlite3 project but
|
||||||
maintained separately.
|
maintained separately.
|
||||||
- `sqlite3-api-glue.js`\
|
- `sqlite3-api-glue.js`\
|
||||||
Invokes the function exposed by `sqlite3-api-prologue.js`, passing
|
Invokes functionality exposed by the previous two files to
|
||||||
it a configuration object to configure it for the current WASM
|
flesh out low-level parts of `sqlite3-api-prologue.js`. Most of
|
||||||
toolchain (noting that it currently requires Emscripten), then
|
these pieces related to the `sqlite3.capi.wasm` object.
|
||||||
removes that function from the global scope. The result of this file
|
|
||||||
is a global-scope `sqlite3` object which acts as a namespace for the
|
|
||||||
API's functionality. This object gets removed from the global scope
|
|
||||||
after the following files have attached their own features to it.
|
|
||||||
- `sqlite3-api-oo1.js`\
|
- `sqlite3-api-oo1.js`\
|
||||||
Provides a high-level object-oriented wrapper to the lower-level C
|
Provides a high-level object-oriented wrapper to the lower-level C
|
||||||
API, colloquially known as OO API #1. Its API is similar to other
|
API, colloquially known as OO API #1. Its API is similar to other
|
||||||
|
@ -192,6 +192,183 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
};
|
};
|
||||||
}/*sqlite3_exec() proxy*/;
|
}/*sqlite3_exec() proxy*/;
|
||||||
|
|
||||||
|
if(1){/* Special-case handling of sqlite3_create_function_v2() */
|
||||||
|
const sqlite3CreateFunction = wasm.xWrap(
|
||||||
|
"sqlite3_create_function_v2", "int",
|
||||||
|
["sqlite3*", "string", "int", "int", "*",
|
||||||
|
"*", "*", "*", "*"]
|
||||||
|
);
|
||||||
|
const __setResult = function(pCx, val){
|
||||||
|
switch(typeof val) {
|
||||||
|
case 'boolean':
|
||||||
|
capi.sqlite3_result_int(pCx, val ? 1 : 0);
|
||||||
|
break;
|
||||||
|
case 'number': {
|
||||||
|
(util.isInt32(val)
|
||||||
|
? capi.sqlite3_result_int
|
||||||
|
: capi.sqlite3_result_double)(pCx, val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'string':
|
||||||
|
capi.sqlite3_result_text(pCx, val, -1, capi.SQLITE_TRANSIENT);
|
||||||
|
break;
|
||||||
|
case 'object':
|
||||||
|
if(null===val) {
|
||||||
|
capi.sqlite3_result_null(pCx);
|
||||||
|
break;
|
||||||
|
}else if(util.isBindableTypedArray(val)){
|
||||||
|
const pBlob = wasm.allocFromTypedArray(val);
|
||||||
|
capi.sqlite3_result_blob(pCx, pBlob, val.byteLength,
|
||||||
|
capi.SQLITE_TRANSIENT);
|
||||||
|
wasm.dealloc(pBlob);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// else fall through
|
||||||
|
default:
|
||||||
|
toss3("Don't not how to handle this UDF result value:",val);
|
||||||
|
};
|
||||||
|
}/*__setResult()*/;
|
||||||
|
const __extractArgs = function(argc, pArgv){
|
||||||
|
let i, pVal, valType, arg;
|
||||||
|
const tgt = [];
|
||||||
|
for(i = 0; i < argc; ++i){
|
||||||
|
pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * 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.
|
||||||
|
*/
|
||||||
|
valType = capi.sqlite3_value_type(pVal);
|
||||||
|
switch(valType){
|
||||||
|
case capi.SQLITE_INTEGER:
|
||||||
|
case capi.SQLITE_FLOAT:
|
||||||
|
arg = capi.sqlite3_value_double(pVal);
|
||||||
|
break;
|
||||||
|
case capi.SQLITE_TEXT:
|
||||||
|
arg = capi.sqlite3_value_text(pVal);
|
||||||
|
break;
|
||||||
|
case capi.SQLITE_BLOB:{
|
||||||
|
const n = capi.sqlite3_value_bytes(pVal);
|
||||||
|
const pBlob = capi.sqlite3_value_blob(pVal);
|
||||||
|
arg = new Uint8Array(n);
|
||||||
|
let i;
|
||||||
|
const heap = n ? wasm.heap8() : false;
|
||||||
|
for(i = 0; i < n; ++i) arg[i] = heap[pBlob+i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case capi.SQLITE_NULL:
|
||||||
|
arg = null; break;
|
||||||
|
default:
|
||||||
|
toss3("Unhandled sqlite3_value_type()",valType,
|
||||||
|
"is possibly indicative of incorrect",
|
||||||
|
"pointer size assumption.");
|
||||||
|
}
|
||||||
|
tgt.push(arg);
|
||||||
|
}
|
||||||
|
return tgt;
|
||||||
|
}/*__extractArgs()*/;
|
||||||
|
|
||||||
|
const __setCxErr = (pCx, e)=>{
|
||||||
|
if(e instanceof capi.WasmAllocError){
|
||||||
|
capi.sqlite3_result_error_nomem(pCx);
|
||||||
|
}else{
|
||||||
|
capi.sqlite3_result_error(pCx, e.message, -1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const __xFunc = function(callback){
|
||||||
|
return function(pCx, argc, pArgv){
|
||||||
|
try{__setResult(pCx, callback(...__extractArgs(argc, pArgv)))}
|
||||||
|
catch(e){ __setCxErr(pCx, e) }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const __xStep = function(callback){
|
||||||
|
return function(pCx, argc, pArgv){
|
||||||
|
try{ callback(...__extractArgs(argc, pArgv)) }
|
||||||
|
catch(e){ __setCxErr(pCx, e) }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const __xFinal = function(callback){
|
||||||
|
return function(pCx){
|
||||||
|
try{ __setResult(pCx, callback()) }
|
||||||
|
catch(e){ __setCxErr(pCx, e) }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const __xDestroy = function(callback){
|
||||||
|
return function(pVoid){
|
||||||
|
try{ callback(pVoid) }
|
||||||
|
catch(e){
|
||||||
|
console.error("UDF xDestroy method threw:",e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/* Documented in the api object's initializer. */
|
||||||
|
capi.sqlite3_create_function_v2 = function f(
|
||||||
|
pDb, funcName, nArg, eTextRep, pApp,
|
||||||
|
xFunc, //void (*xFunc)(sqlite3_context*,int,sqlite3_value**)
|
||||||
|
xStep, //void (*xStep)(sqlite3_context*,int,sqlite3_value**)
|
||||||
|
xFinal, //void (*xFinal)(sqlite3_context*)
|
||||||
|
xDestroy //void (*xDestroy)(void*)
|
||||||
|
){
|
||||||
|
if(9!==arguments.length){
|
||||||
|
return __dbArgcMismatch(pDb,"sqlite3_create_function_v2",9);
|
||||||
|
}
|
||||||
|
if(!f._sigs){
|
||||||
|
f._wrap = Object.assign(Object.create(null), {
|
||||||
|
xFunc: {sig:'v(pip)', f:__xFunc},
|
||||||
|
xStep: {sig:'v(pip)', f:__xStep},
|
||||||
|
xFinal: {sig:'v(p)', f:__xFinal},
|
||||||
|
xDestroy: {sig:'v(p)', f:__xDestroy}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const callbacks = [];
|
||||||
|
/* Wrap the callbacks in a WASM-bound functions... */
|
||||||
|
const wasm = capi.wasm;
|
||||||
|
const funcArgs = [], uninstall = [/*funcs to uninstall on error*/],
|
||||||
|
theFuncs = {xFunc, xStep, xFinal, xDestroy};
|
||||||
|
let rc;
|
||||||
|
try{
|
||||||
|
let k;
|
||||||
|
for(k in theFuncs){
|
||||||
|
let fArg = theFuncs[k];
|
||||||
|
if('function'===typeof fArg){
|
||||||
|
const w = f._wrap[k];
|
||||||
|
fArg = wasm.installFunction(w.sig, w.f(fArg));
|
||||||
|
uninstall.push(fArg);
|
||||||
|
}
|
||||||
|
funcArgs.push(fArg);
|
||||||
|
}
|
||||||
|
rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep,
|
||||||
|
pApp, ...funcArgs);
|
||||||
|
}catch(e){
|
||||||
|
console.error("sqlite3_create_function_v2() setup threw:",e);
|
||||||
|
for(let v of uninstall){
|
||||||
|
wasm.uninstallFunction(v);
|
||||||
|
}
|
||||||
|
rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
|
||||||
|
"Creation of UDF threw: "+e.message);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
};
|
||||||
|
|
||||||
|
capi.sqlite3_create_function = function(
|
||||||
|
pDb, funcName, nArg, eTextRep, pApp,
|
||||||
|
xFunc, xStep, xFinal
|
||||||
|
){
|
||||||
|
if(8!==arguments.length){
|
||||||
|
return __dbArgcMismatch(pDb,"sqlite3_create_function",8);
|
||||||
|
}
|
||||||
|
return capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep,
|
||||||
|
pApp, xFunc, xStep, xFinal, 0);
|
||||||
|
|
||||||
|
};
|
||||||
|
}/*sqlite3_create_function_v2() proxy*/;
|
||||||
|
|
||||||
if(1){/* Special-case handling of sqlite3_prepare_v2() and
|
if(1){/* Special-case handling of sqlite3_prepare_v2() and
|
||||||
sqlite3_prepare_v3() */
|
sqlite3_prepare_v3() */
|
||||||
/**
|
/**
|
||||||
|
@ -37,12 +37,6 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
it.
|
it.
|
||||||
*/
|
*/
|
||||||
const __ptrMap = new WeakMap();
|
const __ptrMap = new WeakMap();
|
||||||
/**
|
|
||||||
Map of DB instances to objects, each object being a map of UDF
|
|
||||||
names to wasm function _pointers_ added to that DB handle via
|
|
||||||
createFunction().
|
|
||||||
*/
|
|
||||||
const __udfMap = new WeakMap();
|
|
||||||
/**
|
/**
|
||||||
Map of DB instances to objects, each object being a map of Stmt
|
Map of DB instances to objects, each object being a map of Stmt
|
||||||
wasm pointers to Stmt objects.
|
wasm pointers to Stmt objects.
|
||||||
@ -154,7 +148,6 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
this.filename = fnJs;
|
this.filename = fnJs;
|
||||||
__ptrMap.set(this, ptr);
|
__ptrMap.set(this, ptr);
|
||||||
__stmtMap.set(this, Object.create(null));
|
__stmtMap.set(this, Object.create(null));
|
||||||
__udfMap.set(this, Object.create(null));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -453,12 +446,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
Object.keys(__stmtMap.get(this)).forEach((k,s)=>{
|
Object.keys(__stmtMap.get(this)).forEach((k,s)=>{
|
||||||
if(s && s.pointer) s.finalize();
|
if(s && s.pointer) s.finalize();
|
||||||
});
|
});
|
||||||
Object.values(__udfMap.get(this)).forEach(
|
|
||||||
wasm.uninstallFunction.bind(capi.wasm)
|
|
||||||
);
|
|
||||||
__ptrMap.delete(this);
|
__ptrMap.delete(this);
|
||||||
__stmtMap.delete(this);
|
__stmtMap.delete(this);
|
||||||
__udfMap.delete(this);
|
|
||||||
capi.sqlite3_close_v2(pDb);
|
capi.sqlite3_close_v2(pDb);
|
||||||
if(this.onclose && (this.onclose.after instanceof Function)){
|
if(this.onclose && (this.onclose.after instanceof Function)){
|
||||||
try{this.onclose.after(this)}
|
try{this.onclose.after(this)}
|
||||||
@ -785,15 +774,11 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
|
|
||||||
On success, returns this object. Throws on error.
|
On success, returns this object. Throws on error.
|
||||||
|
|
||||||
When called from SQL, arguments to the UDF, and its result,
|
When called from SQL arguments to the UDF, and its result,
|
||||||
will be converted between JS and SQL with as much fidelity
|
will be converted between JS and SQL with as much fidelity as
|
||||||
as is feasible, triggering an exception if a type
|
is feasible, triggering an exception if a type conversion
|
||||||
conversion cannot be determined. Some freedom is afforded
|
cannot be determined. The docs for sqlite3_create_function_v2()
|
||||||
to numeric conversions due to friction between the JS and C
|
describe the conversions in more detail.
|
||||||
worlds: integers which are larger than 32 bits will be
|
|
||||||
treated as doubles, as JS does not support 64-bit integers
|
|
||||||
and it is (as of this writing) illegal to use WASM
|
|
||||||
functions which take or return 64-bit integers from JS.
|
|
||||||
|
|
||||||
The optional options object may contain flags to modify how
|
The optional options object may contain flags to modify how
|
||||||
the function is defined:
|
the function is defined:
|
||||||
@ -813,11 +798,6 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
- .deterministic = SQLITE_DETERMINISTIC
|
- .deterministic = SQLITE_DETERMINISTIC
|
||||||
- .directOnly = SQLITE_DIRECTONLY
|
- .directOnly = SQLITE_DIRECTONLY
|
||||||
- .innocuous = SQLITE_INNOCUOUS
|
- .innocuous = SQLITE_INNOCUOUS
|
||||||
|
|
||||||
Maintenance reminder: the ability to add new
|
|
||||||
WASM-accessible functions to the runtime requires that the
|
|
||||||
WASM build is compiled with emcc's `-sALLOW_TABLE_GROWTH`
|
|
||||||
flag.
|
|
||||||
*/
|
*/
|
||||||
createFunction: function f(name, callback,opt){
|
createFunction: function f(name, callback,opt){
|
||||||
switch(arguments.length){
|
switch(arguments.length){
|
||||||
@ -840,113 +820,16 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
}else if('string' !== typeof name){
|
}else if('string' !== typeof name){
|
||||||
toss3("Invalid arguments: missing function name.");
|
toss3("Invalid arguments: missing function name.");
|
||||||
}
|
}
|
||||||
if(!f._extractArgs){
|
|
||||||
/* Static init */
|
|
||||||
f._extractArgs = function(argc, pArgv){
|
|
||||||
let i, pVal, valType, arg;
|
|
||||||
const tgt = [];
|
|
||||||
for(i = 0; i < argc; ++i){
|
|
||||||
pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * 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.
|
|
||||||
*/
|
|
||||||
valType = capi.sqlite3_value_type(pVal);
|
|
||||||
switch(valType){
|
|
||||||
case capi.SQLITE_INTEGER:
|
|
||||||
case capi.SQLITE_FLOAT:
|
|
||||||
arg = capi.sqlite3_value_double(pVal);
|
|
||||||
break;
|
|
||||||
case capi.SQLITE_TEXT:
|
|
||||||
arg = capi.sqlite3_value_text(pVal);
|
|
||||||
break;
|
|
||||||
case capi.SQLITE_BLOB:{
|
|
||||||
const n = capi.sqlite3_value_bytes(pVal);
|
|
||||||
const pBlob = capi.sqlite3_value_blob(pVal);
|
|
||||||
arg = new Uint8Array(n);
|
|
||||||
let i;
|
|
||||||
const heap = n ? wasm.heap8() : false;
|
|
||||||
for(i = 0; i < n; ++i) arg[i] = heap[pBlob+i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case capi.SQLITE_NULL:
|
|
||||||
arg = null; break;
|
|
||||||
default:
|
|
||||||
toss3("Unhandled sqlite3_value_type()",valType,
|
|
||||||
"is possibly indicative of incorrect",
|
|
||||||
"pointer size assumption.");
|
|
||||||
}
|
|
||||||
tgt.push(arg);
|
|
||||||
}
|
|
||||||
return tgt;
|
|
||||||
}/*_extractArgs()*/;
|
|
||||||
f._setResult = function(pCx, val){
|
|
||||||
switch(typeof val) {
|
|
||||||
case 'boolean':
|
|
||||||
capi.sqlite3_result_int(pCx, val ? 1 : 0);
|
|
||||||
break;
|
|
||||||
case 'number': {
|
|
||||||
(util.isInt32(val)
|
|
||||||
? capi.sqlite3_result_int
|
|
||||||
: capi.sqlite3_result_double)(pCx, val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'string':
|
|
||||||
capi.sqlite3_result_text(pCx, val, -1, capi.SQLITE_TRANSIENT);
|
|
||||||
break;
|
|
||||||
case 'object':
|
|
||||||
if(null===val) {
|
|
||||||
capi.sqlite3_result_null(pCx);
|
|
||||||
break;
|
|
||||||
}else if(util.isBindableTypedArray(val)){
|
|
||||||
const pBlob = wasm.allocFromTypedArray(val);
|
|
||||||
capi.sqlite3_result_blob(pCx, pBlob, val.byteLength,
|
|
||||||
capi.SQLITE_TRANSIENT);
|
|
||||||
wasm.dealloc(pBlob);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// else fall through
|
|
||||||
default:
|
|
||||||
toss3("Don't not how to handle this UDF result value:",val);
|
|
||||||
};
|
|
||||||
}/*_setResult()*/;
|
|
||||||
}/*static init*/
|
|
||||||
const wrapper = function(pCx, argc, pArgv){
|
|
||||||
try{
|
|
||||||
f._setResult(pCx, callback.apply(null, f._extractArgs(argc, pArgv)));
|
|
||||||
}catch(e){
|
|
||||||
if(e instanceof capi.WasmAllocError){
|
|
||||||
capi.sqlite3_result_error_nomem(pCx);
|
|
||||||
}else{
|
|
||||||
capi.sqlite3_result_error(pCx, e.message, -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const pUdf = wasm.installFunction(wrapper, "v(iii)");
|
|
||||||
let fFlags = 0 /*flags for sqlite3_create_function_v2()*/;
|
let fFlags = 0 /*flags for sqlite3_create_function_v2()*/;
|
||||||
if(getOwnOption(opt, 'deterministic')) fFlags |= capi.SQLITE_DETERMINISTIC;
|
if(getOwnOption(opt, 'deterministic')) fFlags |= capi.SQLITE_DETERMINISTIC;
|
||||||
if(getOwnOption(opt, 'directOnly')) fFlags |= capi.SQLITE_DIRECTONLY;
|
if(getOwnOption(opt, 'directOnly')) fFlags |= capi.SQLITE_DIRECTONLY;
|
||||||
if(getOwnOption(opt, 'innocuous')) fFlags |= capi.SQLITE_INNOCUOUS;
|
if(getOwnOption(opt, 'innocuous')) fFlags |= capi.SQLITE_INNOCUOUS;
|
||||||
name = name.toLowerCase();
|
name = name.toLowerCase();
|
||||||
try {
|
|
||||||
DB.checkRc(this, capi.sqlite3_create_function_v2(
|
DB.checkRc(this, capi.sqlite3_create_function_v2(
|
||||||
this.pointer, name,
|
this.pointer, name,
|
||||||
(opt.hasOwnProperty('arity') ? +opt.arity : callback.length),
|
(opt.hasOwnProperty('arity') ? +opt.arity : callback.length),
|
||||||
capi.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf,
|
capi.SQLITE_UTF8 | fFlags, null/*pApp*/, callback,
|
||||||
null/*xStep*/, null/*xFinal*/, null/*xDestroy*/));
|
null/*xStep*/, null/*xFinal*/, null/*xDestroy*/));
|
||||||
}catch(e){
|
|
||||||
wasm.uninstallFunction(pUdf);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
const udfMap = __udfMap.get(this);
|
|
||||||
if(udfMap[name]){
|
|
||||||
try{wasm.uninstallFunction(udfMap[name])}
|
|
||||||
catch(e){/*ignore*/}
|
|
||||||
}
|
|
||||||
udfMap[name] = pUdf;
|
|
||||||
return this;
|
return this;
|
||||||
}/*createFunction()*/,
|
}/*createFunction()*/,
|
||||||
/**
|
/**
|
||||||
|
@ -285,29 +285,94 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
*/
|
*/
|
||||||
const capi = {
|
const capi = {
|
||||||
/**
|
/**
|
||||||
When using sqlite3_open_v2() it is important to keep the following
|
sqlite3_create_function_v2() differs from its native
|
||||||
in mind:
|
counterpart only in the following ways:
|
||||||
|
|
||||||
https://www.sqlite.org/c3ref/open.html
|
1) The fourth argument (`eTextRep`) argument must not specify
|
||||||
|
any encoding other than sqlite.SQLITE_UTF8. The JS API does not
|
||||||
|
currently support any other encoding and likely never
|
||||||
|
will. This function does not replace that argument on its own
|
||||||
|
because it may contain other flags.
|
||||||
|
|
||||||
- The flags for use with its 3rd argument are installed in this
|
2) Any of the four final arguments may be either WASM pointers
|
||||||
object using their C-side names, e.g. SQLITE_OPEN_CREATE.
|
(assumed to be function pointers) or JS Functions. In the
|
||||||
|
latter case, each gets bound to WASM using
|
||||||
|
sqlite3.capi.wasm.installFunction() and that wrapper is passed
|
||||||
|
on to the native implementation.
|
||||||
|
|
||||||
- If the combination of flags passed to it are invalid,
|
The semantics of JS functions are:
|
||||||
behavior is undefined. Thus is is never okay to call this
|
|
||||||
with fewer than 3 arguments, as JS will default the
|
|
||||||
missing arguments to `undefined`, which will result in a
|
|
||||||
flag value of 0. Most of the available SQLITE_OPEN_xxx
|
|
||||||
flags are meaningless in the WASM build, e.g. the mutext-
|
|
||||||
and cache-related flags, but they are retained in this
|
|
||||||
API for consistency's sake.
|
|
||||||
|
|
||||||
- The final argument to this function specifies the VFS to use,
|
xFunc: is passed `(arrayOfValues)`. Its return value becomes
|
||||||
which is largely (but not entirely!) meaningless in the WASM
|
the new SQL function's result.
|
||||||
environment. It may be null, undefined, or 0 to denote the
|
|
||||||
default.
|
xStep: is passed `(arrayOfValues)`. Its return value is
|
||||||
|
ignored.
|
||||||
|
|
||||||
|
xFinal: is passed no arguments. Its return value becomes the
|
||||||
|
new aggragate SQL function's result.
|
||||||
|
|
||||||
|
xDestroy: is passed `(void*)`. Its return value is ignored. The
|
||||||
|
pointer passed to it is the one from the 5th argument to
|
||||||
|
sqlite3_create_function_v2().
|
||||||
|
|
||||||
|
Note that JS callback implementations have different call
|
||||||
|
signatures than their native counterparts (namely, they do not
|
||||||
|
get passed an `sqlite3_context*` argument) because practice has
|
||||||
|
shown that this is almost always more convenient and desirable
|
||||||
|
in JS code. Clients which need access to the full range of
|
||||||
|
native arguments will have to create a WASM-bound function
|
||||||
|
themselves (using wasm.installFunction() or equivalent) and
|
||||||
|
pass that function's WASM pointer to this function, rather than
|
||||||
|
passing a JS function. Be warned, however, that working with
|
||||||
|
UDFs at that level from JS is quite tedious.
|
||||||
|
|
||||||
|
For xFunc(), xStep(), and xFinal():
|
||||||
|
|
||||||
|
- When called from SQL, arguments to the UDF, and its result,
|
||||||
|
will be converted between JS and SQL with as much fidelity as
|
||||||
|
is feasible, triggering an exception if a type conversion
|
||||||
|
cannot be determined. Some freedom is afforded to numeric
|
||||||
|
conversions due to friction between the JS and C worlds:
|
||||||
|
integers which are larger than 32 bits will be treated as
|
||||||
|
doubles. TODO: use BigInt support if enabled. That feature
|
||||||
|
was added after this functionality was implemented.
|
||||||
|
|
||||||
|
If any JS-side functions throw, those exceptions are
|
||||||
|
intercepted and converted to database-side errors with
|
||||||
|
the exception of xFinal(): any exception from it is
|
||||||
|
ignored, possibly generating a console.error() message.
|
||||||
|
Destructors must not throw.
|
||||||
|
|
||||||
|
Once installed, there is currently no way to uninstall the
|
||||||
|
bound methods from WASM. They can be uninstalled from the
|
||||||
|
database as documented in the C API, but this wrapper currently
|
||||||
|
has no infrastructure in place to also free the WASM-bound JS
|
||||||
|
wrappers, effectively resulting in a memory leak if the client
|
||||||
|
uninstalls the UDF. Improving that is a potential TODO, but
|
||||||
|
removing client-installed UDFs is rare in practice.
|
||||||
|
|
||||||
|
Maintenance reminder: the ability to add new
|
||||||
|
WASM-accessible functions to the runtime requires that the
|
||||||
|
WASM build is compiled with emcc's `-sALLOW_TABLE_GROWTH`
|
||||||
|
flag.
|
||||||
*/
|
*/
|
||||||
sqlite3_open_v2: function(filename,dbPtrPtr,flags,vfsStr){}/*installed later*/,
|
sqlite3_create_function_v2: function(
|
||||||
|
pDb, funcName, nArg, eTextRep, pApp,
|
||||||
|
xFunc, //function(arrayOfValues)
|
||||||
|
xStep, //function(arrayOfValues)
|
||||||
|
xFinal, //function()
|
||||||
|
xDestroy //function(void*)
|
||||||
|
){/*installed later*/},
|
||||||
|
/**
|
||||||
|
Equivalent to passing the same arguments to
|
||||||
|
sqlite3_create_function_v2(), with 0 as the final argument.
|
||||||
|
*/
|
||||||
|
sqlite3_create_function:function(
|
||||||
|
pDb, funcName, nArg, eTextRep, pApp,
|
||||||
|
xFunc, //function(arrayOfValues)
|
||||||
|
xStep, //function(arrayOfValues)
|
||||||
|
xFinal //function()
|
||||||
|
){/*installed later*/},
|
||||||
/**
|
/**
|
||||||
The sqlite3_prepare_v3() binding handles two different uses
|
The sqlite3_prepare_v3() binding handles two different uses
|
||||||
with differing JS/WASM semantics:
|
with differing JS/WASM semantics:
|
||||||
@ -642,8 +707,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
["sqlite3_column_type","int", "sqlite3_stmt*", "int"],
|
["sqlite3_column_type","int", "sqlite3_stmt*", "int"],
|
||||||
["sqlite3_compileoption_get", "string", "int"],
|
["sqlite3_compileoption_get", "string", "int"],
|
||||||
["sqlite3_compileoption_used", "int", "string"],
|
["sqlite3_compileoption_used", "int", "string"],
|
||||||
["sqlite3_create_function_v2", "int",
|
/* sqlite3_create_function_v2() is handled separate to simplify conversion
|
||||||
"sqlite3*", "string", "int", "int", "*", "*", "*", "*", "*"],
|
of its callback argument */
|
||||||
["sqlite3_data_count", "int", "sqlite3_stmt*"],
|
["sqlite3_data_count", "int", "sqlite3_stmt*"],
|
||||||
["sqlite3_db_filename", "string", "sqlite3*", "string"],
|
["sqlite3_db_filename", "string", "sqlite3*", "string"],
|
||||||
["sqlite3_db_handle", "sqlite3*", "sqlite3_stmt*"],
|
["sqlite3_db_handle", "sqlite3*", "sqlite3_stmt*"],
|
||||||
|
@ -15,11 +15,17 @@
|
|||||||
|
|
||||||
https://fossil.wanderinghorse.net/r/jaccwabyt
|
https://fossil.wanderinghorse.net/r/jaccwabyt
|
||||||
|
|
||||||
|
and sqlite3:
|
||||||
|
|
||||||
|
https://sqlite.org
|
||||||
|
|
||||||
|
This file is kept in sync between both of those trees.
|
||||||
|
|
||||||
Maintenance reminder: If you're reading this in a tree other than
|
Maintenance reminder: If you're reading this in a tree other than
|
||||||
the Jaccwabyt tree, note that this copy may be replaced with
|
one of those listed above, note that this copy may be replaced with
|
||||||
upstream copies of that one from time to time. Thus the code
|
upstream copies of that one from time to time. Thus the code
|
||||||
installed by this function "should not" be edited outside of that
|
installed by this function "should not" be edited outside of those
|
||||||
project, else it risks getting overwritten.
|
projects, else it risks getting overwritten.
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
This function is intended to simplify porting around various bits
|
This function is intended to simplify porting around various bits
|
||||||
@ -109,8 +115,8 @@
|
|||||||
following symbols:
|
following symbols:
|
||||||
|
|
||||||
- `memory`: a WebAssembly.Memory object representing the WASM
|
- `memory`: a WebAssembly.Memory object representing the WASM
|
||||||
memory. _Alternately_, the `memory` property can be set on the
|
memory. _Alternately_, the `memory` property can be set as
|
||||||
target instance, in particular if the WASM heap memory is
|
`target.memory`, in particular if the WASM heap memory is
|
||||||
initialized in JS an _imported_ into WASM, as opposed to being
|
initialized in JS an _imported_ into WASM, as opposed to being
|
||||||
initialized in WASM and exported to JS.
|
initialized in WASM and exported to JS.
|
||||||
|
|
||||||
@ -132,7 +138,11 @@
|
|||||||
false. If it is false, certain BigInt-related features will trigger
|
false. If it is false, certain BigInt-related features will trigger
|
||||||
an exception if invoked. This property, if not set when this is
|
an exception if invoked. This property, if not set when this is
|
||||||
called, will get a default value of true only if the BigInt64Array
|
called, will get a default value of true only if the BigInt64Array
|
||||||
constructor is available, else it will default to false.
|
constructor is available, else it will default to false. Note that
|
||||||
|
having the BigInt type is not sufficient for full int64 integration
|
||||||
|
with WASM: the target WASM file must also have been built with
|
||||||
|
that support. In Emscripten that's done using the `-sWASM_BIGINT`
|
||||||
|
flag.
|
||||||
|
|
||||||
Some optional APIs require that the target have the following
|
Some optional APIs require that the target have the following
|
||||||
methods:
|
methods:
|
||||||
@ -295,7 +305,7 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
an integer as the first argument and unsigned is truthy then
|
an integer as the first argument and unsigned is truthy then
|
||||||
the "U" (unsigned) variant of that view is returned, else the
|
the "U" (unsigned) variant of that view is returned, else the
|
||||||
signed variant is returned. If passed a TypedArray value, the
|
signed variant is returned. If passed a TypedArray value, the
|
||||||
2nd argument is ignores. Note that Float32Array and
|
2nd argument is ignored. Note that Float32Array and
|
||||||
Float64Array views are not supported by this function.
|
Float64Array views are not supported by this function.
|
||||||
|
|
||||||
Note that growth of the heap will invalidate any references to
|
Note that growth of the heap will invalidate any references to
|
||||||
@ -511,7 +521,7 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
if(2!==arguments.length){
|
if(2!==arguments.length){
|
||||||
toss("installFunction() requires exactly 2 arguments");
|
toss("installFunction() requires exactly 2 arguments");
|
||||||
}
|
}
|
||||||
if('string'===typeof func && sig instanceof Function){
|
if('string'===typeof func){
|
||||||
const x = sig;
|
const x = sig;
|
||||||
sig = func;
|
sig = func;
|
||||||
func = x;
|
func = x;
|
||||||
@ -1137,7 +1147,7 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
e.g. using setMemValue() or getMemValue(), it is important that
|
e.g. using setMemValue() or getMemValue(), it is important that
|
||||||
the pointer in question be aligned to an 8-byte boundary or else
|
the pointer in question be aligned to an 8-byte boundary or else
|
||||||
it will not be fetched or written properly and will corrupt or
|
it will not be fetched or written properly and will corrupt or
|
||||||
read neighboring memory. It i only safe to pass false when the
|
read neighboring memory. It is only safe to pass false when the
|
||||||
client code is certain that it will only get/fetch 4-byte values
|
client code is certain that it will only get/fetch 4-byte values
|
||||||
(or smaller).
|
(or smaller).
|
||||||
*/
|
*/
|
||||||
@ -1239,12 +1249,12 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
return v ? xcv.arg[ptrIR](v) : null;
|
return v ? xcv.arg[ptrIR](v) : null;
|
||||||
};
|
};
|
||||||
xcv.result.string = (i)=>target.cstringToJs(i);
|
xcv.result.string = (i)=>target.cstringToJs(i);
|
||||||
xcv.result['string:free'] = function(i){
|
xcv.result['string:free'] = (i)=>{
|
||||||
try { return i ? target.cstringToJs(i) : null }
|
try { return i ? target.cstringToJs(i) : null }
|
||||||
finally{ target.dealloc(i) }
|
finally{ target.dealloc(i) }
|
||||||
};
|
};
|
||||||
xcv.result.json = (i)=>JSON.parse(target.cstringToJs(i));
|
xcv.result.json = (i)=>JSON.parse(target.cstringToJs(i));
|
||||||
xcv.result['json:free'] = function(i){
|
xcv.result['json:free'] = (i)=>{
|
||||||
try{ return i ? JSON.parse(target.cstringToJs(i)) : null }
|
try{ return i ? JSON.parse(target.cstringToJs(i)) : null }
|
||||||
finally{ target.dealloc(i) }
|
finally{ target.dealloc(i) }
|
||||||
}
|
}
|
||||||
@ -1374,14 +1384,26 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
C-string, ownership of which has just been transfered to the
|
C-string, ownership of which has just been transfered to the
|
||||||
caller. It copies the C-string to a JS string, frees 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
|
C-string, and returns the JS string. If such a result value is
|
||||||
NULL, the JS result is `null`.
|
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)=>{
|
||||||
|
try { return i ? target.cstringToJs(i) : null }
|
||||||
|
finally{ target.exports.my_free(i) }
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
- `json` (results): treats the result as a const C-string and
|
- `json` (results): treats the result as a const C-string and
|
||||||
returns the result of passing the converted-to-JS string to
|
returns the result of passing the converted-to-JS string to
|
||||||
JSON.parse(). Returns `null` if the C-string is a NULL pointer.
|
JSON.parse(). Returns `null` if the C-string is a NULL pointer.
|
||||||
|
|
||||||
- `json:free` (results): works exactly like `string:free` but
|
- `json:free` (results): works exactly like `string:free` but
|
||||||
returns the same thing as the `json` adapter.
|
returns the same thing as the `json` adapter. Note the
|
||||||
|
warning in `string:free` regarding maching allocators and
|
||||||
|
deallocators.
|
||||||
|
|
||||||
The type names for results and arguments are validated when
|
The type names for results and arguments are validated when
|
||||||
xWrap() is called and any unknown names will trigger an
|
xWrap() is called and any unknown names will trigger an
|
||||||
@ -1532,11 +1554,10 @@ self.WhWasmUtilInstaller = function(target){
|
|||||||
type name, as documented for xWrap() (use a falsy value or an
|
type name, as documented for xWrap() (use a falsy value or an
|
||||||
empty array for nullary functions). The 4th+ arguments are
|
empty array for nullary functions). The 4th+ arguments are
|
||||||
arguments for the call, with the special case that if the 4th
|
arguments for the call, with the special case that if the 4th
|
||||||
argument is an array, it is used as the arguments for the call
|
argument is an array, it is used as the arguments for the
|
||||||
(again, falsy or an empty array for nullary functions). Returns
|
call. Returns the converted result of the call.
|
||||||
the converted result of the call.
|
|
||||||
|
|
||||||
This is just a thin wrapp around xWrap(). If the given function
|
This is just a thin wrapper around xWrap(). If the given function
|
||||||
is to be called more than once, it's more efficient to use
|
is to be called more than once, it's more efficient to use
|
||||||
xWrap() to create a wrapper, then to call that wrapper as many
|
xWrap() to create a wrapper, then to call that wrapper as many
|
||||||
times as needed. For one-shot calls, however, this variant is
|
times as needed. For one-shot calls, however, this variant is
|
||||||
|
20
manifest
20
manifest
@ -1,5 +1,5 @@
|
|||||||
C Doc\stypo\sfixes.
|
C js:\simplement\sa\shand-written\swrapper\sfor\ssqlite3_create_function_v2()\swhich\sconverts,\sif\snecessary,\sJS-function-type\sargs\sto\sWASM\sfunction\swrappers.\sReplace\sDB.createFunction()\simpl\swith\sthe\snew\sone.
|
||||||
D 2022-10-02T03:14:38.605
|
D 2022-10-02T18:47:39.889
|
||||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||||
@ -478,17 +478,17 @@ F ext/wasm/GNUmakefile b313a82060c733c990b91afa981e10f5e21a0b33a483f33b739ce932e
|
|||||||
F ext/wasm/README.md 1e5b28158b74ab3ffc9d54fcbc020f0bbeb82c2ff8bbd904214c86c70e8a3066
|
F ext/wasm/README.md 1e5b28158b74ab3ffc9d54fcbc020f0bbeb82c2ff8bbd904214c86c70e8a3066
|
||||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api c6b05641d733227a995cc05a2ba7ddc9ef2323081ae0cae5ed1d1f83d5b54b36
|
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api c6b05641d733227a995cc05a2ba7ddc9ef2323081ae0cae5ed1d1f83d5b54b36
|
||||||
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
|
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
|
||||||
F ext/wasm/api/README.md b6d19392984fdd428ae7260512d45742160c06baefed9da703da3fdeb68e7124
|
F ext/wasm/api/README.md 1e350b611465566cfa2e5eccf7c9b29a34f48ee38bbf6d5fb086dd06ce32b3ff
|
||||||
F ext/wasm/api/extern-post-js.js dc68cbf552d8ea085181400a6963907c32e0b088b03ffd8969b1869fea246629
|
F ext/wasm/api/extern-post-js.js dc68cbf552d8ea085181400a6963907c32e0b088b03ffd8969b1869fea246629
|
||||||
F ext/wasm/api/extern-pre-js.js 20143b16b672d0a576fbf768a786d12ee1f84e222126477072389b992542a5b2
|
F ext/wasm/api/extern-pre-js.js 20143b16b672d0a576fbf768a786d12ee1f84e222126477072389b992542a5b2
|
||||||
F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c
|
F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c
|
||||||
F ext/wasm/api/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1ecc5ba9e64ef90a8b
|
F ext/wasm/api/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1ecc5ba9e64ef90a8b
|
||||||
F ext/wasm/api/pre-js.js 2db711eb637991b383fc6b5c0f3df65ec48a7201e5730e304beba8de2d3f9b0b
|
F ext/wasm/api/pre-js.js 2db711eb637991b383fc6b5c0f3df65ec48a7201e5730e304beba8de2d3f9b0b
|
||||||
F ext/wasm/api/sqlite3-api-cleanup.js 5d22d1d3818ecacb23bfa223d5970cd0617d8cdbb48c8bc4bbd463f05b021a99
|
F ext/wasm/api/sqlite3-api-cleanup.js 5d22d1d3818ecacb23bfa223d5970cd0617d8cdbb48c8bc4bbd463f05b021a99
|
||||||
F ext/wasm/api/sqlite3-api-glue.js 20b98987dd0c6d02ca308e0cf9299750d618459e0876b354380b87ae2dc5cba0
|
F ext/wasm/api/sqlite3-api-glue.js d834733fbdc216beb65382655e61a436e29018412005aab413ff022d4e5c25eb
|
||||||
F ext/wasm/api/sqlite3-api-oo1.js 066e67f3033e1b300140d431557c468f5cd0a4c17253f156e05b8a2e2c802da7
|
F ext/wasm/api/sqlite3-api-oo1.js 48d2269544301cd97726ef2d9a82e4384350d12dcf832fa417f211811ae5272d
|
||||||
F ext/wasm/api/sqlite3-api-opfs.js 1b097808b7b081b0f0700cf97d49ef19760e401706168edff9cd45cf9169f541
|
F ext/wasm/api/sqlite3-api-opfs.js 1b097808b7b081b0f0700cf97d49ef19760e401706168edff9cd45cf9169f541
|
||||||
F ext/wasm/api/sqlite3-api-prologue.js 9b0c5150f0129b3dc558fec0bfc9c8bf7ebda402b58965bcf98b2ed117b55239
|
F ext/wasm/api/sqlite3-api-prologue.js 32795679b72a5ad685c58a9599d10dac04787907f623825408d539b703e080a4
|
||||||
F ext/wasm/api/sqlite3-api-worker1.js 7f4f46cb6b512a48572d7567233896e6a9c46570c44bdc3d13419730c7c221c8
|
F ext/wasm/api/sqlite3-api-worker1.js 7f4f46cb6b512a48572d7567233896e6a9c46570c44bdc3d13419730c7c221c8
|
||||||
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
||||||
F ext/wasm/api/sqlite3-wasm.c 2a0f9e4bf1b141a787918951360601128d6a0a190a31a8e5cfe237c99fa640c6
|
F ext/wasm/api/sqlite3-wasm.c 2a0f9e4bf1b141a787918951360601128d6a0a190a31a8e5cfe237c99fa640c6
|
||||||
@ -497,7 +497,7 @@ F ext/wasm/batch-runner.js ce92650a6681586c89bef26ceae96674a55ca5a9727815202ca62
|
|||||||
F ext/wasm/common/SqliteTestUtil.js 647bf014bd30bdd870a7e9001e251d12fc1c9ec9ce176a1004b838a4b33c5c05
|
F ext/wasm/common/SqliteTestUtil.js 647bf014bd30bdd870a7e9001e251d12fc1c9ec9ce176a1004b838a4b33c5c05
|
||||||
F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
|
F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
|
||||||
F ext/wasm/common/testing.css 3a5143699c2b73a85b962271e1a9b3241b30d90e30d895e4f55665e648572962
|
F ext/wasm/common/testing.css 3a5143699c2b73a85b962271e1a9b3241b30d90e30d895e4f55665e648572962
|
||||||
F ext/wasm/common/whwasmutil.js af9bb6586d8a2ab7c55c7f1554bb6b0328a7bd6a86ea5fa9ef5273a738156ac3
|
F ext/wasm/common/whwasmutil.js 427eb8b695bd5f38497601a6bda933e83d1a5900b75f5f1af8dbb381898d2ee4
|
||||||
F ext/wasm/demo-123-worker.html e419b66495d209b5211ec64903b4cfb3ca7df20d652b41fcd28bf018a773234f
|
F ext/wasm/demo-123-worker.html e419b66495d209b5211ec64903b4cfb3ca7df20d652b41fcd28bf018a773234f
|
||||||
F ext/wasm/demo-123.html aa281d33b7eefa755f3122b7b5a18f39a42dc5fb69c8879171bf14b4c37c4ec4
|
F ext/wasm/demo-123.html aa281d33b7eefa755f3122b7b5a18f39a42dc5fb69c8879171bf14b4c37c4ec4
|
||||||
F ext/wasm/demo-123.js 536579fd587974c2511c5bf82034b253d4fdeceabb726927ad7599ef6b7578e8
|
F ext/wasm/demo-123.js 536579fd587974c2511c5bf82034b253d4fdeceabb726927ad7599ef6b7578e8
|
||||||
@ -2029,8 +2029,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
|||||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||||
P eb5726677a727a958df11f1fba078d30c7c0ba2a9bdb158e8641b35b5f971af3
|
P e528675da3971907666c7d2d09763975a105ec585dc5122145f65905d535bed8
|
||||||
R c02b54a31af3ce39f04c3d79de05d508
|
R 905afd6e962345097748ac52db8a0889
|
||||||
U stephan
|
U stephan
|
||||||
Z a6c5eed947d3361089be1f642079a7a3
|
Z 8de102f5519af227a27849d21742ab64
|
||||||
# Remove this line to create a well-formed Fossil manifest.
|
# Remove this line to create a well-formed Fossil manifest.
|
||||||
|
@ -1 +1 @@
|
|||||||
e528675da3971907666c7d2d09763975a105ec585dc5122145f65905d535bed8
|
435ab33384017967e46f52b70bee851a85a28808990a0e58dd5288f606b89c9c
|
Loading…
Reference in New Issue
Block a user