{-# LANGUAGE OverloadedStrings, UnicodeSyntax, CPP #-} {-# LANGUAGE ForeignFunctionInterface, CApiFFI, EmptyDataDecls #-} module Scripting.Duktape.Raw where import Foreign(peek, poke, malloc, free) import Foreign.C.Types import Foreign.C.String import Foreign.Ptr import Foreign.ForeignPtr hiding (newForeignPtr, addForeignPtrFinalizer) import Foreign.Concurrent (newForeignPtr, addForeignPtrFinalizer) import Control.Concurrent.MVar (withMVar, newMVar, MVar) foreign import capi "duktape.h value DUK_TYPE_NONE" c_DUK_TYPE_NONE ∷ CInt foreign import capi "duktape.h value DUK_TYPE_UNDEFINED" c_DUK_TYPE_UNDEFINED ∷ CInt foreign import capi "duktape.h value DUK_TYPE_NULL" c_DUK_TYPE_NULL ∷ CInt foreign import capi "duktape.h value DUK_TYPE_BOOLEAN" c_DUK_TYPE_BOOLEAN ∷ CInt foreign import capi "duktape.h value DUK_TYPE_NUMBER" c_DUK_TYPE_NUMBER ∷ CInt foreign import capi "duktape.h value DUK_TYPE_STRING" c_DUK_TYPE_STRING ∷ CInt foreign import capi "duktape.h value DUK_TYPE_OBJECT" c_DUK_TYPE_OBJECT ∷ CInt foreign import capi "duktape.h value DUK_TYPE_BUFFER" c_DUK_TYPE_BUFFER ∷ CInt foreign import capi "duktape.h value DUK_TYPE_POINTER" c_DUK_TYPE_POINTER ∷ CInt foreign import capi "duktape.h value DUK_TYPE_LIGHTFUNC" c_DUK_TYPE_LIGHTFUNC ∷ CInt -- Duktape return vals foreign import capi "duktape.h value DUK_RET_TYPE_ERROR" c_DUK_RET_TYPE_ERROR ∷ CInt data DuktapeHeap type DuktapeCtx = MVar (ForeignPtr DuktapeHeap) type DukAllocFunction = Ptr () → CSize → IO (Ptr ()) type DukReallocFunction = Ptr () → Ptr () → CSize → IO (Ptr ()) type DukFreeFunction = Ptr () → Ptr () → IO () type DukFatalFunction = Ptr DuktapeHeap → CInt → CString → IO () type DukExecTimeoutCheckFunction = Ptr () → IO (CUInt) type TimeoutCheck = IO Bool type TimeoutCheckWrapped = FunPtr (IO Bool) type CheckActionUData = Ptr TimeoutCheckWrapped newtype InternalUData = InternalUData { getInternalUData ∷ Ptr () } -- Static callback foreign export ccall "hsduk_exec_timeout_check" execTimeoutCheck ∷ DukExecTimeoutCheckFunction -- | Will always be invoked regularly by duktape runtime but returns false (do not timeout) -- unless it receives a TimeoutCheck through udata execTimeoutCheck ∷ DukExecTimeoutCheckFunction execTimeoutCheck udata = if udata == nullPtr then return 0 else invoke where checkAction ∷ CheckActionUData checkAction = castPtr udata invoke = do action ← peek checkAction result ← unwrapTimeoutCheck action return $ if result then 1 else 0 -- FunPtr wrappers / unwrappers foreign import ccall safe "wrapper" c_wrapper ∷ (Ptr DuktapeHeap → IO CInt) → IO (FunPtr (Ptr DuktapeHeap → IO CInt)) foreign import ccall "dynamic" unwrapTimeoutCheck ∷ TimeoutCheckWrapped → IO Bool foreign import ccall safe "wrapper" wrapTimeoutCheck ∷ (IO Bool) → IO TimeoutCheckWrapped -- Heap lifecycle foreign import capi safe "duktape.h duk_create_heap" c_duk_create_heap ∷ FunPtr DukAllocFunction → FunPtr DukReallocFunction → FunPtr DukFreeFunction → Ptr () → FunPtr DukFatalFunction → IO (Ptr DuktapeHeap) foreign import capi safe "duktape.h duk_destroy_heap" c_duk_destroy_heap ∷ Ptr DuktapeHeap → IO () -- Evaluation foreign import capi safe "duktape.h duk_eval_raw" c_duk_eval_raw ∷ Ptr DuktapeHeap → CString → CSize → CUInt → IO CInt foreign import capi safe "duktape.h duk_peval_lstring" c_duk_peval_lstring ∷ Ptr DuktapeHeap → CString → CSize → IO CInt foreign import capi safe "duktape.h duk_pcall_prop" c_duk_pcall_prop ∷ Ptr DuktapeHeap → CInt → CInt → IO CInt -- Managing the stack foreign import capi safe "duktape.h duk_pop" c_duk_pop ∷ Ptr DuktapeHeap → IO () -- Properties foreign import capi safe "duktape.h duk_put_prop_index" c_duk_put_prop_index ∷ Ptr DuktapeHeap → CInt → CInt → IO CInt foreign import capi safe "duktape.h duk_put_prop_string" c_duk_put_prop_string ∷ Ptr DuktapeHeap → CInt → CString → IO CInt -- Pushing to the stack foreign import capi safe "duktape.h duk_push_string" c_duk_push_string ∷ Ptr DuktapeHeap → CString → IO CString foreign import capi safe "duktape.h duk_push_lstring" c_duk_push_lstring ∷ Ptr DuktapeHeap → CString → CSize → IO CString foreign import capi safe "duktape.h duk_push_number" c_duk_push_number ∷ Ptr DuktapeHeap → CDouble → IO () foreign import capi safe "duktape.h duk_push_boolean" c_duk_push_boolean ∷ Ptr DuktapeHeap → CInt → IO () foreign import capi safe "duktape.h duk_push_null" c_duk_push_null ∷ Ptr DuktapeHeap → IO () foreign import capi safe "duktape.h duk_push_array" c_duk_push_array ∷ Ptr DuktapeHeap → IO CInt foreign import capi safe "duktape.h duk_push_object" c_duk_push_object ∷ Ptr DuktapeHeap → IO CInt foreign import capi safe "duktape.h duk_push_c_function" c_duk_push_c_function ∷ Ptr DuktapeHeap → FunPtr (Ptr DuktapeHeap → IO CInt) → CInt → IO CInt foreign import capi safe "duktape.h duk_push_global_object" c_duk_push_global_object ∷ Ptr DuktapeHeap → IO () foreign import capi safe "duktape.h duk_get_global_string" c_duk_get_global_string ∷ Ptr DuktapeHeap → CString → IO CInt -- Fetching from the stack foreign import capi safe "duktape.h duk_safe_to_lstring" c_duk_safe_to_lstring ∷ Ptr DuktapeHeap → CInt → Ptr CSize → IO CString foreign import capi safe "duktape.h duk_safe_to_string" c_duk_safe_to_string ∷ Ptr DuktapeHeap → CInt → IO CString foreign import capi safe "duktape.h duk_get_string" c_duk_get_string ∷ Ptr DuktapeHeap → CInt → IO CString foreign import capi safe "duktape.h duk_get_lstring" c_duk_get_lstring ∷ Ptr DuktapeHeap → CInt → Ptr CSize → IO CString foreign import capi safe "duktape.h duk_get_int" c_duk_get_int ∷ Ptr DuktapeHeap → CInt → IO CInt foreign import capi safe "duktape.h duk_get_uint" c_duk_get_uint ∷ Ptr DuktapeHeap → CInt → IO CUInt foreign import capi safe "duktape.h duk_get_number" c_duk_get_number ∷ Ptr DuktapeHeap → CInt → IO CDouble foreign import capi safe "duktape.h duk_get_boolean" c_duk_get_boolean ∷ Ptr DuktapeHeap → CInt → IO CInt foreign import capi safe "duktape.h duk_get_type" c_duk_get_type ∷ Ptr DuktapeHeap → CInt → IO CInt -- Encoding/decoding foreign import capi safe "duktape.h duk_json_encode" c_duk_json_encode ∷ Ptr DuktapeHeap → CInt → IO CString foreign import capi safe "duktape.h duk_json_decode" c_duk_json_decode ∷ Ptr DuktapeHeap → CInt → IO () -- Debugging foreign import capi safe "duktape.h duk_push_context_dump" c_duk_push_context_dump ∷ Ptr DuktapeHeap → IO () ------------------------------------------------------------------------------------------------------- nullUData ∷ InternalUData nullUData = InternalUData nullPtr createHeap ∷ FunPtr DukAllocFunction → FunPtr DukReallocFunction → FunPtr DukFreeFunction → InternalUData → FunPtr DukFatalFunction → IO (Maybe DuktapeCtx) createHeap allocf reallocf freef udata fatalf = do ptr ← c_duk_create_heap allocf reallocf freef (getInternalUData udata) fatalf if ptr /= nullPtr then newForeignPtr ptr (c_duk_destroy_heap ptr) >>= newMVar >>= return . Just else return Nothing createHeapF ∷ FunPtr DukFatalFunction → IO (Maybe DuktapeCtx) createHeapF = createHeap nullFunPtr nullFunPtr nullFunPtr nullUData -- | A TimeoutCheck is an IO action that returns True when the current script evaluation -- should timeout (interpreter throws RangeError). createGovernedHeap ∷ FunPtr DukAllocFunction → FunPtr DukReallocFunction → FunPtr DukFreeFunction → TimeoutCheck → FunPtr DukFatalFunction → IO (Maybe DuktapeCtx) createGovernedHeap allocf reallocf freef timeoutCheck fatalf = do (udata, release) ← wrapTimeoutCheckUData timeoutCheck mctx ← createHeap allocf reallocf freef udata fatalf case mctx of Just ctx → withMVar ctx $ \fptr → do addForeignPtrFinalizer fptr release return mctx Nothing → return Nothing where -- TimeoutCheck is wrapped to pass as void* udata in `createHeap` and will be provided (by duktape) -- back to `execTimeoutCheck` when the interpreter invokes that callback. wrapTimeoutCheckUData ∷ TimeoutCheck → IO (InternalUData, IO ()) wrapTimeoutCheckUData check = do wrapped ← wrapTimeoutCheck check ptr ← malloc poke ptr wrapped let finalizers = free ptr >> freeHaskellFunPtr wrapped return (InternalUData $ castPtr ptr, finalizers)