jvm-streaming (empty) → 0.1
raw patch · 7 files changed
+277/−0 lines, 7 filesdep +basedep +distributed-closuredep +hspec
Dependencies added: base, distributed-closure, hspec, inline-java, jni, jvm, jvm-streaming, singletons, streaming
Files
- LICENSE +30/−0
- README.md +3/−0
- jvm-streaming.cabal +51/−0
- src/Language/Java/Streaming.hs +148/−0
- tests/Language/Java/StreamingSpec.hs +36/−0
- tests/Main.hs +8/−0
- tests/Spec.hs +1/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright EURL Tweag (c) 2016++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Author name here nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,3 @@+# jvm-streaming++Expose Java iterators as streams from the streaming package.
+ jvm-streaming.cabal view
@@ -0,0 +1,51 @@+name: jvm-streaming+version: 0.1+synopsis: Expose Java iterators as streams from the streaming package.+description: Please see README.md.+homepage: http://github.com/tweag/inline-java/tree/master/jvm-streaming#readme+license: BSD3+license-file: LICENSE+author: Tweag I/O+maintainer: m@tweag.io+copyright: 2015-2016 EURL Tweag.+category: FFI, JVM, Java+build-type: Simple+cabal-version: >=1.10+extra-source-files: README.md++source-repository head+ type: git+ location: https://github.com/tweag/inline-java+ subdir: jvm-streaming++library+ hs-source-dirs: src+ default-language: Haskell2010+ exposed-modules:+ Language.Java.Streaming+ build-depends:+ base >= 4.7 && < 5,+ distributed-closure >= 0.3,+ jni >= 0.2.1,+ jvm >= 0.1,+ inline-java >= 0.6,+ singletons >= 2.2,+ streaming >= 0.1.4++test-suite spec+ type:+ exitcode-stdio-1.0+ hs-source-dirs: tests+ main-is: Main.hs+ other-modules:+ Language.Java.StreamingSpec+ Spec+ build-depends:+ base,+ hspec,+ inline-java,+ jvm,+ jvm-streaming,+ streaming+ default-language: Haskell2010+ extra-libraries: pthread
+ src/Language/Java/Streaming.hs view
@@ -0,0 +1,148 @@+-- | Expose Java iterators as streams from the+-- <http://hackage.haskell.org/package/streaming streaming> package.++{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE UndecidableInstances #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# OPTIONS_GHC -fno-warn-unused-top-binds #-}++module Language.Java.Streaming () where++import Control.Distributed.Closure.TH+import Data.IORef (IORef, newIORef, readIORef, writeIORef)+import Data.Int (Int64)+import Data.Singletons (SomeSing(..))+import Data.Word (Word8)+import Foreign.Ptr (FunPtr, freeHaskellFunPtr, intPtrToPtr, ptrToIntPtr)+import qualified Foreign.JNI as JNI+import Foreign.JNI.Types (jnull)+import GHC.Stable+ ( castPtrToStablePtr+ , castStablePtrToPtr+ , deRefStablePtr+ , freeStablePtr+ , newStablePtr+ )+import Language.Java+import Language.Java.Inline+import Streaming (Stream, Of)+import qualified Streaming.Prelude as Streaming++isPoppableStream :: IORef (Stream (Of a) IO ()) -> IO Word8+isPoppableStream ref = do+ stream <- readIORef ref+ Streaming.uncons stream >>= \case+ Nothing -> return $ fromIntegral $ fromEnum False+ Just _ -> return $ fromIntegral $ fromEnum True++popStream :: Reflect a ty => IORef (Stream (Of a) IO ()) -> IO (J ty)+popStream ref = do+ stream <- readIORef ref+ Streaming.uncons stream >>= \case+ Nothing -> do+ exc :: J ('Class "java.util.NoSuchElementException") <- new []+ JNI.throw exc+ return jnull+ Just (x, stream') -> do+ writeIORef ref stream'+ reflect x++type JNIFun a = JNIEnv -> JObject -> IO a++foreign import ccall "wrapper" wrapObjectFun+ :: JNIFun (J ty) -> IO (FunPtr (JNIFun (J ty)))+foreign import ccall "wrapper" wrapBooleanFun+ :: JNIFun Word8 -> IO (FunPtr (JNIFun Word8))++-- Export only to get a FunPtr.+foreign export ccall "jvm_streaming_freeIterator" freeIterator+ :: JNIEnv -> JObject -> Int64 -> IO ()+foreign import ccall "&jvm_streaming_freeIterator" freeIteratorPtr+ :: FunPtr (JNIEnv -> JObject -> Int64 -> IO ())++data FunPtrTable = forall ty. FunPtrTable+ { hasNextPtr :: FunPtr (JNIFun Word8)+ , nextPtr :: FunPtr (JNIFun (J ty))+ }++freeIterator :: JNIEnv -> JObject -> Int64 -> IO ()+freeIterator _ _ ptr = do+ let sptr = castPtrToStablePtr $ intPtrToPtr $ fromIntegral ptr+ FunPtrTable{..} <- deRefStablePtr sptr+ freeHaskellFunPtr hasNextPtr+ freeHaskellFunPtr nextPtr+ freeStablePtr sptr++newIterator+ :: Reflect a ty+ => Stream (Of a) IO ()+ -> IO (J ('Iface "java.util.Iterator"))+newIterator stream = do+ ref <- newIORef stream+ hasNextPtr <- wrapBooleanFun $ \_ _ -> isPoppableStream ref+ nextPtr <- wrapObjectFun $ \_ _ -> popStream ref+ -- Keep FunPtr's in a table that can be referenced from the Java side, so+ -- that they can be freed.+ tblPtr :: Int64 <- fromIntegral . ptrToIntPtr . castStablePtrToPtr <$> newStablePtr FunPtrTable{..}+ iterator <-+ [java| new java.util.Iterator() {+ @Override+ public native boolean hasNext();++ @Override+ public native Object next();++ @Override+ public void remove() {+ throw new UnsupportedOperationException();+ }++ private native void hsFinalize(long tblPtr);++ @Override+ public void finalize() {+ hsFinalize($tblPtr);+ }+ } |]+ klass <- JNI.getObjectClass iterator+ JNI.registerNatives klass+ [ JNI.JNINativeMethod+ "hasNext"+ (methodSignature [] (sing :: Sing ('Prim "boolean")))+ hasNextPtr+ , JNI.JNINativeMethod+ "next"+ (methodSignature [] (sing :: Sing ('Class "java.lang.Object")))+ nextPtr+ , JNI.JNINativeMethod+ "hsFinalize"+ (methodSignature [SomeSing (sing :: Sing ('Prim "long"))] (sing :: Sing 'Void))+ freeIteratorPtr+ ]+ return iterator++withStatic [d|+ type instance Interp (Stream (Of a) m r) = 'Iface "java.util.Iterator"++ instance Reify a ty => Reify (Stream (Of a) IO ()) ('Iface "java.util.Iterator") where+ reify it = return $ Streaming.untilRight $ do+ call it "hasNext" [] >>= \case+ False -> return (Right ())+ True -> do+ obj <- call it "next" []+ Left <$> reify (unsafeCast (obj :: JObject))++ instance Reflect a ty => Reflect (Stream (Of a) IO ()) ('Iface "java.util.Iterator") where+ reflect x = newIterator x+ |]
+ tests/Language/Java/StreamingSpec.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE QuasiQuotes #-}++module Language.Java.StreamingSpec where++import Data.Int+import Language.Java+import Language.Java.Inline+import Language.Java.Streaming ()+import Test.Hspec+import Streaming (Stream, Of)+import qualified Streaming.Prelude as Streaming++spec :: Spec+spec = do+ describe "iteration" $ do+ it "succeeds on empty lists" $ do+ vals <- reflect [1..0 :: Int32]+ iterator <- [java| java.util.Arrays.asList($vals).iterator() |]+ stream <- reify iterator+ Streaming.toList_ stream `shouldReturn` [1..0 :: Int32]+ it "succeeds on singleton lists" $ do+ vals <- reflect [1 :: Int32]+ iterator <- [java| java.util.Arrays.asList($vals).iterator() |]+ stream <- reify iterator+ Streaming.toList_ stream `shouldReturn` [1 :: Int32]+ it "succeeds on non-trivial lists" $ do+ vals <- reflect [1..10000 :: Int32]+ iterator <- [java| java.util.Arrays.asList($vals).iterator() |]+ stream <- reify iterator+ Streaming.toList_ stream `shouldReturn` [1..10000 :: Int32]+ describe "streams" $ do+ it "have the property that reify . reflect == id" $ do+ iterator <- reflect (Streaming.each [1..10000] :: Stream (Of Int32) IO ())+ stream <- reify iterator+ Streaming.toList_ stream `shouldReturn` [1..10000 :: Int32]
+ tests/Main.hs view
@@ -0,0 +1,8 @@+module Main where++import Language.Java (withJVM)+import qualified Spec+import Test.Hspec++main :: IO ()+main = withJVM [] $ hspec Spec.spec
+ tests/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=Spec #-}