char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
44fe2c6
main
1import { 2ArrayPrototypePush as push , 3FinalizationRegistry , 4getter , 5ObjectCreate , 6ObjectFreeze as freeze , 7ReflectApply as apply , 8ReflectConstruct as construct , 9Set , 10SetPrototypeAdd as retain , 11SetPrototypeDelete as release , 12SymbolIterator , 13TypeError , 14uncurryThis , 15WeakMap , 16WeakMapPrototypeGet as get , 17WeakMapPrototypeSet as set , 18} from "ext:core/primordials" ; 19import { enqueueTask } from "ext:core/tasks" ; 20import { promiseThen as then } from "ext:core/promises" ; 21import { 22op_port_adopt , 23op_port_close , 24op_port_move , 25op_port_pair , 26op_port_peer , 27op_port_send , 28op_port_take , 29op_port_wait , 30op_port_wait_ref , 31} from "ext:core/ops" ; 32import { array , dictionary , required , string , usv } from "./idl.js" ; 33import { 34addListener , 35dataCloneError , 36dispatch , 37Event , 38EventTarget , 39hasListener , 40isDispatching , 41observeListeners , 42preventDefault , 43removeListener , 44} from "./events.js" ; 45import { 46classify , 47decodeWire , 48registerTransferable , 49registerUncloneable , 50registerWire , 51transferCallbacks , 52} from "./serialization.js" ; 53 54const WeakRef = globalThis . WeakRef , 55deref = uncurryThis ( WeakRef . prototype . deref ); 56interface Endpoint { 57id :number ; 58epoch :number ; 59port :WeakRef < MessagePort > | null ; 60live :boolean ; 61started :boolean ; 62closed :boolean ; 63scheduled :boolean ; 64referenced :boolean ; 65onclose ?:() => void ; 66} 67interface PortState { 68endpoint :Endpoint | null ; 69detached :boolean ; 70onmessage :(( event :Event ) => unknown ) | null ; 71onmessageerror :(( event :Event ) => unknown ) | null ; 72messageListener :(( event :Event ) => void ) | null ; 73errorListener :(( event :Event ) => void ) | null ; 74} 75interface MessageEventState { 76data :unknown ; 77origin :string ; 78lastEventId :string ; 79source :MessagePort | null ; 80ports :readonly MessagePort []; 81} 82interface ChannelState { 83port1 :MessagePort ; 84port2 :MessagePort ; 85} 86const activePorts = new Set < MessagePort >(); 87const initEvent = uncurryThis ( Event . prototype . initEvent ); 88const registerFinalizer = uncurryThis ( FinalizationRegistry . prototype . register ); 89const unregisterFinalizer = uncurryThis ( 90FinalizationRegistry . prototype . unregister , 91); 92const portFinalizer = new FinalizationRegistry < number >(( id ) => 93op_port_close ( id ) 94); 95const ports = new WeakMap < object , PortState >(), 96messageEvents = new WeakMap < object , MessageEventState >(), 97channels = new WeakMap < object , ChannelState >(), 98secret = ObjectCreate ( null ); 99registerTransferable ( ports , preparePort , validatePort , commitPort ); 100registerWire ({ 101name :"MessagePort" , 102states :ports , 103encode :( current :unknown ) => ( current as PortState ). endpoint ?. id ?? null , 104decode :( id :unknown ) => adoptPort ( id as number | null ), 105prepare :( _current :unknown , source :unknown ) => { 106validatePort ( null , source ); 107return null ; 108}, 109validate :validatePort , 110commit :( _current :unknown , source :unknown ) => { 111const origin = state ( ports , source ); 112if ( origin . endpoint ) op_port_move ( origin . endpoint . id ); 113unregisterFinalizer ( portFinalizer , source as MessagePort ); 114origin . endpoint = null ; 115origin . detached = true ; 116retainPort ( source as MessagePort ); 117}, 118}); 119registerUncloneable ( messageEvents ); 120registerUncloneable ( channels ); 121 122function state < K extends object , V > ( map :WeakMap < K , V >, value :unknown ) :V { 123const result = get ( map , value ); 124if ( result === undefined ) throw new TypeError ( "Illegal invocation" ); 125return result ; 126} 127 128function port ( value :unknown ) :value isMessagePort { 129return get ( ports , value ) !== undefined ; 130} 131 132function freezePorts ( values :readonly MessagePort []) :readonly MessagePort [] { 133const result :MessagePort [] = []; 134for ( let i = 0 ; i < values . length ; i ++ ) push ( result , values [ i ]); 135return freeze ( result ); 136} 137 138function portList ( 139value :unknown , 140requirePorts :true , 141method ?:unknown , 142methodRead ?:boolean , 143) :MessagePort []; 144function portList ( 145value :unknown , 146requirePorts ?:false , 147method ?:unknown , 148methodRead ?:boolean , 149) :object []; 150function portList ( 151value :unknown , 152requirePorts = false , 153method :unknown = undefined , 154methodRead = false , 155) { 156if ( value === undefined ) return array < object >(); 157if ( value === null ) { 158throw new TypeError ( "Expected an iterable of MessagePorts" ); 159} 160if ( ! methodRead ) { 161method = ( value as Record < PropertyKey , unknown >)[ SymbolIterator ]; 162} 163if ( typeof method !== "function" ) { 164throw new TypeError ( "Expected an iterable of MessagePorts" ); 165} 166const iterator = apply ( 167method as ( ...args :never []) => unknown , 168value , 169[], 170) as Record < PropertyKey , unknown >; 171if ( 172iterator === null || 173( typeof iterator !== "object" && typeof iterator !== "function" ) 174) { 175throw new TypeError ( "Invalid transfer iterator" ); 176} 177const next = iterator . next ; 178if ( typeof next !== "function" ) { 179throw new TypeError ( "Invalid transfer iterator next method" ); 180} 181const result = array < object >(); 182while ( true ) { 183const step = apply ( 184next as ( ...args :never []) => unknown , 185iterator , 186[], 187) as Record < PropertyKey , unknown >; 188if ( 189step === null || ( typeof step !== "object" && typeof step !== "function" ) 190) { 191throw new TypeError ( "Invalid transfer iterator result" ); 192} 193if ( step . done ) return result ; 194const item = step . value ; 195if ( 196item === null || ( typeof item !== "object" && typeof item !== "function" ) 197) { 198throw new TypeError ( "Sequence entries must be objects" ); 199} 200if ( requirePorts && ! port ( item )) { 201throw new TypeError ( "Expected a MessagePort" ); 202} 203push ( result , item ); 204} 205} 206 207function transferList ( options :unknown ) :object [] { 208if ( options === undefined ) return array (); 209if ( 210options !== null && 211( typeof options === "object" || typeof options === "function" ) 212) { 213const method = ( options as Record < PropertyKey , unknown >)[ SymbolIterator ]; 214if ( typeof method === "function" ) { 215return portList ( options , false , method , true ); 216} 217} 218const value = dictionary ( options ). transfer ; 219return portList ( value ); 220} 221 222function preparePort ( _state :unknown , source :unknown ) :MessagePort { 223if ( state ( ports , source ). detached ) { 224throw dataCloneError ( "MessagePort has already been transferred" ); 225} 226return new MessagePort ( secret ); 227} 228 229function validatePort ( _state :unknown , source :unknown ) :void { 230if ( state ( ports , source ). detached ) { 231throw dataCloneError ( "MessagePort has already been transferred" ); 232} 233} 234 235function commitPort ( 236_state :unknown , 237source :unknown , 238destination :unknown , 239) :void { 240const origin = state ( ports , source ), target = state ( ports , destination ); 241target . endpoint = origin . endpoint ; 242target . detached = false ; 243if ( origin . endpoint ) { 244op_port_move ( origin . endpoint . id ); 245origin . endpoint . epoch = op_port_adopt ( origin . endpoint . id ); 246origin . endpoint . port = new WeakRef ( destination as MessagePort ); 247origin . endpoint . started = false ; 248origin . endpoint . scheduled = false ; 249registerFinalizer ( 250portFinalizer , 251destination as MessagePort , 252origin . endpoint . id , 253destination as MessagePort , 254); 255} 256unregisterFinalizer ( portFinalizer , source as MessagePort ); 257origin . endpoint = null ; 258origin . detached = true ; 259retainPort ( source as MessagePort ); 260retainPort ( destination as MessagePort ); 261} 262 263function retainPort ( value :object ) :void { 264const endpoint = state ( ports , value ). endpoint ; 265if ( 266endpoint && endpoint . live && endpoint . started && ! endpoint . closed && 267( endpoint . referenced || hasListener ( value , "message" ) || 268hasListener ( value , "messageerror" )) 269) { 270retain ( activePorts , value as MessagePort ); 271} else release ( activePorts , value as MessagePort ); 272} 273 274function schedule ( endpoint :Endpoint | null ) :void { 275if ( ! endpoint || endpoint . closed || ! endpoint . started || endpoint . scheduled ) { 276return ; 277} 278endpoint . scheduled = true ; 279const epoch = endpoint . epoch ; 280const wait = endpoint . referenced ?op_port_wait_ref :op_port_wait ; 281then ( wait ([ endpoint . id , epoch ]), ( ready ) => { 282if ( endpoint . epoch !== epoch ) return ; 283endpoint . scheduled = false ; 284if ( ! ready ) { 285endpoint . live = false ; 286const target = endpoint . port && deref ( endpoint . port ); 287if ( target ) retainPort ( target ); 288endpoint . onclose ?.(); 289return ; 290} 291if ( endpoint . closed || ! endpoint . started ) return ; 292enqueueTask (() => { 293if ( endpoint . epoch !== epoch || endpoint . closed || ! endpoint . started ) { 294return ; 295} 296const target = endpoint . port && deref ( endpoint . port ); 297if ( ! target ) { 298endpoint . closed = true ; 299op_port_close ( endpoint . id ); 300return ; 301} 302let packet ; 303try { 304packet = op_port_take ( endpoint . id , epoch , decodeWire , dataCloneError ); 305} catch { 306if ( target ) { 307dispatch ( 308target , 309new MessageEvent ( "messageerror" , ObjectCreate ( null )), 310); 311} 312return ; 313} 314if ( packet !== null && target ) { 315dispatchMessage ( target , packet . data , packet . ports ); 316} 317}, () => { 318schedule ( endpoint ); 319return true ; 320}); 321}); 322} 323 324function adoptPort ( id :number | null ) :MessagePort { 325const result = new MessagePort ( secret ); 326if ( id !== null ) { 327registerFinalizer ( portFinalizer , result , id , result ); 328state ( ports , result ). endpoint = { 329 id, 330epoch :op_port_adopt ( id ), 331port :new WeakRef ( result ), 332live :true , 333started :false , 334closed :false , 335scheduled :false , 336referenced :false , 337}; 338} 339return result ; 340} 341 342function startPort ( value :MessagePort , referenced = false ) :void { 343const endpoint = state ( ports , value ). endpoint ; 344if ( endpoint && ! endpoint . closed ) { 345endpoint . referenced = referenced ; 346endpoint . started = true ; 347retainPort ( value ); 348schedule ( endpoint ); 349} 350} 351 352function dispatchMessage ( 353target :object , 354data :unknown , 355eventPorts :readonly MessagePort [] = [], 356) :void { 357const event = new MessageEvent ( "message" , ObjectCreate ( null )); 358set ( messageEvents , event , { 359 data, 360origin :"" , 361lastEventId :"" , 362source :null , 363ports :freezePorts ( eventPorts ), 364}); 365dispatch ( target , event ); 366} 367 368class MessageEvent extends Event { 369// @ts-expect-error Reflect.construct preserves new.target without a guest-replaceable super(). 370constructor ( type :unknown , init :unknown = {}) { 371required ( arguments . length , 1 ); 372type = string ( type ); 373const options = dictionary ( init ); 374const self = construct ( Event , [ type , options ], new . target ); 375const rawData = options . data , 376rawOrigin = options . origin , 377rawId = options . lastEventId , 378rawSource = options . source ; 379const data = rawData === undefined ?null :rawData ; 380const origin = rawOrigin === undefined ?"" :usv ( rawOrigin ); 381const lastEventId = rawId === undefined ?"" :string ( rawId ); 382const source = rawSource === undefined ?null :rawSource ; 383if ( source !== null && ! port ( source )) { 384throw new TypeError ( "Expected a MessagePort or null" ); 385} 386const eventPorts = portList ( options . ports , true ); 387set ( messageEvents , self , { 388 data, 389 origin, 390 lastEventId, 391 source, 392ports :freezePorts ( eventPorts ), 393}); 394return self as MessageEvent ; 395} 396get data () { 397return state ( messageEvents , this ). data ; 398} 399get origin () { 400return state ( messageEvents , this ). origin ; 401} 402get lastEventId () { 403return state ( messageEvents , this ). lastEventId ; 404} 405get source () { 406return state ( messageEvents , this ). source ; 407} 408get ports () { 409return state ( messageEvents , this ). ports ; 410} 411initMessageEvent ( 412type :unknown , 413bubbles :unknown = false , 414cancelable :unknown = false , 415data :unknown = null , 416origin :unknown = "" , 417lastEventId :unknown = "" , 418source :unknown = null , 419eventPorts :unknown = [], 420) { 421required ( arguments . length , 1 ); 422type = string ( type ); 423bubbles = !! bubbles ; 424cancelable = !! cancelable ; 425origin = string ( origin ); 426lastEventId = string ( lastEventId ); 427if ( source !== null && ! port ( source )) { 428throw new TypeError ( "Expected a MessagePort or null" ); 429} 430eventPorts = portList ( eventPorts , true ); 431if ( isDispatching ( this )) return ; 432initEvent ( this , type , bubbles , cancelable ); 433const current = state ( messageEvents , this ); 434current . data = data ; 435current . origin = origin as string ; 436current . lastEventId = lastEventId as string ; 437current . source = source ; 438current . ports = freezePorts ( eventPorts as MessagePort []); 439} 440} 441 442class MessagePort extends EventTarget { 443// @ts-expect-error Reflect.construct preserves new.target without a guest-replaceable super(). 444constructor ( key :unknown = undefined ) { 445if ( key !== secret ) throw new TypeError ( "Illegal constructor" ); 446const self = construct ( EventTarget , [], new . target ) as MessagePort ; 447set ( ports , self , { 448endpoint :null , 449detached :false , 450onmessage :null , 451onmessageerror :null , 452messageListener :null , 453errorListener :null , 454}); 455observeListeners ( self , retainPort ); 456return self as MessagePort ; 457} 458get onmessage () { 459return state ( ports , this ). onmessage ; 460} 461set onmessage ( value ) { 462const current = state ( ports , this ); 463value = typeof value === "function" ?value :null ; 464current . onmessage = value ; 465if ( ! value ) { 466if ( current . messageListener ) { 467removeListener ( this , "message" , current . messageListener ); 468} 469current . messageListener = null ; 470} else { 471if ( ! current . messageListener ) { 472current . messageListener = ( event :Event ) => { 473if ( apply ( current . onmessage ! , this , [ event ]) === false ) { 474preventDefault ( event ); 475} 476}; 477addListener ( this , "message" , current . messageListener ); 478} 479const endpoint = current . endpoint ; 480if ( endpoint && ! endpoint . closed ) { 481endpoint . started = true ; 482retainPort ( this ); 483schedule ( endpoint ); 484} 485} 486} 487 488get onmessageerror () { 489return state ( ports , this ). onmessageerror ; 490} 491set onmessageerror ( value ) { 492const current = state ( ports , this ); 493value = typeof value === "function" ?value :null ; 494current . onmessageerror = value ; 495if ( ! value ) { 496if ( current . errorListener ) { 497removeListener ( this , "messageerror" , current . errorListener ); 498} 499current . errorListener = null ; 500} else if ( ! current . errorListener ) { 501current . errorListener = ( event ) => { 502if ( apply ( current . onmessageerror ! , this , [ event ]) === false ) { 503preventDefault ( event ); 504} 505}; 506addListener ( this , "messageerror" , current . errorListener ); 507} 508} 509 510postMessage ( data :unknown , options :unknown = undefined ) :void { 511required ( arguments . length , 1 ); 512const transfer = transferList ( options ); 513const current = state ( ports , this ), endpoint = current . endpoint ; 514const peer = endpoint ?op_port_peer ( endpoint . id ) :null ; 515let doomed = false ; 516const messagePorts = array < MessagePort >(); 517for ( let i = 0 ; i < transfer . length ; i ++ ) { 518if ( transfer [ i ] === this ) { 519throw dataCloneError ( "A MessagePort cannot be transferred to itself" ); 520} 521if ( port ( transfer [ i ])) { 522if ( state ( ports , transfer [ i ]). endpoint ?. id === peer ) doomed = true ; 523push ( messagePorts , transfer [ i ] as MessagePort ); 524} 525} 526const callbacks = transferCallbacks ( transfer , dataCloneError , true ); 527try { 528callbacks . prepare (); 529op_port_send ( 530doomed ?null :endpoint ?. id ?? null , 531{ data, ports :messagePorts }, 532transfer , 533classify , 534callbacks . encodeHost , 535dataCloneError , 536callbacks . validate , 537callbacks . commit , 538); 539} finally { 540callbacks . clear (); 541} 542} 543start () { 544startPort ( this ); 545} 546close () { 547const current = state ( ports , this ), endpoint = current . endpoint ; 548if ( ! endpoint ) return ; 549endpoint . closed = true ; 550endpoint . port = null ; 551op_port_close ( endpoint . id ); 552unregisterFinalizer ( portFinalizer , this ); 553current . endpoint = null ; 554retainPort ( this ); 555} 556} 557 558class MessageChannel { 559constructor () { 560const ids = op_port_pair (); 561const port1 = adoptPort ( ids [ 0 ]), port2 = adoptPort ( ids [ 1 ]); 562set ( channels , this , { port1, port2}); 563} 564get port1 () { 565return state ( channels , this ). port1 ; 566} 567get port2 () { 568return state ( channels , this ). port2 ; 569} 570} 571 572const messageData = getter ( MessageEvent . prototype , "data" ); 573const messagePorts = getter ( MessageEvent . prototype , "ports" ); 574 575function setPortCloseHandler ( value :MessagePort , callback :() => void ) :void { 576const endpoint = state ( ports , value ). endpoint ; 577if ( endpoint ) endpoint . onclose = callback ; 578} 579function referencePort ( value :MessagePort ) :Promise < boolean > { 580const endpoint = state ( ports , value ). endpoint ; 581return op_port_wait_ref ([ endpoint ! . id , endpoint ! . epoch ]); 582} 583export { 584adoptPort , 585dispatchMessage , 586MessageChannel , 587messageData , 588MessageEvent , 589MessagePort , 590messagePorts , 591port as isMessagePort , 592referencePort , 593setPortCloseHandler , 594startPort , 595transferList , 596};