packages feed

wasmedge-hs-0.17.1.0: src/WasmEdge/Global.hs

{- |
Module      : WasmEdge.Global
Description : WasmEdge global variable contexts
Copyright   : (c) 2026 Tushar Adhatrao
License     : MIT
Stability   : stable

Provides contexts for WebAssembly global variables:

* 'GlobalType' — describes the value type and mutability of a global
* 'GlobalInstance' — a runtime global variable instance
-}
module WasmEdge.Global
  ( -- * Global type
    GlobalType
  , createGlobalType
  , deleteGlobalType
  , withGlobalType
  , getGlobalTypeValType
  , getGlobalTypeMutability

    -- * Global instance
  , GlobalInstance
  , createGlobal
  , deleteGlobal
  , withGlobal
  , getGlobalType
  , getGlobalValue
  , setGlobalValue

    -- * Internal / borrowed pointers
  , unsafeGlobalPtr
  , borrowGlobal
  , unsafeGlobalTypePtr
  , borrowGlobalType
  , disarmGlobal
  ) where

import Control.Exception (bracket, throwIO)
import Data.Word (Word32)
import Foreign.Ptr

import WasmEdge.Error
import WasmEdge.Internal.FFI.Instance
import WasmEdge.Internal.Ownership (OwnedCtx, disarmOwned, finalizeOwned, mkOwned, withOwned)
import WasmEdge.Internal.Storable (allocaCResult, allocaCValType, allocaCValue)
import WasmEdge.Internal.Types (GlobalInstanceTag, GlobalTypeTag)
import WasmEdge.Internal.Utils (checkResult)
import WasmEdge.Types
import WasmEdge.Value (decodeVal, encodeVal, encodeValType, peekValTypes)

-- ---------------------------------------------------------------------------
-- GlobalType
-- ---------------------------------------------------------------------------

-- | A WebAssembly global type context, describing its type and mutability.
newtype GlobalType = GlobalType (OwnedCtx GlobalTypeTag)

-- | Create a 'GlobalType' with the specified 'ValType' and 'Mutability'.
createGlobalType :: ValType -> Mutability -> IO GlobalType
createGlobalType vt mut =
  encodeValType vt $ \vtPtr -> do
    let mutCode = case mut of
          Const -> 0 :: Word32
          Var -> 1 :: Word32
    p <- c_GlobalTypeCreate vtPtr mutCode
    if p == nullPtr
      then throwIO $ WasmEdgeError ErrCategory_WASM 0 "WasmEdge_GlobalTypeCreate returned NULL"
      else GlobalType <$> mkOwned (appFunPtr c_GlobalTypeDelete) p

-- | Delete a 'GlobalType' context.
deleteGlobalType :: GlobalType -> IO ()
deleteGlobalType (GlobalType o) = finalizeOwned o

-- | Scoped 'GlobalType' context.
withGlobalType :: ValType -> Mutability -> (GlobalType -> IO a) -> IO a
withGlobalType vt mut = bracket (createGlobalType vt mut) deleteGlobalType

-- | Retrieve the 'ValType' of a 'GlobalType'.
getGlobalTypeValType :: GlobalType -> IO ValType
getGlobalTypeValType (GlobalType o) =
  withOwned o $ \p ->
    allocaCValType $ \vtPtr -> do
      c_GlobalTypeGetValType p vtPtr
      types <- peekValTypes vtPtr 1
      case types of
        (t : _) -> pure t
        [] -> pure I32

-- | Retrieve the 'Mutability' of a 'GlobalType'.
getGlobalTypeMutability :: GlobalType -> IO Mutability
getGlobalTypeMutability (GlobalType o) =
  withOwned o $ \p -> do
    m <- c_GlobalTypeGetMutability p
    pure (if m == 0 then Const else Var)

-- ---------------------------------------------------------------------------
-- GlobalInstance
-- ---------------------------------------------------------------------------

-- | A runtime WebAssembly global variable instance.
newtype GlobalInstance = GlobalInstance (OwnedCtx GlobalInstanceTag)

