packages feed

wasmedge-hs-0.17.1.0: examples/HostFunc.hs

{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE OverloadedStrings #-}

module Main (main) where

import Data.ByteString qualified as BS
import Data.Text qualified as T
import System.IO (BufferMode (..), hSetBuffering, stdout)
import WasmEdge.Error (ErrCategory (..), WasmEdgeError (..), WasmResult, formatError)
import WasmEdge.Function
import WasmEdge.Module
import WasmEdge.Types
import WasmEdge.VM

-- WASM guest module that imports "add_one" from "host_module":
-- (module
--   (import "host_module" "add_one" (func $add_one (param i32) (result i32)))
--   (func (export "call_host") (param i32) (result i32)
--     local.get 0
--     call $add_one))
guestWasm :: BS.ByteString
guestWasm =
  BS.pack
    [ 0x00
    , 0x61
    , 0x73
    , 0x6D
    , 0x01
    , 0x00
    , 0x00
    , 0x00 -- magic & version 1
    , 0x01
    , 0x06
    , 0x01
    , 0x60
    , 0x01
    , 0x7F
    , 0x01
    , 0x7F -- type 0: (i32) -> i32
    , 0x02
    , 0x17
    , 0x01 -- import section: 1 import
    , 0x0B
    , 0x68
    , 0x6F
    , 0x73
    , 0x74
    , 0x5F
    , 0x6D
    , 0x6F
    , 0x64
    , 0x75
    , 0x6C
    , 0x65 -- "host_module"
    , 0x07
    , 0x61
    , 0x64
    , 0x64
    , 0x5F
    , 0x6F
    , 0x6E
    , 0x65 -- "add_one"
    , 0x00
    , 0x00 -- kind func, type 0
    , 0x03
    , 0x02
    , 0x01
    , 0x00 -- func 1 has type 0
    , 0x07
    , 0x0D
    , 0x01 -- export section: 1 export
    , 0x09
    , 0x63
    , 0x61
    , 0x6C
    , 0x6C
    , 0x5F
    , 0x68
    , 0x6F
    , 0x73
    , 0x74 -- "call_host"
    , 0x00
    , 0x01 -- kind func, func idx 1
    , 0x0A
    , 0x08
    , 0x01 -- code section: 1 func
    , 0x06
    , 0x00 -- size 6, 0 locals
    , 0x20
    , 0x00 -- local.get 0
    , 0x10
    , 0x00 -- call 0 (the imported func)
    , 0x0B -- end
    ]

-- Haskell implementation of the host function
haskellAddOne :: CallingFrame -> [WasmVal] -> IO (WasmResult [WasmVal])
haskellAddOne _ [WasmI32 n] = do
  putStrLn $ "  [Haskell HostFunc] Received value from WASM: " ++ show n
  let result = n + 1
  putStrLn $ "  [Haskell HostFunc] Returning incremented value: " ++ show result
  pure $ Right [WasmI32 result]
haskellAddOne _ args =
  pure $
    Left $
      WasmEdgeError ErrCategory_UserDefined 2 ("add_one: unexpected arguments: " <> T.pack (show args))

main :: IO ()
main = do
  hSetBuffering stdout LineBuffering
  putStrLn "=== WasmEdge Haskell Binding: Host Function Example ==="
  putStrLn "Step 1: creating function type"

  -- 1. Create function type and host function
  withFunctionType [I32] [I32] $ \fnType -> do
    putStrLn "Step 2: creating host function"
    withFunction fnType haskellAddOne $ \hostFn -> do
      putStrLn "Step 3: building host module"
      -- 2. Build host module exporting "add_one"
      hostMod <- buildHostModule "host_module" $ \b ->
        builderAddFunction b "add_one" hostFn

      putStrLn "Step 4: creating VM"
      -- 3. Run guest WASM in VM with the host module registered
      withVM Nothing Nothing $ \vm -> do
        putStrLn "Step 5: registering host module"
        regRes <- registerModuleFromImport vm hostMod
        case regRes of
          Left err -> putStrLn $ "Registration failed: " ++ formatError err
          Right () -> do
            putStrLn "Host module registered successfully."
            putStrLn "Executing guest WASM function 'call_host(99)'..."
            res <- runWasmBytes vm guestWasm "call_host" [WasmI32 99]
            case res of
              Left err -> putStrLn $ "Execution failed: " ++ formatError err
              Right [WasmI32 ans] -> putStrLn $ "Final result from WASM: " ++ show ans
              Right vals -> putStrLn $ "Unexpected return: " ++ show vals