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  });