jvm 0.3.0 → 0.4.0
raw patch · 2 files changed
+110/−87 lines, 2 filesdep ~jni
Dependency ranges changed: jni
Files
- jvm.cabal +9/−9
- src/Language/Java.hs +101/−78
jvm.cabal view
@@ -1,5 +1,5 @@ name: jvm-version: 0.3.0+version: 0.4.0 synopsis: Call JVM methods from Haskell. description: Please see README.md. homepage: http://github.com/tweag/inline-java/tree/master/jvm#readme@@ -23,14 +23,14 @@ exposed-modules: Language.Java build-depends:- base >= 4.7 && < 5,+ base >=4.7 && <5, bytestring >=0.10,- constraints >= 0.8,- choice >= 0.1,+ constraints >=0.8,+ choice >=0.1, distributed-closure >=0.3,- exceptions >= 0.8,- jni >= 0.4.0,- singletons >= 2.0,+ exceptions >=0.8,+ jni >=0.4.0 && <0.6,+ singletons >=2.0, text >=1.2, vector >=0.11 default-language: Haskell2010@@ -58,9 +58,9 @@ main-is: Main.hs hs-source-dirs: benchmarks build-depends:- base >= 4.8 && < 5,+ base >=4.8 && <5, criterion,- deepseq >= 1.4.2,+ deepseq >=1.4.2, jni, jvm, singletons
src/Language/Java.hs view
@@ -7,12 +7,13 @@ -- -- @ -- {-\# LANGUAGE DataKinds \#-}+-- {-\# LANGUAGE DeriveAnyClass \#-} -- module Object where -- -- import Language.Java as J -- -- newtype Object = Object ('J' (''Class' "java.lang.Object"))--- instance 'Coercible' Object+-- deriving (J.Coercible, J.Interpretation, J.Reify, J.Reflect) -- -- clone :: Object -> IO Object -- clone obj = J.'call' obj "clone" []@@ -26,8 +27,8 @@ -- To call Java methods using quasiquoted Java syntax instead, see -- "Language.Java.Inline". ----- __NOTE 1:__ To use any function in this module, you'll need an initialized JVM in the--- current process, using 'withJVM' or otherwise.+-- __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@@ -41,12 +42,12 @@ {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE UndecidableInstances #-} module Language.Java ( module Foreign.JNI.Types@@ -73,9 +74,9 @@ , jvalue , jobject -- * Conversions+ , Interpretation(..) , Reify(..) , Reflect(..)- , Interp -- * Re-exports , sing ) where@@ -117,7 +118,7 @@ data Pop a where PopValue :: a -> Pop a PopObject- :: (Coercible a ty, Coerce.Coercible a (J ty), IsReferenceType ty)+ :: (ty ~ Ty a, Coercible a, Coerce.Coercible a (J ty), IsReferenceType ty) => a -> Pop a @@ -145,7 +146,7 @@ -- | Pop a frame and return a JVM object. popWithObject- :: (Coercible a ty, Coerce.Coercible a (J ty), IsReferenceType ty, Monad m)+ :: (ty ~ Ty a, Coercible a, Coerce.Coercible a (J ty), IsReferenceType ty, Monad m) => a -> m (Pop a) popWithObject x = return (PopObject x)@@ -165,26 +166,28 @@ -- | 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.-class SingI ty => Coercible a (ty :: JType) | a -> ty where+class SingI (Ty a) => Coercible a where+ type Ty a :: JType coerce :: a -> JValue unsafeUncoerce :: JValue -> a default coerce- :: Coerce.Coercible a (J ty)+ :: Coerce.Coercible a (J (Ty a)) => a -> JValue- coerce x = JObject (Coerce.coerce x :: J ty)+ coerce x = JObject (Coerce.coerce x :: J (Ty a)) default unsafeUncoerce- :: Coerce.Coercible (J ty) a+ :: Coerce.Coercible (J (Ty a)) a => JValue -> a- unsafeUncoerce (JObject obj) = Coerce.coerce (unsafeCast obj :: J ty)+ unsafeUncoerce (JObject obj) = Coerce.coerce (unsafeCast obj :: J (Ty a)) unsafeUncoerce _ = error "Cannot unsafeUncoerce: object expected but value of primitive type found." -- | The identity instance.-instance SingI ty => Coercible (J ty) ty+instance SingI ty => Coercible (J ty) where+ type Ty (J ty) = ty -- | A JNI call may cause a (Java) exception to be raised. This module raises it -- as a Haskell exception wrapping the Java exception.@@ -202,51 +205,65 @@ withTypeRep :: Typeable a => (TypeRep -> a) -> a withTypeRep f = let x = f (typeOf x) in x -instance Coercible Bool ('Prim "boolean") where+instance Coercible Bool where+ type Ty Bool = 'Prim "boolean" coerce x = JBoolean (fromIntegral (fromEnum x)) unsafeUncoerce (JBoolean x) = toEnum (fromIntegral x) unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)-instance Coercible CChar ('Prim "byte") where+instance Coercible CChar where+ type Ty CChar = 'Prim "byte" coerce = JByte unsafeUncoerce (JByte x) = x unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)-instance Coercible Char ('Prim "char") where+instance Coercible Char where+ type Ty Char = 'Prim "char" coerce x = JChar (fromIntegral (ord x)) unsafeUncoerce (JChar x) = chr (fromIntegral x) unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)-instance Coercible Word16 ('Prim "char") where+instance Coercible Word16 where+ type Ty Word16 = 'Prim "char" coerce = JChar unsafeUncoerce (JChar x) = x unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)-instance Coercible Int16 ('Prim "short") where+instance Coercible Int16 where+ type Ty Int16 = 'Prim "short" coerce = JShort unsafeUncoerce (JShort x) = x unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)-instance Coercible Int32 ('Prim "int") where+instance Coercible Int32 where+ type Ty Int32 = 'Prim "int" coerce = JInt unsafeUncoerce (JInt x) = x unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)-instance Coercible Int64 ('Prim "long") where+instance Coercible Int64 where+ type Ty Int64 = 'Prim "long" coerce = JLong unsafeUncoerce (JLong x) = x unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)-instance Coercible Float ('Prim "float") where+instance Coercible Float where+ type Ty Float = 'Prim "float" coerce = JFloat unsafeUncoerce (JFloat x) = x unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)-instance Coercible Double ('Prim "double") where+instance Coercible Double where+ type Ty Double = 'Prim "double" coerce = JDouble unsafeUncoerce (JDouble x) = x unsafeUncoerce val = withTypeRep (throw . CoercionFailure val)-instance Coercible () 'Void where+instance Coercible () where+ type Ty () = 'Void coerce = error "Void value undefined." unsafeUncoerce _ = ()-instance Coercible (Choice.Choice a) ('Prim "boolean") where+instance Coercible (Choice.Choice a) where+ type Ty (Choice.Choice a) = 'Prim "boolean" coerce = coerce . Choice.toBool unsafeUncoerce = Choice.fromBool . unsafeUncoerce -- | Get the Java class of an object or anything 'Coercible' to one.-classOf :: forall a sym. (Coercible a ('Class sym), KnownSymbol sym) => a -> JNI.String+classOf+ :: forall a sym. (Ty a ~ 'Class sym, Coercible a, KnownSymbol sym)+ => a+ -> JNI.String classOf x = JNI.fromChars (symbolVal (Proxy :: Proxy sym)) `const` coerce x -- | Creates a new instance of the class whose name is resolved from the return@@ -258,8 +275,9 @@ -- @ new :: forall a sym.- ( Coerce.Coercible a (J ('Class sym))- , Coercible a ('Class sym)+ ( Ty a ~ 'Class sym+ , Coerce.Coercible a (J ('Class sym))+ , Coercible a ) => [JValue] -> IO a@@ -333,7 +351,7 @@ -- appropriately on the class instance and/or on the arguments to invoke the -- right method. call- :: forall a b ty1 ty2. (IsReferenceType ty1, Coercible a ty1, Coercible b ty2, Coerce.Coercible a (J ty1))+ :: forall a b ty1 ty2. (ty1 ~ Ty a, ty2 ~ Ty b, IsReferenceType ty1, Coercible a, Coercible b, Coerce.Coercible a (J ty1)) => a -- ^ Any object or value 'Coercible' to one -> JNI.String -- ^ Method name -> [JValue] -- ^ Arguments@@ -365,7 +383,7 @@ -- | Same as 'call', but for static methods. callStatic- :: forall a ty. Coercible a ty+ :: forall a ty. (ty ~ Ty a, Coercible a) => JNI.String -- ^ Class name -> JNI.String -- ^ Method name -> [JValue] -- ^ Arguments@@ -398,16 +416,18 @@ -- | Get a static field. getStaticField- :: forall a ty. Coercible a ty+ :: forall a ty. (ty ~ Ty a, Coercible a) => JNI.String -- ^ Class name -> JNI.String -- ^ Static field name -> IO a {-# INLINE getStaticField #-} getStaticField cname fname = do let retsing = sing :: Sing ty- klass = unsafeDupablePerformIO $- findClass (referenceTypeName (SClass (JNI.toChars cname)))- >>= newGlobalRef+ klass = unsafeDupablePerformIO $ do+ lk <- findClass (referenceTypeName (SClass (JNI.toChars cname)))+ gk <- newGlobalRef lk+ deleteLocalRef lk+ return gk field = unsafeDupablePerformIO $ getStaticFieldID klass fname (signature retsing) case retsing of SPrim "boolean" -> unsafeUncoerce . coerce . w2b <$> getStaticBooleanField klass field@@ -427,51 +447,40 @@ -- | Inject a value (of primitive or reference type) to a 'JValue'. This -- datatype is useful for e.g. passing arguments as a list of homogeneous type. -- Synonym for 'coerce'.-jvalue :: Coercible a ty => a -> JValue+jvalue :: (ty ~ Ty a, Coercible a) => a -> JValue jvalue = coerce -- | If @ty@ is a reference type, then it should be possible to get an object -- from a value.-jobject :: (Coercible a ty, IsReferenceType ty) => a -> J ty+jobject :: (ty ~ Ty a, Coercible a, IsReferenceType ty) => a -> J ty jobject x | JObject jobj <- coerce x = unsafeCast jobj | otherwise = error "impossible" --- | Map a Haskell type to the symbolic representation of a Java type.-type family Interp (a :: k) :: JType+-- | The 'Interp' type family is used by both 'Reify' and 'Reflect'. In order to+-- benefit from @-XGeneralizedNewtypeDeriving@ of new instances, we make this an+-- /associated/ type family instead of a standalone one.+class (SingI (Interp a), IsReferenceType (Interp a)) => Interpretation (a :: k) where+ -- | Map a Haskell type to the symbolic representation of a Java type.+ type Interp a :: JType -- | 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.------ 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.------ WARNING: The default method just creates a global reference to the Java--- object. Widespread use of this mechanism can cause memory problems since--- objects in the Java heap cause no pressure on the Haskell garbage collector.--- If the Haskell's GC is not executed by the time the Java heap is full, the--- Java code might sporadically fail with OutOfMemory exceptions.----class (SingI (Interp a), IsReferenceType (Interp a))- => Reify a where+class Interpretation a => Reify a where reify :: J (Interp a) -> IO a - default reify :: Coercible a (Interp a) => J (Interp a) -> IO a- reify x = (unsafeUncoerce . JObject) <$> newGlobalRef x+ default reify :: (Coercible a, Interp a ~ Ty a) => J (Interp a) -> IO a+ reify x = return (unsafeUncoerce (JObject x)) -- | 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.------ Instances of this class /must not/ claim global ownership.-class (SingI (Interp a), IsReferenceType (Interp a))- => Reflect a where+class Interpretation a => Reflect a where reflect :: a -> IO (J (Interp a)) - default reflect :: Coercible a (Interp a) => a -> IO (J (Interp a))- reflect x = newLocalRef (jobject x)+ default reflect :: (Coercible a, Interp a ~ Ty a) => a -> IO (J (Interp a))+ reflect x = return (jobject x) #if ! (__GLASGOW_HASKELL__ == 800 && __GLASGOW_HASKELL_PATCHLEVEL1__ == 1) reifyMVector@@ -506,15 +515,18 @@ #endif withStatic [d|- type instance Interp () = 'Class "java.lang.Object"-- instance Reify () where- reify _ = return ()+ instance (SingI ty, IsReferenceType ty) => Interpretation (J ty) where type Interp (J ty) = ty+ instance Interpretation (J ty) => Reify (J ty)+ instance Interpretation (J ty) => Reflect (J ty) - instance Reflect () where- reflect () = new []+ -- Ugly work around the fact that java has no equivalent of the 'unit' type:+ -- We take an arbitrary serializable type to represent it.+ instance Interpretation () where type Interp () = 'Class "java.lang.Short"+ instance Reify () where reify _ = return ()+ instance Reflect () where reflect () = new [JShort 0] - type instance Interp ByteString = 'Array ('Prim "byte")+ instance Interpretation ByteString where+ type Interp ByteString = 'Array ('Prim "byte") instance Reify ByteString where reify jobj = do@@ -532,7 +544,8 @@ setByteArrayRegion arr 0 (fromIntegral n) content return arr - type instance Interp Bool = 'Class "java.lang.Boolean"+ instance Interpretation Bool where+ type Interp Bool = 'Class "java.lang.Boolean" instance Reify Bool where reify jobj = do@@ -548,7 +561,8 @@ instance Reflect Bool where reflect x = new [JBoolean (fromIntegral (fromEnum x))] - type instance Interp CChar = 'Class "java.lang.Byte"+ instance Interpretation CChar where+ type Interp CChar = 'Class "java.lang.Byte" instance Reify CChar where reify jobj = do@@ -563,7 +577,8 @@ instance Reflect CChar where reflect x = Language.Java.new [JByte x] - type instance Interp Int16 = 'Class "java.lang.Short"+ instance Interpretation Int16 where+ type Interp Int16 = 'Class "java.lang.Short" instance Reify Int16 where reify jobj = do@@ -578,7 +593,8 @@ instance Reflect Int16 where reflect x = new [JShort x] - type instance Interp Int32 = 'Class "java.lang.Integer"+ instance Interpretation Int32 where+ type Interp Int32 = 'Class "java.lang.Integer" instance Reify Int32 where reify jobj = do@@ -594,7 +610,8 @@ instance Reflect Int32 where reflect x = new [JInt x] - type instance Interp Int64 = 'Class "java.lang.Long"+ instance Interpretation Int64 where+ type Interp Int64 = 'Class "java.lang.Long" instance Reify Int64 where reify jobj = do@@ -609,7 +626,8 @@ instance Reflect Int64 where reflect x = new [JLong x] - type instance Interp Word16 = 'Class "java.lang.Character"+ instance Interpretation Word16 where+ type Interp Word16 = 'Class "java.lang.Character" instance Reify Word16 where reify jobj = do@@ -625,7 +643,8 @@ instance Reflect Word16 where reflect x = new [JChar x] - type instance Interp Double = 'Class "java.lang.Double"+ instance Interpretation Double where+ type Interp Double = 'Class "java.lang.Double" instance Reify Double where reify jobj = do@@ -640,7 +659,8 @@ instance Reflect Double where reflect x = new [JDouble x] - type instance Interp Float = 'Class "java.lang.Float"+ instance Interpretation Float where+ type Interp Float = 'Class "java.lang.Float" instance Reify Float where reify jobj = do@@ -655,7 +675,8 @@ instance Reflect Float where reflect x = new [JFloat x] - type instance Interp Text = 'Class "java.lang.String"+ instance Interpretation Text where+ type Interp Text = 'Class "java.lang.String" instance Reify Text where reify jobj = do@@ -673,7 +694,8 @@ -- Instances can't be compiled on GHC 8.0.1 due to -- https://ghc.haskell.org/trac/ghc/ticket/12082. #if ! (__GLASGOW_HASKELL__ == 800 && __GLASGOW_HASKELL_PATCHLEVEL1__ == 1)- type instance Interp (IOVector Int32) = 'Array ('Prim "int")+ instance Interpretation (IOVector Int32) where+ type Interp (IOVector Int32) = 'Array ('Prim "int") instance Reify (IOVector Int32) where reify = reifyMVector (getIntArrayElements) (releaseIntArrayElements)@@ -681,7 +703,8 @@ instance Reflect (IOVector Int32) where reflect = reflectMVector (newIntArray) (setIntArrayRegion) - type instance Interp (Vector Int32) = 'Array ('Prim "int")+ instance Interpretation (Vector Int32) where+ type Interp (Vector Int32) = 'Array ('Prim "int") instance Reify (Vector Int32) where reify = Vector.freeze <=< reify@@ -689,8 +712,8 @@ instance Reflect (Vector Int32) where reflect = reflect <=< Vector.thaw #endif-- type instance Interp [a] = 'Array (Interp a)+ instance Interpretation a => Interpretation [a] where+ type Interp [a] = 'Array (Interp a) instance Reify a => Reify [a] where reify jobj = do