Fixes default log output to console for macOS
[sqlcipher.git] / ext / wasm / jaccwabyt / jaccwabyt.md
blob431741edcab2973390c24b26b476696f0529d17f
1 Jaccwabyt 🐇
2 ============================================================
4 **Jaccwabyt**: _JavaScript ⇄ C Struct Communication via WASM Byte
5 Arrays_
7 Welcome to Jaccwabyt, a JavaScript API which creates bindings for
8 WASM-compiled C structs, defining them in such a way that changes to
9 their state in JS are visible in C/WASM, and vice versa, permitting
10 two-way interchange of struct state with very little user-side
11 friction.
13 (If that means nothing to you, neither will the rest of this page!)
15 **Browser compatibility**: this library requires a _recent_ browser
16 and makes no attempt whatsoever to accommodate "older" or
17 lesser-capable ones, where "recent," _very roughly_, means released in
18 mid-2018 or later, with late 2021 releases required for some optional
19 features in some browsers (e.g. [BigInt64Array][] in Safari). It also
20 relies on a couple non-standard, but widespread, features, namely
21 [TextEncoder][] and [TextDecoder][]. It is developed primarily on
22 Firefox and Chrome on Linux and all claims of Safari compatibility
23 are based solely on feature compatibility tables provided at
24 [MDN][].
26 **Formalities:**
28 - Author: [Stephan Beal][sgb]
29 - Project Homes:
30   - <https://fossil.wanderinghorse.net/r/jaccwabyt>\  
31     Is the primary home but...
32   - <https://sqlite.org/src/dir/ext/wasm/jaccwabyt>\  
33     ... most development happens here.
35 The license for both this documentation and the software it documents
36 is the same as [sqlite3][], the project from which this spinoff
37 project was spawned:
39 -----
41 > 2022-06-30:
43 > The author disclaims copyright to this source code.  In place of a
44 > legal notice, here is a blessing:
46 >    May you do good and not evil.
47 >    May you find forgiveness for yourself and forgive others.
48 >    May you share freely, never taking more than you give.
50 -----
52 <a name='overview'></a>
53 Table of Contents
54 ============================================================
56 - [Overview](#overview)
57   - [Architecture](#architecture)
58 - [Creating and Binding Structs](#creating-binding)
59   - [Step 1: Configure Jaccwabyt](#step-1)
60   - [Step 2: Struct Description](#step-2)
61      - [`P` vs `p`](#step-2-pvsp)
62   - [Step 3: Binding a Struct](#step-3)
63   - [Step 4: Creating, Using, and Destroying Instances](#step-4)
64 - APIs
65   - [Struct Binder Factory](#api-binderfactory)
66   - [Struct Binder](#api-structbinder)
67   - [Struct Type](#api-structtype)
68   - [Struct Constructors](#api-structctor)
69   - [Struct Protypes](#api-structprototype)
70   - [Struct Instances](#api-structinstance)
71 - Appendices
72   - [Appendix A: Limitations, TODOs, etc.](#appendix-a)
73   - [Appendix D: Debug Info](#appendix-d)
74   - [Appendix G: Generating Struct Descriptions](#appendix-g)
76 <a name='overview'></a>
77 Overview
78 ============================================================
80 Management summary: this JavaScript-only framework provides limited
81 two-way bindings between C structs and JavaScript objects, such that
82 changes to the struct in one environment are visible in the other.
84 Details...
86 It works by creating JavaScript proxies for C structs. Reads and
87 writes of the JS-side members are marshaled through a flat byte array
88 allocated from the WASM heap. As that heap is shared with the C-side
89 code, and the memory block is written using the same approach C does,
90 that byte array can be used to access and manipulate a given struct
91 instance from both JS and C.
93 Motivating use case: this API was initially developed as an
94 experiment to determine whether it would be feasible to implement,
95 completely in JS, custom "VFS" and "virtual table" objects for the
96 WASM build of [sqlite3][]. Doing so was going to require some form of
97 two-way binding of several structs.  Once the proof of concept was
98 demonstrated, a rabbit hole appeared and _down we went_... It has
99 since grown beyond its humble proof-of-concept origins and is believed
100 to be a useful (or at least interesting) tool for mixed JS/C
101 applications.
103 Portability notes:
105 - These docs sometimes use [Emscripten][] as a point of reference
106   because it is the most widespread WASM toolchain, but this code is
107   specifically designed to be usable in arbitrary WASM environments.
108   It abstracts away a few Emscripten-specific features into
109   configurable options. Similarly, the build tree requires Emscripten
110   but Jaccwabyt does not have any hard Emscripten dependencies.
111 - This code is encapsulated into a single JavaScript function. It
112   should be trivial to copy/paste into arbitrary WASM/JS-using
113   projects.
114 - The source tree includes C code, but only for testing and
115   demonstration purposes. It is not part of the core distributable.
117 <a name='architecture'></a>
118 Architecture
119 ------------------------------------------------------------
121 <!--
122 bug(?) (fossil): using "center" shrinks pikchr too much.
125 ```pikchr
126 BSBF: box rad 0.3*boxht "StructBinderFactory" fit fill lightblue
127 BSB: box same "StructBinder" fit at 0.75 e of 0.7 s of BSBF.c
128 BST: box same "StructType<T>" fit at 1.5 e of BSBF
129 BSC: box same "Struct<T>" "Ctor" fit at 1.5 s of BST
130 BSI: box same "Struct<T>" "Instances" fit at 1 right of BSB.e
131 BC: box same at 0.25 right of 1.6 e of BST "C Structs" fit fill lightgrey
133 arrow -> from BSBF.s to BSB.w "Generates" aligned above
134 arrow -> from BSB.n to BST.sw "Contains" aligned above
135 arrow -> from BSB.s to BSC.nw "Generates" aligned below
136 arrow -> from BSC.ne to BSI.s "Constructs" aligned below
137 arrow <- from BST.se to BSI.n "Inherits" aligned above
138 arrow <-> from BSI.e to BC.s dotted "Shared" aligned above "Memory" aligned below
139 arrow -> from BST.e to BC.w dotted "Mirrors Struct" aligned above "Model From" aligned below
140 arrow -> from BST.s to BSC.n "Prototype of" aligned above
143 Its major classes and functions are:
145 - **[StructBinderFactory][StructBinderFactory]** is a factory function which
146   accepts a configuration object to customize it for a given WASM
147   environment. A client will typically call this only one time, with
148   an appropriate configuration, to generate a single...
149 - **[StructBinder][]** is a factory function which converts an
150   arbitrary number struct descriptions into...
151 - **[StructTypes][StructCtors]** are constructors, one per struct
152   description, which inherit from
153   **[`StructBinder.StructType`][StructType]** and are used to instantiate...
154 - **[Struct instances][StructInstance]** are objects representing
155   individual instances of generated struct types.
157 An app may have any number of StructBinders, but will typically
158 need only one. Each StructBinder is effectively a separate
159 namespace for struct creation.
162 <a name='creating-binding'></a>
163 Creating and Binding Structs
164 ============================================================
166 From the amount of documentation provided, it may seem that
167 creating and using struct bindings is a daunting task, but it
168 essentially boils down to:
170 1. [Confire Jaccwabyt for your WASM environment](#step-1). This is a
171    one-time task per project and results is a factory function which
172    can create new struct bindings.
173 2. [Create a JSON-format description of your C structs](#step-2). This is
174    required once for each struct and required updating if the C
175    structs change.
176 3. [Feed (2) to the function generated by (1)](#step-3) to create JS
177    constuctor functions for each struct. This is done at runtime, as
178    opposed to during a build-process step, and can be set up in such a
179    way that it does not require any maintenace after its initial
180    setup.
181 4. [Create and use instances of those structs](#step-4).
183 Detailed instructions for each of those steps follows...
185 <a name='step-1'></a>
186 Step 1: Configure Jaccwabyt for the Environment
187 ------------------------------------------------------------
189 Jaccwabyt's highest-level API is a single function. It creates a
190 factory for processing struct descriptions, but does not process any
191 descriptions itself. This level of abstraction exist primarily so that
192 the struct-specific factories can be configured for a given WASM
193 environment. Its usage looks like:
195 >  
196 ```javascript
197 const MyBinder = StructBinderFactory({
198   // These config options are all required:
199   heap: WebAssembly.Memory instance or a function which returns
200         a Uint8Array or Int8Array view of the WASM memory,
201   alloc:   function(howMuchMemory){...},
202   dealloc: function(pointerToFree){...}
206 It also offers a number of other settings, but all are optional except
207 for the ones shown above. Those three config options abstract away
208 details which are specific to a given WASM environment. They provide
209 the WASM "heap" memory, the memory allocator, and the deallocator. In
210 a conventional Emscripten setup, that config might simply look like:
212 >  
213 ```javascript
215     heap:    Module['asm']['memory'],
216     //Or:
217     // heap: ()=>Module['HEAP8'],
218     alloc:   (n)=>Module['_malloc'](n),
219     dealloc: (m)=>Module['_free'](m)
223 The StructBinder factory function returns a function which can then be
224 used to create bindings for our structs.
226 <a name='step-2'></a>
227 Step 2: Create a Struct Description
228 ------------------------------------------------------------
230 The primary input for this framework is a JSON-compatible construct
231 which describes a struct we want to bind. For example, given this C
232 struct:
234 >  
235 ```c
236 // C-side:
237 struct Foo {
238   int member1;
239   void * member2;
240   int64_t member3;
244 Its JSON description looks like:
246 >  
247 ```json
249   "name": "Foo",
250   "sizeof": 16,
251   "members": {
252     "member1": {"offset": 0,"sizeof": 4,"signature": "i"},
253     "member2": {"offset": 4,"sizeof": 4,"signature": "p"},
254     "member3": {"offset": 8,"sizeof": 8,"signature": "j"}
255   }
259 These data _must_ match up with the C-side definition of the struct
260 (if any). See [Appendix G][appendix-g] for one way to easily generate
261 these from C code.
263 Each entry in the `members` object maps the member's name to
264 its low-level layout:
266 - `offset`: the byte offset from the start of the struct, as reported
267   by C's `offsetof()` feature.
268 - `sizeof`: as reported by C's `sizeof()`.
269 - `signature`: described below.
270 - `readOnly`: optional. If set to true, the binding layer will
271   throw if JS code tries to set that property.
273 The order of the `members` entries is not important: their memory
274 layout is determined by their `offset` and `sizeof` members. The
275 `name` property is technically optional, but one of the steps in the
276 binding process requires that either it be passed an explicit name or
277 there be one in the struct description. The names of the `members`
278 entries need not match their C counterparts. Project conventions may
279 call for giving them different names in the JS side and the
280 [StructBinderFactory][] can be configured to automatically add a
281 prefix and/or suffix to their names.
283 Nested structs are as-yet unsupported by this tool.
285 Struct member "signatures" describe the data types of the members and
286 are an extended variant of the format used by Emscripten's
287 `addFunction()`. A signature for a non-function-pointer member, or
288 function pointer member which is to be modelled as an opaque pointer,
289 is a single letter. A signature for a function pointer may also be
290 modelled as a series of letters describing the call signature. The
291 supported letters are:
293 - **`v`** = `void` (only used as return type for function pointer members)
294 - **`i`** = `int32` (4 bytes)
295 - **`j`** = `int64` (8 bytes) is only really usable if this code is built
296   with BigInt support (e.g. using the Emscripten `-sWASM_BIGINT` build
297   flag). Without that, this API may throw when encountering the `j`
298   signature entry.
299 - **`f`** = `float` (4 bytes)
300 - **`d`** = `double` (8 bytes)
301 - **`c`** = `int8` (1 byte) char - see notes below!
302 - **`C`** = `uint8` (1 byte) unsigned char - see notes below!
303 - **`p`** = `int32` (see notes below!)
304 - **`P`** = Like `p` but with extra handling. Described below.
305 - **`s`** = like `int32` but is a _hint_ that it's a pointer to a
306   string so that _some_ (very limited) contexts may treat it as such,
307   noting that such algorithms must, for lack of information to the
308   contrary, assume both that the encoding is UTF-8 and that the
309   pointer's member is NUL-terminated. If that is _not_ the case for a
310   given string member, do not use `s`: use `i` or `p` instead and do
311   any string handling yourself.
313 Noting that:
315 - **All of these types are numeric**. Attempting to set any
316   struct-bound property to a non-numeric value will trigger an
317   exception except in cases explicitly noted otherwise.
318 - **"Char" types**: WASM does not define an `int8` type, nor does it
319   distinguish between signed and unsigned. This API treats `c` as
320   `int8` and `C` as `uint8` for purposes of getting and setting values
321   when using the `DataView` class. It is _not_ recommended that client
322   code use these types in new WASM-capable code, but they were added
323   for the sake of binding some immutable legacy code to WASM.
325 > Sidebar: Emscripten's public docs do not mention `p`, but their
326 generated code includes `p` as an alias for `i`, presumably to mean
327 "pointer". Though `i` is legal for pointer types in the signature, `p`
328 is more descriptive, so this framework encourages the use of `p` for
329 pointer-type members. Using `p` for pointers also helps future-proof
330 the signatures against the eventuality that WASM eventually supports
331 64-bit pointers. Note that sometimes `p` really means
332 pointer-to-pointer, but the Emscripten JS/WASM glue does not offer
333 that level of expressiveness in these signatures. We simply have to be
334 aware of when we need to deal with pointers and pointers-to-pointers
335 in JS code.
337 > Trivia: this API treates `p` as distinctly different from `i` in
338 some contexts, so its use is encouraged for pointer types.
340 Signatures in the form `x(...)` denote function-pointer members and
341 `x` denotes non-function members. Functions with no arguments use the
342 form `x()`. For function-type signatures, the strings are formulated
343 such that they can be passed to Emscripten's `addFunction()` after
344 stripping out the `(` and `)` characters. For good measure, to match
345 the public Emscripten docs, `p`, `c`, and `C`, should also be replaced
346 with `i`. In JavaScript that might look like:
348 >  
350 signature.replace(/[^vipPsjfdcC]/g,'').replace(/[pPscC]/g,'i');
353 <a name='step-2-pvsp'></a>
354 ### `P` vs `p` in Method Signatures
356 *This support is experimental and subject to change.*
358 The method signature letter `p` means "pointer," which, in WASM, means
359 "integer." `p` is treated as an integer for most contexts, while still
360 also being a separate type (analog to how pointers in C are just a
361 special use of unsigned numbers). A capital `P` changes the semantics
362 of plain member pointers (but not, as of this writing, function
363 pointer members) as follows:
365 - When a `P`-type member is **set** via `myStruct.x=y`, if
366   [`(y instanceof StructType)`][StructType] then the value of `y.pointer` is
367   stored in `myStruct.x`. If `y` is neither a number nor
368   a [StructType][], an exception is triggered (regardless of whether
369   `p` or `P` is used).
372 <a name='step-3'></a>
373 Step 3: Binding the Struct
374 ------------------------------------------------------------
376 We can now use the results of steps 1 and 2:
378 >  
379 ```javascript
380 const MyStruct = MyBinder(myStructDescription);
383 That creates a new constructor function, `MyStruct`, which can be used
384 to instantiate new instances. The binder will throw if it encounters
385 any problems.
387 That's all there is to it.
389 > Sidebar: that function may modify the struct description object
390 and/or its sub-objects, or may even replace sub-objects, in order to
391 simplify certain later operations. If that is not desired, then feed
392 it a copy of the original, e.g. by passing it
393 `JSON.parse(JSON.stringify(structDefinition))`.
395 <a name='step-4'></a>
396 Step 4: Creating, Using, and Destroying Struct Instances
397 ------------------------------------------------------------
399 Now that we have our constructor...
401 >  
402 ```javascript
403 const my = new MyStruct();
406 It is important to understand that creating a new instance allocates
407 memory on the WASM heap. We must not simply rely on garbage collection
408 to clean up the instances because doing so will not free up the WASM
409 heap memory. The correct way to free up that memory is to use the
410 object's `dispose()` method.
412 The following usage pattern offers one way to easily ensure proper
413 cleanup of struct instances:
415 >  
416 ```javascript
417 const my = new MyStruct();
418 try {
419   console.log(my.member1, my.member2, my.member3);
420   my.member1 = 12;
421   assert(12 === my.member1);
422   /* ^^^ it may seem silly to test that, but recall that assigning that
423      property encodes the value into a byte array in heap memory, not
424      a normal JS property. Similarly, fetching the property decodes it
425      from the byte array. */
426   // Pass the struct to C code which takes a MyStruct pointer:
427   aCFunction( my.pointer );
428 } finally {
429   my.dispose();
433 > Sidebar: the `finally` block will be run no matter how the `try`
434 exits, whether it runs to completion, propagates an exception, or uses
435 flow-control keywords like `return` or `break`. It is perfectly legal
436 to use `try`/`finally` without a `catch`, and doing so is an ideal
437 match for the memory management requirements of Jaccwaby-bound struct
438 instances.
440 It is often useful to wrap an existing instance of a C-side struct
441 without taking over ownership of its memory. That can be achieved by
442 simply passing a pointer to the constructor. For example:
444 ```js
445 const m = new MyStruct( functionReturningASharedPtr() );
446 // calling m.dispose() will _not_ free the wrapped C-side instance
447 // but will trigger any ondispose handler.
450 Now that we have struct instances, there are a number of things we
451 can do with them, as covered in the rest of this document.
454 <a name='api'></a>
455 API Reference
456 ============================================================
458 <a name='api-binderfactory'></a>
459 API: Binder Factory
460 ------------------------------------------------------------
462 This is the top-most function of the API, from which all other
463 functions and types are generated. The binder factory's signature is:
465 >  
467 Function StructBinderFactory(object configOptions);
470 It returns a function which these docs refer to as a [StructBinder][]
471 (covered in the next section). It throws on error.
473 The binder factory supports the following options in its
474 configuration object argument:
477 - `heap`  
478   Must be either a `WebAssembly.Memory` instance representing the WASM
479   heap memory OR a function which returns an Int8Array or Uint8Array
480   view of the WASM heap. In the latter case the function should, if
481   appropriate for the environment, account for the heap being able to
482   grow. Jaccwabyt uses this property in such a way that it "should" be
483   okay for the WASM heap to grow at runtime (that case is, however,
484   untested).
486 - `alloc`  
487   Must be a function semantically compatible with Emscripten's
488   `Module._malloc()`. That is, it is passed the number of bytes to
489   allocate and it returns a pointer. On allocation failure it may
490   either return 0 or throw an exception. This API will throw an
491   exception if allocation fails or will propagate whatever exception
492   the allocator throws. The allocator _must_ use the same heap as the
493   `heap` config option.
495 - `dealloc`  
496   Must be a function semantically compatible with Emscripten's
497   `Module._free()`. That is, it takes a pointer returned from
498   `alloc()` and releases that memory. It must never throw and must
499   accept a value of 0/null to mean "do nothing" (noting that 0 is
500   _technically_ a legal memory address in WASM, but that seems like a
501   design flaw).
503 - `bigIntEnabled` (bool=true if BigInt64Array is available, else false)  
504   If true, the WASM bits this code is used with must have been
505   compiled with int64 support (e.g. using Emscripten's `-sWASM_BIGINT`
506   flag). If that's not the case, this flag should be set to false. If
507   it's enabled, BigInt support is assumed to work and certain extra
508   features are enabled. Trying to use features which requires BigInt
509   when it is disabled (e.g. using 64-bit integer types) will trigger
510   an exception.
512 - `memberPrefix` and `memberSuffix` (string="")  
513   If set, struct-defined properties get bound to JS with this string
514   as a prefix resp. suffix. This can be used to avoid symbol name
515   collisions between the struct-side members and the JS-side ones
516   and/or to make more explicit which object-level properties belong to
517   the struct mapping and which to the JS side. This does not modify
518   the values in the struct description objects, just the property
519   names through which they are accessed via property access operations
520   and the various a [StructInstance][] APIs (noting that the latter
521   tend to permit both the original names and the names as modified by
522   these settings).
524 - `log`  
525   Optional function used for debugging output. By default
526   `console.log` is used but by default no debug output is generated.
527   This API assumes that the function will space-separate each argument
528   (like `console.log` does). See [Appendix D](#appendix-d) for info
529   about enabling debugging output.
532 <a name='api-structbinder'></a>
533 API: Struct Binder
534 ------------------------------------------------------------
536 Struct Binders are factories which are created by the
537 [StructBinderFactory][].  A given Struct Binder can process any number
538 of distinct structs. In a typical setup, an app will have ony one
539 shared Binder Factory and one Struct Binder. Struct Binders which are
540 created via different [StructBinderFactory][] calls are unrelated to each
541 other, sharing no state except, perhaps, indirectly via
542 [StructBinderFactory][] configuration (e.g. the memory heap).
544 These factories have two call signatures:
546 >  
547 ```javascript
548 Function StructBinder([string structName,] object structDescription)
551 If the struct description argument has a `name` property then the name
552 argument is optional, otherwise it is required.
554 The returned object is a constructor for instances of the struct
555 described by its argument(s), each of which derives from
556 a separate [StructType][] instance.
558 The Struct Binder has the following members:
560 - `allocCString(str)`  
561   Allocates a new UTF-8-encoded, NUL-terminated copy of the given JS
562   string and returns its address relative to `config.heap()`. If
563   allocation returns 0 this function throws. Ownership of the memory
564   is transfered to the caller, who must eventually pass it to the
565   configured `config.dealloc()` function.
567 - `config`  
568   The configuration object passed to the [StructBinderFactory][],
569   primarily for accessing the memory (de)allocator and memory. Modifying
570   any of its "significant" configuration values may have undefined
571   results.
573 <a name='api-structtype'></a>
574 API: Struct Type
575 ------------------------------------------------------------
577 The StructType class is a property of the [StructBinder][] function.
579 Each constructor created by a [StructBinder][] inherits from _its own
580 instance_ of the StructType class, which contains state specific to
581 that struct type (e.g. the struct name and description metadata).
582 StructTypes which are created via different [StructBinder][] instances
583 are unrelated to each other, sharing no state except [StructBinderFactory][]
584 config options.
586 The StructType constructor cannot be called from client code. It is
587 only called by the [StructBinder][]-generated
588 [constructors][StructCtors]. The `StructBinder.StructType` object
589 has the following "static" properties (^Which are accessible from
590 individual instances via `theInstance.constructor`.):
592 - `addOnDispose(...value)`\  
593   If this object has no `ondispose` property, this function creates it
594   as an array and pushes the given value(s) onto it. If the object has
595   a function-typed `ondispose` property, this call replaces it with an
596   array and moves that function into the array. In all other cases,
597   `ondispose` is assumed to be an array and the argument(s) is/are
598   appended to it. Returns `this`.
600 - `allocCString(str)`  
601   Identical to the [StructBinder][] method of the same name.
603 - `hasExternalPointer(object)`  
604   Returns true if the given object's `pointer` member refers to an
605   "external" object. That is the case when a pointer is passed to a
606   [struct's constructor][StructCtors]. If true, the memory is owned by
607   someone other than the object and must outlive the object.
609 - `isA(value)`  
610   Returns true if its argument is a StructType instance _from the same
611   [StructBinder][]_ as this StructType.
613 - `memberKey(string)`  
614   Returns the given string wrapped in the configured `memberPrefix`
615   and `memberSuffix` values. e.g. if passed `"x"` and `memberPrefix`
616   is `"$"` then it returns `"$x"`. This does not verify that the
617   property is actually a struct a member, it simply transforms the
618   given string.  TODO(?): add a 2nd parameter indicating whether it
619   should validate that it's a known member name.
621 The base StructType prototype has the following members, all of which
622 are inherited by [struct instances](#api-structinstance) and may only
623 legally be called on concrete struct instances unless noted otherwise:
625 - `dispose()`  
626   Frees, if appropriate, the WASM-allocated memory which is allocated
627   by the constructor. If this is not called before the JS engine
628   cleans up the object, a leak in the WASM heap memory pool will result.  
629   When `dispose()` is called, if the object has a property named `ondispose`
630   then it is treated as follows:  
631   - If it is a function, it is called with the struct object as its `this`.
632   That method must not throw - if it does, the exception will be
633   ignored.
634   - If it is an array, it may contain functions, pointers, other
635     [StructType] instances, and/or JS strings. If an entry is a
636     function, it is called as described above. If it's a number, it's
637     assumed to be a pointer and is passed to the `dealloc()` function
638     configured for the parent [StructBinder][]. If it's a
639     [StructType][] instance then its `dispose()` method is called. If
640     it's a JS string, it's assumed to be a helpful description of the
641     next entry in the list and is simply ignored. Strings are
642     supported primarily for use as debugging information.
643   - Some struct APIs will manipulate the `ondispose` member, creating
644     it as an array or converting it from a function to array as
645     needed.
647 - `lookupMember(memberName,throwIfNotFound=true)`  
648   Given the name of a mapped struct member, it returns the member
649   description object. If not found, it either throws (if the 2nd
650   argument is true) or returns `undefined` (if the second argument is
651   false). The first argument may be either the member name as it is
652   mapped in the struct description or that same name with the
653   configured `memberPrefix` and `memberSuffix` applied, noting that
654   the lookup in the former case is faster.\  
655   This method may be called directly on the prototype, without a
656   struct instance.
658 - `memberToJsString(memberName)`  
659   Uses `this.lookupMember(memberName,true)` to look up the given
660   member. If its signature is `s` then it is assumed to refer to a
661   NUL-terminated, UTF-8-encoded string and its memory is decoded as
662   such. If its signature is not one of those then an exception is
663   thrown.  If its address is 0, `null` is returned. See also:
664   `setMemberCString()`.
666 - `memberIsString(memberName [,throwIfNotFound=true])`  
667   Uses `this.lookupMember(memberName,throwIfNotFound)` to look up the
668   given member. Returns the member description object if the member
669   has a signature of `s`, else returns false. If the given member is
670   not found, it throws if the 2nd argument is true, else it returns
671   false.
673 - `memberKey(string)`  
674   Works identically to `StructBinder.StructType.memberKey()`.
676 - `memberKeys()`  
677   Returns an array of the names of the properties of this object
678   which refer to C-side struct counterparts.
680 - `memberSignature(memberName [,emscriptenFormat=false])`  
681   Returns the signature for a given a member property, either in this
682   framework's format or, if passed a truthy 2nd argument, in a format
683   suitable for the 2nd argument to Emscripten's `addFunction()`.
684   Throws if the first argument does not resolve to a struct-bound
685   member name. The member name is resolved using `this.lookupMember()`
686   and throws if the member is found mapped.
688 - `memoryDump()`  
689   Returns a Uint8Array which contains the current state of this
690   object's raw memory buffer. Potentially useful for debugging, but
691   not much else. Note that the memory is necessarily, for
692   compatibility with C, written in the host platform's endianness and
693   is thus not useful as a persistent/portable serialization format.
695 - `setMemberCString(memberName,str)`  
696   Uses `StructType.allocCString()` to allocate a new C-style string,
697   assign it to the given member, and add the new string to this
698   object's `ondispose` list for cleanup when `this.dispose()` is
699   called. This function throws if `lookupMember()` fails for the given
700   member name, if allocation of the string fails, or if the member has
701   a signature value of anything other than `s`. Returns `this`.  
702   *Achtung*: calling this repeatedly will not immediately free the
703   previous values because this code cannot know whether they are in
704   use in other places, namely C. Instead, each time this is called,
705   the prior value is retained in the `ondispose` list for cleanup when
706   the struct is disposed of. Because of the complexities and general
707   uncertainties of memory ownership and lifetime in such
708   constellations, it is recommended that the use of C-string members
709   from JS be kept to a minimum or that the relationship be one-way:
710   let C manage the strings and only fetch them from JS using, e.g.,
711   `memberToJsString()`.
712   
714 <a name='api-structctor'></a>
715 API: Struct Constructors
716 ------------------------------------------------------------
718 Struct constructors (the functions returned from [StructBinder][])
719 are used for, intuitively enough, creating new instances of a given
720 struct type:
722 >  
724 const x = new MyStruct;
727 Normally they should be passed no arguments, but they optionally
728 accept a single argument: a WASM heap pointer address of memory
729 which the object will use for storage. It does _not_ take over
730 ownership of that memory and that memory must be valid at
731 for least as long as this struct instance. This is used, for example,
732 to proxy static/shared C-side instances:
734 >  
736 const x = new MyStruct( someCFuncWhichReturnsAMyStructPointer() );
738 x.dispose(); // does NOT free the memory
741 The JS-side construct does not own the memory in that case and has no
742 way of knowing when the C-side struct is destroyed. Results are
743 specifically undefined if the JS-side struct is used after the C-side
744 struct's member is freed.
746 > Potential TODO: add a way of passing ownership of the C-side struct
747 to the JS-side object. e.g. maybe simply pass `true` as the second
748 argument to tell the constructor to take over ownership. Currently the
749 pointer can be taken over using something like
750 `myStruct.ondispose=[myStruct.pointer]` immediately after creation.
752 These constructors have the following "static" members:
754 - `isA(value)`  
755   Returns true if its argument was created by this constructor.
757 - `memberKey(string)`  
758   Works exactly as documented for [StructType][].
760 - `memberKeys(string)`  
761   Works exactly as documented for [StructType][].
763 - `structInfo`  
764   The structure description passed to [StructBinder][] when this
765   constructor was generated.
767 - `structName`  
768   The structure name passed to [StructBinder][] when this constructor
769   was generated.
770   
772 <a name='api-structprototype'></a>
773 API: Struct Prototypes
774 ------------------------------------------------------------
776 The prototypes of structs created via [the constructors described in
777 the previous section][StructCtors] are each a struct-type-specific
778 instance of [StructType][] and add the following struct-type-specific
779 properties to the mix:
781 - `structInfo`  
782   The struct description metadata, as it was given to the
783   [StructBinder][] which created this class.
785 - `structName`  
786   The name of the struct, as it was given to the [StructBinder][] which
787   created this class.
789 <a name='api-structinstance'></a>
790 API: Struct Instances
791 ------------------------------------------------------------------------
793 Instances of structs created via [the constructors described
794 above][StructCtors] each have the following instance-specific state in
795 common:
797 - `pointer`  
798   A read-only numeric property which is the "pointer" returned by the
799   configured allocator when this object is constructed. After
800   `dispose()` (inherited from [StructType][]) is called, this property
801   has the `undefined` value. When calling C-side code which takes a
802   pointer to a struct of this type, simply pass it `myStruct.pointer`.
804 <a name='appendices'></a>
805 Appendices
806 ============================================================
808 <a name='appendix-a'></a>
809 Appendix A: Limitations, TODOs, and Non-TODOs
810 ------------------------------------------------------------
812 - This library only supports the basic set of member types supported
813   by WASM: numbers (which includes pointers). Nested structs are not
814   handled except that a member may be a _pointer_ to such a
815   struct. Whether or not it ever will depends entirely on whether its
816   developer ever needs that support. Conversion of strings between
817   JS and C requires infrastructure specific to each WASM environment
818   and is not directly supported by this library.
820 - Binding functions to struct instances, such that C can see and call
821   JS-defined functions, is not as transparent as it really could be,
822   due to [shortcomings in the Emscripten
823   `addFunction()`/`removeFunction()`
824   interfaces](https://github.com/emscripten-core/emscripten/issues/17323). Until
825   a replacement for that API can be written, this support will be
826   quite limited. It _is_ possible to bind a JS-defined function to a
827   C-side function pointer and call that function from C. What's
828   missing is easier-to-use/more transparent support for doing so.
829   - In the meantime, a [standalone
830   subproject](/file/common/whwasmutil.js) of Jaccwabyt provides such a
831   binding mechanism, but integrating it directly with Jaccwabyt would
832   not only more than double its size but somehow feels inappropriate, so
833   experimentation is in order for how to offer that capability via
834   completely optional [StructBinderFactory][] config options.
836 - It "might be interesting" to move access of the C-bound members into
837   a sub-object. e.g., from JS they might be accessed via
838   `myStructInstance.s.structMember`. The main advantage is that it would
839   eliminate any potential confusion about which members are part of
840   the C struct and which exist purely in JS. "The problem" with that
841   is that it requires internally mapping the `s` member back to the
842   object which contains it, which makes the whole thing more costly
843   and adds one more moving part which can break. Even so, it's
844   something to try out one rainy day. Maybe even make it optional and
845   make the `s` name configurable via the [StructBinderFactory][]
846   options. (Over-engineering is an arguably bad habit of mine.)
848 - It "might be interesting" to offer (de)serialization support. It
849   would be very limited, e.g. we can't serialize arbitrary pointers in
850   any meaningful way, but "might" be useful for structs which contain
851   only numeric or C-string state. As it is, it's easy enough for
852   client code to write wrappers for that and handle the members in
853   ways appropriate to their apps. Any impl provided in this library
854   would have the shortcoming that it may inadvertently serialize
855   pointers (since they're just integers), resulting in potential chaos
856   after deserialization. Perhaps the struct description can be
857   extended to tag specific members as serializable and how to
858   serialize them.
860 <a name='appendix-d'></a>
861 Appendix D: Debug Info
862 ------------------------------------------------------------
864 The [StructBinderFactory][], [StructBinder][], and [StructType][] classes
865 all have the following "unsupported" method intended primarily
866 to assist in their own development, as opposed to being for use in
867 client code:
869 - `debugFlags(flags)` (integer)  
870   An "unsupported" debugging option which may change or be removed at
871   any time. Its argument is a set of flags to enable/disable certain
872   debug/tracing output for property accessors: 0x01 for getters, 0x02
873   for setters, 0x04 for allocations, 0x08 for deallocations. Pass 0 to
874   disable all flags and pass a negative value to _completely_ clear
875   all flags. The latter has the side effect of telling the flags to be
876   inherited from the next-higher-up class in the hierarchy, with
877   [StructBinderFactory][] being top-most, followed by [StructBinder][], then
878   [StructType][].
881 <a name='appendix-g'></a>
882 Appendix G: Generating Struct Descriptions From C
883 ------------------------------------------------------------
885 Struct definitions are _ideally_ generated from WASM-compiled C, as
886 opposed to simply guessing the sizeofs and offsets, so that the sizeof
887 and offset information can be collected using C's `sizeof()` and
888 `offsetof()` features (noting that struct padding may impact offsets
889 in ways which might not be immediately obvious, so writing them by
890 hand is _most certainly not recommended_).
892 How exactly the desciption is generated is necessarily
893 project-dependent. It's tempting say, "oh, that's easy! We'll just
894 write it by hand!" but that would be folly. The struct sizes and byte
895 offsets into the struct _must_ be precisely how C-side code sees the
896 struct or the runtime results are completely undefined.
898 The approach used in developing and testing _this_ software is...
900 Below is a complete copy/pastable example of how we can use a small
901 set of macros to generate struct descriptions from C99 or later into
902 static string memory. Simply add such a file to your WASM build,
903 arrange for its function to be exported[^export-func], and call it
904 from JS (noting that it requires environment-specific JS glue to
905 convert the returned pointer to a JS-side string). Use `JSON.parse()`
906 to process it, then feed the included struct descriptions into the
907 binder factory at your leisure.
909 ------------------------------------------------------------
911 ```c
912 #include <string.h> /* memset() */
913 #include <stddef.h> /* offsetof() */
914 #include <stdio.h>  /* snprintf() */
915 #include <stdint.h> /* int64_t */
916 #include <assert.h>
918 struct ExampleStruct {
919   int v4;
920   void * ppV;
921   int64_t v8;
922   void (*xFunc)(void*);
924 typedef struct ExampleStruct ExampleStruct;
926 const char * wasm__ctype_json(void){
927   static char strBuf[512 * 8] = {0}
928     /* Static buffer which must be sized large enough for
929        our JSON. The string-generation macros try very
930        hard to assert() if this buffer is too small. */;
931   int n = 0, structCount = 0 /* counters for the macros */;
932   char * pos = &strBuf[1]
933     /* Write-position cursor. Skip the first byte for now to help
934        protect against a small race condition */;
935   char const * const zEnd = pos + sizeof(strBuf)
936     /* one-past-the-end cursor (virtual EOF) */;
937   if(strBuf[0]) return strBuf; // Was set up in a previous call.
939   ////////////////////////////////////////////////////////////////////
940   // First we need to build up our macro framework...
942   ////////////////////////////////////////////////////////////////////
943   // Core output-generating macros...
944 #define lenCheck assert(pos < zEnd - 100)
945 #define outf(format,...) \
946   pos += snprintf(pos, ((size_t)(zEnd - pos)), format, __VA_ARGS__); \
947   lenCheck
948 #define out(TXT) outf("%s",TXT)
949 #define CloseBrace(LEVEL) \
950   assert(LEVEL<5); memset(pos, '}', LEVEL); pos+=LEVEL; lenCheck
952   ////////////////////////////////////////////////////////////////////
953   // Macros for emiting StructBinders...
954 #define StructBinder__(TYPE)                 \
955   n = 0;                                     \
956   outf("%s{", (structCount++ ? ", " : ""));  \
957   out("\"name\": \"" # TYPE "\",");          \
958   outf("\"sizeof\": %d", (int)sizeof(TYPE)); \
959   out(",\"members\": {");
960 #define StructBinder_(T) StructBinder__(T)
961 // ^^^ extra indirection needed to expand CurrentStruct
962 #define StructBinder StructBinder_(CurrentStruct)
963 #define _StructBinder CloseBrace(2)
964 #define M(MEMBER,SIG)                                         \
965   outf("%s\"%s\": "                                           \
966        "{\"offset\":%d,\"sizeof\": %d,\"signature\":\"%s\"}", \
967        (n++ ? ", " : ""), #MEMBER,                            \
968        (int)offsetof(CurrentStruct,MEMBER),                   \
969        (int)sizeof(((CurrentStruct*)0)->MEMBER),              \
970        SIG)
971   // End of macros.
972   ////////////////////////////////////////////////////////////////////
974   ////////////////////////////////////////////////////////////////////
975   // With that out of the way, we can do what we came here to do.
976   out("\"structs\": ["); {
978 // For each struct description, do...
979 #define CurrentStruct ExampleStruct
980     StructBinder {
981       M(v4,"i");
982       M(ppV,"p");
983       M(v8,"j");
984       M(xFunc,"v(p)");
985     } _StructBinder;
986 #undef CurrentStruct
988   } out( "]"/*structs*/);
989   ////////////////////////////////////////////////////////////////////
990   // Done! Finalize the output...
991   out("}"/*top-level wrapper*/);
992   *pos = 0;
993   strBuf[0] = '{'/*end of the race-condition workaround*/;
994   return strBuf;
996 // If this file will ever be concatenated or #included with others,
997 // it's good practice to clean up our macros:
998 #undef StructBinder
999 #undef StructBinder_
1000 #undef StructBinder__
1001 #undef M
1002 #undef _StructBinder
1003 #undef CloseBrace
1004 #undef out
1005 #undef outf
1006 #undef lenCheck
1010 ------------------------------------------------------------
1012 <style>
1013 div.content {
1014   counter-reset: h1 -1;
1016 div.content h1, div.content h2, div.content h3 {
1017   border-radius: 0.25em;
1018   border-bottom: 1px solid #70707070;
1020 div.content h1 {
1021   counter-reset: h2;
1023 div.content h1::before, div.content h2::before, div.content h3::before {
1024   background-color: #a5a5a570;
1025   margin-right: 0.5em;
1026   border-radius: 0.25em;
1028 div.content h1::before {
1029   counter-increment: h1;
1030   content: counter(h1) ;
1031   padding: 0 0.5em;
1032   border-radius: 0.25em;
1034 div.content h2::before {
1035   counter-increment: h2;
1036   content: counter(h1) "." counter(h2);
1037   padding: 0 0.5em 0 1.75em;
1038   border-radius: 0.25em;
1040 div.content h2 {
1041   counter-reset: h3;
1043 div.content h3::before {
1044   counter-increment: h3;
1045   content: counter(h1) "." counter(h2) "." counter(h3);
1046   padding: 0 0.5em 0 2.5em;
1048 div.content h3 {border-left-width: 2.5em}
1049 </style>
1051 [sqlite3]: https://sqlite.org
1052 [emscripten]: https://emscripten.org
1053 [sgb]: https://wanderinghorse.net/home/stephan/
1054 [appendix-g]: #appendix-g
1055 [StructBinderFactory]: #api-binderfactory
1056 [StructCtors]: #api-structctor
1057 [StructType]: #api-structtype
1058 [StructBinder]: #api-structbinder
1059 [StructInstance]: #api-structinstance
1060 [^export-func]: In Emscripten, add its name, prefixed with `_`, to the
1061   project's `EXPORT_FUNCTIONS` list.
1062 [BigInt64Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt64Array
1063 [TextDecoder]: https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder
1064 [TextEncoder]: https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder
1065 [MDN]: https://developer.mozilla.org/docs/Web/API