content: {-# LANGUAGE CPP #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE FlexibleInstances #-}
  {-# LANGUAGE TypeSynonymInstances #-}
  {-# LANGUAGE LambdaCase #-}
  #ifdef ghcjs_HOST_OS
  {-# LANGUAGE ForeignFunctionInterface, JavaScriptFFI #-}
  {-# OPTIONS_GHC -Wno-dodgy-exports -Wno-dodgy-imports #-}
  #endif
  {-# OPTIONS_GHC -fno-warn-orphans #-}
  -----------------------------------------------------------------------------
  --
  -- Module      :  Language.Javascript.JSaddle.Value
  -- Copyright   :  (c) Hamish Mackenzie
  -- License     :  MIT
  --
  -- Maintainer  :  Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
  --
  -- | Deals with JavaScript values.  These can be
  --
  --   * null
  --
  --   * undefined
  --
  --   * true | false
  --
  --   * a double precision floating point number
  --
  --   * a string
  --
  --   * an object
  --
  -----------------------------------------------------------------------------
  
  module Language.Javascript.JSaddle.Value (
    -- * JavaScript value references
      JSVal
    , ToJSVal(..)
  
    -- * Haskell types for JavaScript values
    , JSNull(..)
    , JSUndefined
    , JSString
    , JSValue(..)
    , showJSValue
  
    -- * Converting JavaScript values
    , isTruthy
    , valToBool
    , valToNumber
    , valToStr
    , valToObject
    , valToText
    , valToJSON
  
    -- * Make JavaScript values from Haskell ones
    , val
    , jsNull
    , valNull
    , isNull
    , valIsNull
    , jsUndefined
    , valUndefined
    , isUndefined
    , valIsUndefined
    , maybeNullOrUndefined
    , maybeNullOrUndefined'
    , toJSBool
    , jsTrue
    , jsFalse
    , valBool
    , valMakeNumber
    , valMakeString
    , valMakeText
    , valMakeJSON
  
    -- * Convert to and from JSValue
    , deRefVal
    , valMakeRef
    , strictEqual
    , instanceOf
  ) where
  
  import Data.Text (Text)
  import qualified Data.Text as T (pack, unpack)
  import Data.Aeson (Value)
  import Data.JSString.Text (textToJSString)
  
  #ifdef ghcjs_HOST_OS
  import Language.Javascript.JSaddle.Types
         (Object(..), JSString(..), JSVal(..), ghcjsPure)
  import GHCJS.Marshal (ToJSVal(..))
  #else
  import Data.Char (chr, ord)
  import Data.Word (Word, Word8, Word16, Word32)
  import Data.Int (Int8, Int16, Int32)
  import GHCJS.Marshal.Internal (ToJSVal(..), FromJSVal(..))
  import Language.Javascript.JSaddle.Types
         (Object(..), JSString(..), JSVal(..), ghcjsPure)
  import Language.Javascript.JSaddle.Native
         (valueToNumber, valueToString, valueToJSON, numberToValue, stringToValue, jsonValueToValue)
  import qualified Language.Javascript.JSaddle.Native as N
         (deRefVal, strictEqual, instanceOf)
  import Language.Javascript.JSaddle.Run (Result(..))
  #endif
  import Language.Javascript.JSaddle.Monad (JSM)
  import Language.Javascript.JSaddle.Classes
         (MakeObject(..), MakeArgs(..))
  import Language.Javascript.JSaddle.Marshal.String (ToJSString(..), FromJSString(..))
  import Language.Javascript.JSaddle.String (strToText, textToStr)
  import GHCJS.Foreign.Internal (jsTrue, jsFalse, jsNull, toJSBool, jsUndefined, isTruthy, isNull, isUndefined)
  
  -- $setup
  -- >>> import Language.Javascript.JSaddle.Test (testJSaddle)
  -- >>> import Language.Javascript.JSaddle.Monad (catch)
  -- >>> import Language.Javascript.JSaddle.Exception (JSException(..))
  -- >>> import Language.Javascript.JSaddle.Object (obj, jsg)
  -- >>> import qualified Data.Text as T (pack)
  
  data JSNull      = JSNull -- ^ Type that represents a value that can only be null.
                            --   Haskell of course has no null so we are adding this type.
  type JSUndefined = ()     -- ^ A type that can only be undefined in JavaScript.  Using ()
                            --   because functions in JavaScript that have no return, impicitly
                            --   return undefined.
  -- type JSBool      = Bool   -- ^ JavaScript boolean values map the 'Bool' haskell type.
  -- type JSNumber    = Double -- ^ A number in JavaScript maps nicely to 'Double'.
  -- type JSString    = Text   -- ^ JavaScript strings can be represented with the Haskell 'Text' type.
  
  -- | An algebraic data type that can represent a JavaScript value.  Any JavaScriptCore
  --   'JSVal' can be converted into this type.
  data JSValue = ValNull                   -- ^ null
               | ValUndefined              -- ^ undefined
               | ValBool      Bool         -- ^ true or false
               | ValNumber    Double       -- ^ a number
               | ValString    Text         -- ^ a string
               | ValObject    Object       -- ^ an object
  
  -- | Show a JSValue but just say "object" if the value is a JavaScript object.
  showJSValue :: JSValue -> String
  showJSValue ValNull         = "null"
  showJSValue ValUndefined    = "undefined"
  showJSValue (ValBool True)  = "true"
  showJSValue (ValBool False) = "false"
  showJSValue (ValNumber x)   = show x
  showJSValue (ValString s)   = show s
  showJSValue (ValObject _)   = "object"
  
  -- | Given a JavaScript value get its boolean value.
  --   All values in JavaScript convert to bool.
  --
  -- >>> testJSaddle $ valToBool JSNull
  -- false
  -- >>> testJSaddle $ valToBool ()
  -- false
  -- >>> testJSaddle $ valToBool True
  -- true
  -- >>> testJSaddle $ valToBool False
  -- false
  -- >>> testJSaddle $ valToBool (1.0 :: Double)
  -- true
  -- >>> testJSaddle $ valToBool (0.0 :: Double)
  -- false
  -- >>> testJSaddle $ valToBool ""
  -- false
  -- >>> testJSaddle $ valToBool "1"
  -- true
  valToBool :: ToJSVal value => value -> JSM Bool
  valToBool value = toJSVal value >>= ghcjsPure . isTruthy
  
  -- | Given a JavaScript value get its numeric value.
  --   May throw JSException.
  --
  -- >>> testJSaddle $ show <$> valToNumber JSNull
  -- 0.0
  -- >>> testJSaddle $ show <$> valToNumber ()
  -- NaN
  -- >>> testJSaddle $ show <$> valToNumber True
  -- 1.0
  -- >>> testJSaddle $ show <$> valToNumber False
  -- 0.0
  -- >>> testJSaddle $ show <$> valToNumber (1.0 :: Double)
  -- 1.0
  -- >>> testJSaddle $ show <$> valToNumber (0.0 :: Double)
  -- 0.0
  -- >>> testJSaddle $ show <$> valToNumber ""
  -- 0.0
  -- >>> testJSaddle $ show <$> valToNumber "1"
  -- 1.0
  valToNumber :: ToJSVal value => value -> JSM Double
  #ifdef ghcjs_HOST_OS
  valToNumber value = jsrefToNumber <$> toJSVal value
  foreign import javascript unsafe
  #if __GLASGOW_HASKELL__ >= 900
    "(($1) => { return Number($1); })"
  #else
    "$r = Number($1);"
  #endif
       jsrefToNumber :: JSVal -> Double
  #else
  valToNumber value = toJSVal value >>= valueToNumber
  #endif
  
  -- | Given a JavaScript value get its string value (as a JavaScript string).
  --   May throw JSException.
  --
  -- >>> testJSaddle $ strToText <$> valToStr JSNull
  -- null
  -- >>> testJSaddle $ strToText <$> valToStr ()
  -- undefined
  -- >>> testJSaddle $ strToText <$> valToStr True
  -- true
  -- >>> testJSaddle $ strToText <$> valToStr False
  -- false
  -- >>> testJSaddle $ strToText <$> valToStr (1.0 :: Double)
  -- 1
  -- >>> testJSaddle $ strToText <$> valToStr (0.0 :: Double)
  -- 0
  -- >>> testJSaddle $ strToText <$> valToStr ""
  -- <BLANKLINE>
  -- >>> testJSaddle $ strToText <$> valToStr "1"
  -- 1
  valToStr :: ToJSVal value => value -> JSM JSString
  #ifdef ghcjs_HOST_OS
  valToStr value = jsrefToString <$> toJSVal value
  foreign import javascript unsafe
  #if __GLASGOW_HASKELL__ >= 900
    "(($1) => { return $1.toString(); })"
  #else
    "$r = $1.toString();"
  #endif
      jsrefToString :: JSVal -> JSString
  #else
  valToStr value = toJSVal value >>= valueToString
  #endif
  
  -- | Given a JavaScript value get its string value (as a Haskell 'Text').
  --   May throw JSException.
  --
  -- >>> testJSaddle $ show <$> valToText JSNull
  -- "null"
  -- >>> testJSaddle $ show <$> valToText ()
  -- "undefined"
  -- >>> testJSaddle $ show <$> valToText True
  -- "true"
  -- >>> testJSaddle $ show <$> valToText False
  -- "false"
  -- >>> testJSaddle $ show <$> valToText (1.0 :: Double)
  -- "1"
  -- >>> testJSaddle $ show <$> valToText (0.0 :: Double)
  -- "0"
  -- >>> testJSaddle $ show <$> valToText ""
  -- ""
  -- >>> testJSaddle $ show <$> valToText "1"
  -- "1"
  valToText :: ToJSVal value => value -> JSM Text
  valToText jsvar = strToText <$> valToStr jsvar
  
  -- | Given a JavaScript value get a JSON string value.
  --   May throw JSException.
  --
  -- >>> testJSaddle $ strToText <$> valToJSON JSNull
  -- null
  -- >>> testJSaddle $ strToText <$> valToJSON ()
  -- <BLANKLINE>
  -- >>> testJSaddle $ strToText <$> valToJSON True
  -- true
  -- >>> testJSaddle $ strToText <$> valToJSON False
  -- false
  -- >>> testJSaddle $ strToText <$> valToJSON (1.0 :: Double)
  -- 1
  -- >>> testJSaddle $ strToText <$> valToJSON (0.0 :: Double)
  -- 0
  -- >>> testJSaddle $ strToText <$> valToJSON ""
  -- ""
  -- >>> testJSaddle $ strToText <$> valToJSON "1"
  -- "1"
  -- >>> testJSaddle $ strToText <$> (obj >>= valToJSON)
  -- {}
  valToJSON :: ToJSVal value => value -> JSM JSString
  #ifdef ghcjs_HOST_OS
  valToJSON value = jsrefToJSON <$> toJSVal value
  foreign import javascript unsafe
  #if __GLASGOW_HASKELL__ >= 900
    "(($1) => { return $1 === undefined ? \"\" : JSON.stringify($1); })"
  #else
    "$r = $1 === undefined ? \"\" : JSON.stringify($1);"
  #endif
      jsrefToJSON :: JSVal -> JSString
  #else
  valToJSON value = toJSVal value >>= valueToJSON
  #endif
  
  -- | Given a JavaScript value get its object value.
  --   May throw JSException.
  --
  -- >>> testJSaddle $ (valToObject JSNull >>= valToText) `catch` \ (JSException e) -> valToText e
  -- null
  -- >>> testJSaddle $ (valToObject () >>= valToText) `catch` \ (JSException e) -> valToText e
  -- undefined
  -- >>> testJSaddle $ valToObject True
  -- true
  -- >>> testJSaddle $ valToObject False
  -- false
  -- >>> testJSaddle $ valToObject (1.0 :: Double)
  -- 1
  -- >>> testJSaddle $ valToObject (0.0 :: Double)
  -- 0
  -- >>> testJSaddle $ valToObject ""
  -- <BLANKLINE>
  -- >>> testJSaddle $ valToObject "1"
  -- 1
  valToObject :: ToJSVal value => value -> JSM Object
  valToObject value = Object <$> toJSVal value
  
  instance MakeObject JSVal where
      makeObject = return . Object
  
  instance ToJSVal Object where
      toJSVal (Object r) = return r
  
  -- | Convert to a JavaScript value (just an alias for 'toJSVal')
  val :: ToJSVal value
      => value          -- ^ value to convert to a JavaScript value
      -> JSM JSVal
  val = toJSVal
  
  #ifndef ghcjs_HOST_OS
  -- | If we already have a JSVal we are fine
  instance ToJSVal JSVal where
      toJSVal = return
      {-# INLINE toJSVal #-}
  #endif
  
  -- | A single JSVal can be used as the argument list
  instance MakeArgs JSVal where
      makeArgs arg = return [arg]
  
  -- | JSVal can be made by evaluating a function in 'JSM' as long
  --   as it returns something we can make into a JSVal.
  instance ToJSVal v => ToJSVal (JSM v) where
      toJSVal v = v >>= toJSVal
      {-# INLINE toJSVal #-}
  
  ----------- null ---------------
  -- | A @null@ JavaScript value
  valNull :: JSVal
  valNull = jsNull
  {-# INLINE valNull #-}
  
  -- | Makes a @null@ JavaScript value
  instance ToJSVal JSNull where
      toJSVal = const (return jsNull)
      {-# INLINE toJSVal #-}
  
  -- | Makes an argument list with just a single @null@ JavaScript value
  instance MakeArgs JSNull where
      makeArgs _ = return [jsNull]
  
  #ifndef ghcjs_HOST_OS
  -- | Makes a JSVal or @null@ JavaScript value
  instance ToJSVal a => ToJSVal (Maybe a) where
      toJSVal Nothing = return jsNull
      toJSVal (Just a) = toJSVal a
      {-# INLINE toJSVal #-}
  instance FromJSVal a => FromJSVal (Maybe a) where
      fromJSValUnchecked x =
          ghcjsPure (isUndefined x) >>= \case
              True  -> return Nothing
              False -> ghcjsPure (isNull x) >>= \case
                      True  -> return Nothing
                      False -> fromJSVal x
      {-# INLINE fromJSValUnchecked #-}
      fromJSVal x =
          ghcjsPure (isUndefined x) >>= \case
              True  -> return (Just Nothing)
              False -> ghcjsPure (isNull x) >>= \case
                      True  -> return (Just Nothing)
                      False -> fmap (fmap Just) fromJSVal x
      {-# INLINE fromJSVal #-}
  
  -- Make an array out of various lists
  instance ToJSVal a => ToJSVal [a] where
      toJSVal = toJSValListOf
      {-# INLINE toJSVal #-}
  
  instance FromJSVal a => FromJSVal [a] where
      fromJSVal = fromJSValListOf
      {-# INLINE fromJSVal #-}
  #endif
  
  -- | Test a JavaScript value to see if it is @null@
  valIsNull :: ToJSVal value => value -> JSM Bool
  valIsNull value = toJSVal value >>= ghcjsPure . isNull
  
  ----------- undefined ---------------
  -- | An @undefined@ JavaScript value
  valUndefined :: JSVal
  valUndefined = jsUndefined
  {-# INLINE valUndefined #-}
  
  -- | Makes an @undefined@ JavaScript value
  instance ToJSVal JSUndefined where
      toJSVal = const (return jsUndefined)
  
  --We can't allow this if JSUndefined is () as () is no args not "(null)".
  --Use [()] instead.
  --instance MakeArgs JSUndefined where
  --    makeArgs _ = valMakeUndefined >>= (\ref -> return [ref])
  
  -- | This allows us to pass no arguments easily (altenative would be to use @[]::[JSVal]@).
  instance MakeArgs () where
      makeArgs _ = return []
  
  -- | Test a JavaScript value to see if it is @undefined@
  valIsUndefined :: ToJSVal value => value -> JSM Bool
  valIsUndefined value = toJSVal value >>= ghcjsPure . isUndefined
  
  -- | Convert a JSVal to a Maybe JSVal (converting null and undefined to Nothing)
  maybeNullOrUndefined :: ToJSVal value => value -> JSM (Maybe JSVal)
  maybeNullOrUndefined value = do
      rval <- toJSVal value
      ghcjsPure (isNull rval) >>= \case
          True -> return Nothing
          _    ->
              ghcjsPure (isUndefined rval) >>= \case
                  True -> return Nothing
                  _    -> return (Just rval)
  
  maybeNullOrUndefined' :: ToJSVal value => (JSVal -> JSM a) -> value -> JSM (Maybe a)
  maybeNullOrUndefined' f value = do
      rval <- toJSVal value
      ghcjsPure (isNull rval) >>= \case
          True -> return Nothing
          _    ->
              ghcjsPure (isUndefined rval) >>= \case
                  True -> return Nothing
                  _    -> Just <$> f rval
  
  ----------- booleans ---------------
  -- | A JavaScript boolean value
  valBool :: Bool -> JSVal
  valBool = toJSBool
  {-# INLINE valBool #-}
  
  #ifndef ghcjs_HOST_OS
  -- | Make a JavaScript boolean value
  instance ToJSVal Bool where
      toJSVal = return . valBool
      {-# INLINE toJSVal #-}
  #endif
  
  -- | Makes an argument list with just a single JavaScript boolean value
  instance MakeArgs Bool where
      makeArgs b = return [valBool b]
  
  ----------- numbers ---------------
  -- | Make a JavaScript number
  valMakeNumber :: Double -> JSM JSVal
  valMakeNumber = toJSVal
  {-# INLINE valMakeNumber #-}
  
  #ifndef ghcjs_HOST_OS
  -- | Makes a JavaScript number
  instance ToJSVal Double where
      toJSVal = numberToValue
      {-# INLINE toJSVal #-}
  
  instance ToJSVal Float where
      toJSVal = numberToValue . realToFrac
      {-# INLINE toJSVal #-}
  
  instance ToJSVal Word where
      toJSVal = numberToValue . fromIntegral
      {-# INLINE toJSVal #-}
  
  instance ToJSVal Word8 where
      toJSVal = numberToValue . fromIntegral
      {-# INLINE toJSVal #-}
  
  instance ToJSVal Word16 where
      toJSVal = numberToValue . fromIntegral
      {-# INLINE toJSVal #-}
  
  instance ToJSVal Word32 where
      toJSVal = numberToValue . fromIntegral
      {-# INLINE toJSVal #-}
  
  instance ToJSVal Int where
      toJSVal = numberToValue . fromIntegral
      {-# INLINE toJSVal #-}
  
  instance ToJSVal Int8 where
      toJSVal = numberToValue . fromIntegral
      {-# INLINE toJSVal #-}
  
  instance ToJSVal Int16 where
      toJSVal = numberToValue . fromIntegral
      {-# INLINE toJSVal #-}
  
  instance ToJSVal Int32 where
      toJSVal = numberToValue . fromIntegral
      {-# INLINE toJSVal #-}
  #endif
  
  -- | Makes an argument list with just a single JavaScript number
  instance MakeArgs Double where
      makeArgs n = valMakeNumber n >>= (\ref -> return [ref])
  
  -- | Make a JavaScript string from `Text`
  valMakeText :: Text -> JSM JSVal
  valMakeText = toJSVal . textToJSString
  {-# INLINE valMakeText #-}
  
  -- | Make a JavaScript string from `JSString`
  valMakeString :: JSString -> JSM JSVal
  valMakeString = toJSVal
  {-# INLINE valMakeString #-}
  
  #ifndef ghcjs_HOST_OS
  -- | Makes a JavaScript string
  instance ToJSVal Text where
      toJSVal = stringToValue . JSString
      {-# INLINE toJSVal #-}
  instance FromJSVal Text where
      fromJSValUnchecked = valToText
      {-# INLINE fromJSValUnchecked #-}
      fromJSVal = fmap Just . valToText
      {-# INLINE fromJSVal #-}
  #endif
  
  -- | Makes an argument list with just a single JavaScript string
  instance MakeArgs Text where
      makeArgs t = valMakeText t >>= (\ref -> return [ref])
  
  #ifndef ghcjs_HOST_OS
  -- | Makes a JavaScript string
  instance ToJSVal JSString where
      toJSVal = stringToValue
      {-# INLINE toJSVal #-}
  instance FromJSVal JSString where
      fromJSValUnchecked = valToStr
      {-# INLINE fromJSValUnchecked #-}
      fromJSVal = fmap Just . valToStr
      {-# INLINE fromJSVal #-}
  #endif
  
  -- | If we already have a JSString we are fine
  instance ToJSString JSString where
      toJSString = id
  
  instance ToJSString Text where
      toJSString = textToStr
  
  instance ToJSString String where
      toJSString = textToStr . T.pack
  
  instance FromJSString Text where
      fromJSString = strToText
  
  instance FromJSString String where
      fromJSString v = T.unpack $ strToText v
  
  instance FromJSString JSString where
      fromJSString = id
  
  #ifndef ghcjs_HOST_OS
  instance ToJSVal Char where
      toJSVal = valMakeNumber . fromIntegral . ord
      {-# INLINE toJSVal #-}
      toJSValListOf = valMakeText . T.pack
      {-# INLINE toJSValListOf #-}
  instance FromJSVal Char where
      fromJSValUnchecked = fmap (chr . round) . valToNumber
      {-# INLINE fromJSValUnchecked #-}
      fromJSVal = fmap (Just . chr . round) . valToNumber
      {-# INLINE fromJSVal #-}
      fromJSValUncheckedListOf = fmap (T.unpack . strToText) . valToStr
      {-# INLINE fromJSValListOf #-}
      fromJSValListOf = fmap (Just . T.unpack . strToText) . valToStr
      {-# INLINE fromJSValUncheckedListOf #-}
  #endif
  
  -- | Make a JavaScript string from AESON `Value`
  valMakeJSON :: Value -> JSM JSVal
  valMakeJSON = toJSVal
  
  #ifndef ghcjs_HOST_OS
  -- | Makes a JSON value
  instance ToJSVal Value where
      toJSVal = jsonValueToValue
      {-# INLINE toJSVal #-}
  #endif
  
  -- | Makes an argument list with just a single JSON value
  instance MakeArgs Value where
      makeArgs t = valMakeJSON t >>= (\ref -> return [ref])
  
  -- | Derefernce a value reference.
  --
  -- >>> testJSaddle $ showJSValue <$> deRefVal JSNull
  -- null
  -- >>> testJSaddle $ showJSValue <$> deRefVal ()
  -- undefined
  -- >>> testJSaddle $ showJSValue <$> deRefVal True
  -- true
  -- >>> testJSaddle $ showJSValue <$> deRefVal False
  -- false
  -- >>> testJSaddle $ showJSValue <$> deRefVal (1.0 :: Double)
  -- 1.0
  -- >>> testJSaddle $ showJSValue <$> deRefVal (0.0 :: Double)
  -- 0.0
  -- >>> testJSaddle $ showJSValue <$> deRefVal ""
  -- ""
  -- >>> testJSaddle $ showJSValue <$> deRefVal "1"
  -- "1"
  -- >>> testJSaddle $ showJSValue <$> (valToObject True >>= deRefVal)
  -- true
  -- >>> testJSaddle $ showJSValue <$> (obj >>= deRefVal)
  -- object
  deRefVal :: ToJSVal value => value -> JSM JSValue
  #ifdef ghcjs_HOST_OS
  deRefVal value = do
      valref <- toJSVal value
      case (jsrefGetType valref :: Int) of
          0 -> return ValUndefined
          1 -> return ValNull
          2 -> ValBool   <$> valToBool valref
          3 -> ValNumber <$> valToNumber valref
          4 -> ValString <$> valToText valref
          5 -> ValObject <$> valToObject valref
          _ -> error "Unexpected result dereferencing JSaddle value"
  foreign import javascript unsafe
  #if __GLASGOW_HASKELL__ >= 900
    "(($1) => { return ($1 === undefined)?0:\
                       ($1===null)?1:\
                       (typeof $1===\"boolean\")?2:\
                       (typeof $1===\"number\")?3:\
                       (typeof $1===\"string\")?4:\
                       (typeof $1===\"object\")?5:-1; })"
  #else
    "$r = ($1 === undefined)?0:\
          ($1===null)?1:\
          (typeof $1===\"boolean\")?2:\
          (typeof $1===\"number\")?3:\
          (typeof $1===\"string\")?4:\
          (typeof $1===\"object\")?5:-1;"
  #endif
      jsrefGetType :: JSVal -> Int
  #else
  deRefVal value = do
      v <- toJSVal value
      result <- N.deRefVal v
      return $ case result of
          DeRefValResult 0    _ -> ValNull
          DeRefValResult 1    _ -> ValUndefined
          DeRefValResult 2    _ -> ValBool False
          DeRefValResult 3    _ -> ValBool True
          DeRefValResult (-1) s -> ValNumber (read (T.unpack s))
          DeRefValResult (-2) s -> ValString s
          DeRefValResult (-3) _ -> ValObject (Object v)
          _                     -> error "Unexpected result dereferencing JSaddle value"
  #endif
  
  -- | Make a JavaScript value out of a 'JSValue' ADT.
  --
  -- >>> testJSaddle $ valMakeRef ValNull
  -- null
  -- >>> testJSaddle $ valMakeRef ValUndefined
  -- undefined
  -- >>> testJSaddle $ valMakeRef (ValBool True)
  -- true
  -- >>> testJSaddle $ valMakeRef (ValNumber 1)
  -- 1
  -- >>> testJSaddle $ valMakeRef (ValString $ T.pack "Hello")
  -- Hello
  valMakeRef :: JSValue -> JSM JSVal
  valMakeRef value =
      case value of
          ValNull              -> return valNull
          ValUndefined         -> return valUndefined
          ValBool b            -> return $ valBool b
          ValNumber n          -> valMakeNumber n
          ValString s          -> valMakeText s
          ValObject (Object o) -> return o
  
  -- | Makes a JavaScript value from a 'JSValue' ADT.
  instance ToJSVal JSValue where
      toJSVal = valMakeRef
      {-# INLINE toJSVal #-}
  
  -- | Makes an argument list with just a single JavaScript value from a 'JSValue' ADT.
  instance MakeArgs JSValue where
      makeArgs v = valMakeRef v >>= (\ref -> return [ref])
  
  --instance MakeObjectRef JSNull where
  --    makeObjectRef _ = Object <$> valMakeNull
  --    {-# INLINE makeObjectRef #-}
  
  #ifdef ghcjs_HOST_OS
  foreign import javascript unsafe
  #if __GLASGOW_HASKELL__ >= 900
    "(($1,$2) => { return $1===$2; })"
  #else
    "$1===$2"
  #endif
      jsvalueisstrictequal :: JSVal -> JSVal -> Bool
  #endif
  
  -- | Determine if two values are equal (JavaScripts ===)
  -- >>> testJSaddle $ strictEqual True False
  -- false
  -- >>> testJSaddle $ strictEqual True True
  -- true
  -- >>> testJSaddle $ strictEqual "Hello" ()
  -- false
  -- >>> testJSaddle $ strictEqual "Hello" "Hello"
  -- true
  strictEqual :: (ToJSVal a, ToJSVal b) => a -> b -> JSM Bool
  strictEqual a b = do
      aval <- toJSVal a
      bval <- toJSVal b
  #ifdef ghcjs_HOST_OS
      return $ jsvalueisstrictequal aval bval
  #else
      N.strictEqual aval bval
  #endif
  
  #ifdef ghcjs_HOST_OS
  foreign import javascript unsafe
  #if __GLASGOW_HASKELL__ >= 900
    "(($1,$2) => { return $1 instanceof $2; })"
  #else
    "$1 instanceof $2"
  #endif
    js_isInstanceOf :: JSVal -> Object -> Bool
  #endif
  
  -- | Determine if two values are equal (JavaScripts ===)
  -- >>> testJSaddle $ instanceOf obj (Object <$> jsg "Object")
  -- true
  instanceOf :: (ToJSVal value, MakeObject constructor) => value -> constructor -> JSM Bool
  instanceOf value constructor = do
      v <- toJSVal value
      c <- makeObject constructor
  #ifdef ghcjs_HOST_OS
      return $ js_isInstanceOf v c
  #else
      N.instanceOf v c
  #endif
  
  
  
  
  
  
  
  
  
  
  
  