packages feed

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

{- |
Module      : WasmEdge.Table
Description : WasmEdge table contexts
Author      : Tushar Adhatrao <tusharadhatrao@gmail.com>
Copyright   : (c) 2026 Tushar Adhatrao
License     : MIT
Stability   : stable

Provides types and functions for WebAssembly table instances and types:

* 'TableType' — describes reference type and element limits
* 'TableInstance' — a runtime table instance
-}
module WasmEdge.Table
  ( -- * Table type
    TableType
  , createTableType
  , deleteTableType
  , withTableType
  , getTableTypeRefType
  , getTableTypeLimit

    -- * Table instance
  , TableInstance
  , createTable
  , deleteTable
  , withTable
  , getTableType
  , getTableData
  , setTableData
  , getTableSize
  , growTable

    -- * Internal / borrowed pointers
  , unsafeTablePtr
  , borrowTable
  , unsafeTableTypePtr
  , borrowTableType
  , disarmTable
  ) where

import Control.Exception (bracket, throwIO)
import Data.Word (Word32)
import Foreign.C (CBool (..))
import Foreign.Marshal.Alloc (alloca)
import Foreign.Ptr (FunPtr, Ptr, nullPtr)
import Foreign.Storable (peek)

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 (TableInstanceTag, TableTypeTag)
import WasmEdge.Internal.Utils (checkResult)
import WasmEdge.Types
import WasmEdge.Value (decodeVal, encodeVal, encodeValType, peekValTypes)

-- ---------------------------------------------------------------------------
-- TableType
-- ---------------------------------------------------------------------------

-- | A WebAssembly table type, specifying the reference type (FuncRef or ExternRef) and element limits.
newtype TableType = TableType (OwnedCtx TableTypeTag)

-- | Create a 'TableType' with the given element reference type and 'Limit'.
createTableType :: ValType -> Limit -> IO TableType
createTableType refType lim =
  encodeValType refType $ \vtPtr -> do
    let hasMax = CBool (case limitMax lim of Just _ -> 1; Nothing -> 0)
        minVal = fromIntegral (limitMin lim)
        maxVal = maybe 0 fromIntegral (limitMax lim)
        shared = CBool (if limitShared lim then 1 else 0)
        is64 = CBool 0
    p <- c_TableTypeCreate vtPtr hasMax minVal maxVal shared is64
    if p == nullPtr
      then throwIO $ WasmEdgeError ErrCategory_WASM 0 "WasmEdge_TableTypeCreate returned NULL"
      else TableType <$> mkOwned (appFunPtr c_TableTypeDelete) p

-- | Delete a 'TableType'.
deleteTableType :: TableType -> IO ()
deleteTableType (TableType o) = finalizeOwned o

-- | Scoped 'TableType'.
withTableType :: ValType -> Limit -> (TableType -> IO a) -> IO a
withTableType vt lim = bracket (createTableType vt lim) deleteTableType

-- | Get the reference type of a 'TableType'.
getTableTypeRefType :: TableType -> IO ValType
getTableTypeRefType (TableType o) =
  withOwned o $ \p ->
    allocaCValType $ \vtPtr -> do
      c_TableTypeGetRefType p vtPtr
      types <- peekValTypes vtPtr 1
      case types of
        (t : _) -> pure t
        [] -> pure FuncRefT

-- | Get the 'Limit' of a 'TableType'.
getTableTypeLimit :: TableType -> IO Limit
getTableTypeLimit (TableType o) =
  withOwned o $ \p -> do
    limPtr <- c_TableTypeGetLimit p
    if limPtr == nullPtr
      then pure (Limit 0 Nothing False)
      else alloca $ \hasMaxPtr ->
        alloca $ \minPtr ->
          alloca $ \maxPtr ->
            alloca $ \sharedPtr ->
              alloca $ \is64Ptr -> do
                c_LimitGetInfo limPtr hasMaxPtr minPtr maxPtr sharedPtr is64Ptr
                CBool hasMax <- peek hasMaxPtr
                minVal <- peek minPtr
                maxVal <- peek maxPtr
                CBool shared <- peek sharedPtr
                pure
                  Limit
                    { limitMin = fromIntegral minVal
                    , limitMax = if hasMax /= 0 then Just (fromIntegral maxVal) else Nothing
                    , limitShared = shared /= 0
                    }

-- ---------------------------------------------------------------------------
-- TableInstance
-- ---------------------------------------------------------------------------

-- | A runtime WebAssembly table instance.
newtype TableInstance = TableInstance (OwnedCtx TableInstanceTag)

-- | Create a 'TableInstance' from a 'TableType'.
createTable :: TableType -> IO TableInstance
createTable (TableType ttOwned) =
  withOwned ttOwned $ \ttPtr -> do
    p <- c_TableInstanceCreate ttPtr
    if p == nullPtr
      then throwIO $ WasmEdgeError ErrCategory_WASM 0 "WasmEdge_TableInstanceCreate returned NULL"
      else TableInstance <$> mkOwned (appFunPtr c_TableInstanceDelete) p

-- | Delete a 'TableInstance'.
deleteTable :: TableInstance -> IO ()
deleteTable (TableInstance o) = finalizeOwned o

-- | Scoped 'TableInstance'.
withTable :: TableType -> (TableInstance -> IO a) -> IO a
withTable tt = bracket (createTable tt) deleteTable

-- | Get the 'TableType' of a 'TableInstance'.
getTableType :: TableInstance -> IO TableType
getTableType (TableInstance o) =
  withOwned o $ \p -> do
    tp <- c_TableInstanceGetTableType p
    if tp == nullPtr
      then throwIO $ WasmEdgeError ErrCategory_WASM 0 "WasmEdge_TableInstanceGetTableType returned NULL"
      else borrowTableType tp

-- | Get the reference value at an offset in the table.
getTableData :: TableInstance -> Word32 -> IO (WasmResult WasmVal)
getTableData (TableInstance o) offset =
  withOwned o $ \p ->
    allocaCValue $ \valPtr ->
      allocaCResult $ \resPtr -> do
        c_TableInstanceGetData resPtr p valPtr offset
        res <- checkResult resPtr
        case res of
          Left err -> pure (Left err)
          Right () -> Right <$> decodeVal valPtr

-- | Set the reference value at an offset in the table.
setTableData :: TableInstance -> Word32 -> WasmVal -> IO (WasmResult ())
setTableData (TableInstance o) offset val =
  withOwned o $ \p ->
    encodeVal val $ \valPtr ->
      allocaCResult $ \resPtr -> do
        c_TableInstanceSetData resPtr p valPtr offset
        checkResult resPtr

-- | Get the current element count of a table.
getTableSize :: TableInstance -> IO Word32
getTableSize (TableInstance o) =
  withOwned o c_TableInstanceGetSize

-- | Grow the table by @count@ elements.
growTable :: TableInstance -> Word32 -> IO (WasmResult ())
growTable (TableInstance o) count =
  withOwned o $ \p ->
    allocaCResult $ \resPtr -> do
      c_TableInstanceGrow resPtr p count
      checkResult resPtr

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

-- | Extract the raw pointer of a 'TableInstance'.
unsafeTablePtr :: TableInstance -> IO (Ptr TableInstanceTag)
unsafeTablePtr (TableInstance o) = withOwned o pure

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

-- | Extract the raw pointer of a 'TableType'.
unsafeTableTypePtr :: TableType -> IO (Ptr TableTypeTag)
unsafeTableTypePtr (TableType o) = withOwned o pure

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

-- | Disarm finalizer when ownership is transferred to a module instance.
disarmTable :: TableInstance -> IO ()
disarmTable (TableInstance 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 ()