[Git][ghc/ghc][wip/jsbits-userguide] JS/userguide: wip explanation of writing jsbits 2

Josh Meredith (@JoshMeredith) gitlab at gitlab.haskell.org
Tue Sep 19 13:03:34 UTC 2023



Josh Meredith pushed to branch wip/jsbits-userguide at Glasgow Haskell Compiler / GHC


Commits:
16ac5ac4 by Josh Meredith at 2023-09-19T23:03:22+10:00
JS/userguide: wip explanation of writing jsbits 2

- - - - -


1 changed file:

- docs/users_guide/javascript.rst


Changes:

=====================================
docs/users_guide/javascript.rst
=====================================
@@ -191,7 +191,7 @@ to negate this purpose.
 
 Instead, it is more effective for a library to provide an alternate
 implementation for functions using the C FFI - either by providing direct
-one-to-one replacement JavaScript functions, or by using C-preprocessor
+one-to-one replacement JavaScript functions, or by using C preprocessor
 directives to replace C FFI imports with some combination of JS FFI imports
 and pure-Haskell implementation.
 
@@ -231,7 +231,7 @@ C types used:
 * pointer values, including ``CString``, are passed as an unboxed ``(ptr, offset)``
   pair. For arguments, being unboxed will mean these are passed as two top-level
   arguments to the function. For return values, unboxed values are returned using
-  a special C-preprocessor macro, ``RETURN_UBX_TUP2(ptr, offset)``
+  a special C preprocessor macro, ``RETURN_UBX_TUP2(ptr, offset)``
 
 * ``CString``, in addition to the above pointer handling, will need to be decoded
   and encoded to convert them between character arrays and JavaScript strings.
@@ -280,16 +280,75 @@ Next, the JavaScript ``h$getcwd`` function demonstrates a several details:
   past the end of the buffer
 
 * Lastly, the newly allocated buffer is returned to fulfill the behaviour expected by the
-  C function. This is done by ``RETURN_UBX_TUP2(x, y)``, which is a C-preprocessor
+  C function. This is done by ``RETURN_UBX_TUP2(x, y)``, which is a C preprocessor
   macro that expands to place the second value in a special variable before ``return``-ing
   the first value. Because it expands into a return statement, ``RETURN_UBX_TUP2`` can
   be used for control flow as expected
 
-* To use C-preprocessor macros in linked JavaScript files, the file must open with the
+* To use C preprocessor macros in linked JavaScript files, the file must open with the
   ``//#OPTIONS: CPP`` line, as is shown towards the start of this snippet
 
 * If an error occurs, the catch clause will pass it to ``h$setErrno`` and return -1 - which
   is a behaviour expected by the JavaScript backend.
 
-Using the C-preprocessor to replace C FFI imports
+Writing JavaScript Functions to be NodeJS and Browser Aware
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In the above example of implmeneting ``getcwd``, the function we use in the JavaScript
+implementation is from NodeJS, and the behaviour doesn't make sense to implement in a
+browser. Therefore, the actual implementation will include a C preprocessor condition
+to check if we're compiling for the browser, in which case ``h$unsupported(-1)`` will
+be called. There can be multiple non-browser JavaScript runtimes, so we'll also have
+to check at runtime to make sure that NodeJS is in use.
+
+.. code-block:: javascript
+
+  function h$getcwd(buf, off, buf_size) {
+  #ifndef GHCJS_BROWSER
+    if (h$isNode()) {
+      try {
+        var cwd = h$encodeUtf8(process.cwd());
+        h$copyMutableByteArray(cwd, 0, buf, off, Math.min(cwd.len, buf_size));
+        RETURN_UBX_TUP2(cwd, 0);
+      } catch (e) {
+        h$setErrno(e);
+        return -1;
+      }
+    } else
+  #endif
+      h$unsupported(-1);
+  }
+
+Using the C Preprocessor to Replace C FFI Imports
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Instead of providing a direct JavaScript implementation for each C FFI import, we can
+instead use the C preprocessor to conditionally remove these C imports (and possibly
+use sites as well). Then, some combination of JavaScript FFI imports and Haskell
+implementation can be added instead. As in the direct implementation section, any
+linked JavaScript files should usually be in a ``if arch(javascript)`` condition in
+the cabal file.
+
+As an example of a mixed Haskell and JavaScript implementation replacing a C
+implementation, consider ``base:GHC.Clock``:
+
+.. code-block:: haskell
+
+  #if defined(javascript_HOST_ARCH)
+  getMonotonicTimeNSec :: IO Word64
+  getMonotonicTimeNSec = do
+    w <- getMonotonicTimeMSec
+    return (floor w * 1000000)
+
+  foreign import javascript unsafe "performance.now"
+    getMonotonicTimeMSec :: IO Double
+
+  #else
+  foreign import ccall unsafe "getMonotonicNSec"
+    getMonotonicTimeNSec :: IO Word64
+  #endif
+
+Here, the ``getMonotonicTimeNSec`` C FFI import is replaced by the JavaScript FFI
+import ``getMonotonicTimeMSec``. However, because the JavaScript implementation
+returns the time as a ``Double`` of floating point seconds, it must be wrapped by
+a Haskell function to extract the integral value that's expected.



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/16ac5ac4d115416c743382a430d5c05c384f7c5c

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/16ac5ac4d115416c743382a430d5c05c384f7c5c
You're receiving this email because of your account on gitlab.haskell.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20230919/78bfd435/attachment-0001.html>


More information about the ghc-commits mailing list