packages feed

jvm 0.1.2 → 0.2.0

raw patch · 4 files changed

+118/−17 lines, 4 filesdep +criteriondep ~basedep ~jninew-uploader

Dependencies added: criterion

Dependency ranges changed: base, jni

Files

README.md view
@@ -1,5 +1,8 @@ # jvm: Call any JVM function from Haskell +[![jvm on Stackage LTS](http://stackage.org/package/jvm/badge/lts)](http://stackage.org/lts/package/jvm)+[![jvm on Stackage Nightly](http://stackage.org/package/jvm/badge/nightly)](http://stackage.org/nightly/package/jvm)+ This package enables calling any JVM function from Haskell. If you'd like to call JVM methods using Java syntax and hence get the Java compiler to scope check and type check all your foreign calls, see
+ benchmarks/Main.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Main where++import Data.Int+import Language.Java+import Foreign.JNI+import Criterion.Main as Criterion++incrementExact :: Int32 -> IO Int32+incrementExact x = callStatic (sing :: Sing "java.lang.Math") "incrementExact" [coerce x]++jniIncrementExact :: JClass -> JMethodID -> Int32 -> IO Int32+jniIncrementExact klass method x = callStaticIntMethod klass method [coerce x]++intValue :: Int32 -> IO Int32+intValue x = do+    jx <- reflect x+    call jx "intValue" []++compareTo :: Int32 -> Int32 -> IO Int32+compareTo x y = do+    jx <- reflect x+    jy <- reflect y+    call jx "compareTo" [coerce jy]++incrHaskell :: Int32 -> IO Int32+incrHaskell x = return (x + 1)++foreign import ccall unsafe getpid :: IO Int++main :: IO ()+main = withJVM [] $ do+    klass <- findClass "java/lang/Math"+    method <- getStaticMethodID klass "incrementExact" "(I)I"+    Criterion.defaultMain+      [ bgroup "Java calls"+        [ bench "static method call: unboxed single arg / unboxed return" $ nfIO $ incrementExact 1+        , bench "jni static method call: unboxed single arg / unboxed return" $ nfIO $ jniIncrementExact klass method 1+        , bench "method call: no args / unboxed return" $ nfIO $ intValue 1+        , bench "method call: boxed single arg / unboxed return" $ nfIO $ compareTo 1 1+        ]+      , bgroup "Haskell calls"+        [ bench "incr haskell" $ nfIO $ incrHaskell 1+        , bench "ffi haskell" $ nfIO $ getpid+        ]+      ]
jvm.cabal view
@@ -1,5 +1,5 @@ name:                jvm-version:             0.1.2+version:             0.2.0 synopsis:            Call JVM methods from Haskell. description:         Please see README.md. homepage:            http://github.com/tweag/inline-java/tree/master/jvm#readme@@ -26,7 +26,7 @@     base >= 4.7 && < 5,     bytestring >=0.10,     distributed-closure >=0.3,-    jni >= 0.1,+    jni >= 0.3.0,     singletons >= 2.0,     text >=1.2,     vector >=0.11@@ -48,3 +48,15 @@     text   default-language: Haskell2010   extra-libraries: pthread++benchmark micro-benchmarks+  type: exitcode-stdio-1.0+  main-is: Main.hs+  hs-source-dirs: benchmarks+  build-depends:+    base >= 4.8 && < 5,+    criterion,+    jni,+    jvm+  default-language: Haskell2010+  ghc-options: -threaded
src/Language/Java.hs view
@@ -26,8 +26,12 @@ -- To call Java methods using quasiquoted Java syntax instead, see -- "Language.Java.Inline". ----- __NOTE:__ To use any function in this module, you'll need an initialized JVM in the+-- __NOTE 1:__ To use any function in this module, you'll need an initialized JVM in the -- current process, using 'withJVM' or otherwise.+--+-- __NOTE 2:__ Functions in this module memoize (cache) any implicitly performed+-- class and method lookups, for performance. This memoization is safe only when+-- no new named classes are defined at runtime.  {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-}@@ -86,7 +90,15 @@ import Foreign.JNI.Types import qualified Foreign.JNI.String as JNI import GHC.TypeLits (KnownSymbol, Symbol)+import System.IO.Unsafe (unsafeDupablePerformIO) +-- Note [Class lookup memoization]+--+-- By using unsafeDupablePerformIO, we mark the lookup actions as pure. When the+-- body of the function is inlined within the calling context, the lookups+-- typically become closed expressions, therefore are CAF's that can be floated+-- to top-level by the GHC optimizer.+ -- | Tag data types that can be coerced in O(1) time without copy to a Java -- object or primitive type (i.e. have the same representation) by declaring an -- instance of this type class for that data type.@@ -175,10 +187,13 @@      )   => [JValue]   -> IO a+{-# INLINE new #-} new args = do     let argsings = map jtypeOf args         voidsing = sing :: Sing 'Void-    klass <- findClass (referenceTypeName (sing :: Sing ('Class sym)))+        klass = unsafeDupablePerformIO $+          findClass (referenceTypeName (sing :: Sing ('Class sym)))+            >>= newGlobalRef     Coerce.coerce <$> newObject klass (methodSignature argsings voidsing) args  -- | The Swiss Army knife for calling Java methods. Give it an object or@@ -196,11 +211,14 @@   -> JNI.String -- ^ Method name   -> [JValue] -- ^ Arguments   -> IO b+{-# INLINE call #-} call obj mname args = do     let argsings = map jtypeOf args         retsing = sing :: Sing ty2-    klass <- findClass (referenceTypeName (sing :: Sing ty1))-    method <- getMethodID klass mname (methodSignature argsings retsing)+        klass = unsafeDupablePerformIO $+                  findClass (referenceTypeName (sing :: Sing ty1))+                    >>= newGlobalRef+        method = unsafeDupablePerformIO $ getMethodID klass mname (methodSignature argsings retsing)     case retsing of       SPrim "boolean" -> unsafeUncoerce . coerce <$> callBooleanMethod obj method args       SPrim "byte" -> unsafeUncoerce . coerce <$> callByteMethod obj method args@@ -218,11 +236,14 @@  -- | Same as 'call', but for static methods. callStatic :: forall a ty sym. Coercible a ty => Sing (sym :: Symbol) -> JNI.String -> [JValue] -> IO a+{-# INLINE callStatic #-} callStatic cname mname args = do     let argsings = map jtypeOf args         retsing = sing :: Sing ty-    klass <- findClass (referenceTypeName (SClass (fromString (fromSing cname))))-    method <- getStaticMethodID klass mname (methodSignature argsings retsing)+        klass = unsafeDupablePerformIO $+                  findClass (referenceTypeName (SClass (fromString (fromSing cname))))+                    >>= newGlobalRef+        method = unsafeDupablePerformIO $ getStaticMethodID klass mname (methodSignature argsings retsing)     case retsing of       SPrim "boolean" -> unsafeUncoerce . coerce <$> callStaticBooleanMethod klass method args       SPrim "byte" -> unsafeUncoerce . coerce <$> callStaticByteMethod klass method args@@ -279,13 +300,21 @@ -- | Extract a concrete Haskell value from the space of Java objects. That is to -- say, unmarshall a Java object to a Haskell value. Unlike coercing, in general -- reifying induces allocations and copies.-class (Interp (Uncurry a) ~ ty, SingI ty) => Reify a ty where+--+-- Instances of this class /must/ guarantee that the result is managed on the+-- Haskell heap. That is, the Haskell runtime has /global ownership/ of the+-- result.+class (Interp (Uncurry a) ~ ty, SingI ty, IsReferenceType ty)+      => Reify a ty where   reify :: J ty -> IO a  -- | Inject a concrete Haskell value into the space of Java objects. That is to -- say, marshall a Haskell value to a Java object. Unlike coercing, in general -- reflection induces allocations and copies.-class (Interp (Uncurry a) ~ ty, SingI ty) => Reflect a ty where+--+-- Instances of this class /must not/ claim global ownership.+class (Interp (Uncurry a) ~ ty, SingI ty, IsReferenceType ty)+      => Reflect a ty where   reflect :: a -> IO (J ty)  #if ! __GLASGOW_HASKELL__ == 800@@ -322,11 +351,17 @@ withStatic [d|   type instance Interp (J ty) = ty -  instance SingI ty => Reify (J ty) ty where-    reify x = return x+  -- Use this instance to claim global ownership of a Java object on the+  -- Haskell heap until the Haskell garbage collector determines that it is+  -- inaccessible. Objects that need to survive the dynamic scope delimited by+  -- the topmost Java frame on the call stack must have global ownership.+  instance (SingI ty, IsReferenceType ty) => Reify (J ty) ty where+    reify x = newGlobalRef x -  instance SingI ty => Reflect (J ty) ty where-    reflect x = return x+  -- Use this instance to relinquish global ownership of a Java object. You+  -- /must not/ refer to the argument anywhere after a call to 'reflect'.+  instance (SingI ty, IsReferenceType ty) => Reflect (J ty) ty where+    reflect x = newLocalRef x    type instance Interp () = 'Class "java.lang.Object" @@ -478,9 +513,9 @@   instance Reflect a ty => Reflect [a] ('Array ty) where     reflect xs = do       let n = fromIntegral (length xs)-      klass <- findClass "java/lang/Object"-      array <- newObjectArray n klass-      forM_ (zip [0..n-1] xs) $ \(i, x) -> do+      array <- findClass (referenceTypeName (sing :: Sing ty))+                 >>= newObjectArray n+      forM_ (zip [0..n-1] xs) $ \(i, x) ->         setObjectArrayElement array i =<< reflect x       return (unsafeCast array)   |]