{-# LANGUAGE MagicHash #-}
{-# LANGUAGE StrictData #-}
{-# LANGUAGE UnboxedTuples #-}

module Language.JavaScript.Inline.Core.JSVal where

import Foreign
import GHC.Exts
import GHC.Types

-- $jsval-notes
--
-- Lifecycle of a 'JSVal':
--   1. If the 'returnType' is specified as 'RawJSVal', the eval server makes
--      a new 'JSVal' out of the return value and sends it back.
--   2. The 'JSVal' values can be passed around and later put into a 'JSExpr'
--      for evaluation.
--   3. When a 'JSVal' is garbage collected on the client side, the finalizer is
--      called which frees it on the eval server side.
--
-- Notes to keep in mind:
--   1. When putting 'JSVal's into a 'JSExpr', ensure the value is used
--      synchronously, as opposed to being used in the function body of a
--      callback. Otherwise, by the time it's actually used, it may have already
--      been freed.
--   2. The finalizer sends a 'JSVal' free message to the eval server. Ensure
--      the free message doesn't appear before the eval message which uses the
--      'JSVal' in the send queue.
--   3. There is no response message for the 'JSVal' free message. Freeing or
--      using a non-existent 'JSVal' should result in a fatal error.

-- | An opaque reference of a JavaScript value. Each 'JSVal' is registered with
-- a finalizer which frees the reference on the @node@ side when it's garbage
-- collected in Haskell. It's also possible to manually free a 'JSVal' using
-- 'freeJSVal'.
data JSVal
  = JSVal Word64 (MutVar# RealWorld ()) (Weak# ())

instance Show JSVal where
  show :: JSVal -> String
show (JSVal Word64
i MutVar# RealWorld ()
_ Weak# ()
_) = String
"JSVal " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Word64 -> String
forall a. Show a => a -> String
show Word64
i

newJSVal :: Bool -> Word64 -> IO () -> IO JSVal
newJSVal :: Bool -> Word64 -> IO () -> IO JSVal
newJSVal Bool
gc Word64
i (IO State# RealWorld -> (# State# RealWorld, () #)
f) = (State# RealWorld -> (# State# RealWorld, JSVal #)) -> IO JSVal
forall a. (State# RealWorld -> (# State# RealWorld, a #)) -> IO a
IO ((State# RealWorld -> (# State# RealWorld, JSVal #)) -> IO JSVal)
-> (State# RealWorld -> (# State# RealWorld, JSVal #)) -> IO JSVal
forall a b. (a -> b) -> a -> b
$ \State# RealWorld
s0 -> case ()
-> State# RealWorld -> (# State# RealWorld, MutVar# RealWorld () #)
forall a d. a -> State# d -> (# State# d, MutVar# d a #)
newMutVar# () State# RealWorld
s0 of
  (# State# RealWorld
s1, MutVar# RealWorld ()
v #) ->
    if Bool
gc
      then case MutVar# RealWorld ()
-> ()
-> (State# RealWorld -> (# State# RealWorld, () #))
-> State# RealWorld
-> (# State# RealWorld, Weak# () #)
forall a b c.
a
-> b
-> (State# RealWorld -> (# State# RealWorld, c #))
-> State# RealWorld
-> (# State# RealWorld, Weak# b #)
mkWeak# MutVar# RealWorld ()
v () State# RealWorld -> (# State# RealWorld, () #)
f State# RealWorld
s1 of
        (# State# RealWorld
s2, Weak# ()
w #) -> (# State# RealWorld
s2, Word64 -> MutVar# RealWorld () -> Weak# () -> JSVal
JSVal Word64
i MutVar# RealWorld ()
v Weak# ()
w #)
      else case ()
-> ()
-> (State# RealWorld -> (# State# RealWorld, () #))
-> State# RealWorld
-> (# State# RealWorld, Weak# () #)
forall a b c.
a
-> b
-> (State# RealWorld -> (# State# RealWorld, c #))
-> State# RealWorld
-> (# State# RealWorld, Weak# b #)
mkWeak# () () State# RealWorld -> (# State# RealWorld, () #)
f State# RealWorld
s1 of
        (# State# RealWorld
s2, Weak# ()
w #) -> (# State# RealWorld
s2, Word64 -> MutVar# RealWorld () -> Weak# () -> JSVal
JSVal Word64
i MutVar# RealWorld ()
v Weak# ()
w #)

unsafeUseJSVal :: JSVal -> Word64
unsafeUseJSVal :: JSVal -> Word64
unsafeUseJSVal (JSVal Word64
i MutVar# RealWorld ()
_ Weak# ()
_) = Word64
i

freeJSVal :: JSVal -> IO ()
freeJSVal :: JSVal -> IO ()
freeJSVal (JSVal Word64
_ MutVar# RealWorld ()
_ Weak# ()
w) = (State# RealWorld -> (# State# RealWorld, () #)) -> IO ()
forall a. (State# RealWorld -> (# State# RealWorld, a #)) -> IO a
IO ((State# RealWorld -> (# State# RealWorld, () #)) -> IO ())
-> (State# RealWorld -> (# State# RealWorld, () #)) -> IO ()
forall a b. (a -> b) -> a -> b
$ \State# RealWorld
s0 -> case Weak# ()
-> State# RealWorld
-> (# State# RealWorld, Int#,
      State# RealWorld -> (# State# RealWorld, () #) #)
forall a b.
Weak# a
-> State# RealWorld
-> (# State# RealWorld, Int#,
      State# RealWorld -> (# State# RealWorld, b #) #)
finalizeWeak# Weak# ()
w State# RealWorld
s0 of
  (# State# RealWorld
s1, Int#
0#, State# RealWorld -> (# State# RealWorld, () #)
_ #) -> (# State# RealWorld
s1, () #)
  (# State# RealWorld
s1, Int#
_, State# RealWorld -> (# State# RealWorld, () #)
f #) -> State# RealWorld -> (# State# RealWorld, () #)
f State# RealWorld
s1