[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