flaw-lua/Flaw/Script/Lua/FFI.hs

Summary

Maintainability
Test Coverage
{-|
Module: Flaw.Script.Lua.FFI
Description: Lua FFI definitions.
License: MIT
-}

{-# LANGUAGE OverloadedStrings, PatternSynonyms #-}
{-# OPTIONS_GHC -fno-warn-missing-pattern-synonym-signatures #-}

module Flaw.Script.Lua.FFI
  ( luaNewState
  , luaLoadChunk

  , pattern LUA_OK
  , pattern LUA_YIELD
  , pattern LUA_ERRRUN
  , pattern LUA_ERRSYNTAX
  , pattern LUA_ERRMEM
  , pattern LUA_ERRGCMM
  , pattern LUA_ERRERR

  , pattern LUA_TNIL
  , pattern LUA_TBOOLEAN
  , pattern LUA_TLIGHTUSERDATA
  , pattern LUA_TNUMBER
  , pattern LUA_TSTRING
  , pattern LUA_TTABLE
  , pattern LUA_TFUNCTION
  , pattern LUA_TUSERDATA
  , pattern LUA_TTHREAD

  , pattern LUA_TSHRSTR
  , pattern LUA_TLNGSTR
  , pattern LUA_TNUMFLT
  , pattern LUA_TNUMINT

  , pattern LUA_SIGNATURE
  , pattern LUAC_DATA
  , pattern LUAC_INT
  , pattern LUAC_NUM
  , pattern OP_MOVE
  , pattern OP_LOADK
  , pattern OP_LOADKX
  , pattern OP_LOADBOOL
  , pattern OP_LOADNIL
  , pattern OP_GETUPVAL
  , pattern OP_GETTABUP
  , pattern OP_GETTABLE
  , pattern OP_SETTABUP
  , pattern OP_SETUPVAL
  , pattern OP_SETTABLE
  , pattern OP_NEWTABLE
  , pattern OP_SELF
  , pattern OP_ADD
  , pattern OP_SUB
  , pattern OP_MUL
  , pattern OP_MOD
  , pattern OP_POW
  , pattern OP_DIV
  , pattern OP_IDIV
  , pattern OP_BAND
  , pattern OP_BOR
  , pattern OP_BXOR
  , pattern OP_SHL
  , pattern OP_SHR
  , pattern OP_UNM
  , pattern OP_BNOT
  , pattern OP_NOT
  , pattern OP_LEN
  , pattern OP_CONCAT
  , pattern OP_JMP
  , pattern OP_EQ
  , pattern OP_LT
  , pattern OP_LE
  , pattern OP_TEST
  , pattern OP_TESTSET
  , pattern OP_CALL
  , pattern OP_TAILCALL
  , pattern OP_RETURN
  , pattern OP_FORLOOP
  , pattern OP_FORPREP
  , pattern OP_TFORCALL
  , pattern OP_TFORLOOP
  , pattern OP_SETLIST
  , pattern OP_CLOSURE
  , pattern OP_VARARG
  , pattern OP_EXTRAARG
  ) where

import Control.Exception
import Control.Monad
import qualified Data.ByteString as B
import qualified Data.ByteString.Unsafe as B
import Data.IORef
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Foreign.C.Types
import Foreign.Marshal.Alloc
import Foreign.Ptr
import Foreign.Storable

import Flaw.Script.Lua

data C_lua_State

luaNewState :: IO (Ptr C_lua_State, IO ())
luaNewState = do
  alloc <- wrap_C_lua_Alloc $ \_ ptr _osize nsize ->
    if nsize > 0 then reallocBytes ptr (fromIntegral nsize)
    else do
      free ptr
      return nullPtr
  statePtr <- lua_newstate alloc nullPtr
  return (statePtr, lua_close statePtr)

-- | Load Lua code or bytecode, and return bytecode.
luaLoadChunk :: Ptr C_lua_State -> T.Text -> B.ByteString -> IO B.ByteString
luaLoadChunk luaStatePtr chunkName bytes = do
  B.unsafeUseAsCStringLen bytes $ \(bytesPtr, bytesLen) -> do
    finishedReadingRef <- newIORef False
    reader <- wrap_C_lua_Reader $ \_ _ sizePtr -> do
      finishedReading <- readIORef finishedReadingRef
      if finishedReading then do
        poke sizePtr 0
        return nullPtr
      else do
        writeIORef finishedReadingRef True
        poke sizePtr (fromIntegral bytesLen)
        return bytesPtr
    B.useAsCString (T.encodeUtf8 chunkName) $ \chunkNamePtr -> do
      r <- lua_load luaStatePtr reader nullPtr chunkNamePtr nullPtr
      when (r /= LUA_OK) $ do
        errBytes <- B.packCString =<< lua_tostring luaStatePtr (-1)
        lua_pop luaStatePtr 1
        throwIO $ LuaLoadError $ T.decodeUtf8 errBytes
  chunksRef <- newIORef []
  writer <- wrap_C_lua_Writer $ \_ ptr size _ -> do
    bs <- B.packCStringLen (castPtr ptr, fromIntegral size)
    modifyIORef' chunksRef (bs :)
    return 0
  r <- lua_dump luaStatePtr writer nullPtr 0
  lua_pop luaStatePtr 1
  when (r /= LUA_OK) $ throwIO $ LuaLoadError "failed to dump chunk"
  chunks <- readIORef chunksRef
  return $ B.concat $ reverse chunks

type C_lua_Alloc = Ptr () -> Ptr () -> CSize -> CSize -> IO (Ptr ())
type C_lua_Reader = Ptr C_lua_State -> Ptr () -> Ptr CSize -> IO (Ptr CChar)
type C_lua_Writer = Ptr C_lua_State -> Ptr () -> CSize -> Ptr () -> IO CInt

foreign import ccall safe lua_newstate :: FunPtr C_lua_Alloc -> Ptr () -> IO (Ptr C_lua_State)
foreign import ccall safe lua_close :: Ptr C_lua_State -> IO ()
foreign import ccall safe lua_load :: Ptr C_lua_State -> FunPtr C_lua_Reader -> Ptr () -> Ptr CChar -> Ptr CChar -> IO CInt
foreign import ccall safe lua_dump :: Ptr C_lua_State -> FunPtr C_lua_Writer -> Ptr () -> CInt -> IO CInt

foreign import ccall safe lua_settop :: Ptr C_lua_State -> CInt -> IO ()
lua_pop :: Ptr C_lua_State -> CInt -> IO ()
lua_pop s n = lua_settop s ((-1) - n)

foreign import ccall safe lua_tolstring :: Ptr C_lua_State -> CInt -> Ptr CSize -> IO (Ptr CChar)
lua_tostring :: Ptr C_lua_State -> CInt -> IO (Ptr CChar)
lua_tostring s i = lua_tolstring s i nullPtr

foreign import ccall "wrapper" wrap_C_lua_Alloc :: C_lua_Alloc -> IO (FunPtr C_lua_Alloc)
foreign import ccall "wrapper" wrap_C_lua_Reader :: C_lua_Reader -> IO (FunPtr C_lua_Reader)
foreign import ccall "wrapper" wrap_C_lua_Writer :: C_lua_Writer -> IO (FunPtr C_lua_Writer)

pattern LUA_OK = 0
pattern LUA_YIELD = 1
pattern LUA_ERRRUN = 2
pattern LUA_ERRSYNTAX = 3
pattern LUA_ERRMEM = 4
pattern LUA_ERRGCMM = 5
pattern LUA_ERRERR = 6

pattern LUA_TNIL = 0
pattern LUA_TBOOLEAN = 1
pattern LUA_TLIGHTUSERDATA = 2
pattern LUA_TNUMBER = 3
pattern LUA_TSTRING = 4
pattern LUA_TTABLE = 5
pattern LUA_TFUNCTION = 6
pattern LUA_TUSERDATA = 7
pattern LUA_TTHREAD = 8

pattern LUA_TSHRSTR = 4 -- short strings
pattern LUA_TLNGSTR = 20 -- long strings
pattern LUA_TNUMFLT = 3
pattern LUA_TNUMINT = 19

pattern LUA_SIGNATURE = "\x1bLua"
pattern LUAC_DATA = "\x19\x93\r\n\x1a\n"
pattern LUAC_INT = 0x5678
pattern LUAC_NUM = 370.5

pattern OP_MOVE = 0
pattern OP_LOADK = 1
pattern OP_LOADKX = 2
pattern OP_LOADBOOL = 3
pattern OP_LOADNIL = 4
pattern OP_GETUPVAL = 5
pattern OP_GETTABUP = 6
pattern OP_GETTABLE = 7
pattern OP_SETTABUP = 8
pattern OP_SETUPVAL = 9
pattern OP_SETTABLE = 10
pattern OP_NEWTABLE = 11
pattern OP_SELF = 12
pattern OP_ADD = 13
pattern OP_SUB = 14
pattern OP_MUL = 15
pattern OP_MOD = 16
pattern OP_POW = 17
pattern OP_DIV = 18
pattern OP_IDIV = 19
pattern OP_BAND = 20
pattern OP_BOR = 21
pattern OP_BXOR = 22
pattern OP_SHL = 23
pattern OP_SHR = 24
pattern OP_UNM = 25
pattern OP_BNOT = 26
pattern OP_NOT = 27
pattern OP_LEN = 28
pattern OP_CONCAT = 29
pattern OP_JMP = 30
pattern OP_EQ = 31
pattern OP_LT = 32
pattern OP_LE = 33
pattern OP_TEST = 34
pattern OP_TESTSET = 35
pattern OP_CALL = 36
pattern OP_TAILCALL = 37
pattern OP_RETURN = 38
pattern OP_FORLOOP = 39
pattern OP_FORPREP = 40
pattern OP_TFORCALL = 41
pattern OP_TFORLOOP = 42
pattern OP_SETLIST = 43
pattern OP_CLOSURE = 44
pattern OP_VARARG = 45
pattern OP_EXTRAARG = 46