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