2022-08-10 11:26:08 +00:00
/ * *
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 .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2022-12-07 03:42:39 +00:00
The Jaccwabyt API is documented in detail in an external file ,
_possibly _ called jaccwabyt . md in the same directory as this file .
2022-08-10 11:26:08 +00:00
2022-12-07 03:42:39 +00:00
Project homes :
- https : //fossil.wanderinghorse.net/r/jaccwabyt
- https : //sqlite.org/src/dir/ext/wasm/jaccwabyt
2022-08-10 11:26:08 +00:00
* /
'use strict' ;
2023-03-07 19:12:06 +00:00
globalThis . Jaccwabyt = function StructBinderFactory ( config ) {
2022-08-10 11:26:08 +00:00
/ * ^ ^ ^ ^ i t i s r e c o m m e n d e d t h a t c l i e n t s m o v e t h a t o b j e c t i n t o w h e r e v e r
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 .
* /
/ * * T h r o w s a n e w E r r o r , t h e m e s s a g e o f w h i c h i s t h e c o n c a t e n a t i o n
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
2023-06-26 07:05:05 +00:00
? ! ! globalThis [ 'BigInt64Array' ] : ! ! config . bigIntEnabled ) ,
BigInt = globalThis [ 'BigInt' ] ,
BigInt64Array = globalThis [ 'BigInt64Array' ] ,
2022-08-10 11:26:08 +00:00
/* 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 .
* /
/ * * T r u e i f S I G s l o o k s l i k e a f u n c t i o n s i g n a t u r e , e l s e
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 ] ;
/ * * R e t u r n s t h e W A S M I R f o r m o f t h e E m s c r i p t e n - c o n v e n t i o n a l l e t t e r
at SIG s [ 0 ] . Throws for an unknown SIG . * /
const sigIR = function ( s ) {
switch ( sigLetter ( s ) ) {
2022-12-05 05:30:03 +00:00
case 'c' : case 'C' : return 'i8' ;
2022-08-10 11:26:08 +00:00
case 'i' : return 'i32' ;
case 'p' : case 'P' : case 's' : return ptrIR ;
2022-10-03 08:30:22 +00:00
case 'j' : return 'i64' ;
2022-08-10 11:26:08 +00:00
case 'f' : return 'float' ;
case 'd' : return 'double' ;
}
toss ( "Unhandled signature IR:" , s ) ;
} ;
2022-12-05 05:45:00 +00:00
2022-08-10 11:26:08 +00:00
const affirmBigIntArray = BigInt64Array
? ( ) => true : ( ) => toss ( 'BigInt64Array is not available.' ) ;
/ * * R e t u r n s t h e n a m e o f a D a t a V i e w g e t t e r m e t h o d c o r r e s p o n d i n g
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' ;
2022-12-05 05:30:03 +00:00
case 'c' : return 'getInt8' ;
case 'C' : return 'getUint8' ;
2022-10-03 08:30:22 +00:00
case 'j' : return affirmBigIntArray ( ) && 'getBigInt64' ;
2022-08-10 11:26:08 +00:00
case 'f' : return 'getFloat32' ;
case 'd' : return 'getFloat64' ;
}
toss ( "Unhandled DataView getter for signature:" , s ) ;
} ;
/ * * R e t u r n s t h e n a m e o f a D a t a V i e w s e t t e r m e t h o d c o r r e s p o n d i n g
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' ;
2022-12-05 05:30:03 +00:00
case 'c' : return 'setInt8' ;
case 'C' : return 'setUint8' ;
2022-10-03 08:30:22 +00:00
case 'j' : return affirmBigIntArray ( ) && 'setBigInt64' ;
2022-08-10 11:26:08 +00:00
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 ) ) {
2022-12-05 05:30:03 +00:00
case 'i' : case 'f' : case 'c' : case 'C' : case 'd' : return Number ;
2022-10-03 08:30:22 +00:00
case 'j' : return affirmBigIntArray ( ) && BigInt ;
2022-08-10 11:26:08 +00:00
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 ) ;
} ;
2022-12-05 05:45:00 +00:00
/ * * R e t u r n s t h e g i v e n s t r u c t a n d m e m b e r n a m e i n a f o r m s u i t a b l e f o r
debugging and error output . * /
2022-08-10 11:26:08 +00:00
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)' ;
/ * * F r e e s t h e o b j . p o i n t e r m e m o r y a n d c l e a r s t h e p o i n t e r
property . * /
const _ _freeStruct = function ( ctor , obj , m ) {
if ( ! m ) m = _ _instancePointerMap . get ( obj ) ;
if ( m ) {
2022-12-07 07:22:34 +00:00
_ _instancePointerMap . delete ( obj ) ;
2022-12-07 03:42:39 +00:00
if ( Array . isArray ( obj . ondispose ) ) {
let x ;
while ( ( x = obj . ondispose . shift ( ) ) ) {
2022-08-10 11:26:08 +00:00
try {
if ( x instanceof Function ) x . call ( obj ) ;
2022-12-06 06:09:03 +00:00
else if ( x instanceof StructType ) x . dispose ( ) ;
2022-08-10 11:26:08 +00:00
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 ) ;
}
2022-12-07 03:42:39 +00:00
}
} 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 ) ;
}
2022-08-10 11:26:08 +00:00
}
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 ) ;
}
} ;
/ * * R e t u r n s a s k e l e t o n f o r a r e a d - o n l y p r o p e r t y a c c e s s o r w r a p p i n g
value v . * /
const rop = ( v ) => { return { configurable : false , writable : false ,
iterable : false , value : v } } ;
/ * * A l l o c a t e s o b j ' s m e m o r y b u f f e r b a s e d o n t h e s i z e d e f i n e d i n
2022-12-06 08:21:23 +00:00
ctor . structInfo . sizeof . * /
2022-08-10 11:26:08 +00:00
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
2022-12-06 08:21:23 +00:00
memberPrefix and memberSuffix settings .
2022-08-10 11:26:08 +00:00
* /
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 ) {
2022-12-05 05:30:03 +00:00
if ( ! f . _ ) f . _ = ( x ) => x . replace ( /[^vipPsjrdcC]/g , "" ) . replace ( /[pPscC]/g , 'i' ) ;
2022-08-10 11:26:08 +00:00
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 = [ ] ;
2022-12-07 03:42:39 +00:00
for ( const k of Object . keys ( this . structInfo . members ) ) {
a . push ( this . memberKey ( k ) ) ;
}
2022-08-10 11:26:08 +00:00
return a ;
} ) ;
const _ _utf8Decoder = new TextDecoder ( 'utf-8' ) ;
const _ _utf8Encoder = new TextEncoder ( ) ;
2022-08-13 13:42:07 +00:00
/ * * I n t e r n a l h e l p e r t o u s e i n o p e r a t i o n s w h i c h n e e d t o d i s t i n g u i s h
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 )
) ;
} ;
2022-08-10 11:26:08 +00:00
/ * *
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);
2022-08-13 13:42:07 +00:00
return ( addr === pos ) ? "" : _ _utf8Decode ( mem , addr , pos ) ;
2022-08-10 11:26:08 +00:00
} ;
/ * *
Adds value v to obj . ondispose , creating ondispose ,
or converting it to an array , if needed .
* /
2022-12-07 03:42:39 +00:00
const _ _addOnDispose = function ( obj , ... v ) {
2022-08-10 11:26:08 +00:00
if ( obj . ondispose ) {
2022-12-07 03:42:39 +00:00
if ( ! Array . isArray ( obj . ondispose ) ) {
2022-08-10 11:26:08 +00:00
obj . ondispose = [ obj . ondispose ] ;
2022-12-07 03:42:39 +00:00
}
2022-08-10 11:26:08 +00:00
} else {
obj . ondispose = [ ] ;
}
2022-12-07 03:42:39 +00:00
obj . ondispose . push ( ... v ) ;
2022-08-10 11:26:08 +00:00
} ;
/ * *
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 ( ) ;
2022-12-06 08:21:23 +00:00
//let i = 0;
//for( ; i < u.length; ++i ) h[mem + i] = u[i];
h . set ( u , mem ) ;
2022-08-10 11:26:08 +00:00
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 .
2022-12-06 08:21:23 +00:00
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 .
2022-08-10 11:26:08 +00:00
* /
const _ _setMemberCString = function ( obj , memberName , str ) {
const m = _ _lookupMember ( obj . structInfo , memberName , true ) ;
_ _affirmCStringSignature ( m ) ;
/ * P o t e n t i a l T O D O : i f o b j . o n d i s p o s e c o n t a i n s o b j [ m . k e y ] t h e n
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 ) ;
} )
} ) ;
2022-12-07 03:42:39 +00:00
// Function-type non-Property inherited members
Object . assign ( StructType . prototype , {
addOnDispose : function ( ... v ) {
_ _addOnDispose ( this , ... v ) ;
return this ;
}
} ) ;
2022-08-10 11:26:08 +00:00
/ * *
"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 . _ ) {
/ * c a c h e a l l a v a i l a b l e g e t t e r s / s e t t e r s / s e t - w r a p p e r s f o r
direct reuse in each accessor function . * /
f . _ = { getters : { } , setters : { } , sw : { } } ;
2022-12-05 05:30:03 +00:00
const a = [ 'i' , 'c' , 'C' , 'p' , 'P' , 's' , 'f' , 'd' , 'v()' ] ;
2022-10-03 08:30:22 +00:00
if ( bigIntEnabled ) a . push ( 'j' ) ;
2022-08-10 11:26:08 +00:00
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 * / ;
} ) ;
2022-12-05 05:30:03 +00:00
const rxSig1 = /^[ipPsjfdcC]$/ ,
rxSig2 = /^[vipPsjfdcC]\([ipPsjfdcC]*\)$/ ;
2022-08-10 11:26:08 +00:00
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 / e l s e i f / e l s e
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 ) ,
2022-12-05 05:30:03 +00:00
xPropName , '@' , this . pointer , '+' , descr . offset , 'sz' , descr . sizeof ) ;
2022-08-10 11:26:08 +00:00
}
let rc = (
2022-12-05 05:30:03 +00:00
new DataView ( heap ( ) . buffer , this . pointer + descr . offset , descr . sizeof )
2022-08-10 11:26:08 +00:00
) [ 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 ) ,
2022-12-05 05:30:03 +00:00
xPropName , '@' , this . pointer , '+' , descr . offset , 'sz' , descr . sizeof , v ) ;
2022-08-10 11:26:08 +00:00
}
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 + '.' ) ;
}
(
2022-12-05 05:30:03 +00:00
new DataView ( heap ( ) . buffer , this . pointer + descr . offset , descr . sizeof )
2022-08-10 11:26:08 +00:00
) [ 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 ) => {
2022-12-05 05:30:03 +00:00
// Sanity checks of sizeof/offset info...
2022-08-10 11:26:08 +00:00
const m = structInfo . members [ k ] ;
if ( ! m . sizeof ) toss ( structName , "member" , k , "is missing sizeof." ) ;
2022-12-05 05:45:00 +00:00
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.
2022-12-05 05:30:03 +00:00
if ( 0 !== ( m . sizeof % 4 ) ) {
2022-12-05 05:45:00 +00:00
console . warn ( "Invalid struct member description =" , m , "from" , structInfo ) ;
2022-12-05 05:30:03 +00:00
toss ( structName , "member" , k , "sizeof is not aligned. sizeof=" + m . sizeof ) ;
}
if ( 0 !== ( m . offset % 4 ) ) {
2022-12-05 05:45:00 +00:00
console . warn ( "Invalid struct member description =" , m , "from" , structInfo ) ;
2022-12-05 05:30:03 +00:00
toss ( structName , "member" , k , "offset is not aligned. offset=" + m . offset ) ;
}
2022-08-10 11:26:08 +00:00
}
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 )
/ * i f w e a s s i g n S t r u c t C t o r . p r o t o t y p e a n d d o n ' t d o
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*/ ;