-- | Create a 'GlobalInstance' with the given type and initial value.
createGlobal :: GlobalType -> WasmVal -> IO GlobalInstance
createGlobal (GlobalType gtOwned) val =
  withOwned gtOwned $ \gtPtr ->
    encodeVal val $ \valPtr -> do
      p <- c_GlobalInstanceCreate gtPtr valPtr
      if p == nullPtr
        then throwIO $ WasmEdgeError ErrCategory_WASM 0 "WasmEdge_GlobalInstanceCreate returned NULL"
        else GlobalInstance <$> mkOwned (appFunPtr c_GlobalInstanceDelete) p

-- | Delete a 'GlobalInstance'.
deleteGlobal :: GlobalInstance -> IO ()
deleteGlobal (GlobalInstance o) = finalizeOwned o

-- | Scoped 'GlobalInstance'.
withGlobal :: GlobalType -> WasmVal -> (GlobalInstance -> IO a) -> IO a
withGlobal gt val = bracket (createGlobal gt val) deleteGlobal

{- | Get the 'GlobalType' of a 'GlobalInstance'.
Note: the returned 'GlobalType' borrows the type context from the instance.
-}
getGlobalType :: GlobalInstance -> IO GlobalType
getGlobalType (GlobalInstance o) =
  withOwned o $ \p -> do
    tp <- c_GlobalInstanceGetGlobalType p
    if tp == nullPtr
      then throwIO $ WasmEdgeError ErrCategory_WASM 0 "WasmEdge_GlobalInstanceGetGlobalType returned NULL"
      else borrowGlobalType tp

-- | Get the current value of a global variable.
getGlobalValue :: GlobalInstance -> IO WasmVal
getGlobalValue (GlobalInstance o) =
  withOwned o $ \p ->
    allocaCValue $ \valPtr -> do
      c_GlobalInstanceGetValue valPtr p
      decodeVal valPtr

{- | Set a new value for a mutable global variable.
Fails with a 'WasmEdgeError' if the global is immutable or type mismatches.
-}
setGlobalValue :: GlobalInstance -> WasmVal -> IO (WasmResult ())
setGlobalValue (GlobalInstance o) val =
  withOwned o $ \p ->
    encodeVal val $ \valPtr ->
      allocaCResult $ \resPtr -> do
        c_GlobalInstanceSetValue resPtr p valPtr
        checkResult resPtr

-- ---------------------------------------------------------------------------
-- Unsafe / internal pointer helpers
-- ---------------------------------------------------------------------------

-- | Extract the raw pointer of a 'GlobalInstance'.
unsafeGlobalPtr :: GlobalInstance -> IO (Ptr GlobalInstanceTag)
unsafeGlobalPtr (GlobalInstance o) = withOwned o pure

-- | Create a borrowed 'GlobalInstance' from a raw pointer (does not finalize).
borrowGlobal :: Ptr GlobalInstanceTag -> IO GlobalInstance
borrowGlobal p = GlobalInstance <$> mkOwned (\_ -> pure ()) p

-- | Extract the raw pointer of a 'GlobalType'.
unsafeGlobalTypePtr :: GlobalType -> IO (Ptr GlobalTypeTag)
unsafeGlobalTypePtr (GlobalType o) = withOwned o pure

-- | Create a borrowed 'GlobalType' from a raw pointer (does not finalize).
borrowGlobalType :: Ptr GlobalTypeTag -> IO GlobalType
borrowGlobalType p = GlobalType <$> mkOwned (\_ -> pure ()) p

-- | Disarm finalizer when ownership is transferred to a module instance.
disarmGlobal :: GlobalInstance -> IO ()
disarmGlobal (GlobalInstance o) = disarmOwned o

-- ---------------------------------------------------------------------------
-- Internal helper
-- ---------------------------------------------------------------------------

appFunPtr :: FunPtr (Ptr a -> IO ()) -> Ptr a -> IO ()
appFunPtr = mkDeleter

foreign import ccall "dynamic"
  mkDeleter :: FunPtr (Ptr a -> IO ()) -> Ptr a -> IO ()