inline-java 0.9.1 → 0.10.0
raw patch · 5 files changed
+122/−11 lines, 5 filesdep +QuickCheckdep +quickcheck-unicodedep +vectordep ~jnidep ~jvm
Dependencies added: QuickCheck, quickcheck-unicode, vector
Dependency ranges changed: jni, jvm
Files
- CHANGELOG.md +6/−0
- README.md +77/−3
- inline-java.cabal +8/−4
- tests/common/Language/Java/InlineSpec.hs +25/−0
- tests/common/Main.hs +6/−4
CHANGELOG.md view
@@ -10,6 +10,12 @@ ### Removed ### Changed +## [0.10.0] - 2020-11-30++### Changed++* Support building with jni-0.8+ ## [0.9.1] - 2020-07-16 ### Changed
README.md view
@@ -1,7 +1,7 @@ # inline-java: Call any JVM function from Haskell [](https://circleci.com/gh/tweag/inline-java)-[](https://buildkite.com/tweag-1/inline-java)+[](https://buildkite.com/tweag-1/inline-java) The Haskell standard includes a native foreign function interface (FFI). Using it can be a bit involved and only C support is@@ -74,7 +74,7 @@ ## Building the safe interface -There is [an experimental interface][safe-interface] which catches+There is [an experimental interface][safe-inline-java] which catches common memory management mistakes at compile time. This interface currently needs a [fork][linear-types-ghc] of GHC which supports the [LinearTypes][linear-types-proposal] language extension. Both the GHC@@ -101,8 +101,12 @@ If you want to know more about how it is implemented, look at [our post][inline-java-plugin] on the plugin implementation. +There is also a post which gives an overview of the+[safe interface][safe-interface-post].+ [inline-java-tutorial]: https://www.tweag.io/posts/2017-09-15-inline-java-tutorial.html [inline-java-plugin]: https://www.tweag.io/posts/2017-09-22-inline-java-ghc-plugin.html+[safe-interface-post]: https://www.tweag.io/blog/2020-02-06-safe-inline-java ## Debugging @@ -114,6 +118,76 @@ If `-ddump-to-file` is in effect (as when using `stack`), the java code is dumped to `<module>.dump-java` instead. +## Troubleshooting++### Build-time error `package or class Blah does not exist`++`inline-java` is going to invoke the `javac` compiler, and any classes+used in `java` quotations need to be reachable via the `CLASSPATH`+environment variable. For instance,+```+CLASSPATH=/path/to/my.jar:/some/other/path ghc --make program.hs+```++### Run-time error `ThreadNotAttached`++Haskell threads need to be attached to the JVM before making JNI calls.+`Foreign.JNI.withJVM` attaches the calling thread, and other threads+can be attached with `Foreign.JNI.runInAttachedThread`. When the JVM+calls into Haskell, the thread is already attached.++### Run-time error `ThreadNotBound`++JNI calls need to be done from bound threads. The thread invoking the+`main` function of a program is bound. Threads created with `forkOS`+are bound. In other threads, `Control.Concurrent.runInBoundThread`+can be used to run a computation in a bound thread.++### Run-time error `java.lang.NoClassDefFoundError`++Classes might not be found at runtime if they are not in a folder or+jar listed in the parameter `-Djava.class.path=<classpath>` passed+to `withJVM`.++```Haskell+withJVM ["-Djava.class.path=/path/to/my.jar:/some/other/path"] $ do+ ...+```++Additionally, classes might not be found if a thread other than the one+calling `main` is trying to use them. One solution is to have the thread+calling `main` load all the classes in advance. Then the classes will+be available in the JVM for other threads that need them.+Calling `Language.Java.Inline.loadJavaWrappers` will have the effect of+loading all classes needed for `java` quotations, which will suffice in+many cases.++Another option is to set the context class loader of other threads,+so they earn the ability to load classes on their own. This might+work when the thread was attached to the JVM via the JNI, and+the context class loader is just `null`.++```Haskell+loader <- [java| Thread.currentThread().getContextClassLoader() |]+...+forkOS $ runInAttachedThread $ do+ [java| { Thread.currentThread().setContextClassLoader($loader); } |]+ ...+```++### Run-time error `JVMException`++Any java exception that goes from Java to Haskell will be wrapped+as a value of type `JVMException` with a reference to the Java object+representing the exception. The message and the stack trace of the+exception can be retrieved from the exception object with more JNI+calls, e.g.++```Haskell+\(JVMException e) -> [java| { $e.printStackTrace(); } |]+```+or with `JNI.Foreign.showException`.+ ## License Copyright (c) 2015-2016 EURL Tweag.@@ -126,7 +200,7 @@ ## Sponsors -[<img src="http://i.imgur.com/0HK8X4y.png" height="65">](http://tweag.io)+[](http://tweag.io) [](http://leapyear.io)
inline-java.cabal view
@@ -1,5 +1,5 @@ name: inline-java-version: 0.9.1+version: 0.10.0 synopsis: Java interop via inline Java code in Haskell modules. description: Please see README.md. homepage: http://github.com/tweag/inline-java#readme@@ -56,8 +56,8 @@ directory >=1.2, filepath >=1, ghc >=8.10.1 && <=8.11,- jni >=0.7 && <0.8,- jvm >=0.5 && <0.6,+ jni >=0.8 && <0.9,+ jvm >=0.6 && <0.7, language-java >=0.2, mtl >=2.2.1, process >=1.2,@@ -90,8 +90,12 @@ jvm, hspec, inline-java,- text+ QuickCheck,+ quickcheck-unicode,+ text,+ vector default-language: Haskell2010+ ghc-options: -threaded cpp-options: -DHSPEC_DISCOVER=hspec-discover if flag(linear-types) hs-source-dirs: tests/linear-types
tests/common/Language/Java/InlineSpec.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE ExplicitNamespaces #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-}@@ -9,11 +10,18 @@ module Language.Java.InlineSpec(spec) where +import Data.Char import Data.Int+import qualified Data.Text as Text+import qualified Data.Vector.Storable as Vector+import Data.Vector.Storable.Mutable (IOVector) import Foreign.JNI (JVMException) import Language.Java import Language.Java.Inline import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck+import Test.QuickCheck.Unicode type ObjectClass = 'Class "java.lang.Object" type ListClass = 'Iface "java.util.List"@@ -122,3 +130,20 @@ st :: J ('Class "java.lang.Thread$State") <- [java| Thread.State.NEW |] [java| $st == Thread.State.NEW |] `shouldReturn` True++ it "Supports modified utf-8 encoding from Haskell to Java" $+ withLocalRef (reflect ("a\NULb" :: Text.Text)) $ \jString ->+ [java| "a\0b".equals($jString) |] `shouldReturn` True++ it "Supports modified utf-8 encoding from Java to Haskell" $ do+ withLocalRef [java| "a\0b" |] reify `shouldReturn` ("a\NULb" :: Text.Text)++ prop "Processes Unicode code points as the JVM does" $ do+ \u -> ioProperty $ do+ let chars :: [Char] = fromUnicode u+ let text = Text.pack chars+ codePoints :: IOVector Int32 <- Vector.thaw $ Vector.fromList $ map (fromIntegral . ord) chars+ withLocalRef (reflect codePoints) $ \jPoints -> do+ jString <- [java| new String($jPoints, 0, $jPoints.length) |]+ jText <- reify jString+ return $ jText === text
tests/common/Main.hs view
@@ -1,11 +1,13 @@ module Main where -import Language.Java (withJVM)+import Control.Concurrent (runInBoundThread)+import Foreign.JNI import qualified SafeSpec import qualified Spec import Test.Hspec main :: IO ()-main = withJVM [] $ hspec $ do- Spec.spec- SafeSpec.spec+main = withJVM [] $ hspec $+ around_ (runInBoundThread . runInAttachedThread) $ do+ Spec.spec+ SafeSpec.spec