jvm-0.6.0: src/common/Language/Java/Internal.hs
-- | Internal functions to invoke JNI methods
--
-- The functions in this module avoid using
-- 'Language.Java.Coercible' so they can be reused in interfaces which
-- use other ways to convert between Haskell and Java values.
--
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeApplications #-}
module Language.Java.Internal
( newJ
, callToJValue
, callStaticToJValue
, getStaticFieldAsJValue
, getClass
, setGetClassFunction
) where
import Data.IORef
import Data.Singletons (SingI(..), SomeSing(..))
import Foreign.JNI hiding (throw)
import Foreign.JNI.Types
import qualified Foreign.JNI.String as JNI
import System.IO.Unsafe (unsafeDupablePerformIO, unsafePerformIO)
-- | Sets the function to use for loading classes.
--
-- 'findClass' is used by default.
--
setGetClassFunction
:: (forall ty. IsReferenceType ty => Sing (ty :: JType) -> IO JClass)
-> IO ()
setGetClassFunction f = writeIORef getClassFunctionRef $ GetClassFun f
-- | Yields a class referece. It behaves as 'findClass' unless
-- 'setGetClassFunction' is used.
getClass :: IsReferenceType ty => Sing (ty :: JType) -> IO JClass
getClass s = readIORef getClassFunctionRef >>= \(GetClassFun f) -> f s
newtype GetClassFun =
GetClassFun (forall ty. IsReferenceType ty =>
Sing (ty :: JType) -> IO JClass
)
{-# NOINLINE getClassFunctionRef #-}
getClassFunctionRef :: IORef GetClassFun
getClassFunctionRef =
unsafePerformIO $ newIORef (GetClassFun (findClass . referenceTypeName))
newJ
:: forall sym ty.
( ty ~ 'Class sym
, SingI ty
)
=> [SomeSing JType] -- ^ Singletons of argument types
-> [JValue]
-> IO (J ty)
{-# INLINE newJ #-}
newJ argsings args = do
let voidsing = sing :: Sing 'Void
klass = unsafeDupablePerformIO $ do
lk <- getClass (sing :: Sing ('Class sym))
gk <- newGlobalRef lk
deleteLocalRef lk
return gk
unsafeCast <$> newObject klass (methodSignature argsings voidsing) args
callToJValue
:: forall ty1 k. (IsReferenceType ty1, SingI ty1)
=> Sing (k :: JType)
-> J ty1 -- ^ Any object
-> JNI.String -- ^ Method name
-> [SomeSing JType] -- ^ Singletons of argument types
-> [JValue] -- ^ Arguments
-> IO JValue
{-# INLINE callToJValue #-}
callToJValue retsing obj mname argsings args = do
let klass = unsafeDupablePerformIO $ do
lk <- getClass (sing :: Sing ty1)
gk <- newGlobalRef lk
deleteLocalRef lk
return gk
method = unsafeDupablePerformIO $ getMethodID klass mname (methodSignature argsings retsing)
case retsing of
SPrim "boolean" -> JBoolean . fromIntegral . fromEnum <$>
callBooleanMethod obj method args
SPrim "byte" -> JByte <$> callByteMethod obj method args
SPrim "char" -> JChar <$> callCharMethod obj method args
SPrim "short" -> JShort <$> callShortMethod obj method args
SPrim "int" -> JInt <$> callIntMethod obj method args
SPrim "long" -> JLong <$> callLongMethod obj method args
SPrim "float" -> JFloat <$> callFloatMethod obj method args
SPrim "double" -> JDouble <$> callDoubleMethod obj method args
SVoid -> do
callVoidMethod obj method args
-- The void result is not inspected.
return (error "inspected output of method returning void")
_ -> JObject <$> callObjectMethod obj method args
callStaticToJValue
:: Sing (k :: JType)
-> JNI.String -- ^ Class name
-> JNI.String -- ^ Method name
-> [SomeSing JType] -- ^ Singletons of argument types
-> [JValue] -- ^ Arguments
-> IO JValue
{-# INLINE callStaticToJValue #-}
callStaticToJValue retsing cname mname argsings args = do
let klass = unsafeDupablePerformIO $ do
lk <- getClass (SClass (JNI.toChars cname))
gk <- newGlobalRef lk
deleteLocalRef lk
return gk
method = unsafeDupablePerformIO $ getStaticMethodID klass mname (methodSignature argsings retsing)
case retsing of
SPrim "boolean" -> JBoolean . fromIntegral . fromEnum <$>
callStaticBooleanMethod klass method args
SPrim "byte" -> JByte <$> callStaticByteMethod klass method args
SPrim "char" -> JChar <$> callStaticCharMethod klass method args
SPrim "short" -> JShort <$> callStaticShortMethod klass method args
SPrim "int" -> JInt <$> callStaticIntMethod klass method args
SPrim "long" -> JLong <$> callStaticLongMethod klass method args
SPrim "float" -> JFloat <$> callStaticFloatMethod klass method args
SPrim "double" -> JDouble <$> callStaticDoubleMethod klass method args
SVoid -> do
callStaticVoidMethod klass method args
-- The void result is not inspected.
return (error "inspected output of method returning void")
_ -> JObject <$> callStaticObjectMethod klass method args
getStaticFieldAsJValue
:: Sing (ty :: JType)
-> JNI.String -- ^ Class name
-> JNI.String -- ^ Static field name
-> IO JValue
{-# INLINE getStaticFieldAsJValue #-}
getStaticFieldAsJValue retsing cname fname = do
let klass = unsafeDupablePerformIO $ do
lk <- getClass (SClass (JNI.toChars cname))
gk <- newGlobalRef lk
deleteLocalRef lk
return gk
field = unsafeDupablePerformIO $ getStaticFieldID klass fname (signature retsing)
case retsing of
SPrim "boolean" -> JBoolean <$> getStaticBooleanField klass field
SPrim "byte" -> JByte <$> getStaticByteField klass field
SPrim "char" -> JChar <$> getStaticCharField klass field
SPrim "short" -> JShort <$> getStaticShortField klass field
SPrim "int" -> JInt <$> getStaticIntField klass field
SPrim "long" -> JLong <$> getStaticLongField klass field
SPrim "float" -> JFloat <$> getStaticFloatField klass field
SPrim "double" -> JDouble <$> getStaticDoubleField klass field
SVoid -> fail "getStaticField cannot yield an object of type void"
_ -> JObject <$> getStaticObjectField klass field