mirror of
				https://github.com/tursodatabase/libsql.git
				synced 2025-11-04 04:48:55 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			697 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			697 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						|
  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*/;
 |