123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697 |
- /**
- 2022-06-30
- The author disclaims copyright to this source code. In place of a
- legal notice, here is a blessing:
- * May you do good and not evil.
- * May you find forgiveness for yourself and forgive others.
- * May you share freely, never taking more than you give.
- ***********************************************************************
- The Jaccwabyt API is documented in detail in an external file,
- _possibly_ called jaccwabyt.md in the same directory as this file.
- Project homes:
- - https://fossil.wanderinghorse.net/r/jaccwabyt
- - https://sqlite.org/src/dir/ext/wasm/jaccwabyt
- */
- 'use strict';
- globalThis.Jaccwabyt = function StructBinderFactory(config){
- /* ^^^^ it is recommended that clients move that object into wherever
- they'd like to have it and delete the self-held copy ("self" being
- the global window or worker object). This API does not require the
- global reference - it is simply installed as a convenience for
- connecting these bits to other co-developed code before it gets
- removed from the global namespace.
- */
- /** Throws a new Error, the message of which is the concatenation
- all args with a space between each. */
- const toss = (...args)=>{throw new Error(args.join(' '))};
- /**
- Implementing function bindings revealed significant
- shortcomings in Emscripten's addFunction()/removeFunction()
- interfaces:
- https://github.com/emscripten-core/emscripten/issues/17323
- Until those are resolved, or a suitable replacement can be
- implemented, our function-binding API will be more limited
- and/or clumsier to use than initially hoped.
- */
- if(!(config.heap instanceof WebAssembly.Memory)
- && !(config.heap instanceof Function)){
- toss("config.heap must be WebAssembly.Memory instance or a function.");
- }
- ['alloc','dealloc'].forEach(function(k){
- (config[k] instanceof Function) ||
- toss("Config option '"+k+"' must be a function.");
- });
- const SBF = StructBinderFactory;
- const heap = (config.heap instanceof Function)
- ? config.heap : (()=>new Uint8Array(config.heap.buffer)),
- alloc = config.alloc,
- dealloc = config.dealloc,
- log = config.log || console.log.bind(console),
- memberPrefix = (config.memberPrefix || ""),
- memberSuffix = (config.memberSuffix || ""),
- bigIntEnabled = (undefined===config.bigIntEnabled
- ? !!globalThis['BigInt64Array'] : !!config.bigIntEnabled),
- BigInt = globalThis['BigInt'],
- BigInt64Array = globalThis['BigInt64Array'],
- /* Undocumented (on purpose) config options: */
- ptrSizeof = config.ptrSizeof || 4,
- ptrIR = config.ptrIR || 'i32'
- ;
- if(!SBF.debugFlags){
- SBF.__makeDebugFlags = function(deriveFrom=null){
- /* This is disgustingly overengineered. :/ */
- if(deriveFrom && deriveFrom.__flags) deriveFrom = deriveFrom.__flags;
- const f = function f(flags){
- if(0===arguments.length){
- return f.__flags;
- }
- if(flags<0){
- delete f.__flags.getter; delete f.__flags.setter;
- delete f.__flags.alloc; delete f.__flags.dealloc;
- }else{
- f.__flags.getter = 0!==(0x01 & flags);
- f.__flags.setter = 0!==(0x02 & flags);
- f.__flags.alloc = 0!==(0x04 & flags);
- f.__flags.dealloc = 0!==(0x08 & flags);
- }
- return f._flags;
- };
- Object.defineProperty(f,'__flags', {
- iterable: false, writable: false,
- value: Object.create(deriveFrom)
- });
- if(!deriveFrom) f(0);
- return f;
- };
- SBF.debugFlags = SBF.__makeDebugFlags();
- }/*static init*/
- const isLittleEndian = (function() {
- const buffer = new ArrayBuffer(2);
- new DataView(buffer).setInt16(0, 256, true /* littleEndian */);
- // Int16Array uses the platform's endianness.
- return new Int16Array(buffer)[0] === 256;
- })();
- /**
- Some terms used in the internal docs:
- StructType: a struct-wrapping class generated by this
- framework.
- DEF: struct description object.
- SIG: struct member signature string.
- */
- /** True if SIG s looks like a function signature, else
- false. */
- const isFuncSig = (s)=>'('===s[1];
- /** True if SIG s is-a pointer signature. */
- const isPtrSig = (s)=>'p'===s || 'P'===s;
- const isAutoPtrSig = (s)=>'P'===s /*EXPERIMENTAL*/;
- const sigLetter = (s)=>isFuncSig(s) ? 'p' : s[0];
- /** Returns the WASM IR form of the Emscripten-conventional letter
- at SIG s[0]. Throws for an unknown SIG. */
- const sigIR = function(s){
- switch(sigLetter(s)){
- case 'c': case 'C': return 'i8';
- case 'i': return 'i32';
- case 'p': case 'P': case 's': return ptrIR;
- case 'j': return 'i64';
- case 'f': return 'float';
- case 'd': return 'double';
- }
- toss("Unhandled signature IR:",s);
- };
- const affirmBigIntArray = BigInt64Array
- ? ()=>true : ()=>toss('BigInt64Array is not available.');
- /** Returns the name of a DataView getter method corresponding
- to the given SIG. */
- const sigDVGetter = function(s){
- switch(sigLetter(s)) {
- case 'p': case 'P': case 's': {
- switch(ptrSizeof){
- case 4: return 'getInt32';
- case 8: return affirmBigIntArray() && 'getBigInt64';
- }
- break;
- }
- case 'i': return 'getInt32';
- case 'c': return 'getInt8';
- case 'C': return 'getUint8';
- case 'j': return affirmBigIntArray() && 'getBigInt64';
- case 'f': return 'getFloat32';
- case 'd': return 'getFloat64';
- }
- toss("Unhandled DataView getter for signature:",s);
- };
- /** Returns the name of a DataView setter method corresponding
- to the given SIG. */
- const sigDVSetter = function(s){
- switch(sigLetter(s)){
- case 'p': case 'P': case 's': {
- switch(ptrSizeof){
- case 4: return 'setInt32';
- case 8: return affirmBigIntArray() && 'setBigInt64';
- }
- break;
- }
- case 'i': return 'setInt32';
- case 'c': return 'setInt8';
- case 'C': return 'setUint8';
- case 'j': return affirmBigIntArray() && 'setBigInt64';
- case 'f': return 'setFloat32';
- case 'd': return 'setFloat64';
- }
- toss("Unhandled DataView setter for signature:",s);
- };
- /**
- Returns either Number of BigInt, depending on the given
- SIG. This constructor is used in property setters to coerce
- the being-set value to the correct size.
- */
- const sigDVSetWrapper = function(s){
- switch(sigLetter(s)) {
- case 'i': case 'f': case 'c': case 'C': case 'd': return Number;
- case 'j': return affirmBigIntArray() && BigInt;
- case 'p': case 'P': case 's':
- switch(ptrSizeof){
- case 4: return Number;
- case 8: return affirmBigIntArray() && BigInt;
- }
- break;
- }
- toss("Unhandled DataView set wrapper for signature:",s);
- };
- /** Returns the given struct and member name in a form suitable for
- debugging and error output. */
- const sPropName = (s,k)=>s+'::'+k;
- const __propThrowOnSet = function(structName,propName){
- return ()=>toss(sPropName(structName,propName),"is read-only.");
- };
- /**
- In order to completely hide StructBinder-bound struct
- pointers from JS code, we store them in a scope-local
- WeakMap which maps the struct-bound objects to their WASM
- pointers. The pointers are accessible via
- boundObject.pointer, which is gated behind an accessor
- function, but are not exposed anywhere else in the
- object. The main intention of that is to make it impossible
- for stale copies to be made.
- */
- const __instancePointerMap = new WeakMap();
- /** Property name for the pointer-is-external marker. */
- const xPtrPropName = '(pointer-is-external)';
- /** Frees the obj.pointer memory and clears the pointer
- property. */
- const __freeStruct = function(ctor, obj, m){
- if(!m) m = __instancePointerMap.get(obj);
- if(m) {
- __instancePointerMap.delete(obj);
- if(Array.isArray(obj.ondispose)){
- let x;
- while((x = obj.ondispose.shift())){
- try{
- if(x instanceof Function) x.call(obj);
- else if(x instanceof StructType) x.dispose();
- else if('number' === typeof x) dealloc(x);
- // else ignore. Strings are permitted to annotate entries
- // to assist in debugging.
- }catch(e){
- console.warn("ondispose() for",ctor.structName,'@',
- m,'threw. NOT propagating it.',e);
- }
- }
- }else if(obj.ondispose instanceof Function){
- try{obj.ondispose()}
- catch(e){
- /*do not rethrow: destructors must not throw*/
- console.warn("ondispose() for",ctor.structName,'@',
- m,'threw. NOT propagating it.',e);
- }
- }
- delete obj.ondispose;
- if(ctor.debugFlags.__flags.dealloc){
- log("debug.dealloc:",(obj[xPtrPropName]?"EXTERNAL":""),
- ctor.structName,"instance:",
- ctor.structInfo.sizeof,"bytes @"+m);
- }
- if(!obj[xPtrPropName]) dealloc(m);
- }
- };
- /** Returns a skeleton for a read-only property accessor wrapping
- value v. */
- const rop = (v)=>{return {configurable: false, writable: false,
- iterable: false, value: v}};
- /** Allocates obj's memory buffer based on the size defined in
- ctor.structInfo.sizeof. */
- const __allocStruct = function(ctor, obj, m){
- let fill = !m;
- if(m) Object.defineProperty(obj, xPtrPropName, rop(m));
- else{
- m = alloc(ctor.structInfo.sizeof);
- if(!m) toss("Allocation of",ctor.structName,"structure failed.");
- }
- try {
- if(ctor.debugFlags.__flags.alloc){
- log("debug.alloc:",(fill?"":"EXTERNAL"),
- ctor.structName,"instance:",
- ctor.structInfo.sizeof,"bytes @"+m);
- }
- if(fill) heap().fill(0, m, m + ctor.structInfo.sizeof);
- __instancePointerMap.set(obj, m);
- }catch(e){
- __freeStruct(ctor, obj, m);
- throw e;
- }
- };
- /** Gets installed as the memoryDump() method of all structs. */
- const __memoryDump = function(){
- const p = this.pointer;
- return p
- ? new Uint8Array(heap().slice(p, p+this.structInfo.sizeof))
- : null;
- };
- const __memberKey = (k)=>memberPrefix + k + memberSuffix;
- const __memberKeyProp = rop(__memberKey);
- /**
- Looks up a struct member in structInfo.members. Throws if found
- if tossIfNotFound is true, else returns undefined if not
- found. The given name may be either the name of the
- structInfo.members key (faster) or the key as modified by the
- memberPrefix and memberSuffix settings.
- */
- const __lookupMember = function(structInfo, memberName, tossIfNotFound=true){
- let m = structInfo.members[memberName];
- if(!m && (memberPrefix || memberSuffix)){
- // Check for a match on members[X].key
- for(const v of Object.values(structInfo.members)){
- if(v.key===memberName){ m = v; break; }
- }
- if(!m && tossIfNotFound){
- toss(sPropName(structInfo.name,memberName),'is not a mapped struct member.');
- }
- }
- return m;
- };
- /**
- Uses __lookupMember(obj.structInfo,memberName) to find a member,
- throwing if not found. Returns its signature, either in this
- framework's native format or in Emscripten format.
- */
- const __memberSignature = function f(obj,memberName,emscriptenFormat=false){
- if(!f._) f._ = (x)=>x.replace(/[^vipPsjrdcC]/g,"").replace(/[pPscC]/g,'i');
- const m = __lookupMember(obj.structInfo, memberName, true);
- return emscriptenFormat ? f._(m.signature) : m.signature;
- };
- const __ptrPropDescriptor = {
- configurable: false, enumerable: false,
- get: function(){return __instancePointerMap.get(this)},
- set: ()=>toss("Cannot assign the 'pointer' property of a struct.")
- // Reminder: leaving `set` undefined makes assignments
- // to the property _silently_ do nothing. Current unit tests
- // rely on it throwing, though.
- };
- /** Impl of X.memberKeys() for StructType and struct ctors. */
- const __structMemberKeys = rop(function(){
- const a = [];
- for(const k of Object.keys(this.structInfo.members)){
- a.push(this.memberKey(k));
- }
- return a;
- });
- const __utf8Decoder = new TextDecoder('utf-8');
- const __utf8Encoder = new TextEncoder();
- /** Internal helper to use in operations which need to distinguish
- between SharedArrayBuffer heap memory and non-shared heap. */
- const __SAB = ('undefined'===typeof SharedArrayBuffer)
- ? function(){} : SharedArrayBuffer;
- const __utf8Decode = function(arrayBuffer, begin, end){
- return __utf8Decoder.decode(
- (arrayBuffer.buffer instanceof __SAB)
- ? arrayBuffer.slice(begin, end)
- : arrayBuffer.subarray(begin, end)
- );
- };
- /**
- Uses __lookupMember() to find the given obj.structInfo key.
- Returns that member if it is a string, else returns false. If the
- member is not found, throws if tossIfNotFound is true, else
- returns false.
- */
- const __memberIsString = function(obj,memberName, tossIfNotFound=false){
- const m = __lookupMember(obj.structInfo, memberName, tossIfNotFound);
- return (m && 1===m.signature.length && 's'===m.signature[0]) ? m : false;
- };
- /**
- Given a member description object, throws if member.signature is
- not valid for assigning to or interpretation as a C-style string.
- It optimistically assumes that any signature of (i,p,s) is
- C-string compatible.
- */
- const __affirmCStringSignature = function(member){
- if('s'===member.signature) return;
- toss("Invalid member type signature for C-string value:",
- JSON.stringify(member));
- };
- /**
- Looks up the given member in obj.structInfo. If it has a
- signature of 's' then it is assumed to be a C-style UTF-8 string
- and a decoded copy of the string at its address is returned. If
- the signature is of any other type, it throws. If an s-type
- member's address is 0, `null` is returned.
- */
- const __memberToJsString = function f(obj,memberName){
- const m = __lookupMember(obj.structInfo, memberName, true);
- __affirmCStringSignature(m);
- const addr = obj[m.key];
- //log("addr =",addr,memberName,"m =",m);
- if(!addr) return null;
- let pos = addr;
- const mem = heap();
- for( ; mem[pos]!==0; ++pos ) {
- //log("mem[",pos,"]",mem[pos]);
- };
- //log("addr =",addr,"pos =",pos);
- return (addr===pos) ? "" : __utf8Decode(mem, addr, pos);
- };
- /**
- Adds value v to obj.ondispose, creating ondispose,
- or converting it to an array, if needed.
- */
- const __addOnDispose = function(obj, ...v){
- if(obj.ondispose){
- if(!Array.isArray(obj.ondispose)){
- obj.ondispose = [obj.ondispose];
- }
- }else{
- obj.ondispose = [];
- }
- obj.ondispose.push(...v);
- };
- /**
- Allocates a new UTF-8-encoded, NUL-terminated copy of the given
- JS string and returns its address relative to heap(). If
- allocation returns 0 this function throws. Ownership of the
- memory is transfered to the caller, who must eventually pass it
- to the configured dealloc() function.
- */
- const __allocCString = function(str){
- const u = __utf8Encoder.encode(str);
- const mem = alloc(u.length+1);
- if(!mem) toss("Allocation error while duplicating string:",str);
- const h = heap();
- //let i = 0;
- //for( ; i < u.length; ++i ) h[mem + i] = u[i];
- h.set(u, mem);
- h[mem + u.length] = 0;
- //log("allocCString @",mem," =",u);
- return mem;
- };
- /**
- Sets the given struct member of obj to a dynamically-allocated,
- UTF-8-encoded, NUL-terminated copy of str. It is up to the caller
- to free any prior memory, if appropriate. The newly-allocated
- string is added to obj.ondispose so will be freed when the object
- is disposed.
- The given name may be either the name of the structInfo.members
- key (faster) or the key as modified by the memberPrefix and
- memberSuffix settings.
- */
- const __setMemberCString = function(obj, memberName, str){
- const m = __lookupMember(obj.structInfo, memberName, true);
- __affirmCStringSignature(m);
- /* Potential TODO: if obj.ondispose contains obj[m.key] then
- dealloc that value and clear that ondispose entry */
- const mem = __allocCString(str);
- obj[m.key] = mem;
- __addOnDispose(obj, mem);
- return obj;
- };
- /**
- Prototype for all StructFactory instances (the constructors
- returned from StructBinder).
- */
- const StructType = function ctor(structName, structInfo){
- if(arguments[2]!==rop){
- toss("Do not call the StructType constructor",
- "from client-level code.");
- }
- Object.defineProperties(this,{
- //isA: rop((v)=>v instanceof ctor),
- structName: rop(structName),
- structInfo: rop(structInfo)
- });
- };
- /**
- Properties inherited by struct-type-specific StructType instances
- and (indirectly) concrete struct-type instances.
- */
- StructType.prototype = Object.create(null, {
- dispose: rop(function(){__freeStruct(this.constructor, this)}),
- lookupMember: rop(function(memberName, tossIfNotFound=true){
- return __lookupMember(this.structInfo, memberName, tossIfNotFound);
- }),
- memberToJsString: rop(function(memberName){
- return __memberToJsString(this, memberName);
- }),
- memberIsString: rop(function(memberName, tossIfNotFound=true){
- return __memberIsString(this, memberName, tossIfNotFound);
- }),
- memberKey: __memberKeyProp,
- memberKeys: __structMemberKeys,
- memberSignature: rop(function(memberName, emscriptenFormat=false){
- return __memberSignature(this, memberName, emscriptenFormat);
- }),
- memoryDump: rop(__memoryDump),
- pointer: __ptrPropDescriptor,
- setMemberCString: rop(function(memberName, str){
- return __setMemberCString(this, memberName, str);
- })
- });
- // Function-type non-Property inherited members
- Object.assign(StructType.prototype,{
- addOnDispose: function(...v){
- __addOnDispose(this,...v);
- return this;
- }
- });
- /**
- "Static" properties for StructType.
- */
- Object.defineProperties(StructType, {
- allocCString: rop(__allocCString),
- isA: rop((v)=>v instanceof StructType),
- hasExternalPointer: rop((v)=>(v instanceof StructType) && !!v[xPtrPropName]),
- memberKey: __memberKeyProp
- });
- const isNumericValue = (v)=>Number.isFinite(v) || (v instanceof (BigInt || Number));
- /**
- Pass this a StructBinder-generated prototype, and the struct
- member description object. It will define property accessors for
- proto[memberKey] which read from/write to memory in
- this.pointer. It modifies descr to make certain downstream
- operations much simpler.
- */
- const makeMemberWrapper = function f(ctor,name, descr){
- if(!f._){
- /*cache all available getters/setters/set-wrappers for
- direct reuse in each accessor function. */
- f._ = {getters: {}, setters: {}, sw:{}};
- const a = ['i','c','C','p','P','s','f','d','v()'];
- if(bigIntEnabled) a.push('j');
- a.forEach(function(v){
- //const ir = sigIR(v);
- f._.getters[v] = sigDVGetter(v) /* DataView[MethodName] values for GETTERS */;
- f._.setters[v] = sigDVSetter(v) /* DataView[MethodName] values for SETTERS */;
- f._.sw[v] = sigDVSetWrapper(v) /* BigInt or Number ctor to wrap around values
- for conversion */;
- });
- const rxSig1 = /^[ipPsjfdcC]$/,
- rxSig2 = /^[vipPsjfdcC]\([ipPsjfdcC]*\)$/;
- f.sigCheck = function(obj, name, key,sig){
- if(Object.prototype.hasOwnProperty.call(obj, key)){
- toss(obj.structName,'already has a property named',key+'.');
- }
- rxSig1.test(sig) || rxSig2.test(sig)
- || toss("Malformed signature for",
- sPropName(obj.structName,name)+":",sig);
- };
- }
- const key = ctor.memberKey(name);
- f.sigCheck(ctor.prototype, name, key, descr.signature);
- descr.key = key;
- descr.name = name;
- const sigGlyph = sigLetter(descr.signature);
- const xPropName = sPropName(ctor.prototype.structName,key);
- const dbg = ctor.prototype.debugFlags.__flags;
- /*
- TODO?: set prototype of descr to an object which can set/fetch
- its prefered representation, e.g. conversion to string or mapped
- function. Advantage: we can avoid doing that via if/else if/else
- in the get/set methods.
- */
- const prop = Object.create(null);
- prop.configurable = false;
- prop.enumerable = false;
- prop.get = function(){
- if(dbg.getter){
- log("debug.getter:",f._.getters[sigGlyph],"for", sigIR(sigGlyph),
- xPropName,'@', this.pointer,'+',descr.offset,'sz',descr.sizeof);
- }
- let rc = (
- new DataView(heap().buffer, this.pointer + descr.offset, descr.sizeof)
- )[f._.getters[sigGlyph]](0, isLittleEndian);
- if(dbg.getter) log("debug.getter:",xPropName,"result =",rc);
- return rc;
- };
- if(descr.readOnly){
- prop.set = __propThrowOnSet(ctor.prototype.structName,key);
- }else{
- prop.set = function(v){
- if(dbg.setter){
- log("debug.setter:",f._.setters[sigGlyph],"for", sigIR(sigGlyph),
- xPropName,'@', this.pointer,'+',descr.offset,'sz',descr.sizeof, v);
- }
- if(!this.pointer){
- toss("Cannot set struct property on disposed instance.");
- }
- if(null===v) v = 0;
- else while(!isNumericValue(v)){
- if(isAutoPtrSig(descr.signature) && (v instanceof StructType)){
- // It's a struct instance: let's store its pointer value!
- v = v.pointer || 0;
- if(dbg.setter) log("debug.setter:",xPropName,"resolved to",v);
- break;
- }
- toss("Invalid value for pointer-type",xPropName+'.');
- }
- (
- new DataView(heap().buffer, this.pointer + descr.offset, descr.sizeof)
- )[f._.setters[sigGlyph]](0, f._.sw[sigGlyph](v), isLittleEndian);
- };
- }
- Object.defineProperty(ctor.prototype, key, prop);
- }/*makeMemberWrapper*/;
-
- /**
- The main factory function which will be returned to the
- caller.
- */
- const StructBinder = function StructBinder(structName, structInfo){
- if(1===arguments.length){
- structInfo = structName;
- structName = structInfo.name;
- }else if(!structInfo.name){
- structInfo.name = structName;
- }
- if(!structName) toss("Struct name is required.");
- let lastMember = false;
- Object.keys(structInfo.members).forEach((k)=>{
- // Sanity checks of sizeof/offset info...
- const m = structInfo.members[k];
- if(!m.sizeof) toss(structName,"member",k,"is missing sizeof.");
- else if(m.sizeof===1){
- (m.signature === 'c' || m.signature === 'C') ||
- toss("Unexpected sizeof==1 member",
- sPropName(structInfo.name,k),
- "with signature",m.signature);
- }else{
- // sizes and offsets of size-1 members may be odd values, but
- // others may not.
- if(0!==(m.sizeof%4)){
- console.warn("Invalid struct member description =",m,"from",structInfo);
- toss(structName,"member",k,"sizeof is not aligned. sizeof="+m.sizeof);
- }
- if(0!==(m.offset%4)){
- console.warn("Invalid struct member description =",m,"from",structInfo);
- toss(structName,"member",k,"offset is not aligned. offset="+m.offset);
- }
- }
- if(!lastMember || lastMember.offset < m.offset) lastMember = m;
- });
- if(!lastMember) toss("No member property descriptions found.");
- else if(structInfo.sizeof < lastMember.offset+lastMember.sizeof){
- toss("Invalid struct config:",structName,
- "max member offset ("+lastMember.offset+") ",
- "extends past end of struct (sizeof="+structInfo.sizeof+").");
- }
- const debugFlags = rop(SBF.__makeDebugFlags(StructBinder.debugFlags));
- /** Constructor for the StructCtor. */
- const StructCtor = function StructCtor(externalMemory){
- if(!(this instanceof StructCtor)){
- toss("The",structName,"constructor may only be called via 'new'.");
- }else if(arguments.length){
- if(externalMemory!==(externalMemory|0) || externalMemory<=0){
- toss("Invalid pointer value for",structName,"constructor.");
- }
- __allocStruct(StructCtor, this, externalMemory);
- }else{
- __allocStruct(StructCtor, this);
- }
- };
- Object.defineProperties(StructCtor,{
- debugFlags: debugFlags,
- isA: rop((v)=>v instanceof StructCtor),
- memberKey: __memberKeyProp,
- memberKeys: __structMemberKeys,
- methodInfoForKey: rop(function(mKey){
- }),
- structInfo: rop(structInfo),
- structName: rop(structName)
- });
- StructCtor.prototype = new StructType(structName, structInfo, rop);
- Object.defineProperties(StructCtor.prototype,{
- debugFlags: debugFlags,
- constructor: rop(StructCtor)
- /*if we assign StructCtor.prototype and don't do
- this then StructCtor!==instance.constructor!*/
- });
- Object.keys(structInfo.members).forEach(
- (name)=>makeMemberWrapper(StructCtor, name, structInfo.members[name])
- );
- return StructCtor;
- };
- StructBinder.StructType = StructType;
- StructBinder.config = config;
- StructBinder.allocCString = __allocCString;
- if(!StructBinder.debugFlags){
- StructBinder.debugFlags = SBF.__makeDebugFlags(SBF.debugFlags);
- }
- return StructBinder;
- }/*StructBinderFactory*/;
|