whwasmutil.js 90 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290
  1. /**
  2. 2022-07-08
  3. The author disclaims copyright to this source code. In place of a
  4. legal notice, here is a blessing:
  5. * May you do good and not evil.
  6. * May you find forgiveness for yourself and forgive others.
  7. * May you share freely, never taking more than you give.
  8. ***********************************************************************
  9. The whwasmutil is developed in conjunction with the Jaccwabyt
  10. project:
  11. https://fossil.wanderinghorse.net/r/jaccwabyt
  12. and sqlite3:
  13. https://sqlite.org
  14. This file is kept in sync between both of those trees.
  15. Maintenance reminder: If you're reading this in a tree other than
  16. one of those listed above, note that this copy may be replaced with
  17. upstream copies of that one from time to time. Thus the code
  18. installed by this function "should not" be edited outside of those
  19. projects, else it risks getting overwritten.
  20. */
  21. /**
  22. This function is intended to simplify porting around various bits
  23. of WASM-related utility code from project to project.
  24. The primary goal of this code is to replace, where possible,
  25. Emscripten-generated glue code with equivalent utility code which
  26. can be used in arbitrary WASM environments built with toolchains
  27. other than Emscripten. As of this writing, this code is capable of
  28. acting as a replacement for Emscripten's generated glue code
  29. _except_ that the latter installs handlers for Emscripten-provided
  30. APIs such as its "FS" (virtual filesystem) API. Loading of such
  31. things still requires using Emscripten's glue, but the post-load
  32. utility APIs provided by this code are still usable as replacements
  33. for their sub-optimally-documented Emscripten counterparts.
  34. Intended usage:
  35. ```
  36. globalThis.WhWasmUtilInstaller(appObject);
  37. delete globalThis.WhWasmUtilInstaller;
  38. ```
  39. Its global-scope symbol is intended only to provide an easy way to
  40. make it available to 3rd-party scripts and "should" be deleted
  41. after calling it. That symbols is _not_ used within the library.
  42. Forewarning: this API explicitly targets only browser
  43. environments. If a given non-browser environment has the
  44. capabilities needed for a given feature (e.g. TextEncoder), great,
  45. but it does not go out of its way to account for them and does not
  46. provide compatibility crutches for them.
  47. It currently offers alternatives to the following
  48. Emscripten-generated APIs:
  49. - OPTIONALLY memory allocation, but how this gets imported is
  50. environment-specific. Most of the following features only work
  51. if allocation is available.
  52. - WASM-exported "indirect function table" access and
  53. manipulation. e.g. creating new WASM-side functions using JS
  54. functions, analog to Emscripten's addFunction() and
  55. uninstallFunction() but slightly different.
  56. - Get/set specific heap memory values, analog to Emscripten's
  57. getValue() and setValue().
  58. - String length counting in UTF-8 bytes (C-style and JS strings).
  59. - JS string to C-string conversion and vice versa, analog to
  60. Emscripten's stringToUTF8Array() and friends, but with slighter
  61. different interfaces.
  62. - JS string to Uint8Array conversion, noting that browsers actually
  63. already have this built in via TextEncoder.
  64. - "Scoped" allocation, such that allocations made inside of a given
  65. explicit scope will be automatically cleaned up when the scope is
  66. closed. This is fundamentally similar to Emscripten's
  67. stackAlloc() and friends but uses the heap instead of the stack
  68. because access to the stack requires C code.
  69. - Create JS wrappers for WASM functions, analog to Emscripten's
  70. ccall() and cwrap() functions, except that the automatic
  71. conversions for function arguments and return values can be
  72. easily customized by the client by assigning custom function
  73. signature type names to conversion functions. Essentially,
  74. it's ccall() and cwrap() on steroids.
  75. How to install...
  76. Passing an object to this function will install the functionality
  77. into that object. Afterwards, client code "should" delete the global
  78. symbol.
  79. This code requires that the target object have the following
  80. properties, noting that they needn't be available until the first
  81. time one of the installed APIs is used (as opposed to when this
  82. function is called) except where explicitly noted:
  83. - `exports` must be a property of the target object OR a property
  84. of `target.instance` (a WebAssembly.Module instance) and it must
  85. contain the symbols exported by the WASM module associated with
  86. this code. In an Enscripten environment it must be set to
  87. `Module['asm']` (versions <=3.1.43) or `wasmExports` (versions
  88. >=3.1.44). The exports object must contain a minimum of the
  89. following symbols:
  90. - `memory`: a WebAssembly.Memory object representing the WASM
  91. memory. _Alternately_, the `memory` property can be set as
  92. `target.memory`, in particular if the WASM heap memory is
  93. initialized in JS an _imported_ into WASM, as opposed to being
  94. initialized in WASM and exported to JS.
  95. - `__indirect_function_table`: the WebAssembly.Table object which
  96. holds WASM-exported functions. This API does not strictly
  97. require that the table be able to grow but it will throw if its
  98. `installFunction()` is called and the table cannot grow.
  99. In order to simplify downstream usage, if `target.exports` is not
  100. set when this is called then a property access interceptor
  101. (read-only, configurable, enumerable) gets installed as `exports`
  102. which resolves to `target.instance.exports`, noting that the latter
  103. property need not exist until the first time `target.exports` is
  104. accessed.
  105. Some APIs _optionally_ make use of the `bigIntEnabled` property of
  106. the target object. It "should" be set to true if the WASM
  107. environment is compiled with BigInt support, else it must be
  108. false. If it is false, certain BigInt-related features will trigger
  109. an exception if invoked. This property, if not set when this is
  110. called, will get a default value of true only if the BigInt64Array
  111. constructor is available, else it will default to false. Note that
  112. having the BigInt type is not sufficient for full int64 integration
  113. with WASM: the target WASM file must also have been built with
  114. that support. In Emscripten that's done using the `-sWASM_BIGINT`
  115. flag.
  116. Some optional APIs require that the target have the following
  117. methods:
  118. - 'alloc()` must behave like C's `malloc()`, allocating N bytes of
  119. memory and returning its pointer. In Emscripten this is
  120. conventionally made available via `Module['_malloc']`. This API
  121. requires that the alloc routine throw on allocation error, as
  122. opposed to returning null or 0.
  123. - 'dealloc()` must behave like C's `free()`, accepting either a
  124. pointer returned from its allocation counterpart or the values
  125. null/0 (for which it must be a no-op). In Emscripten this is
  126. conventionally made available via `Module['_free']`.
  127. APIs which require allocation routines are explicitly documented as
  128. such and/or have "alloc" in their names.
  129. This code is developed and maintained in conjunction with the
  130. Jaccwabyt project:
  131. https://fossil.wanderinghorse.net/r/jaccwabbyt
  132. More specifically:
  133. https://fossil.wanderinghorse.net/r/jaccwabbyt/file/common/whwasmutil.js
  134. */
  135. globalThis.WhWasmUtilInstaller = function(target){
  136. 'use strict';
  137. if(undefined===target.bigIntEnabled){
  138. target.bigIntEnabled = !!globalThis['BigInt64Array'];
  139. }
  140. /** Throws a new Error, the message of which is the concatenation of
  141. all args with a space between each. */
  142. const toss = (...args)=>{throw new Error(args.join(' '))};
  143. if(!target.exports){
  144. Object.defineProperty(target, 'exports', {
  145. enumerable: true, configurable: true,
  146. get: ()=>(target.instance && target.instance.exports)
  147. });
  148. }
  149. /*********
  150. alloc()/dealloc() auto-install...
  151. This would be convenient but it can also cause us to pick up
  152. malloc() even when the client code is using a different exported
  153. allocator (who, me?), which is bad. malloc() may be exported even
  154. if we're not explicitly using it and overriding the malloc()
  155. function, linking ours first, is not always feasible when using a
  156. malloc() proxy, as it can lead to recursion and stack overflow
  157. (who, me?). So... we really need the downstream code to set up
  158. target.alloc/dealloc() itself.
  159. ******/
  160. /******
  161. if(target.exports){
  162. //Maybe auto-install alloc()/dealloc()...
  163. if(!target.alloc && target.exports.malloc){
  164. target.alloc = function(n){
  165. const m = this(n);
  166. return m || toss("Allocation of",n,"byte(s) failed.");
  167. }.bind(target.exports.malloc);
  168. }
  169. if(!target.dealloc && target.exports.free){
  170. target.dealloc = function(ptr){
  171. if(ptr) this(ptr);
  172. }.bind(target.exports.free);
  173. }
  174. }*******/
  175. /**
  176. Pointers in WASM are currently assumed to be 32-bit, but someday
  177. that will certainly change.
  178. */
  179. const ptrIR = target.pointerIR || 'i32';
  180. const ptrSizeof = target.ptrSizeof =
  181. ('i32'===ptrIR ? 4
  182. : ('i64'===ptrIR
  183. ? 8 : toss("Unhandled ptrSizeof:",ptrIR)));
  184. /** Stores various cached state. */
  185. const cache = Object.create(null);
  186. /** Previously-recorded size of cache.memory.buffer, noted so that
  187. we can recreate the view objects if the heap grows. */
  188. cache.heapSize = 0;
  189. /** WebAssembly.Memory object extracted from target.memory or
  190. target.exports.memory the first time heapWrappers() is
  191. called. */
  192. cache.memory = null;
  193. /** uninstallFunction() puts table indexes in here for reuse and
  194. installFunction() extracts them. */
  195. cache.freeFuncIndexes = [];
  196. /**
  197. Used by scopedAlloc() and friends.
  198. */
  199. cache.scopedAlloc = [];
  200. cache.utf8Decoder = new TextDecoder();
  201. cache.utf8Encoder = new TextEncoder('utf-8');
  202. /**
  203. For the given IR-like string in the set ('i8', 'i16', 'i32',
  204. 'f32', 'float', 'i64', 'f64', 'double', '*'), or any string value
  205. ending in '*', returns the sizeof for that value
  206. (target.ptrSizeof in the latter case). For any other value, it
  207. returns the undefined value.
  208. */
  209. target.sizeofIR = (n)=>{
  210. switch(n){
  211. case 'i8': return 1;
  212. case 'i16': return 2;
  213. case 'i32': case 'f32': case 'float': return 4;
  214. case 'i64': case 'f64': case 'double': return 8;
  215. case '*': return ptrSizeof;
  216. default:
  217. return (''+n).endsWith('*') ? ptrSizeof : undefined;
  218. }
  219. };
  220. /**
  221. If (cache.heapSize !== cache.memory.buffer.byteLength), i.e. if
  222. the heap has grown since the last call, updates cache.HEAPxyz.
  223. Returns the cache object.
  224. */
  225. const heapWrappers = function(){
  226. if(!cache.memory){
  227. cache.memory = (target.memory instanceof WebAssembly.Memory)
  228. ? target.memory : target.exports.memory;
  229. }else if(cache.heapSize === cache.memory.buffer.byteLength){
  230. return cache;
  231. }
  232. // heap is newly-acquired or has been resized....
  233. const b = cache.memory.buffer;
  234. cache.HEAP8 = new Int8Array(b); cache.HEAP8U = new Uint8Array(b);
  235. cache.HEAP16 = new Int16Array(b); cache.HEAP16U = new Uint16Array(b);
  236. cache.HEAP32 = new Int32Array(b); cache.HEAP32U = new Uint32Array(b);
  237. if(target.bigIntEnabled){
  238. cache.HEAP64 = new BigInt64Array(b); cache.HEAP64U = new BigUint64Array(b);
  239. }
  240. cache.HEAP32F = new Float32Array(b); cache.HEAP64F = new Float64Array(b);
  241. cache.heapSize = b.byteLength;
  242. return cache;
  243. };
  244. /** Convenience equivalent of this.heapForSize(8,false). */
  245. target.heap8 = ()=>heapWrappers().HEAP8;
  246. /** Convenience equivalent of this.heapForSize(8,true). */
  247. target.heap8u = ()=>heapWrappers().HEAP8U;
  248. /** Convenience equivalent of this.heapForSize(16,false). */
  249. target.heap16 = ()=>heapWrappers().HEAP16;
  250. /** Convenience equivalent of this.heapForSize(16,true). */
  251. target.heap16u = ()=>heapWrappers().HEAP16U;
  252. /** Convenience equivalent of this.heapForSize(32,false). */
  253. target.heap32 = ()=>heapWrappers().HEAP32;
  254. /** Convenience equivalent of this.heapForSize(32,true). */
  255. target.heap32u = ()=>heapWrappers().HEAP32U;
  256. /**
  257. Requires n to be one of:
  258. - integer 8, 16, or 32.
  259. - A integer-type TypedArray constructor: Int8Array, Int16Array,
  260. Int32Array, or their Uint counterparts.
  261. If this.bigIntEnabled is true, it also accepts the value 64 or a
  262. BigInt64Array/BigUint64Array, else it throws if passed 64 or one
  263. of those constructors.
  264. Returns an integer-based TypedArray view of the WASM heap
  265. memory buffer associated with the given block size. If passed
  266. an integer as the first argument and unsigned is truthy then
  267. the "U" (unsigned) variant of that view is returned, else the
  268. signed variant is returned. If passed a TypedArray value, the
  269. 2nd argument is ignored. Note that Float32Array and
  270. Float64Array views are not supported by this function.
  271. Note that growth of the heap will invalidate any references to
  272. this heap, so do not hold a reference longer than needed and do
  273. not use a reference after any operation which may
  274. allocate. Instead, re-fetch the reference by calling this
  275. function again.
  276. Throws if passed an invalid n.
  277. Pedantic side note: the name "heap" is a bit of a misnomer. In a
  278. WASM environment, the stack and heap memory are all accessed via
  279. the same view(s) of the memory.
  280. */
  281. target.heapForSize = function(n,unsigned = true){
  282. let ctor;
  283. const c = (cache.memory && cache.heapSize === cache.memory.buffer.byteLength)
  284. ? cache : heapWrappers();
  285. switch(n){
  286. case Int8Array: return c.HEAP8; case Uint8Array: return c.HEAP8U;
  287. case Int16Array: return c.HEAP16; case Uint16Array: return c.HEAP16U;
  288. case Int32Array: return c.HEAP32; case Uint32Array: return c.HEAP32U;
  289. case 8: return unsigned ? c.HEAP8U : c.HEAP8;
  290. case 16: return unsigned ? c.HEAP16U : c.HEAP16;
  291. case 32: return unsigned ? c.HEAP32U : c.HEAP32;
  292. case 64:
  293. if(c.HEAP64) return unsigned ? c.HEAP64U : c.HEAP64;
  294. break;
  295. default:
  296. if(target.bigIntEnabled){
  297. if(n===globalThis['BigUint64Array']) return c.HEAP64U;
  298. else if(n===globalThis['BigInt64Array']) return c.HEAP64;
  299. break;
  300. }
  301. }
  302. toss("Invalid heapForSize() size: expecting 8, 16, 32,",
  303. "or (if BigInt is enabled) 64.");
  304. };
  305. /**
  306. Returns the WASM-exported "indirect function table."
  307. */
  308. target.functionTable = function(){
  309. return target.exports.__indirect_function_table;
  310. /** -----------------^^^^^ "seems" to be a standardized export name.
  311. From Emscripten release notes from 2020-09-10:
  312. - Use `__indirect_function_table` as the import name for the
  313. table, which is what LLVM does.
  314. */
  315. };
  316. /**
  317. Given a function pointer, returns the WASM function table entry
  318. if found, else returns a falsy value: undefined if fptr is out of
  319. range or null if it's in range but the table entry is empty.
  320. */
  321. target.functionEntry = function(fptr){
  322. const ft = target.functionTable();
  323. return fptr < ft.length ? ft.get(fptr) : undefined;
  324. };
  325. /**
  326. Creates a WASM function which wraps the given JS function and
  327. returns the JS binding of that WASM function. The signature
  328. string must be the Jaccwabyt-format or Emscripten
  329. addFunction()-format function signature string. In short: in may
  330. have one of the following formats:
  331. - Emscripten: `"x..."`, where the first x is a letter representing
  332. the result type and subsequent letters represent the argument
  333. types. Functions with no arguments have only a single
  334. letter. See below.
  335. - Jaccwabyt: `"x(...)"` where `x` is the letter representing the
  336. result type and letters in the parens (if any) represent the
  337. argument types. Functions with no arguments use `x()`. See
  338. below.
  339. Supported letters:
  340. - `i` = int32
  341. - `p` = int32 ("pointer")
  342. - `j` = int64
  343. - `f` = float32
  344. - `d` = float64
  345. - `v` = void, only legal for use as the result type
  346. It throws if an invalid signature letter is used.
  347. Jaccwabyt-format signatures support some additional letters which
  348. have no special meaning here but (in this context) act as aliases
  349. for other letters:
  350. - `s`, `P`: same as `p`
  351. Sidebar: this code is developed together with Jaccwabyt, thus the
  352. support for its signature format.
  353. The arguments may be supplied in either order: (func,sig) or
  354. (sig,func).
  355. */
  356. target.jsFuncToWasm = function f(func, sig){
  357. /** Attribution: adapted up from Emscripten-generated glue code,
  358. refactored primarily for efficiency's sake, eliminating
  359. call-local functions and superfluous temporary arrays. */
  360. if(!f._){/*static init...*/
  361. f._ = {
  362. // Map of signature letters to type IR values
  363. sigTypes: Object.assign(Object.create(null),{
  364. i: 'i32', p: 'i32', P: 'i32', s: 'i32',
  365. j: 'i64', f: 'f32', d: 'f64'
  366. }),
  367. // Map of type IR values to WASM type code values
  368. typeCodes: Object.assign(Object.create(null),{
  369. f64: 0x7c, f32: 0x7d, i64: 0x7e, i32: 0x7f
  370. }),
  371. /** Encodes n, which must be <2^14 (16384), into target array
  372. tgt, as a little-endian value, using the given method
  373. ('push' or 'unshift'). */
  374. uleb128Encode: function(tgt, method, n){
  375. if(n<128) tgt[method](n);
  376. else tgt[method]( (n % 128) | 128, n>>7);
  377. },
  378. /** Intentionally-lax pattern for Jaccwabyt-format function
  379. pointer signatures, the intent of which is simply to
  380. distinguish them from Emscripten-format signatures. The
  381. downstream checks are less lax. */
  382. rxJSig: /^(\w)\((\w*)\)$/,
  383. /** Returns the parameter-value part of the given signature
  384. string. */
  385. sigParams: function(sig){
  386. const m = f._.rxJSig.exec(sig);
  387. return m ? m[2] : sig.substr(1);
  388. },
  389. /** Returns the IR value for the given letter or throws
  390. if the letter is invalid. */
  391. letterType: (x)=>f._.sigTypes[x] || toss("Invalid signature letter:",x),
  392. /** Returns an object describing the result type and parameter
  393. type(s) of the given function signature, or throws if the
  394. signature is invalid. */
  395. /******** // only valid for use with the WebAssembly.Function ctor, which
  396. // is not yet documented on MDN.
  397. sigToWasm: function(sig){
  398. const rc = {parameters:[], results: []};
  399. if('v'!==sig[0]) rc.results.push(f.sigTypes(sig[0]));
  400. for(const x of f._.sigParams(sig)){
  401. rc.parameters.push(f._.typeCodes(x));
  402. }
  403. return rc;
  404. },************/
  405. /** Pushes the WASM data type code for the given signature
  406. letter to the given target array. Throws if letter is
  407. invalid. */
  408. pushSigType: (dest, letter)=>dest.push(f._.typeCodes[f._.letterType(letter)])
  409. };
  410. }/*static init*/
  411. if('string'===typeof func){
  412. const x = sig;
  413. sig = func;
  414. func = x;
  415. }
  416. const sigParams = f._.sigParams(sig);
  417. const wasmCode = [0x01/*count: 1*/, 0x60/*function*/];
  418. f._.uleb128Encode(wasmCode, 'push', sigParams.length);
  419. for(const x of sigParams) f._.pushSigType(wasmCode, x);
  420. if('v'===sig[0]) wasmCode.push(0);
  421. else{
  422. wasmCode.push(1);
  423. f._.pushSigType(wasmCode, sig[0]);
  424. }
  425. f._.uleb128Encode(wasmCode, 'unshift', wasmCode.length)/* type section length */;
  426. wasmCode.unshift(
  427. 0x00, 0x61, 0x73, 0x6d, /* magic: "\0asm" */
  428. 0x01, 0x00, 0x00, 0x00, /* version: 1 */
  429. 0x01 /* type section code */
  430. );
  431. wasmCode.push(
  432. /* import section: */ 0x02, 0x07,
  433. /* (import "e" "f" (func 0 (type 0))): */
  434. 0x01, 0x01, 0x65, 0x01, 0x66, 0x00, 0x00,
  435. /* export section: */ 0x07, 0x05,
  436. /* (export "f" (func 0 (type 0))): */
  437. 0x01, 0x01, 0x66, 0x00, 0x00
  438. );
  439. return (new WebAssembly.Instance(
  440. new WebAssembly.Module(new Uint8Array(wasmCode)), {
  441. e: { f: func }
  442. })).exports['f'];
  443. }/*jsFuncToWasm()*/;
  444. /**
  445. Documented as target.installFunction() except for the 3rd
  446. argument: if truthy, the newly-created function pointer
  447. is stashed in the current scoped-alloc scope and will be
  448. cleaned up at the matching scopedAllocPop(), else it
  449. is not stashed there.
  450. */
  451. const __installFunction = function f(func, sig, scoped){
  452. if(scoped && !cache.scopedAlloc.length){
  453. toss("No scopedAllocPush() scope is active.");
  454. }
  455. if('string'===typeof func){
  456. const x = sig;
  457. sig = func;
  458. func = x;
  459. }
  460. if('string'!==typeof sig || !(func instanceof Function)){
  461. toss("Invalid arguments: expecting (function,signature) "+
  462. "or (signature,function).");
  463. }
  464. const ft = target.functionTable();
  465. const oldLen = ft.length;
  466. let ptr;
  467. while(cache.freeFuncIndexes.length){
  468. ptr = cache.freeFuncIndexes.pop();
  469. if(ft.get(ptr)){ /* Table was modified via a different API */
  470. ptr = null;
  471. continue;
  472. }else{
  473. break;
  474. }
  475. }
  476. if(!ptr){
  477. ptr = oldLen;
  478. ft.grow(1);
  479. }
  480. try{
  481. /*this will only work if func is a WASM-exported function*/
  482. ft.set(ptr, func);
  483. if(scoped){
  484. cache.scopedAlloc[cache.scopedAlloc.length-1].push(ptr);
  485. }
  486. return ptr;
  487. }catch(e){
  488. if(!(e instanceof TypeError)){
  489. if(ptr===oldLen) cache.freeFuncIndexes.push(oldLen);
  490. throw e;
  491. }
  492. }
  493. // It's not a WASM-exported function, so compile one...
  494. try {
  495. const fptr = target.jsFuncToWasm(func, sig);
  496. ft.set(ptr, fptr);
  497. if(scoped){
  498. cache.scopedAlloc[cache.scopedAlloc.length-1].push(ptr);
  499. }
  500. }catch(e){
  501. if(ptr===oldLen) cache.freeFuncIndexes.push(oldLen);
  502. throw e;
  503. }
  504. return ptr;
  505. };
  506. /**
  507. Expects a JS function and signature, exactly as for
  508. this.jsFuncToWasm(). It uses that function to create a
  509. WASM-exported function, installs that function to the next
  510. available slot of this.functionTable(), and returns the
  511. function's index in that table (which acts as a pointer to that
  512. function). The returned pointer can be passed to
  513. uninstallFunction() to uninstall it and free up the table slot for
  514. reuse.
  515. If passed (string,function) arguments then it treats the first
  516. argument as the signature and second as the function.
  517. As a special case, if the passed-in function is a WASM-exported
  518. function then the signature argument is ignored and func is
  519. installed as-is, without requiring re-compilation/re-wrapping.
  520. This function will propagate an exception if
  521. WebAssembly.Table.grow() throws or this.jsFuncToWasm() throws.
  522. The former case can happen in an Emscripten-compiled
  523. environment when building without Emscripten's
  524. `-sALLOW_TABLE_GROWTH` flag.
  525. Sidebar: this function differs from Emscripten's addFunction()
  526. _primarily_ in that it does not share that function's
  527. undocumented behavior of reusing a function if it's passed to
  528. addFunction() more than once, which leads to uninstallFunction()
  529. breaking clients which do not take care to avoid that case:
  530. https://github.com/emscripten-core/emscripten/issues/17323
  531. */
  532. target.installFunction = (func, sig)=>__installFunction(func, sig, false);
  533. /**
  534. Works exactly like installFunction() but requires that a
  535. scopedAllocPush() is active and uninstalls the given function
  536. when that alloc scope is popped via scopedAllocPop().
  537. This is used for implementing JS/WASM function bindings which
  538. should only persist for the life of a call into a single
  539. C-side function.
  540. */
  541. target.scopedInstallFunction = (func, sig)=>__installFunction(func, sig, true);
  542. /**
  543. Requires a pointer value previously returned from
  544. this.installFunction(). Removes that function from the WASM
  545. function table, marks its table slot as free for re-use, and
  546. returns that function. It is illegal to call this before
  547. installFunction() has been called and results are undefined if
  548. ptr was not returned by that function. The returned function
  549. may be passed back to installFunction() to reinstall it.
  550. To simplify certain use cases, if passed a falsy non-0 value
  551. (noting that 0 is a valid function table index), this function
  552. has no side effects and returns undefined.
  553. */
  554. target.uninstallFunction = function(ptr){
  555. if(!ptr && 0!==ptr) return undefined;
  556. const fi = cache.freeFuncIndexes;
  557. const ft = target.functionTable();
  558. fi.push(ptr);
  559. const rc = ft.get(ptr);
  560. ft.set(ptr, null);
  561. return rc;
  562. };
  563. /**
  564. Given a WASM heap memory address and a data type name in the form
  565. (i8, i16, i32, i64, float (or f32), double (or f64)), this
  566. fetches the numeric value from that address and returns it as a
  567. number or, for the case of type='i64', a BigInt (noting that that
  568. type triggers an exception if this.bigIntEnabled is
  569. falsy). Throws if given an invalid type.
  570. If the first argument is an array, it is treated as an array of
  571. addresses and the result is an array of the values from each of
  572. those address, using the same 2nd argument for determining the
  573. value type to fetch.
  574. As a special case, if type ends with a `*`, it is considered to
  575. be a pointer type and is treated as the WASM numeric type
  576. appropriate for the pointer size (`i32`).
  577. While likely not obvious, this routine and its poke()
  578. counterpart are how pointer-to-value _output_ parameters
  579. in WASM-compiled C code can be interacted with:
  580. ```
  581. const ptr = alloc(4);
  582. poke(ptr, 0, 'i32'); // clear the ptr's value
  583. aCFuncWithOutputPtrToInt32Arg( ptr ); // e.g. void foo(int *x);
  584. const result = peek(ptr, 'i32'); // fetch ptr's value
  585. dealloc(ptr);
  586. ```
  587. scopedAlloc() and friends can be used to make handling of
  588. `ptr` safe against leaks in the case of an exception:
  589. ```
  590. let result;
  591. const scope = scopedAllocPush();
  592. try{
  593. const ptr = scopedAlloc(4);
  594. poke(ptr, 0, 'i32');
  595. aCFuncWithOutputPtrArg( ptr );
  596. result = peek(ptr, 'i32');
  597. }finally{
  598. scopedAllocPop(scope);
  599. }
  600. ```
  601. As a rule poke() must be called to set (typically zero
  602. out) the pointer's value, else it will contain an essentially
  603. random value.
  604. ACHTUNG: calling this often, e.g. in a loop, can have a noticably
  605. painful impact on performance. Rather than doing so, use
  606. heapForSize() to fetch the heap object and read directly from it.
  607. See: poke()
  608. */
  609. target.peek = function f(ptr, type='i8'){
  610. if(type.endsWith('*')) type = ptrIR;
  611. const c = (cache.memory && cache.heapSize === cache.memory.buffer.byteLength)
  612. ? cache : heapWrappers();
  613. const list = Array.isArray(ptr) ? [] : undefined;
  614. let rc;
  615. do{
  616. if(list) ptr = arguments[0].shift();
  617. switch(type){
  618. case 'i1':
  619. case 'i8': rc = c.HEAP8[ptr>>0]; break;
  620. case 'i16': rc = c.HEAP16[ptr>>1]; break;
  621. case 'i32': rc = c.HEAP32[ptr>>2]; break;
  622. case 'float': case 'f32': rc = c.HEAP32F[ptr>>2]; break;
  623. case 'double': case 'f64': rc = Number(c.HEAP64F[ptr>>3]); break;
  624. case 'i64':
  625. if(target.bigIntEnabled){
  626. rc = BigInt(c.HEAP64[ptr>>3]);
  627. break;
  628. }
  629. /* fallthru */
  630. default:
  631. toss('Invalid type for peek():',type);
  632. }
  633. if(list) list.push(rc);
  634. }while(list && arguments[0].length);
  635. return list || rc;
  636. };
  637. /**
  638. The counterpart of peek(), this sets a numeric value at the given
  639. WASM heap address, using the 3rd argument to define how many
  640. bytes are written. Throws if given an invalid type. See peek()
  641. for details about the `type` argument. If the 3rd argument ends
  642. with `*` then it is treated as a pointer type and this function
  643. behaves as if the 3rd argument were `i32`.
  644. If the first argument is an array, it is treated like a list
  645. of pointers and the given value is written to each one.
  646. Returns `this`. (Prior to 2022-12-09 it returned this function.)
  647. ACHTUNG: calling this often, e.g. in a loop to populate a large
  648. chunk of memory, can have a noticably painful impact on
  649. performance. Rather than doing so, use heapForSize() to fetch the
  650. heap object and assign directly to it or use the heap's set()
  651. method.
  652. */
  653. target.poke = function(ptr, value, type='i8'){
  654. if (type.endsWith('*')) type = ptrIR;
  655. const c = (cache.memory && cache.heapSize === cache.memory.buffer.byteLength)
  656. ? cache : heapWrappers();
  657. for(const p of (Array.isArray(ptr) ? ptr : [ptr])){
  658. switch (type) {
  659. case 'i1':
  660. case 'i8': c.HEAP8[p>>0] = value; continue;
  661. case 'i16': c.HEAP16[p>>1] = value; continue;
  662. case 'i32': c.HEAP32[p>>2] = value; continue;
  663. case 'float': case 'f32': c.HEAP32F[p>>2] = value; continue;
  664. case 'double': case 'f64': c.HEAP64F[p>>3] = value; continue;
  665. case 'i64':
  666. if(c.HEAP64){
  667. c.HEAP64[p>>3] = BigInt(value);
  668. continue;
  669. }
  670. /* fallthru */
  671. default:
  672. toss('Invalid type for poke(): ' + type);
  673. }
  674. }
  675. return this;
  676. };
  677. /**
  678. Convenience form of peek() intended for fetching
  679. pointer-to-pointer values. If passed a single non-array argument
  680. it returns the value of that one pointer address. If passed
  681. multiple arguments, or a single array of arguments, it returns an
  682. array of their values.
  683. */
  684. target.peekPtr = (...ptr)=>target.peek( (1===ptr.length ? ptr[0] : ptr), ptrIR );
  685. /**
  686. A variant of poke() intended for setting pointer-to-pointer
  687. values. Its differences from poke() are that (1) it defaults to a
  688. value of 0 and (2) it always writes to the pointer-sized heap
  689. view.
  690. */
  691. target.pokePtr = (ptr, value=0)=>target.poke(ptr, value, ptrIR);
  692. /**
  693. Convenience form of peek() intended for fetching i8 values. If
  694. passed a single non-array argument it returns the value of that
  695. one pointer address. If passed multiple arguments, or a single
  696. array of arguments, it returns an array of their values.
  697. */
  698. target.peek8 = (...ptr)=>target.peek( (1===ptr.length ? ptr[0] : ptr), 'i8' );
  699. /**
  700. Convience form of poke() intended for setting individual bytes.
  701. Its difference from poke() is that it always writes to the
  702. i8-sized heap view.
  703. */
  704. target.poke8 = (ptr, value)=>target.poke(ptr, value, 'i8');
  705. /** i16 variant of peek8(). */
  706. target.peek16 = (...ptr)=>target.peek( (1===ptr.length ? ptr[0] : ptr), 'i16' );
  707. /** i16 variant of poke8(). */
  708. target.poke16 = (ptr, value)=>target.poke(ptr, value, 'i16');
  709. /** i32 variant of peek8(). */
  710. target.peek32 = (...ptr)=>target.peek( (1===ptr.length ? ptr[0] : ptr), 'i32' );
  711. /** i32 variant of poke8(). */
  712. target.poke32 = (ptr, value)=>target.poke(ptr, value, 'i32');
  713. /** i64 variant of peek8(). Will throw if this build is not
  714. configured for BigInt support. */
  715. target.peek64 = (...ptr)=>target.peek( (1===ptr.length ? ptr[0] : ptr), 'i64' );
  716. /** i64 variant of poke8(). Will throw if this build is not
  717. configured for BigInt support. Note that this returns
  718. a BigInt-type value, not a Number-type value. */
  719. target.poke64 = (ptr, value)=>target.poke(ptr, value, 'i64');
  720. /** f32 variant of peek8(). */
  721. target.peek32f = (...ptr)=>target.peek( (1===ptr.length ? ptr[0] : ptr), 'f32' );
  722. /** f32 variant of poke8(). */
  723. target.poke32f = (ptr, value)=>target.poke(ptr, value, 'f32');
  724. /** f64 variant of peek8(). */
  725. target.peek64f = (...ptr)=>target.peek( (1===ptr.length ? ptr[0] : ptr), 'f64' );
  726. /** f64 variant of poke8(). */
  727. target.poke64f = (ptr, value)=>target.poke(ptr, value, 'f64');
  728. /** Deprecated alias for getMemValue() */
  729. target.getMemValue = target.peek;
  730. /** Deprecated alias for peekPtr() */
  731. target.getPtrValue = target.peekPtr;
  732. /** Deprecated alias for poke() */
  733. target.setMemValue = target.poke;
  734. /** Deprecated alias for pokePtr() */
  735. target.setPtrValue = target.pokePtr;
  736. /**
  737. Returns true if the given value appears to be legal for use as
  738. a WASM pointer value. Its _range_ of values is not (cannot be)
  739. validated except to ensure that it is a 32-bit integer with a
  740. value of 0 or greater. Likewise, it cannot verify whether the
  741. value actually refers to allocated memory in the WASM heap.
  742. */
  743. target.isPtr32 = (ptr)=>('number'===typeof ptr && (ptr===(ptr|0)) && ptr>=0);
  744. /**
  745. isPtr() is an alias for isPtr32(). If/when 64-bit WASM pointer
  746. support becomes widespread, it will become an alias for either
  747. isPtr32() or the as-yet-hypothetical isPtr64(), depending on a
  748. configuration option.
  749. */
  750. target.isPtr = target.isPtr32;
  751. /**
  752. Expects ptr to be a pointer into the WASM heap memory which
  753. refers to a NUL-terminated C-style string encoded as UTF-8.
  754. Returns the length, in bytes, of the string, as for `strlen(3)`.
  755. As a special case, if !ptr or if it's not a pointer then it
  756. returns `null`. Throws if ptr is out of range for
  757. target.heap8u().
  758. */
  759. target.cstrlen = function(ptr){
  760. if(!ptr || !target.isPtr(ptr)) return null;
  761. const h = heapWrappers().HEAP8U;
  762. let pos = ptr;
  763. for( ; h[pos] !== 0; ++pos ){}
  764. return pos - ptr;
  765. };
  766. /** Internal helper to use in operations which need to distinguish
  767. between SharedArrayBuffer heap memory and non-shared heap. */
  768. const __SAB = ('undefined'===typeof SharedArrayBuffer)
  769. ? function(){} : SharedArrayBuffer;
  770. const __utf8Decode = function(arrayBuffer, begin, end){
  771. return cache.utf8Decoder.decode(
  772. (arrayBuffer.buffer instanceof __SAB)
  773. ? arrayBuffer.slice(begin, end)
  774. : arrayBuffer.subarray(begin, end)
  775. );
  776. };
  777. /**
  778. Expects ptr to be a pointer into the WASM heap memory which
  779. refers to a NUL-terminated C-style string encoded as UTF-8. This
  780. function counts its byte length using cstrlen() then returns a
  781. JS-format string representing its contents. As a special case, if
  782. ptr is falsy or not a pointer, `null` is returned.
  783. */
  784. target.cstrToJs = function(ptr){
  785. const n = target.cstrlen(ptr);
  786. return n ? __utf8Decode(heapWrappers().HEAP8U, ptr, ptr+n) : (null===n ? n : "");
  787. };
  788. /**
  789. Given a JS string, this function returns its UTF-8 length in
  790. bytes. Returns null if str is not a string.
  791. */
  792. target.jstrlen = function(str){
  793. /** Attribution: derived from Emscripten's lengthBytesUTF8() */
  794. if('string'!==typeof str) return null;
  795. const n = str.length;
  796. let len = 0;
  797. for(let i = 0; i < n; ++i){
  798. let u = str.charCodeAt(i);
  799. if(u>=0xd800 && u<=0xdfff){
  800. u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF);
  801. }
  802. if(u<=0x7f) ++len;
  803. else if(u<=0x7ff) len += 2;
  804. else if(u<=0xffff) len += 3;
  805. else len += 4;
  806. }
  807. return len;
  808. };
  809. /**
  810. Encodes the given JS string as UTF8 into the given TypedArray
  811. tgt, starting at the given offset and writing, at most, maxBytes
  812. bytes (including the NUL terminator if addNul is true, else no
  813. NUL is added). If it writes any bytes at all and addNul is true,
  814. it always NUL-terminates the output, even if doing so means that
  815. the NUL byte is all that it writes.
  816. If maxBytes is negative (the default) then it is treated as the
  817. remaining length of tgt, starting at the given offset.
  818. If writing the last character would surpass the maxBytes count
  819. because the character is multi-byte, that character will not be
  820. written (as opposed to writing a truncated multi-byte character).
  821. This can lead to it writing as many as 3 fewer bytes than
  822. maxBytes specifies.
  823. Returns the number of bytes written to the target, _including_
  824. the NUL terminator (if any). If it returns 0, it wrote nothing at
  825. all, which can happen if:
  826. - str is empty and addNul is false.
  827. - offset < 0.
  828. - maxBytes == 0.
  829. - maxBytes is less than the byte length of a multi-byte str[0].
  830. Throws if tgt is not an Int8Array or Uint8Array.
  831. Design notes:
  832. - In C's strcpy(), the destination pointer is the first
  833. argument. That is not the case here primarily because the 3rd+
  834. arguments are all referring to the destination, so it seems to
  835. make sense to have them grouped with it.
  836. - Emscripten's counterpart of this function (stringToUTF8Array())
  837. returns the number of bytes written sans NUL terminator. That
  838. is, however, ambiguous: str.length===0 or maxBytes===(0 or 1)
  839. all cause 0 to be returned.
  840. */
  841. target.jstrcpy = function(jstr, tgt, offset = 0, maxBytes = -1, addNul = true){
  842. /** Attribution: the encoding bits are taken from Emscripten's
  843. stringToUTF8Array(). */
  844. if(!tgt || (!(tgt instanceof Int8Array) && !(tgt instanceof Uint8Array))){
  845. toss("jstrcpy() target must be an Int8Array or Uint8Array.");
  846. }
  847. if(maxBytes<0) maxBytes = tgt.length - offset;
  848. if(!(maxBytes>0) || !(offset>=0)) return 0;
  849. let i = 0, max = jstr.length;
  850. const begin = offset, end = offset + maxBytes - (addNul ? 1 : 0);
  851. for(; i < max && offset < end; ++i){
  852. let u = jstr.charCodeAt(i);
  853. if(u>=0xd800 && u<=0xdfff){
  854. u = 0x10000 + ((u & 0x3FF) << 10) | (jstr.charCodeAt(++i) & 0x3FF);
  855. }
  856. if(u<=0x7f){
  857. if(offset >= end) break;
  858. tgt[offset++] = u;
  859. }else if(u<=0x7ff){
  860. if(offset + 1 >= end) break;
  861. tgt[offset++] = 0xC0 | (u >> 6);
  862. tgt[offset++] = 0x80 | (u & 0x3f);
  863. }else if(u<=0xffff){
  864. if(offset + 2 >= end) break;
  865. tgt[offset++] = 0xe0 | (u >> 12);
  866. tgt[offset++] = 0x80 | ((u >> 6) & 0x3f);
  867. tgt[offset++] = 0x80 | (u & 0x3f);
  868. }else{
  869. if(offset + 3 >= end) break;
  870. tgt[offset++] = 0xf0 | (u >> 18);
  871. tgt[offset++] = 0x80 | ((u >> 12) & 0x3f);
  872. tgt[offset++] = 0x80 | ((u >> 6) & 0x3f);
  873. tgt[offset++] = 0x80 | (u & 0x3f);
  874. }
  875. }
  876. if(addNul) tgt[offset++] = 0;
  877. return offset - begin;
  878. };
  879. /**
  880. Works similarly to C's strncpy(), copying, at most, n bytes (not
  881. characters) from srcPtr to tgtPtr. It copies until n bytes have
  882. been copied or a 0 byte is reached in src. _Unlike_ strncpy(), it
  883. returns the number of bytes it assigns in tgtPtr, _including_ the
  884. NUL byte (if any). If n is reached before a NUL byte in srcPtr,
  885. tgtPtr will _not_ be NULL-terminated. If a NUL byte is reached
  886. before n bytes are copied, tgtPtr will be NUL-terminated.
  887. If n is negative, cstrlen(srcPtr)+1 is used to calculate it, the
  888. +1 being for the NUL byte.
  889. Throws if tgtPtr or srcPtr are falsy. Results are undefined if:
  890. - either is not a pointer into the WASM heap or
  891. - srcPtr is not NUL-terminated AND n is less than srcPtr's
  892. logical length.
  893. ACHTUNG: it is possible to copy partial multi-byte characters
  894. this way, and converting such strings back to JS strings will
  895. have undefined results.
  896. */
  897. target.cstrncpy = function(tgtPtr, srcPtr, n){
  898. if(!tgtPtr || !srcPtr) toss("cstrncpy() does not accept NULL strings.");
  899. if(n<0) n = target.cstrlen(strPtr)+1;
  900. else if(!(n>0)) return 0;
  901. const heap = target.heap8u();
  902. let i = 0, ch;
  903. for(; i < n && (ch = heap[srcPtr+i]); ++i){
  904. heap[tgtPtr+i] = ch;
  905. }
  906. if(i<n) heap[tgtPtr + i++] = 0;
  907. return i;
  908. };
  909. /**
  910. For the given JS string, returns a Uint8Array of its contents
  911. encoded as UTF-8. If addNul is true, the returned array will have
  912. a trailing 0 entry, else it will not.
  913. */
  914. target.jstrToUintArray = (str, addNul=false)=>{
  915. return cache.utf8Encoder.encode(addNul ? (str+"\0") : str);
  916. // Or the hard way...
  917. /** Attribution: derived from Emscripten's stringToUTF8Array() */
  918. //const a = [], max = str.length;
  919. //let i = 0, pos = 0;
  920. //for(; i < max; ++i){
  921. // let u = str.charCodeAt(i);
  922. // if(u>=0xd800 && u<=0xdfff){
  923. // u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF);
  924. // }
  925. // if(u<=0x7f) a[pos++] = u;
  926. // else if(u<=0x7ff){
  927. // a[pos++] = 0xC0 | (u >> 6);
  928. // a[pos++] = 0x80 | (u & 63);
  929. // }else if(u<=0xffff){
  930. // a[pos++] = 0xe0 | (u >> 12);
  931. // a[pos++] = 0x80 | ((u >> 6) & 63);
  932. // a[pos++] = 0x80 | (u & 63);
  933. // }else{
  934. // a[pos++] = 0xf0 | (u >> 18);
  935. // a[pos++] = 0x80 | ((u >> 12) & 63);
  936. // a[pos++] = 0x80 | ((u >> 6) & 63);
  937. // a[pos++] = 0x80 | (u & 63);
  938. // }
  939. // }
  940. // return new Uint8Array(a);
  941. };
  942. const __affirmAlloc = (obj,funcName)=>{
  943. if(!(obj.alloc instanceof Function) ||
  944. !(obj.dealloc instanceof Function)){
  945. toss("Object is missing alloc() and/or dealloc() function(s)",
  946. "required by",funcName+"().");
  947. }
  948. };
  949. const __allocCStr = function(jstr, returnWithLength, allocator, funcName){
  950. __affirmAlloc(target, funcName);
  951. if('string'!==typeof jstr) return null;
  952. if(0){/* older impl, possibly more widely compatible? */
  953. const n = target.jstrlen(jstr),
  954. ptr = allocator(n+1);
  955. target.jstrcpy(jstr, target.heap8u(), ptr, n+1, true);
  956. return returnWithLength ? [ptr, n] : ptr;
  957. }else{/* newer, (probably) faster and (certainly) simpler impl */
  958. const u = cache.utf8Encoder.encode(jstr),
  959. ptr = allocator(u.length+1),
  960. heap = heapWrappers().HEAP8U;
  961. heap.set(u, ptr);
  962. heap[ptr + u.length] = 0;
  963. return returnWithLength ? [ptr, u.length] : ptr;
  964. }
  965. };
  966. /**
  967. Uses target.alloc() to allocate enough memory for jstrlen(jstr)+1
  968. bytes of memory, copies jstr to that memory using jstrcpy(),
  969. NUL-terminates it, and returns the pointer to that C-string.
  970. Ownership of the pointer is transfered to the caller, who must
  971. eventually pass the pointer to dealloc() to free it.
  972. If passed a truthy 2nd argument then its return semantics change:
  973. it returns [ptr,n], where ptr is the C-string's pointer and n is
  974. its cstrlen().
  975. Throws if `target.alloc` or `target.dealloc` are not functions.
  976. */
  977. target.allocCString =
  978. (jstr, returnWithLength=false)=>__allocCStr(jstr, returnWithLength,
  979. target.alloc, 'allocCString()');
  980. /**
  981. Starts an "allocation scope." All allocations made using
  982. scopedAlloc() are recorded in this scope and are freed when the
  983. value returned from this function is passed to
  984. scopedAllocPop().
  985. This family of functions requires that the API's object have both
  986. `alloc()` and `dealloc()` methods, else this function will throw.
  987. Intended usage:
  988. ```
  989. const scope = scopedAllocPush();
  990. try {
  991. const ptr1 = scopedAlloc(100);
  992. const ptr2 = scopedAlloc(200);
  993. const ptr3 = scopedAlloc(300);
  994. ...
  995. // Note that only allocations made via scopedAlloc()
  996. // are managed by this allocation scope.
  997. }finally{
  998. scopedAllocPop(scope);
  999. }
  1000. ```
  1001. The value returned by this function must be treated as opaque by
  1002. the caller, suitable _only_ for passing to scopedAllocPop().
  1003. Its type and value are not part of this function's API and may
  1004. change in any given version of this code.
  1005. `scopedAlloc.level` can be used to determine how many scoped
  1006. alloc levels are currently active.
  1007. */
  1008. target.scopedAllocPush = function(){
  1009. __affirmAlloc(target, 'scopedAllocPush');
  1010. const a = [];
  1011. cache.scopedAlloc.push(a);
  1012. return a;
  1013. };
  1014. /**
  1015. Cleans up all allocations made using scopedAlloc() in the context
  1016. of the given opaque state object, which must be a value returned
  1017. by scopedAllocPush(). See that function for an example of how to
  1018. use this function.
  1019. Though scoped allocations are managed like a stack, this API
  1020. behaves properly if allocation scopes are popped in an order
  1021. other than the order they were pushed.
  1022. If called with no arguments, it pops the most recent
  1023. scopedAllocPush() result:
  1024. ```
  1025. scopedAllocPush();
  1026. try{ ... } finally { scopedAllocPop(); }
  1027. ```
  1028. It's generally recommended that it be passed an explicit argument
  1029. to help ensure that push/push are used in matching pairs, but in
  1030. trivial code that may be a non-issue.
  1031. */
  1032. target.scopedAllocPop = function(state){
  1033. __affirmAlloc(target, 'scopedAllocPop');
  1034. const n = arguments.length
  1035. ? cache.scopedAlloc.indexOf(state)
  1036. : cache.scopedAlloc.length-1;
  1037. if(n<0) toss("Invalid state object for scopedAllocPop().");
  1038. if(0===arguments.length) state = cache.scopedAlloc[n];
  1039. cache.scopedAlloc.splice(n,1);
  1040. for(let p; (p = state.pop()); ){
  1041. if(target.functionEntry(p)){
  1042. //console.warn("scopedAllocPop() uninstalling function",p);
  1043. target.uninstallFunction(p);
  1044. }
  1045. else target.dealloc(p);
  1046. }
  1047. };
  1048. /**
  1049. Allocates n bytes of memory using this.alloc() and records that
  1050. fact in the state for the most recent call of scopedAllocPush().
  1051. Ownership of the memory is given to scopedAllocPop(), which
  1052. will clean it up when it is called. The memory _must not_ be
  1053. passed to this.dealloc(). Throws if this API object is missing
  1054. the required `alloc()` or `dealloc()` functions or no scoped
  1055. alloc is active.
  1056. See scopedAllocPush() for an example of how to use this function.
  1057. The `level` property of this function can be queried to query how
  1058. many scoped allocation levels are currently active.
  1059. See also: scopedAllocPtr(), scopedAllocCString()
  1060. */
  1061. target.scopedAlloc = function(n){
  1062. if(!cache.scopedAlloc.length){
  1063. toss("No scopedAllocPush() scope is active.");
  1064. }
  1065. const p = target.alloc(n);
  1066. cache.scopedAlloc[cache.scopedAlloc.length-1].push(p);
  1067. return p;
  1068. };
  1069. Object.defineProperty(target.scopedAlloc, 'level', {
  1070. configurable: false, enumerable: false,
  1071. get: ()=>cache.scopedAlloc.length,
  1072. set: ()=>toss("The 'active' property is read-only.")
  1073. });
  1074. /**
  1075. Works identically to allocCString() except that it allocates the
  1076. memory using scopedAlloc().
  1077. Will throw if no scopedAllocPush() call is active.
  1078. */
  1079. target.scopedAllocCString =
  1080. (jstr, returnWithLength=false)=>__allocCStr(jstr, returnWithLength,
  1081. target.scopedAlloc, 'scopedAllocCString()');
  1082. // impl for allocMainArgv() and scopedAllocMainArgv().
  1083. const __allocMainArgv = function(isScoped, list){
  1084. const pList = target[
  1085. isScoped ? 'scopedAlloc' : 'alloc'
  1086. ]((list.length + 1) * target.ptrSizeof);
  1087. let i = 0;
  1088. list.forEach((e)=>{
  1089. target.pokePtr(pList + (target.ptrSizeof * i++),
  1090. target[
  1091. isScoped ? 'scopedAllocCString' : 'allocCString'
  1092. ](""+e));
  1093. });
  1094. target.pokePtr(pList + (target.ptrSizeof * i), 0);
  1095. return pList;
  1096. };
  1097. /**
  1098. Creates an array, using scopedAlloc(), suitable for passing to a
  1099. C-level main() routine. The input is a collection with a length
  1100. property and a forEach() method. A block of memory
  1101. (list.length+1) entries long is allocated and each pointer-sized
  1102. block of that memory is populated with a scopedAllocCString()
  1103. conversion of the (""+value) of each element, with the exception
  1104. that the final entry is a NULL pointer. Returns a pointer to the
  1105. start of the list, suitable for passing as the 2nd argument to a
  1106. C-style main() function.
  1107. Throws if scopedAllocPush() is not active.
  1108. Design note: the returned array is allocated with an extra NULL
  1109. pointer entry to accommodate certain APIs, but client code which
  1110. does not need that functionality should treat the returned array
  1111. as list.length entries long.
  1112. */
  1113. target.scopedAllocMainArgv = (list)=>__allocMainArgv(true, list);
  1114. /**
  1115. Identical to scopedAllocMainArgv() but uses alloc() instead of
  1116. scopedAlloc().
  1117. */
  1118. target.allocMainArgv = (list)=>__allocMainArgv(false, list);
  1119. /**
  1120. Expects to be given a C-style string array and its length. It
  1121. returns a JS array of strings and/or nulls: any entry in the
  1122. pArgv array which is NULL results in a null entry in the result
  1123. array. If argc is 0 then an empty array is returned.
  1124. Results are undefined if any entry in the first argc entries of
  1125. pArgv are neither 0 (NULL) nor legal UTF-format C strings.
  1126. To be clear, the expected C-style arguments to be passed to this
  1127. function are `(int, char **)` (optionally const-qualified).
  1128. */
  1129. target.cArgvToJs = (argc, pArgv)=>{
  1130. const list = [];
  1131. for(let i = 0; i < argc; ++i){
  1132. const arg = target.peekPtr(pArgv + (target.ptrSizeof * i));
  1133. list.push( arg ? target.cstrToJs(arg) : null );
  1134. }
  1135. return list;
  1136. };
  1137. /**
  1138. Wraps function call func() in a scopedAllocPush() and
  1139. scopedAllocPop() block, such that all calls to scopedAlloc() and
  1140. friends from within that call will have their memory freed
  1141. automatically when func() returns. If func throws or propagates
  1142. an exception, the scope is still popped, otherwise it returns the
  1143. result of calling func().
  1144. */
  1145. target.scopedAllocCall = function(func){
  1146. target.scopedAllocPush();
  1147. try{ return func() } finally{ target.scopedAllocPop() }
  1148. };
  1149. /** Internal impl for allocPtr() and scopedAllocPtr(). */
  1150. const __allocPtr = function(howMany, safePtrSize, method){
  1151. __affirmAlloc(target, method);
  1152. const pIr = safePtrSize ? 'i64' : ptrIR;
  1153. let m = target[method](howMany * (safePtrSize ? 8 : ptrSizeof));
  1154. target.poke(m, 0, pIr)
  1155. if(1===howMany){
  1156. return m;
  1157. }
  1158. const a = [m];
  1159. for(let i = 1; i < howMany; ++i){
  1160. m += (safePtrSize ? 8 : ptrSizeof);
  1161. a[i] = m;
  1162. target.poke(m, 0, pIr);
  1163. }
  1164. return a;
  1165. };
  1166. /**
  1167. Allocates one or more pointers as a single chunk of memory and
  1168. zeroes them out.
  1169. The first argument is the number of pointers to allocate. The
  1170. second specifies whether they should use a "safe" pointer size (8
  1171. bytes) or whether they may use the default pointer size
  1172. (typically 4 but also possibly 8).
  1173. How the result is returned depends on its first argument: if
  1174. passed 1, it returns the allocated memory address. If passed more
  1175. than one then an array of pointer addresses is returned, which
  1176. can optionally be used with "destructuring assignment" like this:
  1177. ```
  1178. const [p1, p2, p3] = allocPtr(3);
  1179. ```
  1180. ACHTUNG: when freeing the memory, pass only the _first_ result
  1181. value to dealloc(). The others are part of the same memory chunk
  1182. and must not be freed separately.
  1183. The reason for the 2nd argument is..
  1184. When one of the returned pointers will refer to a 64-bit value,
  1185. e.g. a double or int64, an that value must be written or fetched,
  1186. e.g. using poke() or peek(), it is important that
  1187. the pointer in question be aligned to an 8-byte boundary or else
  1188. it will not be fetched or written properly and will corrupt or
  1189. read neighboring memory. It is only safe to pass false when the
  1190. client code is certain that it will only get/fetch 4-byte values
  1191. (or smaller).
  1192. */
  1193. target.allocPtr =
  1194. (howMany=1, safePtrSize=true)=>__allocPtr(howMany, safePtrSize, 'alloc');
  1195. /**
  1196. Identical to allocPtr() except that it allocates using scopedAlloc()
  1197. instead of alloc().
  1198. */
  1199. target.scopedAllocPtr =
  1200. (howMany=1, safePtrSize=true)=>__allocPtr(howMany, safePtrSize, 'scopedAlloc');
  1201. /**
  1202. If target.exports[name] exists, it is returned, else an
  1203. exception is thrown.
  1204. */
  1205. target.xGet = function(name){
  1206. return target.exports[name] || toss("Cannot find exported symbol:",name);
  1207. };
  1208. const __argcMismatch =
  1209. (f,n)=>toss(f+"() requires",n,"argument(s).");
  1210. /**
  1211. Looks up a WASM-exported function named fname from
  1212. target.exports. If found, it is called, passed all remaining
  1213. arguments, and its return value is returned to xCall's caller. If
  1214. not found, an exception is thrown. This function does no
  1215. conversion of argument or return types, but see xWrap() and
  1216. xCallWrapped() for variants which do.
  1217. If the first argument is a function is is assumed to be
  1218. a WASM-bound function and is used as-is instead of looking up
  1219. the function via xGet().
  1220. As a special case, if passed only 1 argument after the name and
  1221. that argument in an Array, that array's entries become the
  1222. function arguments. (This is not an ambiguous case because it's
  1223. not legal to pass an Array object to a WASM function.)
  1224. */
  1225. target.xCall = function(fname, ...args){
  1226. const f = (fname instanceof Function) ? fname : target.xGet(fname);
  1227. if(!(f instanceof Function)) toss("Exported symbol",fname,"is not a function.");
  1228. if(f.length!==args.length) __argcMismatch(((f===fname) ? f.name : fname),f.length)
  1229. /* This is arguably over-pedantic but we want to help clients keep
  1230. from shooting themselves in the foot when calling C APIs. */;
  1231. return (2===arguments.length && Array.isArray(arguments[1]))
  1232. ? f.apply(null, arguments[1])
  1233. : f.apply(null, args);
  1234. };
  1235. /**
  1236. State for use with xWrap()
  1237. */
  1238. cache.xWrap = Object.create(null);
  1239. cache.xWrap.convert = Object.create(null);
  1240. /** Map of type names to argument conversion functions. */
  1241. cache.xWrap.convert.arg = new Map;
  1242. /** Map of type names to return result conversion functions. */
  1243. cache.xWrap.convert.result = new Map;
  1244. const xArg = cache.xWrap.convert.arg, xResult = cache.xWrap.convert.result;
  1245. if(target.bigIntEnabled){
  1246. xArg.set('i64', (i)=>BigInt(i));
  1247. }
  1248. const __xArgPtr = 'i32' === ptrIR
  1249. ? ((i)=>(i | 0)) : ((i)=>(BigInt(i) | BigInt(0)));
  1250. xArg.set('i32', __xArgPtr )
  1251. .set('i16', (i)=>((i | 0) & 0xFFFF))
  1252. .set('i8', (i)=>((i | 0) & 0xFF))
  1253. .set('f32', (i)=>Number(i).valueOf())
  1254. .set('float', xArg.get('f32'))
  1255. .set('f64', xArg.get('f32'))
  1256. .set('double', xArg.get('f64'))
  1257. .set('int', xArg.get('i32'))
  1258. .set('null', (i)=>i)
  1259. .set(null, xArg.get('null'))
  1260. .set('**', __xArgPtr)
  1261. .set('*', __xArgPtr);
  1262. xResult.set('*', __xArgPtr)
  1263. .set('pointer', __xArgPtr)
  1264. .set('number', (v)=>Number(v))
  1265. .set('void', (v)=>undefined)
  1266. .set('null', (v)=>v)
  1267. .set(null, xResult.get('null'));
  1268. { /* Copy certain xArg[...] handlers to xResult[...] and
  1269. add pointer-style variants of them. */
  1270. const copyToResult = ['i8', 'i16', 'i32', 'int',
  1271. 'f32', 'float', 'f64', 'double'];
  1272. if(target.bigIntEnabled) copyToResult.push('i64');
  1273. const adaptPtr = xArg.get(ptrIR);
  1274. for(const t of copyToResult){
  1275. xArg.set(t+'*', adaptPtr);
  1276. xResult.set(t+'*', adaptPtr);
  1277. xResult.set(t, (xArg.get(t) || toss("Missing arg converter:",t)));
  1278. }
  1279. }
  1280. /**
  1281. In order for args of type string to work in various contexts in
  1282. the sqlite3 API, we need to pass them on as, variably, a C-string
  1283. or a pointer value. Thus for ARGs of type 'string' and
  1284. '*'/'pointer' we behave differently depending on whether the
  1285. argument is a string or not:
  1286. - If v is a string, scopeAlloc() a new C-string from it and return
  1287. that temp string's pointer.
  1288. - Else return the value from the arg adapter defined for ptrIR.
  1289. TODO? Permit an Int8Array/Uint8Array and convert it to a string?
  1290. Would that be too much magic concentrated in one place, ready to
  1291. backfire? We handle that at the client level in sqlite3 with a
  1292. custom argument converter.
  1293. */
  1294. const __xArgString = function(v){
  1295. if('string'===typeof v) return target.scopedAllocCString(v);
  1296. return v ? __xArgPtr(v) : null;
  1297. };
  1298. xArg.set('string', __xArgString)
  1299. .set('utf8', __xArgString)
  1300. .set('pointer', __xArgString);
  1301. //xArg.set('*', __xArgString);
  1302. xResult.set('string', (i)=>target.cstrToJs(i))
  1303. .set('utf8', xResult.get('string'))
  1304. .set('string:dealloc', (i)=>{
  1305. try { return i ? target.cstrToJs(i) : null }
  1306. finally{ target.dealloc(i) }
  1307. })
  1308. .set('utf8:dealloc', xResult.get('string:dealloc'))
  1309. .set('json', (i)=>JSON.parse(target.cstrToJs(i)))
  1310. .set('json:dealloc', (i)=>{
  1311. try{ return i ? JSON.parse(target.cstrToJs(i)) : null }
  1312. finally{ target.dealloc(i) }
  1313. });
  1314. /**
  1315. Internal-use-only base class for FuncPtrAdapter and potentially
  1316. additional stateful argument adapter classes.
  1317. Note that its main interface (convertArg()) is strictly
  1318. internal, not to be exposed to client code, as it may still
  1319. need re-shaping. Only the constructors of concrete subclasses
  1320. should be exposed to clients, and those in such a way that
  1321. does not hinder internal redesign of the convertArg()
  1322. interface.
  1323. */
  1324. const AbstractArgAdapter = class {
  1325. constructor(opt){
  1326. this.name = opt.name || 'unnamed adapter';
  1327. }
  1328. /**
  1329. Gets called via xWrap() to "convert" v to whatever type
  1330. this specific class supports.
  1331. argIndex is the argv index of _this_ argument in the
  1332. being-xWrap()'d call. argv is the current argument list
  1333. undergoing xWrap() argument conversion. argv entries to the
  1334. left of argIndex will have already undergone transformation and
  1335. those to the right will not have (they will have the values the
  1336. client-level code passed in, awaiting conversion). The RHS
  1337. indexes must never be relied upon for anything because their
  1338. types are indeterminate, whereas the LHS values will be
  1339. WASM-compatible values by the time this is called.
  1340. */
  1341. convertArg(v,argv,argIndex){
  1342. toss("AbstractArgAdapter must be subclassed.");
  1343. }
  1344. };
  1345. /**
  1346. An attempt at adding function pointer conversion support to
  1347. xWrap(). This type is recognized by xWrap() as a proxy for
  1348. converting a JS function to a C-side function, either
  1349. permanently, for the duration of a single call into the C layer,
  1350. or semi-contextual, where it may keep track of a single binding
  1351. for a given context and uninstall the binding if it's replaced.
  1352. The constructor requires an options object with these properties:
  1353. - name (optional): string describing the function binding. This
  1354. is solely for debugging and error-reporting purposes. If not
  1355. provided, an empty string is assumed.
  1356. - signature: a function signature string compatible with
  1357. jsFuncToWasm().
  1358. - bindScope (string): one of ('transient', 'context',
  1359. 'singleton', 'permanent'). Bind scopes are:
  1360. - 'transient': it will convert JS functions to WASM only for
  1361. the duration of the xWrap()'d function call, using
  1362. scopedInstallFunction(). Before that call returns, the
  1363. WASM-side binding will be uninstalled.
  1364. - 'singleton': holds one function-pointer binding for this
  1365. instance. If it's called with a different function pointer,
  1366. it uninstalls the previous one after converting the new
  1367. value. This is only useful for use with "global" functions
  1368. which do not rely on any state other than this function
  1369. pointer. If the being-converted function pointer is intended
  1370. to be mapped to some sort of state object (e.g. an
  1371. `sqlite3*`) then "context" (see below) is the proper mode.
  1372. - 'context': similar to singleton mode but for a given
  1373. "context", where the context is a key provided by the user
  1374. and possibly dependent on a small amount of call-time
  1375. context. This mode is the default if bindScope is _not_ set
  1376. but a property named contextKey (described below) is.
  1377. - 'permanent': the function is installed and left there
  1378. forever. There is no way to recover its pointer address
  1379. later on.
  1380. - callProxy (function): if set, this must be a function which
  1381. will act as a proxy for any "converted" JS function. It is
  1382. passed the being-converted function value and must return
  1383. either that function or a function which acts on its
  1384. behalf. The returned function will be the one which gets
  1385. installed into the WASM function table. The proxy must perform
  1386. any required argument conversion (noting that it will be called
  1387. from C code, so will receive C-format arguments) before passing
  1388. them on to the being-converted function. Whether or not the
  1389. proxy itself must return a value depends on the context. If it
  1390. does, it must be a WASM-friendly value, as it will be returning
  1391. from a call made from native code.
  1392. - contextKey (function): is only used if bindScope is 'context'
  1393. or if bindScope is not set and this function is, in which case
  1394. 'context' is assumed. This function gets bound to this object,
  1395. so its "this" is this object. It gets passed (argv,argIndex),
  1396. where argIndex is the index of _this_ function pointer in its
  1397. _wrapping_ function's arguments and argv is the _current_
  1398. still-being-xWrap()-processed args array. All arguments to the
  1399. left of argIndex will have been processed by xWrap() by the
  1400. time this is called. argv[argIndex] will be the value the user
  1401. passed in to the xWrap()'d function for the argument this
  1402. FuncPtrAdapter is mapped to. Arguments to the right of
  1403. argv[argIndex] will not yet have been converted before this is
  1404. called. The function must return a key which uniquely
  1405. identifies this function mapping context for _this_
  1406. FuncPtrAdapter instance (other instances are not considered),
  1407. taking into account that C functions often take some sort of
  1408. state object as one or more of their arguments. As an example,
  1409. if the xWrap()'d function takes `(int,T*,functionPtr,X*)` and
  1410. this FuncPtrAdapter is the argv[2]nd arg, contextKey(argv,2)
  1411. might return 'T@'+argv[1], or even just argv[1]. Note,
  1412. however, that the (X*) argument will not yet have been
  1413. processed by the time this is called and should not be used as
  1414. part of that key because its pre-conversion data type might be
  1415. unpredictable. Similarly, care must be taken with C-string-type
  1416. arguments: those to the left in argv will, when this is called,
  1417. be WASM pointers, whereas those to the right might (and likely
  1418. do) have another data type. When using C-strings in keys, never
  1419. use their pointers in the key because most C-strings in this
  1420. constellation are transient.
  1421. Yes, that ^^^ is quite awkward, but it's what we have.
  1422. The constructor only saves the above state for later, and does
  1423. not actually bind any functions. Its convertArg() method is
  1424. called via xWrap() to perform any bindings.
  1425. Shortcomings:
  1426. - These "reverse" bindings, i.e. calling into a JS-defined
  1427. function from a WASM-defined function (the generated proxy
  1428. wrapper), lack all type conversion support. That means, for
  1429. example, that...
  1430. - Function pointers which include C-string arguments may still
  1431. need a level of hand-written wrappers around them, depending on
  1432. how they're used, in order to provide the client with JS
  1433. strings. Alternately, clients will need to perform such conversions
  1434. on their own, e.g. using cstrtojs(). Or maybe we can find a way
  1435. to perform such conversions here, via addition of an xWrap()-style
  1436. function signature to the options argument.
  1437. */
  1438. xArg.FuncPtrAdapter = class FuncPtrAdapter extends AbstractArgAdapter {
  1439. constructor(opt) {
  1440. super(opt);
  1441. if(xArg.FuncPtrAdapter.warnOnUse){
  1442. console.warn('xArg.FuncPtrAdapter is an internal-only API',
  1443. 'and is not intended to be invoked from',
  1444. 'client-level code. Invoked with:',opt);
  1445. }
  1446. this.name = opt.name || "unnamed";
  1447. this.signature = opt.signature;
  1448. if(opt.contextKey instanceof Function){
  1449. this.contextKey = opt.contextKey;
  1450. if(!opt.bindScope) opt.bindScope = 'context';
  1451. }
  1452. this.bindScope = opt.bindScope
  1453. || toss("FuncPtrAdapter options requires a bindScope (explicit or implied).");
  1454. if(FuncPtrAdapter.bindScopes.indexOf(opt.bindScope)<0){
  1455. toss("Invalid options.bindScope ("+opt.bindMod+") for FuncPtrAdapter. "+
  1456. "Expecting one of: ("+FuncPtrAdapter.bindScopes.join(', ')+')');
  1457. }
  1458. this.isTransient = 'transient'===this.bindScope;
  1459. this.isContext = 'context'===this.bindScope;
  1460. this.isPermanent = 'permanent'===this.bindScope;
  1461. this.singleton = ('singleton'===this.bindScope) ? [] : undefined;
  1462. //console.warn("FuncPtrAdapter()",opt,this);
  1463. this.callProxy = (opt.callProxy instanceof Function)
  1464. ? opt.callProxy : undefined;
  1465. }
  1466. /**
  1467. Note that static class members are defined outside of the class
  1468. to work around an emcc toolchain build problem: one of the
  1469. tools in emsdk v3.1.42 does not support the static keyword.
  1470. */
  1471. /* Dummy impl. Overwritten per-instance as needed. */
  1472. contextKey(argv,argIndex){
  1473. return this;
  1474. }
  1475. /* Returns this objects mapping for the given context key, in the
  1476. form of an an array, creating the mapping if needed. The key
  1477. may be anything suitable for use in a Map. */
  1478. contextMap(key){
  1479. const cm = (this.__cmap || (this.__cmap = new Map));
  1480. let rc = cm.get(key);
  1481. if(undefined===rc) cm.set(key, (rc = []));
  1482. return rc;
  1483. }
  1484. /**
  1485. Gets called via xWrap() to "convert" v to a WASM-bound function
  1486. pointer. If v is one of (a pointer, null, undefined) then
  1487. (v||0) is returned and any earlier function installed by this
  1488. mapping _might_, depending on how it's bound, be uninstalled.
  1489. If v is not one of those types, it must be a Function, for
  1490. which it creates (if needed) a WASM function binding and
  1491. returns the WASM pointer to that binding. If this instance is
  1492. not in 'transient' mode, it will remember the binding for at
  1493. least the next call, to avoid recreating the function binding
  1494. unnecessarily.
  1495. If it's passed a pointer(ish) value for v, it does _not_
  1496. perform any function binding, so this object's bindMode is
  1497. irrelevant for such cases.
  1498. See the parent class's convertArg() docs for details on what
  1499. exactly the 2nd and 3rd arguments are.
  1500. */
  1501. convertArg(v,argv,argIndex){
  1502. //FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.name,this.signature,this.transient,v);
  1503. let pair = this.singleton;
  1504. if(!pair && this.isContext){
  1505. pair = this.contextMap(this.contextKey(argv,argIndex));
  1506. //FuncPtrAdapter.debugOut(this.name, this.signature, "contextKey() =",this.contextKey(argv,argIndex), pair);
  1507. }
  1508. if(pair && pair[0]===v) return pair[1];
  1509. if(v instanceof Function){
  1510. /* Install a WASM binding and return its pointer. */
  1511. //FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.name,this.signature,this.transient,v,pair);
  1512. if(this.callProxy) v = this.callProxy(v);
  1513. const fp = __installFunction(v, this.signature, this.isTransient);
  1514. if(FuncPtrAdapter.debugFuncInstall){
  1515. FuncPtrAdapter.debugOut("FuncPtrAdapter installed", this,
  1516. this.contextKey(argv,argIndex), '@'+fp, v);
  1517. }
  1518. if(pair){
  1519. /* Replace existing stashed mapping */
  1520. if(pair[1]){
  1521. if(FuncPtrAdapter.debugFuncInstall){
  1522. FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this,
  1523. this.contextKey(argv,argIndex), '@'+pair[1], v);
  1524. }
  1525. try{
  1526. /* Because the pending native call might rely on the
  1527. pointer we're replacing, e.g. as is normally the case
  1528. with sqlite3's xDestroy() methods, we don't
  1529. immediately uninstall but instead add its pointer to
  1530. the scopedAlloc stack, which will be cleared when the
  1531. xWrap() mechanism is done calling the native
  1532. function. We're relying very much here on xWrap()
  1533. having pushed an alloc scope.
  1534. */
  1535. cache.scopedAlloc[cache.scopedAlloc.length-1].push(pair[1]);
  1536. }
  1537. catch(e){/*ignored*/}
  1538. }
  1539. pair[0] = v;
  1540. pair[1] = fp;
  1541. }
  1542. return fp;
  1543. }else if(target.isPtr(v) || null===v || undefined===v){
  1544. //FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.name,this.signature,this.transient,v,pair);
  1545. if(pair && pair[1] && pair[1]!==v){
  1546. /* uninstall stashed mapping and replace stashed mapping with v. */
  1547. if(FuncPtrAdapter.debugFuncInstall){
  1548. FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this,
  1549. this.contextKey(argv,argIndex), '@'+pair[1], v);
  1550. }
  1551. try{ cache.scopedAlloc[cache.scopedAlloc.length-1].push(pair[1]) }
  1552. catch(e){/*ignored*/}
  1553. pair[0] = pair[1] = (v | 0);
  1554. }
  1555. return v || 0;
  1556. }else{
  1557. throw new TypeError("Invalid FuncPtrAdapter argument type. "+
  1558. "Expecting a function pointer or a "+
  1559. (this.name ? this.name+' ' : '')+
  1560. "function matching signature "+
  1561. this.signature+".");
  1562. }
  1563. }/*convertArg()*/
  1564. }/*FuncPtrAdapter*/;
  1565. /** If true, the constructor emits a warning. The intent is that
  1566. this be set to true after bootstrapping of the higher-level
  1567. client library is complete, to warn downstream clients that
  1568. they shouldn't be relying on this implemenation detail which
  1569. does not have a stable interface. */
  1570. xArg.FuncPtrAdapter.warnOnUse = false;
  1571. /** If true, convertArg() will FuncPtrAdapter.debugOut() when it
  1572. (un)installs a function binding to/from WASM. Note that
  1573. deinstallation of bindScope=transient bindings happens
  1574. via scopedAllocPop() so will not be output. */
  1575. xArg.FuncPtrAdapter.debugFuncInstall = false;
  1576. /** Function used for debug output. */
  1577. xArg.FuncPtrAdapter.debugOut = console.debug.bind(console);
  1578. xArg.FuncPtrAdapter.bindScopes = [
  1579. 'transient', 'context', 'singleton', 'permanent'
  1580. ];
  1581. const __xArgAdapterCheck =
  1582. (t)=>xArg.get(t) || toss("Argument adapter not found:",t);
  1583. const __xResultAdapterCheck =
  1584. (t)=>xResult.get(t) || toss("Result adapter not found:",t);
  1585. /**
  1586. Fetches the xWrap() argument adapter mapped to t, calls it,
  1587. passing in all remaining arguments, and returns the result.
  1588. Throws if t is not mapped to an argument converter.
  1589. */
  1590. cache.xWrap.convertArg = (t,...args)=>__xArgAdapterCheck(t)(...args);
  1591. /**
  1592. Identical to convertArg() except that it does not perform
  1593. an is-defined check on the mapping to t before invoking it.
  1594. */
  1595. cache.xWrap.convertArgNoCheck = (t,...args)=>xArg.get(t)(...args);
  1596. /**
  1597. Fetches the xWrap() result adapter mapped to t, calls it, passing
  1598. it v, and returns the result. Throws if t is not mapped to an
  1599. argument converter.
  1600. */
  1601. cache.xWrap.convertResult =
  1602. (t,v)=>(null===t ? v : (t ? __xResultAdapterCheck(t)(v) : undefined));
  1603. /**
  1604. Identical to convertResult() except that it does not perform an
  1605. is-defined check on the mapping to t before invoking it.
  1606. */
  1607. cache.xWrap.convertResultNoCheck =
  1608. (t,v)=>(null===t ? v : (t ? xResult.get(t)(v) : undefined));
  1609. /**
  1610. Creates a wrapper for another function which converts the arguments
  1611. of the wrapper to argument types accepted by the wrapped function,
  1612. then converts the wrapped function's result to another form
  1613. for the wrapper.
  1614. The first argument must be one of:
  1615. - A JavaScript function.
  1616. - The name of a WASM-exported function. In the latter case xGet()
  1617. is used to fetch the exported function, which throws if it's not
  1618. found.
  1619. - A pointer into the indirect function table. e.g. a pointer
  1620. returned from target.installFunction().
  1621. It returns either the passed-in function or a wrapper for that
  1622. function which converts the JS-side argument types into WASM-side
  1623. types and converts the result type.
  1624. The second argument, `resultType`, describes the conversion for
  1625. the wrapped functions result. A literal `null` or the string
  1626. `'null'` both mean to return the original function's value as-is
  1627. (mnemonic: there is "null" conversion going on). Literal
  1628. `undefined` or the string `"void"` both mean to ignore the
  1629. function's result and return `undefined`. Aside from those two
  1630. special cases, the `resultType` value may be one of the values
  1631. described below or any mapping installed by the client using
  1632. xWrap.resultAdapter().
  1633. If passed 3 arguments and the final one is an array, that array
  1634. must contain a list of type names (see below) for adapting the
  1635. arguments from JS to WASM. If passed 2 arguments, more than 3,
  1636. or the 3rd is not an array, all arguments after the 2nd (if any)
  1637. are treated as type names. i.e.:
  1638. ```
  1639. xWrap('funcname', 'i32', 'string', 'f64');
  1640. // is equivalent to:
  1641. xWrap('funcname', 'i32', ['string', 'f64']);
  1642. ```
  1643. This function enforces that the given list of arguments has the
  1644. same arity as the being-wrapped function (as defined by its
  1645. `length` property) and it will throw if that is not the case.
  1646. Similarly, the created wrapper will throw if passed a differing
  1647. argument count.
  1648. Type names are symbolic names which map the arguments to an
  1649. adapter function to convert, if needed, the value before passing
  1650. it on to WASM or to convert a return result from WASM. The list
  1651. of built-in names:
  1652. - `i8`, `i16`, `i32` (args and results): all integer conversions
  1653. which convert their argument to an integer and truncate it to
  1654. the given bit length.
  1655. - `N*` (args): a type name in the form `N*`, where N is a numeric
  1656. type name, is treated the same as WASM pointer.
  1657. - `*` and `pointer` (args): are assumed to be WASM pointer values
  1658. and are returned coerced to an appropriately-sized pointer
  1659. value (i32 or i64). Non-numeric values will coerce to 0 and
  1660. out-of-range values will have undefined results (just as with
  1661. any pointer misuse).
  1662. - `*` and `pointer` (results): aliases for the current
  1663. WASM pointer numeric type.
  1664. - `**` (args): is simply a descriptive alias for the WASM pointer
  1665. type. It's primarily intended to mark output-pointer arguments.
  1666. - `i64` (args and results): passes the value to BigInt() to
  1667. convert it to an int64. Only available if bigIntEnabled is
  1668. true.
  1669. - `f32` (`float`), `f64` (`double`) (args and results): pass
  1670. their argument to Number(). i.e. the adapter does not currently
  1671. distinguish between the two types of floating-point numbers.
  1672. - `number` (results): converts the result to a JS Number using
  1673. Number(theValue).valueOf(). Note that this is for result
  1674. conversions only, as it's not possible to generically know
  1675. which type of number to convert arguments to.
  1676. Non-numeric conversions include:
  1677. - `null` literal or `"null"` string (args and results): perform
  1678. no translation and pass the arg on as-is. This is primarily
  1679. useful for results but may have a use or two for arguments.
  1680. - `string` or `utf8` (args): has two different semantics in order
  1681. to accommodate various uses of certain C APIs
  1682. (e.g. output-style strings)...
  1683. - If the arg is a string, it creates a _temporary_
  1684. UTF-8-encoded C-string to pass to the exported function,
  1685. cleaning it up before the wrapper returns. If a long-lived
  1686. C-string pointer is required, that requires client-side code
  1687. to create the string, then pass its pointer to the function.
  1688. - Else the arg is assumed to be a pointer to a string the
  1689. client has already allocated and it's passed on as
  1690. a WASM pointer.
  1691. - `string` or `utf8` (results): treats the result value as a
  1692. const C-string, encoded as UTF-8, copies it to a JS string,
  1693. and returns that JS string.
  1694. - `string:dealloc` or `utf8:dealloc` (results): treats the result
  1695. value as a non-const UTF-8 C-string, ownership of which has
  1696. just been transfered to the caller. It copies the C-string to a
  1697. JS string, frees the C-string, and returns the JS string. If
  1698. such a result value is NULL, the JS result is `null`. Achtung:
  1699. when using an API which returns results from a specific
  1700. allocator, e.g. `my_malloc()`, this conversion _is not
  1701. legal_. Instead, an equivalent conversion which uses the
  1702. appropriate deallocator is required. For example:
  1703. ```js
  1704. target.xWrap.resultAdapter('string:my_free',(i)=>{
  1705. try { return i ? target.cstrToJs(i) : null }
  1706. finally{ target.exports.my_free(i) }
  1707. };
  1708. ```
  1709. - `json` (results): treats the result as a const C-string and
  1710. returns the result of passing the converted-to-JS string to
  1711. JSON.parse(). Returns `null` if the C-string is a NULL pointer.
  1712. - `json:dealloc` (results): works exactly like `string:dealloc` but
  1713. returns the same thing as the `json` adapter. Note the
  1714. warning in `string:dealloc` regarding maching allocators and
  1715. deallocators.
  1716. The type names for results and arguments are validated when
  1717. xWrap() is called and any unknown names will trigger an
  1718. exception.
  1719. Clients may map their own result and argument adapters using
  1720. xWrap.resultAdapter() and xWrap.argAdapter(), noting that not all
  1721. type conversions are valid for both arguments _and_ result types
  1722. as they often have different memory ownership requirements.
  1723. Design note: the ability to pass in a JS function as the first
  1724. argument is of relatively limited use, primarily for testing
  1725. argument and result converters. JS functions, by and large, will
  1726. not want to deal with C-type arguments.
  1727. TODOs:
  1728. - Figure out how/whether we can (semi-)transparently handle
  1729. pointer-type _output_ arguments. Those currently require
  1730. explicit handling by allocating pointers, assigning them before
  1731. the call using poke(), and fetching them with
  1732. peek() after the call. We may be able to automate some
  1733. or all of that.
  1734. - Figure out whether it makes sense to extend the arg adapter
  1735. interface such that each arg adapter gets an array containing
  1736. the results of the previous arguments in the current call. That
  1737. might allow some interesting type-conversion feature. Use case:
  1738. handling of the final argument to sqlite3_prepare_v2() depends
  1739. on the type (pointer vs JS string) of its 2nd
  1740. argument. Currently that distinction requires hand-writing a
  1741. wrapper for that function. That case is unusual enough that
  1742. abstracting it into this API (and taking on the associated
  1743. costs) may well not make good sense.
  1744. */
  1745. target.xWrap = function(fArg, resultType, ...argTypes){
  1746. if(3===arguments.length && Array.isArray(arguments[2])){
  1747. argTypes = arguments[2];
  1748. }
  1749. if(target.isPtr(fArg)){
  1750. fArg = target.functionEntry(fArg)
  1751. || toss("Function pointer not found in WASM function table.");
  1752. }
  1753. const fIsFunc = (fArg instanceof Function);
  1754. const xf = fIsFunc ? fArg : target.xGet(fArg);
  1755. if(fIsFunc) fArg = xf.name || 'unnamed function';
  1756. if(argTypes.length!==xf.length) __argcMismatch(fArg, xf.length);
  1757. if((null===resultType) && 0===xf.length){
  1758. /* Func taking no args with an as-is return. We don't need a wrapper.
  1759. We forego the argc check here, though. */
  1760. return xf;
  1761. }
  1762. /*Verify the arg type conversions are valid...*/;
  1763. if(undefined!==resultType && null!==resultType) __xResultAdapterCheck(resultType);
  1764. for(const t of argTypes){
  1765. if(t instanceof AbstractArgAdapter) xArg.set(t, (...args)=>t.convertArg(...args));
  1766. else __xArgAdapterCheck(t);
  1767. }
  1768. const cxw = cache.xWrap;
  1769. if(0===xf.length){
  1770. // No args to convert, so we can create a simpler wrapper...
  1771. return (...args)=>(args.length
  1772. ? __argcMismatch(fArg, xf.length)
  1773. : cxw.convertResult(resultType, xf.call(null)));
  1774. }
  1775. return function(...args){
  1776. if(args.length!==xf.length) __argcMismatch(fArg, xf.length);
  1777. const scope = target.scopedAllocPush();
  1778. try{
  1779. /*
  1780. Maintenance reminder re. arguments passed to convertArg():
  1781. The public interface of argument adapters is that they take
  1782. ONE argument and return a (possibly) converted result for
  1783. it. The passing-on of arguments after the first is an
  1784. internal implementation detail for the sake of
  1785. AbstractArgAdapter, and not to be relied on or documented
  1786. for other cases. The fact that this is how
  1787. AbstractArgAdapter.convertArgs() gets its 2nd+ arguments,
  1788. and how FuncPtrAdapter.contextKey() gets its args, is also
  1789. an implementation detail and subject to change. i.e. the
  1790. public interface of 1 argument is stable. The fact that any
  1791. arguments may be passed in after that one, and what those
  1792. arguments are, is _not_ part of the public interface and is
  1793. _not_ stable.
  1794. Maintenance reminder: the Ember framework modifies the core
  1795. Array type, breaking for-in loops.
  1796. */
  1797. let i = 0;
  1798. for(; i < args.length; ++i) args[i] = cxw.convertArgNoCheck(
  1799. argTypes[i], args[i], args, i
  1800. );
  1801. return cxw.convertResultNoCheck(resultType, xf.apply(null,args));
  1802. }finally{
  1803. target.scopedAllocPop(scope);
  1804. }
  1805. };
  1806. }/*xWrap()*/;
  1807. /** Internal impl for xWrap.resultAdapter() and argAdapter(). */
  1808. const __xAdapter = function(func, argc, typeName, adapter, modeName, xcvPart){
  1809. if('string'===typeof typeName){
  1810. if(1===argc) return xcvPart.get(typeName);
  1811. else if(2===argc){
  1812. if(!adapter){
  1813. delete xcvPart.get(typeName);
  1814. return func;
  1815. }else if(!(adapter instanceof Function)){
  1816. toss(modeName,"requires a function argument.");
  1817. }
  1818. xcvPart.set(typeName, adapter);
  1819. return func;
  1820. }
  1821. }
  1822. toss("Invalid arguments to",modeName);
  1823. };
  1824. /**
  1825. Gets, sets, or removes a result value adapter for use with
  1826. xWrap(). If passed only 1 argument, the adapter function for the
  1827. given type name is returned. If the second argument is explicit
  1828. falsy (as opposed to defaulted), the adapter named by the first
  1829. argument is removed. If the 2nd argument is not falsy, it must be
  1830. a function which takes one value and returns a value appropriate
  1831. for the given type name. The adapter may throw if its argument is
  1832. not of a type it can work with. This function throws for invalid
  1833. arguments.
  1834. Example:
  1835. ```
  1836. xWrap.resultAdapter('twice',(v)=>v+v);
  1837. ```
  1838. xWrap.resultAdapter() MUST NOT use the scopedAlloc() family of
  1839. APIs to allocate a result value. xWrap()-generated wrappers run
  1840. in the context of scopedAllocPush() so that argument adapters can
  1841. easily convert, e.g., to C-strings, and have them cleaned up
  1842. automatically before the wrapper returns to the caller. Likewise,
  1843. if a _result_ adapter uses scoped allocation, the result will be
  1844. freed before because they would be freed before the wrapper
  1845. returns, leading to chaos and undefined behavior.
  1846. Except when called as a getter, this function returns itself.
  1847. */
  1848. target.xWrap.resultAdapter = function f(typeName, adapter){
  1849. return __xAdapter(f, arguments.length, typeName, adapter,
  1850. 'resultAdapter()', xResult);
  1851. };
  1852. /**
  1853. Functions identically to xWrap.resultAdapter() but applies to
  1854. call argument conversions instead of result value conversions.
  1855. xWrap()-generated wrappers perform argument conversion in the
  1856. context of a scopedAllocPush(), so any memory allocation
  1857. performed by argument adapters really, really, really should be
  1858. made using the scopedAlloc() family of functions unless
  1859. specifically necessary. For example:
  1860. ```
  1861. xWrap.argAdapter('my-string', function(v){
  1862. return ('string'===typeof v)
  1863. ? myWasmObj.scopedAllocCString(v) : null;
  1864. };
  1865. ```
  1866. Contrariwise, xWrap.resultAdapter() must _not_ use scopedAlloc()
  1867. to allocate its results because they would be freed before the
  1868. xWrap()-created wrapper returns.
  1869. Note that it is perfectly legitimate to use these adapters to
  1870. perform argument validation, as opposed (or in addition) to
  1871. conversion.
  1872. */
  1873. target.xWrap.argAdapter = function f(typeName, adapter){
  1874. return __xAdapter(f, arguments.length, typeName, adapter,
  1875. 'argAdapter()', xArg);
  1876. };
  1877. target.xWrap.FuncPtrAdapter = xArg.FuncPtrAdapter;
  1878. /**
  1879. Functions like xCall() but performs argument and result type
  1880. conversions as for xWrap(). The first, second, and third
  1881. arguments are as documented for xWrap(), except that the 3rd
  1882. argument may be either a falsy value or empty array to represent
  1883. nullary functions. The 4th+ arguments are arguments for the call,
  1884. with the special case that if the 4th argument is an array, it is
  1885. used as the arguments for the call. Returns the converted result
  1886. of the call.
  1887. This is just a thin wrapper around xWrap(). If the given function
  1888. is to be called more than once, it's more efficient to use
  1889. xWrap() to create a wrapper, then to call that wrapper as many
  1890. times as needed. For one-shot calls, however, this variant is
  1891. arguably more efficient because it will hypothetically free the
  1892. wrapper function quickly.
  1893. */
  1894. target.xCallWrapped = function(fArg, resultType, argTypes, ...args){
  1895. if(Array.isArray(arguments[3])) args = arguments[3];
  1896. return target.xWrap(fArg, resultType, argTypes||[]).apply(null, args||[]);
  1897. };
  1898. /**
  1899. This function is ONLY exposed in the public API to facilitate
  1900. testing. It should not be used in application-level code, only
  1901. in test code.
  1902. Expects to be given (typeName, value) and returns a conversion
  1903. of that value as has been registered using argAdapter().
  1904. It throws if no adapter is found.
  1905. ACHTUNG: the adapter may require that a scopedAllocPush() is
  1906. active and it may allocate memory within that scope. It may also
  1907. require additional arguments, depending on the type of
  1908. conversion.
  1909. */
  1910. target.xWrap.testConvertArg = cache.xWrap.convertArg;
  1911. /**
  1912. This function is ONLY exposed in the public API to facilitate
  1913. testing. It should not be used in application-level code, only
  1914. in test code.
  1915. Expects to be given (typeName, value) and returns a conversion
  1916. of that value as has been registered using resultAdapter().
  1917. It throws if no adapter is found.
  1918. ACHTUNG: the adapter may allocate memory which the caller may need
  1919. to know how to free.
  1920. */
  1921. target.xWrap.testConvertResult = cache.xWrap.convertResult;
  1922. return target;
  1923. };
  1924. /**
  1925. yawl (Yet Another Wasm Loader) provides very basic wasm loader.
  1926. It requires a config object:
  1927. - `uri`: required URI of the WASM file to load.
  1928. - `onload(loadResult,config)`: optional callback. The first
  1929. argument is the result object from
  1930. WebAssembly.instantiate[Streaming](). The 2nd is the config
  1931. object passed to this function. Described in more detail below.
  1932. - `imports`: optional imports object for
  1933. WebAssembly.instantiate[Streaming](). The default is an empty set
  1934. of imports. If the module requires any imports, this object
  1935. must include them.
  1936. - `wasmUtilTarget`: optional object suitable for passing to
  1937. WhWasmUtilInstaller(). If set, it gets passed to that function
  1938. after the promise resolves. This function sets several properties
  1939. on it before passing it on to that function (which sets many
  1940. more):
  1941. - `module`, `instance`: the properties from the
  1942. instantiate[Streaming]() result.
  1943. - If `instance.exports.memory` is _not_ set then it requires that
  1944. `config.imports.env.memory` be set (else it throws), and
  1945. assigns that to `target.memory`.
  1946. - If `wasmUtilTarget.alloc` is not set and
  1947. `instance.exports.malloc` is, it installs
  1948. `wasmUtilTarget.alloc()` and `wasmUtilTarget.dealloc()`
  1949. wrappers for the exports `malloc` and `free` functions.
  1950. It returns a function which, when called, initiates loading of the
  1951. module and returns a Promise. When that Promise resolves, it calls
  1952. the `config.onload` callback (if set) and passes it
  1953. `(loadResult,config)`, where `loadResult` is the result of
  1954. WebAssembly.instantiate[Streaming](): an object in the form:
  1955. ```
  1956. {
  1957. module: a WebAssembly.Module,
  1958. instance: a WebAssembly.Instance
  1959. }
  1960. ```
  1961. (Note that the initial `then()` attached to the promise gets only
  1962. that object, and not the `config` one.)
  1963. Error handling is up to the caller, who may attach a `catch()` call
  1964. to the promise.
  1965. */
  1966. globalThis.WhWasmUtilInstaller.yawl = function(config){
  1967. const wfetch = ()=>fetch(config.uri, {credentials: 'same-origin'});
  1968. const wui = this;
  1969. const finalThen = function(arg){
  1970. //log("finalThen()",arg);
  1971. if(config.wasmUtilTarget){
  1972. const toss = (...args)=>{throw new Error(args.join(' '))};
  1973. const tgt = config.wasmUtilTarget;
  1974. tgt.module = arg.module;
  1975. tgt.instance = arg.instance;
  1976. //tgt.exports = tgt.instance.exports;
  1977. if(!tgt.instance.exports.memory){
  1978. /**
  1979. WhWasmUtilInstaller requires either tgt.exports.memory
  1980. (exported from WASM) or tgt.memory (JS-provided memory
  1981. imported into WASM).
  1982. */
  1983. tgt.memory = (config.imports && config.imports.env
  1984. && config.imports.env.memory)
  1985. || toss("Missing 'memory' object!");
  1986. }
  1987. if(!tgt.alloc && arg.instance.exports.malloc){
  1988. const exports = arg.instance.exports;
  1989. tgt.alloc = function(n){
  1990. return exports.malloc(n) || toss("Allocation of",n,"bytes failed.");
  1991. };
  1992. tgt.dealloc = function(m){exports.free(m)};
  1993. }
  1994. wui(tgt);
  1995. }
  1996. if(config.onload) config.onload(arg,config);
  1997. return arg /* for any then() handler attached to
  1998. yetAnotherWasmLoader()'s return value */;
  1999. };
  2000. const loadWasm = WebAssembly.instantiateStreaming
  2001. ? function loadWasmStreaming(){
  2002. return WebAssembly.instantiateStreaming(wfetch(), config.imports||{})
  2003. .then(finalThen);
  2004. }
  2005. : function loadWasmOldSchool(){ // Safari < v15
  2006. return wfetch()
  2007. .then(response => response.arrayBuffer())
  2008. .then(bytes => WebAssembly.instantiate(bytes, config.imports||{}))
  2009. .then(finalThen);
  2010. };
  2011. return loadWasm;
  2012. }.bind(globalThis.WhWasmUtilInstaller)/*yawl()*/;