packages feed

jni 0.3.1 → 0.4.0

raw patch · 7 files changed

+219/−82 lines, 7 filesdep +constraintsdep +deepseqdep ~inline-c

Dependencies added: constraints, deepseq

Dependency ranges changed: inline-c

Files

jni.cabal view
@@ -1,5 +1,5 @@ name:                jni-version:             0.3.1+version:             0.4.0 synopsis:            Complete JNI raw bindings. description:         Please see README.md. homepage:            https://github.com/tweag/inline-java/tree/master/jni#readme@@ -12,8 +12,6 @@ build-type:          Simple cabal-version:       >=1.10 extra-source-files:  README.md-extra-tmp-files:-  src/Foreign/JNI.c  source-repository head   type: git@@ -22,13 +20,15 @@  library   hs-source-dirs: src-  c-sources: src/Foreign/JNI.c+  if impl(ghc < 8.2.1)+    c-sources: src/Foreign/JNI.c   cc-options: -std=c11   extra-libraries: jvm   exposed-modules:     Foreign.JNI     Foreign.JNI.Types     Foreign.JNI.String+    Foreign.JNI.Internal   other-modules:     Foreign.JNI.NativeMethod   build-depends:@@ -36,8 +36,15 @@     bytestring >=0.10,     choice >= 0.2.0,     containers >=0.5,-    inline-c >=0.5.6.1,+    constraints >=0.8,+    deepseq >= 1.4.2,     singletons >= 2.0,     thread-local-storage >=0.1+  if impl(ghc < 8.2.1)+    build-depends:+      inline-c >=0.5.6.1+  else+    build-depends:+      inline-c >=0.6   build-tools: cpphs   default-language: Haskell2010
src/Foreign/JNI.c view
@@ -1,6 +1,8 @@  #include <jni.h> +#include <stdio.h>+ #include <errno.h>  #include <stdlib.h>
src/Foreign/JNI.cpphs view
@@ -36,12 +36,18 @@  module Foreign.JNI   ( -- * JNI functions-    -- ** VM creation+    -- ** VM management     withJVM+  , newJVM+  , destroyJVM     -- ** Class loading   , defineClass   , JNINativeMethod(..)   , registerNatives+    -- ** String wrappers+  , ReferenceTypeName+  , MethodSignature+  , Signature     -- ** Exceptions   , JVMException(..)   , throw@@ -56,6 +62,8 @@     -- ** Reference manipulation   , newGlobalRef   , deleteGlobalRef+  , newGlobalRefNonFinalized+  , deleteGlobalRefNonFinalized   , newLocalRef   , deleteLocalRef   , pushLocalFrame@@ -187,6 +195,7 @@   , newForeignPtr_   , withForeignPtr   )+import Foreign.JNI.Internal import Foreign.JNI.NativeMethod import Foreign.JNI.Types import qualified Foreign.JNI.String as JNI@@ -195,12 +204,14 @@ import GHC.ForeignPtr (newConcForeignPtr) import qualified Language.C.Inline as C import qualified Language.C.Inline.Unsafe as CU+import System.IO (fixIO) import System.IO.Unsafe (unsafePerformIO) import Prelude hiding (String)  C.context (C.baseCtx <> C.bsCtx <> jniCtx)  C.include "<jni.h>"+C.include "<stdio.h>" C.include "<errno.h>" C.include "<stdlib.h>" @@ -269,16 +280,15 @@             \st@(readers, aim) -> ((readers - 1, aim), st)     case st of       -- Notify the writer if I'm the last reader.-      (0, Writing mv) -> putMVar mv ()+      (1, Writing mv) -> putMVar mv ()       _               -> return ()  -- | Waits until the current read locks are released and grants a write lock. acquireWriteLock :: RWLock -> IO () acquireWriteLock (RWLock ref) = do     mv <- newEmptyMVar-    join $ atomicModifyIORef ref $ \case-      (0, _) -> ((0, Writing mv),   return ())-      st     -> (               st, takeMVar mv)+    join $ atomicModifyIORef ref $ \(readers, _) ->+      ((readers, Writing mv), when (readers > 0) (takeMVar mv))  -- | This lock is used to avoid the JVM from dying before any finalizers -- deleting global references are finished.@@ -331,40 +341,46 @@   foldr (\str k cstrs -> BS.useAsCString str $ \cstr -> k (cstr:cstrs)) m strs []  -- | Create a new JVM, with the given arguments. /Can only be called once/. Best--- practice: use it to wrap your @main@ function.+-- practice: use 'withJVM' instead. Only useful for GHCi.+newJVM :: [ByteString] -> IO JVM+newJVM options = JVM_ <$> do+    useAsCStrings options $ \cstrs -> do+      withArray cstrs $ \(coptions :: Ptr (Ptr CChar)) -> do+        let n = fromIntegral (length cstrs) :: C.CInt+        [C.block| JavaVM * {+          JavaVM *jvm;+          JNIEnv *env;+          JavaVMInitArgs vm_args;+          JavaVMOption *options = malloc(sizeof(JavaVMOption) * $(int n));+          for(int i = 0; i < $(int n); i++)+                  options[i].optionString = $(char **coptions)[i];+          vm_args.version = JNI_VERSION_1_6;+          vm_args.nOptions = $(int n);+          vm_args.options = options;+          vm_args.ignoreUnrecognized = 0;+          JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);+          free(options);+          return jvm; } |]++-- | Deallocate a 'JVM' created using 'newJVM'.+destroyJVM :: JVM -> IO ()+destroyJVM (JVM_ jvm) = do+    acquireWriteLock globalJVMLock+    [C.block| void { (*$(JavaVM *jvm))->DestroyJavaVM($(JavaVM *jvm)); } |]++-- | Create a new JVM, with the given arguments. Destroy it once the given+-- action completes. /Can only be called once/. Best practice: use it to wrap+-- your @main@ function. withJVM :: [ByteString] -> IO a -> IO a-withJVM options action =-    bracket ini fini (const action)-  where-    ini = do-      useAsCStrings options $ \cstrs -> do-        withArray cstrs $ \(coptions :: Ptr (Ptr CChar)) -> do-          let n = fromIntegral (length cstrs) :: C.CInt-          [C.block| JavaVM * {-            JavaVM *jvm;-            JNIEnv *env;-            JavaVMInitArgs vm_args;-            JavaVMOption *options = malloc(sizeof(JavaVMOption) * $(int n));-            for(int i = 0; i < $(int n); i++)-                    options[i].optionString = $(char **coptions)[i];-            vm_args.version = JNI_VERSION_1_6;-            vm_args.nOptions = $(int n);-            vm_args.options = options;-            vm_args.ignoreUnrecognized = 0;-            JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);-            free(options);-            return jvm; } |]-    fini jvm = do-      acquireWriteLock globalJVMLock-      [C.block| void { (*$(JavaVM *jvm))->DestroyJavaVM($(JavaVM *jvm)); } |]+withJVM options action = bracket (newJVM options) destroyJVM (const action)  defineClass   :: Coercible o (J ('Class "java.lang.ClassLoader"))-  => JNI.String -- ^ Class name-  -> o          -- ^ Loader+  => ReferenceTypeName -- ^ Class name+  -> o -- ^ Loader   -> ByteString -- ^ Bytecode buffer   -> IO JClass-defineClass name (coerce -> upcast -> loader) buf = withJNIEnv $ \env ->+defineClass (coerce -> name) (coerce -> upcast -> loader) buf = withJNIEnv $ \env ->     throwIfException env $     JNI.withString name $ \namep ->     objectFromPtr =<<@@ -404,16 +420,16 @@                                    $(char *msgp)) } |]  findClass-  :: JNI.String -- ^ Class name+  :: ReferenceTypeName -- ^ Class name   -> IO JClass-findClass name = withJNIEnv $ \env ->+findClass (coerce -> name) = withJNIEnv $ \env ->     throwIfException env $     JNI.withString name $ \namep ->     objectFromPtr =<<     [CU.exp| jclass { (*$(JNIEnv *env))->FindClass($(JNIEnv *env), $(char *namep)) } |] -newObject :: JClass -> JNI.String -> [JValue] -> IO JObject-newObject cls sig args = withJNIEnv $ \env ->+newObject :: JClass -> MethodSignature -> [JValue] -> IO JObject+newObject cls (coerce -> sig) args = withJNIEnv $ \env ->     throwIfException env $     withJValues args $ \cargs -> do       constr <- getMethodID cls "<init>" sig@@ -426,9 +442,9 @@ getFieldID   :: JClass -- ^ A class object as returned by 'findClass'   -> JNI.String -- ^ Field name-  -> JNI.String -- ^ JNI signature+  -> Signature -- ^ JNI signature   -> IO JFieldID-getFieldID cls fieldname sig = withJNIEnv $ \env ->+getFieldID cls fieldname (coerce -> sig) = withJNIEnv $ \env ->     throwIfException env $     JNI.withString fieldname $ \fieldnamep ->     JNI.withString sig $ \sigp ->@@ -441,9 +457,9 @@ getStaticFieldID   :: JClass -- ^ A class object as returned by 'findClass'   -> JNI.String -- ^ Field name-  -> JNI.String -- ^ JNI signature+  -> Signature -- ^ JNI signature   -> IO JFieldID-getStaticFieldID cls fieldname sig = withJNIEnv $ \env ->+getStaticFieldID cls fieldname (coerce -> sig) = withJNIEnv $ \env ->     throwIfException env $     JNI.withString fieldname $ \fieldnamep ->     JNI.withString sig $ \sigp ->@@ -548,9 +564,9 @@ getMethodID   :: JClass -- ^ A class object as returned by 'findClass'   -> JNI.String -- ^ Field name-  -> JNI.String -- ^ JNI signature+  -> MethodSignature -- ^ JNI signature   -> IO JMethodID-getMethodID cls methodname sig = withJNIEnv $ \env ->+getMethodID cls methodname (coerce -> sig) = withJNIEnv $ \env ->     throwIfException env $     JNI.withString methodname $ \methodnamep ->     JNI.withString sig $ \sigp ->@@ -563,9 +579,9 @@ getStaticMethodID   :: JClass -- ^ A class object as returned by 'findClass'   -> JNI.String -- ^ Field name-  -> JNI.String -- ^ JNI signature+  -> MethodSignature -- ^ JNI signature   -> IO JMethodID-getStaticMethodID cls methodname sig = withJNIEnv $ \env ->+getStaticMethodID cls methodname (coerce -> sig) = withJNIEnv $ \env ->     throwIfException env $     JNI.withString methodname $ \methodnamep ->     JNI.withString sig $ \sigp ->@@ -593,20 +609,35 @@       [CU.exp| jobject {         (*$(JNIEnv *env))->NewGlobalRef($(JNIEnv *env),                                         $fptr-ptr:(jobject obj)) } |]-    coerce <$> J <$> newConcForeignPtr gobj (finalize gobj)-  where-    finalize gobj = do-      bracket (tryAcquireReadLock globalJVMLock)-              (\doRead -> when (toBool doRead) $ releaseReadLock globalJVMLock)-              $ \doRead ->-        when (toBool doRead) $ withJNIEnv $ \env ->-          [CU.block| void { (*$(JNIEnv *env))->DeleteGlobalRef($(JNIEnv *env)-                                                              ,$(jobject gobj));-                          } |]+    fixIO $ \j ->+      coerce <$> J <$> newConcForeignPtr gobj (deleteGlobalRefNonFinalized j)  deleteGlobalRef :: Coercible o (J ty) => o -> IO () deleteGlobalRef (coerce -> J p) = finalizeForeignPtr p +-- | Like 'newGlobalRef' but it doesn't attach a finalizer to destroy+-- the reference when it is not longer reachable. Use+-- 'deleteGlobalRefNonFinalized' to destroy this reference.+newGlobalRefNonFinalized :: Coercible o (J ty) => o -> IO o+newGlobalRefNonFinalized (coerce -> upcast -> obj) = withJNIEnv $ \env -> do+    gobj <-+      [CU.exp| jobject {+        (*$(JNIEnv *env))->NewGlobalRef($(JNIEnv *env),+                                        $fptr-ptr:(jobject obj)) } |]+    coerce <$> J <$> newForeignPtr_ gobj++-- | Like 'deleteGlobalRef' but it can be used only on references created with+-- 'newGlobalRefNonFinalized'.+deleteGlobalRefNonFinalized :: Coercible o (J ty) => o -> IO ()+deleteGlobalRefNonFinalized (coerce -> upcast -> obj) = do+    bracket (tryAcquireReadLock globalJVMLock)+            (\doRead -> when (toBool doRead) $ releaseReadLock globalJVMLock)+            $ \doRead ->+      when (toBool doRead) $ withJNIEnv $ \env -> do+        [CU.block| void { (*$(JNIEnv *env))->DeleteGlobalRef($(JNIEnv *env)+                                                            ,$fptr-ptr:(jobject obj));+                        } |]+ -- NB: Cannot add a finalizer to local references because it may -- run in a thread where the reference is not valid. newLocalRef :: Coercible o (J ty) => o -> IO (J ty)@@ -819,16 +850,32 @@                                             $fptr-ptr:(jstring jstr),                                             $(jchar *chars)) } |] -getObjectArrayElement :: Coercible o (JArray a) => o -> Int32 -> IO (J a)-getObjectArrayElement (coerce -> upcast -> array) i = withJNIEnv $ \env ->-    unsafeCast <$> (objectFromPtr =<<)+getObjectArrayElement+  :: forall a o.+     (IsReferenceType a, Coercible o (J a))+  => JArray a+  -> Int32+  -> IO o+getObjectArrayElement (arrayUpcast -> array) i = withJNIEnv $ \env ->+    ( (coerce :: J a -> o)+    . (unsafeCast :: JObject -> J a)+    ) <$> (objectFromPtr =<<)     [C.exp| jobject {       (*$(JNIEnv *env))->GetObjectArrayElement($(JNIEnv *env),                                                $fptr-ptr:(jarray array),                                                $(jsize i)) } |] -setObjectArrayElement :: Coercible o (J a) => JObjectArray -> Int32 -> o -> IO ()-setObjectArrayElement array i (coerce -> upcast -> x) = withJNIEnv $ \env ->+setObjectArrayElement+  :: forall a o.+     (IsReferenceType a, Coercible o (J a))+  => JArray a+  -> Int32+  -> o+  -> IO ()+setObjectArrayElement (arrayUpcast -> array)+                      i+                      ((coerce :: o -> J a) -> upcast -> x) =+    withJNIEnv $ \env ->     [C.exp| void {       (*$(JNIEnv *env))->SetObjectArrayElement($(JNIEnv *env),                                                $fptr-ptr:(jobjectArray array),
+ src/Foreign/JNI/Internal.hs view
@@ -0,0 +1,25 @@+module Foreign.JNI.Internal where++import qualified Foreign.JNI.String as JNI++-- | A reference type name is not just any 'JNI.String', but a fully qualified+-- identifier well-formed by construction.+newtype ReferenceTypeName = ReferenceTypeName JNI.String+  deriving (Eq, Ord)++instance Show ReferenceTypeName where+  show (ReferenceTypeName str) = show str++-- | A string representing a signature, well-formed by construction.+newtype Signature = Signature JNI.String+  deriving (Eq, Ord)++instance Show Signature where+  show (Signature str) = show str++-- | A string representing a method signature, well-formed by construction.+newtype MethodSignature = MethodSignature JNI.String+  deriving (Eq, Ord)++instance Show MethodSignature where+  show (MethodSignature str) = show str
src/Foreign/JNI/NativeMethod.hsc view
@@ -6,6 +6,8 @@ module Foreign.JNI.NativeMethod where  import qualified Data.ByteString.Unsafe as BS+import Data.Coerce (coerce)+import Foreign.JNI.Internal import qualified Foreign.JNI.String as JNI import Foreign.Ptr (FunPtr) import Foreign.Storable (Storable(..))@@ -18,7 +20,7 @@  data JNINativeMethod = forall a. JNINativeMethod   { jniNativeMethodName :: JNI.String-  , jniNativeMethodSignature :: JNI.String+  , jniNativeMethodSignature :: MethodSignature   , jniNativeMethodFunPtr :: FunPtr a   } @@ -32,9 +34,9 @@       return $         JNINativeMethod           (JNI.unsafeFromByteString name)-          (JNI.unsafeFromByteString sig)+          (coerce (JNI.unsafeFromByteString sig))           fptr   poke ptr JNINativeMethod{..} = do       JNI.withString jniNativeMethodName $ #{poke JNINativeMethod, name} ptr-      JNI.withString jniNativeMethodSignature $ #{poke JNINativeMethod, signature} ptr+      JNI.withString (coerce jniNativeMethodSignature) $ #{poke JNINativeMethod, signature} ptr       #{poke JNINativeMethod, fnPtr} ptr jniNativeMethodFunPtr
src/Foreign/JNI/String.hs view
@@ -23,6 +23,7 @@   ) where  import qualified Data.ByteString as BS+import qualified Data.ByteString.Internal as BS import qualified Data.ByteString.Unsafe as BS import Data.ByteString (ByteString) import Data.String (IsString(..))@@ -33,6 +34,7 @@ import qualified Prelude import Prelude hiding (String) +-- A JNI string is represented as a NUL terminated bytestring in UTF-8 encoding. newtype String = String ByteString   deriving (Eq, Ord) @@ -44,8 +46,11 @@  fromChars :: Prelude.String -> String {-# INLINE [0] fromChars #-}-fromChars str = unsafeDupablePerformIO $ do-    String <$> (BS.unsafePackCString =<< GHC.newCString GHC.utf8 str)+fromChars str = unsafeDupablePerformIO $+    GHC.withCString GHC.utf8 str $ \cstr -> do+      -- we need to copy the trailing NUL+      len <- BS.c_strlen cstr+      String <$> BS.packCStringLen (cstr, fromIntegral len + 1)  toChars :: String -> Prelude.String toChars (String bs) = unsafeDupablePerformIO $ BS.unsafeUseAsCString bs $ GHC.peekCString GHC.utf8@@ -53,8 +58,9 @@ withString :: String -> (CString -> IO a) -> IO a withString (String bs) f = BS.unsafeUseAsCString bs f +-- Discards the trailing NUL. toByteString :: String -> ByteString-toByteString (String bs) = bs+toByteString (String bs) = BS.init bs  -- | O(1) if the input is null-terminated. Otherwise the input is copied into -- a null-terminated buffer first.
src/Foreign/JNI/Types.hs view
@@ -5,6 +5,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RoleAnnotations #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-}@@ -26,12 +27,17 @@   , J(..)   , jnull   , upcast+  , arrayUpcast   , unsafeCast   , generic   , unsafeUngeneric   , jtypeOf+  , ReferenceTypeName+  , singToIsReferenceType   , referenceTypeName+  , Signature   , signature+  , MethodSignature   , methodSignature   , JVM(..)   , JNIEnv(..)@@ -61,11 +67,13 @@   , jniCtx   ) where +import Control.DeepSeq (NFData) import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString.Builder as Builder import qualified Data.ByteString.Builder.Prim as Prim import Data.ByteString.Builder (Builder) import Data.Char (chr, ord)+import Data.Constraint (Dict(..)) import Data.Int import qualified Data.Map as Map import Data.Monoid ((<>))@@ -77,6 +85,7 @@   , KProxy(..) #endif   )+import Data.Singletons.Prelude (Sing(..)) import Data.Singletons.TypeLits (KnownSymbol, symbolVal) import Data.Word import Foreign.C (CChar)@@ -87,6 +96,7 @@   , withForeignPtr   ) import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)+import Foreign.JNI.Internal import Foreign.JNI.NativeMethod import qualified Foreign.JNI.String as JNI import Foreign.Marshal.Alloc (allocaBytesAligned)@@ -99,19 +109,19 @@  -- | A JVM instance. newtype JVM = JVM_ (Ptr JVM)-  deriving (Eq, Show, Storable)+  deriving (Eq, Show, Storable, NFData)  -- | The thread-local JNI context. Do not share this object between threads. newtype JNIEnv = JNIEnv_ (Ptr JNIEnv)-  deriving (Eq, Show, Storable)+  deriving (Eq, Show, Storable, NFData)  -- | A thread-local reference to a field of an object. newtype JFieldID = JFieldID_ (Ptr JFieldID)-  deriving (Eq, Show, Storable)+  deriving (Eq, Show, Storable, NFData)  -- | A thread-local reference to a method of an object. newtype JMethodID = JMethodID_ (Ptr JMethodID)-  deriving (Eq, Show, Storable)+  deriving (Eq, Show, Storable, NFData)  -- | Not part of the JNI. The kind of 'J' type indices. Useful to reflect the -- object's class at the type-level.@@ -133,7 +143,22 @@ instance IsReferenceType ('Array ty) instance IsReferenceType ty => IsReferenceType ('Generic ty tys) +-- | Produces evidence for IsReferenceType from a `Sing ty`.+singToIsReferenceType :: Sing (ty :: JType) -> Maybe (Dict (IsReferenceType ty))+singToIsReferenceType tysing = case tysing of+    SClass _ -> Just Dict+    SPrim _ -> Nothing+    SIface _ -> Just Dict+    SArray _ -> Just Dict+    SGeneric tysing' _ -> (\Dict -> Dict) <$> singToIsReferenceType tysing'+    SVoid -> Nothing+ data instance Sing (a :: JType) where+  -- Using String instead of JNI.String for the singleton data constructors+  -- is an optimization. Otherwise, the comparisons in Language.Java.call+  -- and callStatic would involve allocations and cannot be cached.+  --+  -- See commit 3da51a4 and https://github.com/tweag/inline-java/issues/11   SClass :: String -> Sing ('Class sym)   SIface :: String -> Sing ('Iface sym)   SPrim :: String -> Sing ('Prim sym)@@ -141,6 +166,24 @@   SGeneric :: Sing ty -> Sing tys -> Sing ('Generic ty tys)   SVoid :: Sing 'Void +instance Show (Sing (a :: JType)) where+  showsPrec d (SClass s) = showParen (d > 10) $+      showString "SClass " . showsPrec 11 s+  showsPrec d (SIface s) = showParen (d > 10) $+      showString "SIface " . showsPrec 11 s+  showsPrec d (SPrim s) = showParen (d > 10) $+      showString "SPrim " . showsPrec 11 s+  showsPrec d (SArray s) = showParen (d > 10) $+      showString "SArray " . showsPrec 11 s+  showsPrec d (SGeneric s sargs) = showParen (d > 10) $+      showString "SGeneric " . showsPrec 11 s . showsPrec 11 sargs+  showsPrec _ SVoid = showString "SVoid"++instance Show (Sing (a :: [JType])) where+  showsPrec _ SNil = showString "SNil"+  showsPrec d (SCons ty tys) = showParen (d > 10) $+      showString "SCons " . showsPrec 11 ty . showChar ' ' . showsPrec 11 tys+ -- XXX SingI constraint temporary hack because GHC 7.10 has trouble inferring -- this constraint in 'signature'. instance (KnownSymbol sym, SingI sym) => SingI ('Class (sym :: Symbol)) where@@ -173,6 +216,10 @@ upcast :: J a -> JObject upcast = unsafeCast +-- | Any array of a reference type can be casted to an array of @Object@s.+arrayUpcast :: IsReferenceType ty => J ('Array ty) -> JObjectArray+arrayUpcast = unsafeCast+ -- | Unsafe type cast. Should only be used to downcast. unsafeCast :: J a -> J b unsafeCast (J x) = J (castForeignPtr x)@@ -278,10 +325,10 @@   (<> Builder.char7 '\NUL')  -- | The name of a type, suitable for passing to 'Foreign.JNI.findClass'.-referenceTypeName :: IsReferenceType ty => Sing (ty :: JType) -> JNI.String-referenceTypeName (SClass sym) = build $ classSymbolBuilder sym-referenceTypeName (SIface sym) = build $ classSymbolBuilder sym-referenceTypeName ty@(SArray _) = build $ signatureBuilder ty+referenceTypeName :: IsReferenceType ty => Sing (ty :: JType) -> ReferenceTypeName+referenceTypeName (SClass sym) = ReferenceTypeName $ build $ classSymbolBuilder sym+referenceTypeName (SIface sym) = ReferenceTypeName $ build $ classSymbolBuilder sym+referenceTypeName ty@(SArray _) = ReferenceTypeName $ build $ signatureBuilder ty referenceTypeName (SGeneric ty@(SClass _) _) = referenceTypeName ty referenceTypeName (SGeneric ty@(SIface _) _) = referenceTypeName ty referenceTypeName _ = error "referenceTypeName: Impossible."@@ -311,8 +358,8 @@ signatureBuilder SVoid = Builder.char7 'V'  -- | Construct a JNI type signature from a Java type.-signature :: Sing (ty :: JType) -> JNI.String-signature = build . signatureBuilder+signature :: Sing (ty :: JType) -> Signature+signature = Signature . build . signatureBuilder  -- | Construct a method's JNI type signature, given the type of the arguments -- and the return type.@@ -323,8 +370,9 @@   :: [SomeSing ('KProxy :: KProxy JType)] #endif   -> Sing (ty :: JType)-  -> JNI.String+  -> MethodSignature methodSignature args ret =+    MethodSignature $     build $     Builder.char7 '(' <>     mconcat (map (\(SomeSing s) -> signatureBuilder s) args) <>