Matthew Pickering pushed to branch wip/wasm-bytearray-marshall at Glasgow Haskell Compiler / GHC

Commits:

3 changed files:

Changes:

  • libraries/ghc-experimental/ghc-experimental.cabal.in
    ... ... @@ -40,6 +40,7 @@ library
    40 40
           Prelude.Experimental
    
    41 41
         if arch(wasm32)
    
    42 42
             exposed-modules:  GHC.Wasm.Prim
    
    43
    +                          GHC.Wasm.Uint8Array
    
    43 44
         other-extensions:
    
    44 45
         build-depends:    base >=4.20 && < 4.22,
    
    45 46
                           ghc-internal == @ProjectVersionForLib@.*
    

  • libraries/ghc-experimental/src/GHC/Wasm/Uint8Array.hs
    1
    +{-# LANGUAGE MagicHash #-}
    
    2
    +{-# LANGUAGE UnboxedTuples #-}
    
    3
    +-- | Utilities to marshall a javascript 'Uint8Array' to a 'ByteArray'
    
    4
    +module GHC.Wasm.Uint8Array
    
    5
    +  ( toJSUint8Array, fromJSUint8Array, JSUint8Array(..), byteLength
    
    6
    +  ) where
    
    7
    +
    
    8
    +import Data.Array.Byte
    
    9
    +import GHC.Wasm.Prim
    
    10
    +import GHC.Ptr
    
    11
    +import GHC.Exts
    
    12
    +import System.IO.Unsafe
    
    13
    +import GHC.IO
    
    14
    +
    
    15
    +newtype JSUint8Array = JSUint8Array { unJSUint8Array :: JSVal }
    
    16
    +
    
    17
    +foreign import javascript unsafe
    
    18
    +  "$1.byteLength"
    
    19
    +  byteLength :: JSUint8Array -> Int
    
    20
    +
    
    21
    +withByteArrayContents :: ByteArray -> (Ptr () -> Int -> IO a) -> IO a
    
    22
    +withByteArrayContents (ByteArray arr#) f = IO $ \s0 ->
    
    23
    +  keepAlive# arr# s0 (unIO $ f (Ptr (byteArrayContents# arr#)) (I# (sizeofByteArray# arr#) ))
    
    24
    +
    
    25
    +fromJSUint8ArrayLen :: Int -> JSUint8Array -> ByteArray
    
    26
    +fromJSUint8ArrayLen (I# len) arr = unsafePerformIO $ IO $
    
    27
    +  \s -> case newByteArray# len s of
    
    28
    +    (# s', mb #) ->
    
    29
    +      let a = mutableByteArrayContents# mb
    
    30
    +      in case unIO (memorySetUint8Array (Ptr a) (I# len) arr) s' of
    
    31
    +            (# s'', () #) ->  case unsafeFreezeByteArray# mb s'' of
    
    32
    +                                (# s''', arr' #) -> (# s''', ByteArray arr' #)
    
    33
    +
    
    34
    +toJSUint8Array :: ByteArray -> JSUint8Array
    
    35
    +toJSUint8Array ba =
    
    36
    +  unsafePerformIO $ withByteArrayContents ba uint8ArrayFromMemory
    
    37
    +
    
    38
    +fromJSUint8Array :: JSUint8Array -> ByteArray
    
    39
    +fromJSUint8Array src_buf = fromJSUint8ArrayLen (byteLength src_buf) src_buf
    
    40
    +
    
    41
    +foreign import javascript unsafe
    
    42
    +  "(new Uint8Array(__exports.memory.buffer, $1, $2)).set($3)"
    
    43
    +  memorySetUint8Array :: Ptr a -> Int -> JSUint8Array -> IO ()
    
    44
    +
    
    45
    +foreign import javascript unsafe
    
    46
    +  "new Uint8Array(new Uint8Array(__exports.memory.buffer, $1, $2))"
    
    47
    +  uint8ArrayFromMemory :: Ptr a -> Int -> IO JSUint8Array
    
    48
    +

  • testsuite/tests/jsffi/textconv.hs
    ... ... @@ -14,36 +14,24 @@ import qualified Data.Text.IO.Utf8 as T
    14 14
     import GHC.Exts
    
    15 15
     import GHC.IO
    
    16 16
     import GHC.Wasm.Prim
    
    17
    -
    
    18
    -newtype JSUint8Array = JSUint8Array JSVal
    
    17
    +import GHC.Wasm.Uint8Array
    
    19 18
     
    
    20 19
     foreign import javascript unsafe "(new TextEncoder()).encode($1)"
    
    21
    -  js_str_encode :: JSString -> IO JSUint8Array
    
    22
    -
    
    23
    -foreign import javascript unsafe "$1.byteLength"
    
    24
    -  js_buf_len :: JSUint8Array -> IO Int
    
    25
    -
    
    26
    -foreign import javascript unsafe "(new Uint8Array(__exports.memory.buffer, $2, $1.byteLength)).set($1)"
    
    27
    -  js_from_buf :: JSUint8Array -> Ptr a -> IO ()
    
    20
    +  js_str_encode :: JSString -> JSUint8Array
    
    28 21
     
    
    29
    -foreign import javascript unsafe "(new TextDecoder('utf-8', {fatal: true})).decode(new Uint8Array(__exports.memory.buffer, $1, $2))"
    
    30
    -  js_to_str :: Ptr a -> Int -> IO JSString
    
    22
    +foreign import javascript unsafe "(new TextDecoder('utf-8', {fatal: true})).decode($1)"
    
    23
    +  js_to_str :: JSUint8Array -> JSString
    
    31 24
     
    
    32 25
     textFromJSString :: JSString -> Text
    
    33
    -textFromJSString str = unsafeDupablePerformIO $ do
    
    34
    -  buf <- js_str_encode str
    
    35
    -  I# len# <- js_buf_len buf
    
    36
    -  IO $ \s0 -> case newByteArray# len# s0 of
    
    37
    -    (# s1, mba# #) -> case unIO (js_from_buf buf (Ptr (mutableByteArrayContents# mba#))) s1 of
    
    38
    -      (# s2, _ #) -> case unIO (freeJSVal (coerce buf)) s2 of
    
    39
    -        (# s3, _ #) -> case unsafeFreezeByteArray# mba# s3 of
    
    40
    -          (# s4, ba# #) -> (# s4, Text (ByteArray ba#) 0 (I# len#) #)
    
    26
    +textFromJSString str =
    
    27
    +  let buf = js_str_encode str
    
    28
    +  in Text (fromJSUint8Array buf) 0 (byteLength buf)
    
    41 29
     
    
    30
    +-- NOTE: Do not copy this into your program, you need to take into account the
    
    31
    +-- offset to do this converson properly
    
    42 32
     textToJSString :: Text -> JSString
    
    43
    -textToJSString (Text (ByteArray ba#) (I# off#) (I# len#)) = unsafeDupablePerformIO $
    
    44
    -  IO $ \s0 -> case newPinnedByteArray# len# s0 of
    
    45
    -    (# s1, mba# #) -> case copyByteArray# ba# off# mba# 0# len# s1 of
    
    46
    -      s2 -> keepAlive# mba# s2 $ unIO $ js_to_str (Ptr (mutableByteArrayContents# mba#)) $ I# len#
    
    33
    +textToJSString (Text ba 0 len) = js_to_str (toJSUint8Array ba)
    
    34
    +textToJSString _ = error "non-zero offset not supported"
    
    47 35
     
    
    48 36
     foreign export javascript "main sync"
    
    49 37
       main :: IO ()