Snapshot of upstream SQLite 3.40.1
[sqlcipher.git] / ext / wasm / jaccwabyt / jaccwabyt.js
blobdee7258c5157a788c8f1be889d42deafe09d454b
1 /**
2 2022-06-30
4 The author disclaims copyright to this source code. In place of a
5 legal notice, here is a blessing:
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.
11 ***********************************************************************
13 The Jaccwabyt API is documented in detail in an external file.
15 Project home: https://fossil.wanderinghorse.net/r/jaccwabyt
18 'use strict';
19 self.Jaccwabyt = function StructBinderFactory(config){
20 /* ^^^^ it is recommended that clients move that object into wherever
21 they'd like to have it and delete the self-held copy ("self" being
22 the global window or worker object). This API does not require the
23 global reference - it is simply installed as a convenience for
24 connecting these bits to other co-developed code before it gets
25 removed from the global namespace.
28 /** Throws a new Error, the message of which is the concatenation
29 all args with a space between each. */
30 const toss = (...args)=>{throw new Error(args.join(' '))};
32 /**
33 Implementing function bindings revealed significant
34 shortcomings in Emscripten's addFunction()/removeFunction()
35 interfaces:
37 https://github.com/emscripten-core/emscripten/issues/17323
39 Until those are resolved, or a suitable replacement can be
40 implemented, our function-binding API will be more limited
41 and/or clumsier to use than initially hoped.
43 if(!(config.heap instanceof WebAssembly.Memory)
44 && !(config.heap instanceof Function)){
45 toss("config.heap must be WebAssembly.Memory instance or a function.");
47 ['alloc','dealloc'].forEach(function(k){
48 (config[k] instanceof Function) ||
49 toss("Config option '"+k+"' must be a function.");
50 });
51 const SBF = StructBinderFactory;
52 const heap = (config.heap instanceof Function)
53 ? config.heap : (()=>new Uint8Array(config.heap.buffer)),
54 alloc = config.alloc,
55 dealloc = config.dealloc,
56 log = config.log || console.log.bind(console),
57 memberPrefix = (config.memberPrefix || ""),
58 memberSuffix = (config.memberSuffix || ""),
59 bigIntEnabled = (undefined===config.bigIntEnabled
60 ? !!self['BigInt64Array'] : !!config.bigIntEnabled),
61 BigInt = self['BigInt'],
62 BigInt64Array = self['BigInt64Array'],
63 /* Undocumented (on purpose) config options: */
64 functionTable = config.functionTable/*EXPERIMENTAL, undocumented*/,
65 ptrSizeof = config.ptrSizeof || 4,
66 ptrIR = config.ptrIR || 'i32'
69 if(!SBF.debugFlags){
70 SBF.__makeDebugFlags = function(deriveFrom=null){
71 /* This is disgustingly overengineered. :/ */
72 if(deriveFrom && deriveFrom.__flags) deriveFrom = deriveFrom.__flags;
73 const f = function f(flags){
74 if(0===arguments.length){
75 return f.__flags;
77 if(flags<0){
78 delete f.__flags.getter; delete f.__flags.setter;
79 delete f.__flags.alloc; delete f.__flags.dealloc;
80 }else{
81 f.__flags.getter = 0!==(0x01 & flags);
82 f.__flags.setter = 0!==(0x02 & flags);
83 f.__flags.alloc = 0!==(0x04 & flags);
84 f.__flags.dealloc = 0!==(0x08 & flags);
86 return f._flags;
88 Object.defineProperty(f,'__flags', {
89 iterable: false, writable: false,
90 value: Object.create(deriveFrom)
91 });
92 if(!deriveFrom) f(0);
93 return f;
95 SBF.debugFlags = SBF.__makeDebugFlags();
96 }/*static init*/
98 const isLittleEndian = (function() {
99 const buffer = new ArrayBuffer(2);
100 new DataView(buffer).setInt16(0, 256, true /* littleEndian */);
101 // Int16Array uses the platform's endianness.
102 return new Int16Array(buffer)[0] === 256;
103 })();
105 Some terms used in the internal docs:
107 StructType: a struct-wrapping class generated by this
108 framework.
109 DEF: struct description object.
110 SIG: struct member signature string.
113 /** True if SIG s looks like a function signature, else
114 false. */
115 const isFuncSig = (s)=>'('===s[1];
116 /** True if SIG s is-a pointer signature. */
117 const isPtrSig = (s)=>'p'===s || 'P'===s;
118 const isAutoPtrSig = (s)=>'P'===s /*EXPERIMENTAL*/;
119 const sigLetter = (s)=>isFuncSig(s) ? 'p' : s[0];
120 /** Returns the WASM IR form of the Emscripten-conventional letter
121 at SIG s[0]. Throws for an unknown SIG. */
122 const sigIR = function(s){
123 switch(sigLetter(s)){
124 case 'i': return 'i32';
125 case 'p': case 'P': case 's': return ptrIR;
126 case 'j': return 'i64';
127 case 'f': return 'float';
128 case 'd': return 'double';
130 toss("Unhandled signature IR:",s);
132 /** Returns the sizeof value for the given SIG. Throws for an
133 unknown SIG. */
134 const sigSizeof = function(s){
135 switch(sigLetter(s)){
136 case 'i': return 4;
137 case 'p': case 'P': case 's': return ptrSizeof;
138 case 'j': return 8;
139 case 'f': return 4 /* C-side floats, not JS-side */;
140 case 'd': return 8;
142 toss("Unhandled signature sizeof:",s);
144 const affirmBigIntArray = BigInt64Array
145 ? ()=>true : ()=>toss('BigInt64Array is not available.');
146 /** Returns the (signed) TypedArray associated with the type
147 described by the given SIG. Throws for an unknown SIG. */
148 /**********
149 const sigTypedArray = function(s){
150 switch(sigIR(s)) {
151 case 'i32': return Int32Array;
152 case 'i64': return affirmBigIntArray() && BigInt64Array;
153 case 'float': return Float32Array;
154 case 'double': return Float64Array;
156 toss("Unhandled signature TypedArray:",s);
158 **************/
159 /** Returns the name of a DataView getter method corresponding
160 to the given SIG. */
161 const sigDVGetter = function(s){
162 switch(sigLetter(s)) {
163 case 'p': case 'P': case 's': {
164 switch(ptrSizeof){
165 case 4: return 'getInt32';
166 case 8: return affirmBigIntArray() && 'getBigInt64';
168 break;
170 case 'i': return 'getInt32';
171 case 'j': return affirmBigIntArray() && 'getBigInt64';
172 case 'f': return 'getFloat32';
173 case 'd': return 'getFloat64';
175 toss("Unhandled DataView getter for signature:",s);
177 /** Returns the name of a DataView setter method corresponding
178 to the given SIG. */
179 const sigDVSetter = function(s){
180 switch(sigLetter(s)){
181 case 'p': case 'P': case 's': {
182 switch(ptrSizeof){
183 case 4: return 'setInt32';
184 case 8: return affirmBigIntArray() && 'setBigInt64';
186 break;
188 case 'i': return 'setInt32';
189 case 'j': return affirmBigIntArray() && 'setBigInt64';
190 case 'f': return 'setFloat32';
191 case 'd': return 'setFloat64';
193 toss("Unhandled DataView setter for signature:",s);
196 Returns either Number of BigInt, depending on the given
197 SIG. This constructor is used in property setters to coerce
198 the being-set value to the correct size.
200 const sigDVSetWrapper = function(s){
201 switch(sigLetter(s)) {
202 case 'i': case 'f': case 'd': return Number;
203 case 'j': return affirmBigIntArray() && BigInt;
204 case 'p': case 'P': case 's':
205 switch(ptrSizeof){
206 case 4: return Number;
207 case 8: return affirmBigIntArray() && BigInt;
209 break;
211 toss("Unhandled DataView set wrapper for signature:",s);
214 const sPropName = (s,k)=>s+'::'+k;
216 const __propThrowOnSet = function(structName,propName){
217 return ()=>toss(sPropName(structName,propName),"is read-only.");
221 When C code passes a pointer of a bound struct to back into
222 a JS function via a function pointer struct member, it
223 arrives in JS as a number (pointer).
224 StructType.instanceForPointer(ptr) can be used to get the
225 instance associated with that pointer, and __ptrBacklinks
226 holds that mapping. WeakMap keys must be objects, so we
227 cannot use a weak map to map pointers to instances. We use
228 the StructType constructor as the WeakMap key, mapped to a
229 plain, prototype-less Object which maps the pointers to
230 struct instances. That arrangement gives us a
231 per-StructType type-safe way to resolve pointers.
233 const __ptrBacklinks = new WeakMap();
235 Similar to __ptrBacklinks but is scoped at the StructBinder
236 level and holds pointer-to-object mappings for all struct
237 instances created by any struct from any StructFactory
238 which this specific StructBinder has created. The intention
239 of this is to help implement more transparent handling of
240 pointer-type property resolution.
242 const __ptrBacklinksGlobal = Object.create(null);
245 In order to completely hide StructBinder-bound struct
246 pointers from JS code, we store them in a scope-local
247 WeakMap which maps the struct-bound objects to their WASM
248 pointers. The pointers are accessible via
249 boundObject.pointer, which is gated behind an accessor
250 function, but are not exposed anywhere else in the
251 object. The main intention of that is to make it impossible
252 for stale copies to be made.
254 const __instancePointerMap = new WeakMap();
256 /** Property name for the pointer-is-external marker. */
257 const xPtrPropName = '(pointer-is-external)';
259 /** Frees the obj.pointer memory and clears the pointer
260 property. */
261 const __freeStruct = function(ctor, obj, m){
262 if(!m) m = __instancePointerMap.get(obj);
263 if(m) {
264 if(obj.ondispose instanceof Function){
265 try{obj.ondispose()}
266 catch(e){
267 /*do not rethrow: destructors must not throw*/
268 console.warn("ondispose() for",ctor.structName,'@',
269 m,'threw. NOT propagating it.',e);
271 }else if(Array.isArray(obj.ondispose)){
272 obj.ondispose.forEach(function(x){
273 try{
274 if(x instanceof Function) x.call(obj);
275 else if('number' === typeof x) dealloc(x);
276 // else ignore. Strings are permitted to annotate entries
277 // to assist in debugging.
278 }catch(e){
279 console.warn("ondispose() for",ctor.structName,'@',
280 m,'threw. NOT propagating it.',e);
284 delete obj.ondispose;
285 delete __ptrBacklinks.get(ctor)[m];
286 delete __ptrBacklinksGlobal[m];
287 __instancePointerMap.delete(obj);
288 if(ctor.debugFlags.__flags.dealloc){
289 log("debug.dealloc:",(obj[xPtrPropName]?"EXTERNAL":""),
290 ctor.structName,"instance:",
291 ctor.structInfo.sizeof,"bytes @"+m);
293 if(!obj[xPtrPropName]) dealloc(m);
297 /** Returns a skeleton for a read-only property accessor wrapping
298 value v. */
299 const rop = (v)=>{return {configurable: false, writable: false,
300 iterable: false, value: v}};
302 /** Allocates obj's memory buffer based on the size defined in
303 DEF.sizeof. */
304 const __allocStruct = function(ctor, obj, m){
305 let fill = !m;
306 if(m) Object.defineProperty(obj, xPtrPropName, rop(m));
307 else{
308 m = alloc(ctor.structInfo.sizeof);
309 if(!m) toss("Allocation of",ctor.structName,"structure failed.");
311 try {
312 if(ctor.debugFlags.__flags.alloc){
313 log("debug.alloc:",(fill?"":"EXTERNAL"),
314 ctor.structName,"instance:",
315 ctor.structInfo.sizeof,"bytes @"+m);
317 if(fill) heap().fill(0, m, m + ctor.structInfo.sizeof);
318 __instancePointerMap.set(obj, m);
319 __ptrBacklinks.get(ctor)[m] = obj;
320 __ptrBacklinksGlobal[m] = obj;
321 }catch(e){
322 __freeStruct(ctor, obj, m);
323 throw e;
326 /** Gets installed as the memoryDump() method of all structs. */
327 const __memoryDump = function(){
328 const p = this.pointer;
329 return p
330 ? new Uint8Array(heap().slice(p, p+this.structInfo.sizeof))
331 : null;
334 const __memberKey = (k)=>memberPrefix + k + memberSuffix;
335 const __memberKeyProp = rop(__memberKey);
338 Looks up a struct member in structInfo.members. Throws if found
339 if tossIfNotFound is true, else returns undefined if not
340 found. The given name may be either the name of the
341 structInfo.members key (faster) or the key as modified by the
342 memberPrefix/memberSuffix settings.
344 const __lookupMember = function(structInfo, memberName, tossIfNotFound=true){
345 let m = structInfo.members[memberName];
346 if(!m && (memberPrefix || memberSuffix)){
347 // Check for a match on members[X].key
348 for(const v of Object.values(structInfo.members)){
349 if(v.key===memberName){ m = v; break; }
351 if(!m && tossIfNotFound){
352 toss(sPropName(structInfo.name,memberName),'is not a mapped struct member.');
355 return m;
359 Uses __lookupMember(obj.structInfo,memberName) to find a member,
360 throwing if not found. Returns its signature, either in this
361 framework's native format or in Emscripten format.
363 const __memberSignature = function f(obj,memberName,emscriptenFormat=false){
364 if(!f._) f._ = (x)=>x.replace(/[^vipPsjrd]/g,"").replace(/[pPs]/g,'i');
365 const m = __lookupMember(obj.structInfo, memberName, true);
366 return emscriptenFormat ? f._(m.signature) : m.signature;
370 Returns the instanceForPointer() impl for the given
371 StructType constructor.
373 const __instanceBacklinkFactory = function(ctor){
374 const b = Object.create(null);
375 __ptrBacklinks.set(ctor, b);
376 return (ptr)=>b[ptr];
379 const __ptrPropDescriptor = {
380 configurable: false, enumerable: false,
381 get: function(){return __instancePointerMap.get(this)},
382 set: ()=>toss("Cannot assign the 'pointer' property of a struct.")
383 // Reminder: leaving `set` undefined makes assignments
384 // to the property _silently_ do nothing. Current unit tests
385 // rely on it throwing, though.
388 /** Impl of X.memberKeys() for StructType and struct ctors. */
389 const __structMemberKeys = rop(function(){
390 const a = [];
391 Object.keys(this.structInfo.members).forEach((k)=>a.push(this.memberKey(k)));
392 return a;
395 const __utf8Decoder = new TextDecoder('utf-8');
396 const __utf8Encoder = new TextEncoder();
397 /** Internal helper to use in operations which need to distinguish
398 between SharedArrayBuffer heap memory and non-shared heap. */
399 const __SAB = ('undefined'===typeof SharedArrayBuffer)
400 ? function(){} : SharedArrayBuffer;
401 const __utf8Decode = function(arrayBuffer, begin, end){
402 return __utf8Decoder.decode(
403 (arrayBuffer.buffer instanceof __SAB)
404 ? arrayBuffer.slice(begin, end)
405 : arrayBuffer.subarray(begin, end)
409 Uses __lookupMember() to find the given obj.structInfo key.
410 Returns that member if it is a string, else returns false. If the
411 member is not found, throws if tossIfNotFound is true, else
412 returns false.
414 const __memberIsString = function(obj,memberName, tossIfNotFound=false){
415 const m = __lookupMember(obj.structInfo, memberName, tossIfNotFound);
416 return (m && 1===m.signature.length && 's'===m.signature[0]) ? m : false;
420 Given a member description object, throws if member.signature is
421 not valid for assigning to or interpretation as a C-style string.
422 It optimistically assumes that any signature of (i,p,s) is
423 C-string compatible.
425 const __affirmCStringSignature = function(member){
426 if('s'===member.signature) return;
427 toss("Invalid member type signature for C-string value:",
428 JSON.stringify(member));
432 Looks up the given member in obj.structInfo. If it has a
433 signature of 's' then it is assumed to be a C-style UTF-8 string
434 and a decoded copy of the string at its address is returned. If
435 the signature is of any other type, it throws. If an s-type
436 member's address is 0, `null` is returned.
438 const __memberToJsString = function f(obj,memberName){
439 const m = __lookupMember(obj.structInfo, memberName, true);
440 __affirmCStringSignature(m);
441 const addr = obj[m.key];
442 //log("addr =",addr,memberName,"m =",m);
443 if(!addr) return null;
444 let pos = addr;
445 const mem = heap();
446 for( ; mem[pos]!==0; ++pos ) {
447 //log("mem[",pos,"]",mem[pos]);
449 //log("addr =",addr,"pos =",pos);
450 return (addr===pos) ? "" : __utf8Decode(mem, addr, pos);
454 Adds value v to obj.ondispose, creating ondispose,
455 or converting it to an array, if needed.
457 const __addOnDispose = function(obj, v){
458 if(obj.ondispose){
459 if(obj.ondispose instanceof Function){
460 obj.ondispose = [obj.ondispose];
461 }/*else assume it's an array*/
462 }else{
463 obj.ondispose = [];
465 obj.ondispose.push(v);
469 Allocates a new UTF-8-encoded, NUL-terminated copy of the given
470 JS string and returns its address relative to heap(). If
471 allocation returns 0 this function throws. Ownership of the
472 memory is transfered to the caller, who must eventually pass it
473 to the configured dealloc() function.
475 const __allocCString = function(str){
476 const u = __utf8Encoder.encode(str);
477 const mem = alloc(u.length+1);
478 if(!mem) toss("Allocation error while duplicating string:",str);
479 const h = heap();
480 let i = 0;
481 for( ; i < u.length; ++i ) h[mem + i] = u[i];
482 h[mem + u.length] = 0;
483 //log("allocCString @",mem," =",u);
484 return mem;
488 Sets the given struct member of obj to a dynamically-allocated,
489 UTF-8-encoded, NUL-terminated copy of str. It is up to the caller
490 to free any prior memory, if appropriate. The newly-allocated
491 string is added to obj.ondispose so will be freed when the object
492 is disposed.
494 const __setMemberCString = function(obj, memberName, str){
495 const m = __lookupMember(obj.structInfo, memberName, true);
496 __affirmCStringSignature(m);
497 /* Potential TODO: if obj.ondispose contains obj[m.key] then
498 dealloc that value and clear that ondispose entry */
499 const mem = __allocCString(str);
500 obj[m.key] = mem;
501 __addOnDispose(obj, mem);
502 return obj;
506 Prototype for all StructFactory instances (the constructors
507 returned from StructBinder).
509 const StructType = function ctor(structName, structInfo){
510 if(arguments[2]!==rop){
511 toss("Do not call the StructType constructor",
512 "from client-level code.");
514 Object.defineProperties(this,{
515 //isA: rop((v)=>v instanceof ctor),
516 structName: rop(structName),
517 structInfo: rop(structInfo)
522 Properties inherited by struct-type-specific StructType instances
523 and (indirectly) concrete struct-type instances.
525 StructType.prototype = Object.create(null, {
526 dispose: rop(function(){__freeStruct(this.constructor, this)}),
527 lookupMember: rop(function(memberName, tossIfNotFound=true){
528 return __lookupMember(this.structInfo, memberName, tossIfNotFound);
530 memberToJsString: rop(function(memberName){
531 return __memberToJsString(this, memberName);
533 memberIsString: rop(function(memberName, tossIfNotFound=true){
534 return __memberIsString(this, memberName, tossIfNotFound);
536 memberKey: __memberKeyProp,
537 memberKeys: __structMemberKeys,
538 memberSignature: rop(function(memberName, emscriptenFormat=false){
539 return __memberSignature(this, memberName, emscriptenFormat);
541 memoryDump: rop(__memoryDump),
542 pointer: __ptrPropDescriptor,
543 setMemberCString: rop(function(memberName, str){
544 return __setMemberCString(this, memberName, str);
549 "Static" properties for StructType.
551 Object.defineProperties(StructType, {
552 allocCString: rop(__allocCString),
553 instanceForPointer: rop((ptr)=>__ptrBacklinksGlobal[ptr]),
554 isA: rop((v)=>v instanceof StructType),
555 hasExternalPointer: rop((v)=>(v instanceof StructType) && !!v[xPtrPropName]),
556 memberKey: __memberKeyProp
559 const isNumericValue = (v)=>Number.isFinite(v) || (v instanceof (BigInt || Number));
562 Pass this a StructBinder-generated prototype, and the struct
563 member description object. It will define property accessors for
564 proto[memberKey] which read from/write to memory in
565 this.pointer. It modifies descr to make certain downstream
566 operations much simpler.
568 const makeMemberWrapper = function f(ctor,name, descr){
569 if(!f._){
570 /*cache all available getters/setters/set-wrappers for
571 direct reuse in each accessor function. */
572 f._ = {getters: {}, setters: {}, sw:{}};
573 const a = ['i','p','P','s','f','d','v()'];
574 if(bigIntEnabled) a.push('j');
575 a.forEach(function(v){
576 //const ir = sigIR(v);
577 f._.getters[v] = sigDVGetter(v) /* DataView[MethodName] values for GETTERS */;
578 f._.setters[v] = sigDVSetter(v) /* DataView[MethodName] values for SETTERS */;
579 f._.sw[v] = sigDVSetWrapper(v) /* BigInt or Number ctor to wrap around values
580 for conversion */;
582 const rxSig1 = /^[ipPsjfd]$/,
583 rxSig2 = /^[vipPsjfd]\([ipPsjfd]*\)$/;
584 f.sigCheck = function(obj, name, key,sig){
585 if(Object.prototype.hasOwnProperty.call(obj, key)){
586 toss(obj.structName,'already has a property named',key+'.');
588 rxSig1.test(sig) || rxSig2.test(sig)
589 || toss("Malformed signature for",
590 sPropName(obj.structName,name)+":",sig);
593 const key = ctor.memberKey(name);
594 f.sigCheck(ctor.prototype, name, key, descr.signature);
595 descr.key = key;
596 descr.name = name;
597 const sizeOf = sigSizeof(descr.signature);
598 const sigGlyph = sigLetter(descr.signature);
599 const xPropName = sPropName(ctor.prototype.structName,key);
600 const dbg = ctor.prototype.debugFlags.__flags;
602 TODO?: set prototype of descr to an object which can set/fetch
603 its prefered representation, e.g. conversion to string or mapped
604 function. Advantage: we can avoid doing that via if/else if/else
605 in the get/set methods.
607 const prop = Object.create(null);
608 prop.configurable = false;
609 prop.enumerable = false;
610 prop.get = function(){
611 if(dbg.getter){
612 log("debug.getter:",f._.getters[sigGlyph],"for", sigIR(sigGlyph),
613 xPropName,'@', this.pointer,'+',descr.offset,'sz',sizeOf);
615 let rc = (
616 new DataView(heap().buffer, this.pointer + descr.offset, sizeOf)
617 )[f._.getters[sigGlyph]](0, isLittleEndian);
618 if(dbg.getter) log("debug.getter:",xPropName,"result =",rc);
619 if(rc && isAutoPtrSig(descr.signature)){
620 rc = StructType.instanceForPointer(rc) || rc;
621 if(dbg.getter) log("debug.getter:",xPropName,"resolved =",rc);
623 return rc;
625 if(descr.readOnly){
626 prop.set = __propThrowOnSet(ctor.prototype.structName,key);
627 }else{
628 prop.set = function(v){
629 if(dbg.setter){
630 log("debug.setter:",f._.setters[sigGlyph],"for", sigIR(sigGlyph),
631 xPropName,'@', this.pointer,'+',descr.offset,'sz',sizeOf, v);
633 if(!this.pointer){
634 toss("Cannot set struct property on disposed instance.");
636 if(null===v) v = 0;
637 else while(!isNumericValue(v)){
638 if(isAutoPtrSig(descr.signature) && (v instanceof StructType)){
639 // It's a struct instance: let's store its pointer value!
640 v = v.pointer || 0;
641 if(dbg.setter) log("debug.setter:",xPropName,"resolved to",v);
642 break;
644 toss("Invalid value for pointer-type",xPropName+'.');
647 new DataView(heap().buffer, this.pointer + descr.offset, sizeOf)
648 )[f._.setters[sigGlyph]](0, f._.sw[sigGlyph](v), isLittleEndian);
651 Object.defineProperty(ctor.prototype, key, prop);
652 }/*makeMemberWrapper*/;
655 The main factory function which will be returned to the
656 caller.
658 const StructBinder = function StructBinder(structName, structInfo){
659 if(1===arguments.length){
660 structInfo = structName;
661 structName = structInfo.name;
662 }else if(!structInfo.name){
663 structInfo.name = structName;
665 if(!structName) toss("Struct name is required.");
666 let lastMember = false;
667 Object.keys(structInfo.members).forEach((k)=>{
668 const m = structInfo.members[k];
669 if(!m.sizeof) toss(structName,"member",k,"is missing sizeof.");
670 else if(0!==(m.sizeof%4)){
671 toss(structName,"member",k,"sizeof is not aligned.");
673 else if(0!==(m.offset%4)){
674 toss(structName,"member",k,"offset is not aligned.");
676 if(!lastMember || lastMember.offset < m.offset) lastMember = m;
678 if(!lastMember) toss("No member property descriptions found.");
679 else if(structInfo.sizeof < lastMember.offset+lastMember.sizeof){
680 toss("Invalid struct config:",structName,
681 "max member offset ("+lastMember.offset+") ",
682 "extends past end of struct (sizeof="+structInfo.sizeof+").");
684 const debugFlags = rop(SBF.__makeDebugFlags(StructBinder.debugFlags));
685 /** Constructor for the StructCtor. */
686 const StructCtor = function StructCtor(externalMemory){
687 if(!(this instanceof StructCtor)){
688 toss("The",structName,"constructor may only be called via 'new'.");
689 }else if(arguments.length){
690 if(externalMemory!==(externalMemory|0) || externalMemory<=0){
691 toss("Invalid pointer value for",structName,"constructor.");
693 __allocStruct(StructCtor, this, externalMemory);
694 }else{
695 __allocStruct(StructCtor, this);
698 Object.defineProperties(StructCtor,{
699 debugFlags: debugFlags,
700 disposeAll: rop(function(){
701 const map = __ptrBacklinks.get(StructCtor);
702 Object.keys(map).forEach(function(ptr){
703 const b = map[ptr];
704 if(b) __freeStruct(StructCtor, b, ptr);
706 __ptrBacklinks.set(StructCtor, Object.create(null));
707 return StructCtor;
709 instanceForPointer: rop(__instanceBacklinkFactory(StructCtor)),
710 isA: rop((v)=>v instanceof StructCtor),
711 memberKey: __memberKeyProp,
712 memberKeys: __structMemberKeys,
713 resolveToInstance: rop(function(v, throwIfNot=false){
714 if(!(v instanceof StructCtor)){
715 v = Number.isSafeInteger(v)
716 ? StructCtor.instanceForPointer(v) : undefined;
718 if(!v && throwIfNot) toss("Value is-not-a",StructCtor.structName);
719 return v;
721 methodInfoForKey: rop(function(mKey){
723 structInfo: rop(structInfo),
724 structName: rop(structName)
726 StructCtor.prototype = new StructType(structName, structInfo, rop);
727 Object.defineProperties(StructCtor.prototype,{
728 debugFlags: debugFlags,
729 constructor: rop(StructCtor)
730 /*if we assign StructCtor.prototype and don't do
731 this then StructCtor!==instance.constructor!*/
733 Object.keys(structInfo.members).forEach(
734 (name)=>makeMemberWrapper(StructCtor, name, structInfo.members[name])
736 return StructCtor;
738 StructBinder.instanceForPointer = StructType.instanceForPointer;
739 StructBinder.StructType = StructType;
740 StructBinder.config = config;
741 StructBinder.allocCString = __allocCString;
742 if(!StructBinder.debugFlags){
743 StructBinder.debugFlags = SBF.__makeDebugFlags(SBF.debugFlags);
745 return StructBinder;
746 }/*StructBinderFactory*/;