packages feed

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 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  [![CircleCI](https://circleci.com/gh/tweag/inline-java.svg?style=svg)](https://circleci.com/gh/tweag/inline-java)-[![Build status](https://badge.buildkite.com/143d77b1eec06bb865d694dbe685f2ed7712caa12852c8808e.svg)](https://buildkite.com/tweag-1/inline-java)+[![Build status](https://badge.buildkite.com/143d77b1eec06bb865d694dbe685f2ed7712caa12852c8808e.svg?branch=master)](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  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-[<img src="http://i.imgur.com/0HK8X4y.png" height="65">](http://tweag.io)+[![Tweag I/O](http://i.imgur.com/0HK8X4y.png)](http://tweag.io) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [![LeapYear](http://i.imgur.com/t9VxRHn.png)](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