github.com/primecitizens/pcz/std@v0.2.1/ffi/js/async/ffi_bindings.ts (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright 2023 The Prime Citizens 3 4 import { Application, CallbackFunc, Pointer, heap, importModule } from "@ffi"; 5 6 importModule("ffi/js/async", (A: Application) => { 7 interface PromiseExt { 8 synchronous?: boolean; 9 ok?: boolean; 10 err?: boolean; 11 data?: any; 12 } 13 14 const EXT_PROP = "_ext_"; 15 16 return { 17 "promise": (fn: heap.Ref<CallbackFunc>): heap.Ref<Promise<any>> => { 18 const executor = A.H.get<CallbackFunc>(fn); 19 return A.H.push( 20 new Promise<any>((resolve, reject) => { 21 executor(this, resolve, reject); 22 }) 23 ); 24 }, 25 "bg": (ref: heap.Ref<Promise<any>>): heap.Ref<boolean> => { 26 const p = A.H.get<Promise<any>>(ref); 27 if (!p) { 28 return A.H.FALSE; 29 } 30 31 if (typeof p[EXT_PROP] === "object") { 32 return A.H.TRUE; 33 } 34 35 const ext: PromiseExt = { 36 ok: false, 37 err: false, 38 data: undefined, 39 }; 40 41 A.H.replace( 42 ref, 43 Object.defineProperty( 44 p.then( 45 (value: any): any => { 46 if (!ext.err && typeof ext.data === "undefined") { 47 ext.data = value; 48 ext.ok = true; 49 } 50 return value; 51 }, 52 (reason: any): any => { 53 if (!ext.ok && typeof ext.data === "undefined") { 54 ext.data = reason; 55 ext.err = true; 56 } 57 return reason; 58 } 59 ), 60 EXT_PROP, 61 { 62 get: (): PromiseExt => { 63 return ext; 64 }, 65 } 66 ) 67 ); 68 69 return A.H.TRUE; 70 }, 71 "resolved": (take: heap.Ref<boolean>, data: heap.Ref<any>): heap.Ref<Promise<any>> => { 72 return A.H.push(Promise.resolve(take === A.H.TRUE ? A.H.take(data) : A.H.get(data))); 73 }, 74 "rejected": (take: heap.Ref<boolean>, reason: heap.Ref<any>): heap.Ref<Promise<any>> => { 75 return A.H.push(Promise.reject(take === A.H.TRUE ? A.H.take(reason) : A.H.get(reason))); 76 }, 77 "allok": (take: heap.Ref<boolean>, ptr: Pointer, n: number): heap.Ref<Promise<any[]>> => { 78 return A.H.push(Promise.all(A.load.Refs(ptr, n, take === A.H.TRUE))); 79 }, 80 "all": (free: heap.Ref<boolean>, ptr: Pointer, n: number): heap.Ref<Promise<PromiseSettledResult<any>[]>> => { 81 return A.H.push(Promise.allSettled<any[]>(A.load.Refs(ptr, n, free === A.H.TRUE))); 82 }, 83 "first": (free: heap.Ref<boolean>, ptr: Pointer, n: number): heap.Ref<Promise<any>> => { 84 return A.H.push(Promise.race(A.load.Refs(ptr, n, free === A.H.TRUE))); 85 }, 86 "then": ( 87 p: heap.Ref<Promise<any>>, 88 onfulfilled: heap.Ref<(value: any) => any>, 89 onrejected: heap.Ref<(reason: any) => any>, 90 onfinally: heap.Ref<() => void> 91 ): void => { 92 let x = A.H.get<Promise<any>>(p).then( 93 onfulfilled !== A.H.UNDEFINED ? A.H.get(onfulfilled) : null, 94 onrejected !== A.H.UNDEFINED ? A.H.get(onrejected) : null 95 ); 96 if (onfinally !== A.H.UNDEFINED) { 97 // available since ES2018, supported by all browsers but IE and Opera Mini. 98 x = x.finally(A.H.get(onfinally)); 99 } 100 A.H.replace(p, x); 101 }, 102 "shouldwait": ( 103 take: heap.Ref<boolean>, 104 ref: heap.Ref<Promise<any>>, 105 pFulfilled: Pointer, 106 pRef: Pointer 107 ): heap.Ref<boolean> => { 108 const promise = take === A.H.TRUE ? A.H.take<Promise<any>>(ref) : A.H.get<Promise<any>>(ref); 109 if (!promise) { 110 return A.H.FALSE; 111 } 112 113 let ext = promise[EXT_PROP] as PromiseExt; 114 if (typeof ext !== "undefined") { 115 // is a background promise, check whether finished 116 if (ext.ok) { 117 A.store.Bool(pFulfilled, true); 118 A.store.Ref(pRef, ext.data); 119 return A.H.FALSE; // do not wait and do not call "takewaited" 120 } 121 122 if (ext.err) { 123 A.store.Ref(pRef, ext.data); 124 return A.H.FALSE; // do not wait 125 } 126 } 127 128 // is a plain/unfinished promise so we have no idea whether 129 // the promise has been resolved, attach callbacks to check. 130 ext = ext ? ext : {}; 131 132 // defensive: ensure not waiting for promise callback that has 133 // fired synchronously 134 ext.synchronous = true; 135 136 promise.then( 137 (value: any): any => { 138 ext.data = value; 139 ext.ok = true; 140 if (ext.synchronous) { 141 ext.synchronous = false; 142 } else { 143 A.call(0, 0); 144 } 145 return value; 146 }, 147 (reason: any): any => { 148 ext.data = reason; 149 ext.err = true; 150 151 if (ext.synchronous) { 152 ext.synchronous = false; 153 } else { 154 A.call(0, 0); 155 } 156 return reason; 157 } 158 ); 159 if (ext.synchronous) { 160 A.store.Ref(pRef, ext); 161 ext.synchronous = false; 162 return A.H.TRUE; // need to wait and call "takewaited" 163 } 164 165 if (ext.ok) { 166 A.store.Bool(pFulfilled, true); 167 A.store.Ref(pRef, ext.data); 168 return A.H.FALSE; // do not wait and do not call "takewaited" 169 } 170 171 if (ext.err) { 172 A.store.Ref(pRef, ext.data); 173 return A.H.FALSE; // do not wait and do not call "takewaited" 174 } 175 176 throw new Error("unreachable"); 177 }, 178 "takewaited": (ref: heap.Ref<PromiseExt>, pDataRef: Pointer): heap.Ref<boolean> => { 179 const ext = A.H.take<PromiseExt>(ref); 180 if (!ext.ok && !ext.err) { 181 throw new Error("unexpected state: promise unfinished"); 182 } 183 184 A.store.Ref(pDataRef, ext.data); 185 return ext.ok ? A.H.TRUE : A.H.FALSE; 186 }, 187 }; 188 });