on master help sign in
Merge pull request #240 from JanBessai/master

FFI Dependencies in Java: Automatic inclusion and resolution + download via maven

commite8d3f09f458132789d15ca8a7f7b88feac1a4d1a authorEdwin Brady <edwin.brady@gmail.com> dateMon 18 Mar 2013, 15:41 (14 years ago) parent35034db1960fba
6 files changed+204−147
java/executable_pom_template.xml+1 java/src/main/java/org/idris/rts/ForeignPrimitives.java+138−123 samples/javaffi.idr+12 src/IRTS/CodegenJava.hs+51−22 src/IRTS/Compiler.hs+1−1 src/IRTS/LParser.hs+1−1
java/executable_pom_template.xml+1
@@ -17,6 +17,7 @@
1717 <artifactId>idris</artifactId>
1818 <version>$RTS-VERSION$</version>
1919 </dependency>
20+$DEPENDENCIES$
2021 </dependencies>
2122
2223
java/src/main/java/org/idris/rts/ForeignPrimitives.java+138−123
@@ -1,5 +1,6 @@
11package org.idris.rts;
22
3+import java.lang.reflect.InvocationTargetException;
34import java.util.Arrays;
45import java.util.List;
56import java.util.Map;
@@ -9,7 +10,6 @@ import java.util.HashSet;
910import java.io.Closeable;
1011import java.io.File;
1112import java.io.IOException;
12−import java.io.EOFException;
1313import java.io.PrintStream;
1414import java.io.InputStream;
1515import java.nio.ByteBuffer;
@@ -25,190 +25,205 @@ import java.util.concurrent.Semaphore;
2525
2626@SuppressWarnings("unchecked")
2727public class ForeignPrimitives {
28+
2829 private static final Map<Thread, List<String>> args = new HashMap<>();
2930 private static final Semaphore messageMutex = new Semaphore(1);
3031 private static final Map<Thread, BlockingQueue<Object>> messages = new HashMap<>();
3132 private static final Set<Object> feofFiles = new HashSet<>();
3233
33− public static synchronized void idris_initArgs(Thread t, String [] args) {
34− ForeignPrimitives.args.put(t, Arrays.asList(args));
34+ public static synchronized void idris_initArgs(Thread t, String[] args) {
35+ ForeignPrimitives.args.put(t, Arrays.asList(args));
3536 }
3637
3738 public static synchronized int idris_numArgs(Object thread) {
38− List<String> argsForThread = ForeignPrimitives.args.get((Thread)thread);
39− return (argsForThread == null ? 0 : argsForThread.size());
39+ List<String> argsForThread = ForeignPrimitives.args.get((Thread) thread);
40+ return (argsForThread == null ? 0 : argsForThread.size());
4041 }
4142
4243 public static synchronized String idris_getArg(Object thread, int num) {
43− List<String> argsForThread = ForeignPrimitives.args.get((Thread)thread);
44− return (argsForThread == null ? null : argsForThread.get(num));
44+ List<String> argsForThread = ForeignPrimitives.args.get((Thread) thread);
45+ return (argsForThread == null ? null : argsForThread.get(num));
4546 }
4647
4748 public static String getenv(String x) {
48− return System.getenv(x);
49+ return System.getenv(x);
4950 }
5051
5152 public static void exit(int exitCode) {
52− System.exit(exitCode);
53+ System.exit(exitCode);
5354 }
5455
5556 public static void usleep(int microsecs) {
56− try {
57− Thread.sleep(microsecs / 1000, microsecs % 1000);
58− } catch (InterruptedException ex) {
59− }
57+ try {
58+ Thread.sleep(microsecs / 1000, microsecs % 1000);
59+ } catch (InterruptedException ex) {
60+ }
6061 }
6162
6263 public static void idris_sendMessage(Object from, Object dest, Object message) throws InterruptedException {
63− messageMutex.acquire();
64− BlockingQueue<Object> messagesForTarget = messages.get((Thread)dest);
65− if (messagesForTarget == null) {
66− messagesForTarget = new LinkedBlockingQueue<>();
67− messages.put((Thread)dest, messagesForTarget);
68− }
69− messagesForTarget.put(message);
64+ messageMutex.acquire();
65+ BlockingQueue<Object> messagesForTarget = messages.get((Thread) dest);
66+ if (messagesForTarget == null) {
67+ messagesForTarget = new LinkedBlockingQueue<>();
68+ messages.put((Thread) dest, messagesForTarget);
69+ }
70+ messagesForTarget.put(message);
7071 messageMutex.release();
7172 }
7273
7374 public static int idris_checkMessage(Object dest) throws InterruptedException {
74− messageMutex.acquire();
75− BlockingQueue<Object> messagesForTarget = messages.get((Thread)dest);
76− messageMutex.release();
77− return (messagesForTarget == null ? 0 : messagesForTarget.size());
75+ messageMutex.acquire();
76+ BlockingQueue<Object> messagesForTarget = messages.get((Thread) dest);
77+ messageMutex.release();
78+ return (messagesForTarget == null ? 0 : messagesForTarget.size());
7879 }
7980
8081 public static Object idris_recvMessage(Object dest) throws InterruptedException {
81− messageMutex.acquire();
82− BlockingQueue<Object> messagesForTarget = messages.get((Thread)dest);
83− if (messagesForTarget == null) {
84− messagesForTarget = new LinkedBlockingQueue();
85− messages.put((Thread)dest, messagesForTarget);
86− }
87− messageMutex.release();
88− return messagesForTarget.take();
82+ messageMutex.acquire();
83+ BlockingQueue<Object> messagesForTarget = messages.get((Thread) dest);
84+ if (messagesForTarget == null) {
85+ messagesForTarget = new LinkedBlockingQueue();
86+ messages.put((Thread) dest, messagesForTarget);
87+ }
88+ messageMutex.release();
89+ return messagesForTarget.take();
8990 }
9091
9192 public static void putStr(String str) {
92− System.out.print(str);
93+ System.out.print(str);
9394 }
9495
9596 public static void putchar(char c) {
96− System.out.print(c);
97+ System.out.print(c);
9798 }
9899
99100 public static int getchar() {
100− try {
101− return (char)System.in.read();
102− } catch (IOException ex) {
103− return -1;
104− }
101+ try {
102+ return (char) System.in.read();
103+ } catch (IOException ex) {
104+ return -1;
105+ }
105106 }
106107
107108 public static SeekableByteChannel fileOpen(String name, String privs) {
108− try {
109− OpenOption [] options;
110− switch (privs) {
111− case "r" :
112− options = new StandardOpenOption []
113− { StandardOpenOption.READ };
114− break;
115− case "r+" :
116− options = new StandardOpenOption []
117− { StandardOpenOption.READ,
118− StandardOpenOption.WRITE };
119− break;
120− case "w" :
121− options = new StandardOpenOption []
122− { StandardOpenOption.WRITE,
123− StandardOpenOption.CREATE };
124− break;
125− case "w+" :
126− options = new StandardOpenOption []
127− { StandardOpenOption.READ,
128− StandardOpenOption.WRITE,
129− StandardOpenOption.CREATE };
130− break;
131− case "a" :
132− options = new StandardOpenOption []
133− { StandardOpenOption.WRITE,
134− StandardOpenOption.CREATE,
135− StandardOpenOption.APPEND };
136− break;
137− case "a+" :
138− options = new StandardOpenOption []
139− { StandardOpenOption.READ,
140− StandardOpenOption.WRITE,
141− StandardOpenOption.CREATE,
142− StandardOpenOption.APPEND };
143− break;
144− default :
145− options = new StandardOpenOption [] {};
146− break;
147− }
148− return Files.newByteChannel(new File(name).toPath(), options);
149− } catch (IOException ex) {
150− return null;
151− }
109+ try {
110+ OpenOption[] options;
111+ switch (privs) {
112+ case "r":
113+ options = new StandardOpenOption[]{StandardOpenOption.READ};
114+ break;
115+ case "r+":
116+ options = new StandardOpenOption[]{StandardOpenOption.READ,
117+ StandardOpenOption.WRITE};
118+ break;
119+ case "w":
120+ options = new StandardOpenOption[]{StandardOpenOption.WRITE,
121+ StandardOpenOption.CREATE};
122+ break;
123+ case "w+":
124+ options = new StandardOpenOption[]{StandardOpenOption.READ,
125+ StandardOpenOption.WRITE,
126+ StandardOpenOption.CREATE};
127+ break;
128+ case "a":
129+ options = new StandardOpenOption[]{StandardOpenOption.WRITE,
130+ StandardOpenOption.CREATE,
131+ StandardOpenOption.APPEND};
132+ break;
133+ case "a+":
134+ options = new StandardOpenOption[]{StandardOpenOption.READ,
135+ StandardOpenOption.WRITE,
136+ StandardOpenOption.CREATE,
137+ StandardOpenOption.APPEND};
138+ break;
139+ default:
140+ options = new StandardOpenOption[]{};
141+ break;
142+ }
143+ return Files.newByteChannel(new File(name).toPath(), options);
144+ } catch (IOException ex) {
145+ return null;
146+ }
152147 }
153148
154149 public static synchronized void fileClose(Object file) throws IOException {
155− ((Closeable)file).close();
156− feofFiles.remove(file);
150+ ((Closeable) file).close();
151+ feofFiles.remove(file);
157152 }
158153
159154 public static void fputStr(Object file, String string) throws IOException {
160− if (file instanceof PrintStream) {
161− ((PrintStream)file).print(string);
162− } else if (file instanceof SeekableByteChannel) {
163− ((SeekableByteChannel)file).write(ByteBuffer.wrap(string.getBytes()));
164− }
155+ if (file instanceof PrintStream) {
156+ ((PrintStream) file).print(string);
157+ } else if (file instanceof SeekableByteChannel) {
158+ ((SeekableByteChannel) file).write(ByteBuffer.wrap(string.getBytes()));
159+ }
165160 }
166161
167162 public static synchronized String idris_readStr(Object file) throws IOException {
168− ReadableByteChannel channel = file instanceof InputStream ?
169− Channels.newChannel((InputStream)file) :
170− (ReadableByteChannel)file;
171− ByteBuffer buf = ByteBuffer.allocate(1);
172− StringBuilder resultBuilder = new StringBuilder("");
173− String delimiter = System.getProperty("line.separator");
174− int read = 0;
175− do {
176− buf.rewind();
177− read = channel.read(buf);
178− if (read > 0) {
179− resultBuilder.append(new String(buf.array()));
180− if (resultBuilder.lastIndexOf(delimiter) > -1) {
181− return resultBuilder.toString();
182− }
183− }
184− if (read < 0) {
185− feofFiles.add(file);
186− }
187− } while (read >= 0);
188− return resultBuilder.toString();
163+ ReadableByteChannel channel = file instanceof InputStream
164+ ? Channels.newChannel((InputStream) file)
165+ : (ReadableByteChannel) file;
166+ ByteBuffer buf = ByteBuffer.allocate(1);
167+ StringBuilder resultBuilder = new StringBuilder("");
168+ String delimiter = System.getProperty("line.separator");
169+ int read = 0;
170+ do {
171+ buf.rewind();
172+ read = channel.read(buf);
173+ if (read > 0) {
174+ resultBuilder.append(new String(buf.array()));
175+ if (resultBuilder.lastIndexOf(delimiter) > -1) {
176+ return resultBuilder.toString();
177+ }
178+ }
179+ if (read < 0) {
180+ feofFiles.add(file);
181+ }
182+ } while (read >= 0);
183+ return resultBuilder.toString();
189184 }
190185
191186 public static int fileEOF(Object file) {
192− return (feofFiles.contains(file) ? 1 : 0);
187+ return (feofFiles.contains(file) ? 1 : 0);
193188 }
194189
195190 public static int isNull(Object o) {
196− return (o == null ? 1 : 0);
191+ return (o == null ? 1 : 0);
197192 }
198193
199194 public final static Object idris_K(final Object result, final Object drop) {
200− return result;
195+ return result;
201196 }
202197
203198 public final static Object idris_flipK(final Object drop, final Object result) {
204− return result;
205− }
206−
207− public final static Object idris_assignStack(final Object [] context, final int start, final Object ... vars) {
208− int i = start;
209− for (Object var : vars) {
210− context[i] = var;
211− }
212− return context;
199+ return result;
200+ }
201+
202+ public final static Object idris_assignStack(final Object[] context, final int start, final Object... vars) {
203+ int i = start;
204+ for (Object var : vars) {
205+ context[i] = var;
206+ }
207+ return context;
208+ }
209+
210+ public final static Object invokeOn(final Object obj, final String methodName, Object... args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
211+ Class cls = (obj instanceof Class ? (Class)obj : obj.getClass());
212+ Class argClasses[] = new Class[args.length];
213+ int i = 0;
214+ for (Object arg : args) {
215+ argClasses[i] = arg.getClass();
216+ }
217+ return cls.getMethod(methodName, argClasses).invoke(obj, args);
218+ }
219+
220+ public final static Object newObject(final Object cls, Object... args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
221+ Class clazz = (cls instanceof Class ? (Class)cls : cls.getClass());
222+ Class argClasses[] = new Class[args.length];
223+ int i = 0;
224+ for (Object arg : args) {
225+ argClasses[i] = arg.getClass();
226+ }
227+ return clazz.getConstructor(argClasses).newInstance(args);
213228 }
214229}
samples/javaffi.idrnew file+12
@@ -0,0 +1,12 @@
1+module Main
2+
3+%include "com.google.common.math.IntMath"
4+%lib "com.google.guava:guava:14.0"
5+
6+binom : Int -> Int -> IO Int
7+binom n k = mkForeign (FFun "IntMath.binomial" [FInt, FInt] FInt) n k
8+
9+main : IO ()
10+main = do print "The number of possibilities in lotto is 49 choose 6:"
11+ res <- binom 49 6
12+ print res
src/IRTS/CodegenJava.hs+51−22
@@ -15,7 +15,7 @@ import qualified Control.Monad.Trans as T
1515import Control.Monad.Trans.State
1616import Data.Char
1717import Data.Maybe (fromJust)
18−import Data.List (isPrefixOf, intercalate)
18+import Data.List (isPrefixOf, isSuffixOf, intercalate)
1919import qualified Data.Text as T
2020import qualified Data.Text.IO as TIO
2121import Language.Java.Parser
@@ -35,9 +35,11 @@ type CodeGeneration = StateT (CodeGenerationEnv) (Either String)
3535codegenJava :: [(Name, SExp)] -> -- initialization of globals
3636 [(Name, SDecl)] ->
3737 FilePath -> -- output file name
38+ [String] -> -- headers
39+ [String] -> -- libs
3840 OutputType ->
3941 IO ()
40−codegenJava globalInit defs out exec = do
42+codegenJava globalInit defs out hdrs libs exec = do
4143 withTempdir (takeBaseName out) $ \ tmpDir -> do
4244 let srcdir = tmpDir </> "src" </> "main" </> "java"
4345 createDirectoryIfMissing True srcdir
@@ -46,7 +48,7 @@ codegenJava globalInit defs out exec = do
4648 let outjava = srcdir </> clsName <.> "java"
4749 let jout = either error
4850 (flatIndent . prettyPrint)
49− (evalStateT (mkCompilationUnit globalInit defs out) (mkCodeGenEnv globalInit))
51+ (evalStateT (mkCompilationUnit globalInit defs hdrs out) (mkCodeGenEnv globalInit))
5052 writeFile outjava jout
5153 if (exec == Raw)
5254 then copyFile outjava (takeDirectory out </> clsName <.> "java")
@@ -57,12 +59,16 @@ codegenJava globalInit defs out exec = do
5759 (T.pack clsName)
5860 (T.replace (T.pack "$ARTIFACT-NAME$")
5961 (T.pack $ takeBaseName out)
60− execPomTemplate)
62+ (T.replace (T.pack "$DEPENDENCIES$")
63+ (mkPomDependencies libs)
64+ execPomTemplate
65+ )
66+ )
6167 TIO.writeFile (tmpDir </> "pom.xml") execPom
6268 mvnCmd <- getMvn
6369 let args = ["-f", (tmpDir </> "pom.xml")]
64− (exit, _, err) <- readProcessWithExitCode mvnCmd (args ++ ["compile"]) ""
65− when (exit /= ExitSuccess) $ error ("FAILURE: " ++ mvnCmd ++ " compile\n" ++ err)
70+ (exit, mvout, err) <- readProcessWithExitCode mvnCmd (args ++ ["compile"]) ""
71+ when (exit /= ExitSuccess) $ error ("FAILURE: " ++ mvnCmd ++ " compile\n" ++ err ++ mvout)
6672 if (exec == Object)
6773 then do
6874 classFiles <-
@@ -72,8 +78,8 @@ codegenJava globalInit defs out exec = do
7278 mapM_ (\ clsFile -> copyFile clsFile (takeDirectory out </> takeFileName clsFile))
7379 classFiles
7480 else do
75− (exit, _, err) <- readProcessWithExitCode mvnCmd (args ++ ["package"]) ""
76− when (exit /= ExitSuccess) (error ("FAILURE: " ++ mvnCmd ++ " package\n" ++ err))
81+ (exit, mvout, err) <- readProcessWithExitCode mvnCmd (args ++ ["package"]) ""
82+ when (exit /= ExitSuccess) (error ("FAILURE: " ++ mvnCmd ++ " package\n" ++ err ++ mvout))
7783 copyFile (tmpDir </> "target" </> (takeBaseName out) <.> "jar") out
7884 handle <- openBinaryFile out ReadMode
7985 contents <- TIO.hGetContents handle
@@ -99,21 +105,40 @@ jarHeader =
99105 ++ "exec \"$java\" $java_args -jar $MYSELF \"$@\""
100106 ++ "exit 1\n"
101107
108+mkPomDependencies :: [String] -> T.Text
109+mkPomDependencies deps =
110+ T.concat $ map (T.concat . map (T.append (T.pack " ")) . mkDependency . T.pack) deps
111+ where
112+ mkDependency s =
113+ case T.splitOn (T.pack ":") s of
114+ [g, a, v] ->
115+ [ T.pack $ "<dependency>\n"
116+ , T.append (T.pack " ") $ mkGroupId g
117+ , T.append (T.pack " ") $ mkArtifactId a
118+ , T.append (T.pack " ") $ mkVersion v
119+ , T.pack $ "</dependency>\n"
120+ ]
121+ _ -> []
122+ mkGroupId g = T.append (T.pack $ "<groupId>") (T.append g $ T.pack "</groupId>\n")
123+ mkArtifactId a = T.append (T.pack $ "<artifactId>") (T.append a $ T.pack "</artifactId>\n")
124+ mkVersion v = T.append (T.pack $ "<version>") (T.append v $ T.pack "</version>\n")
125+
102126mkCodeGenEnv :: [(Name, SExp)] -> CodeGenerationEnv
103127mkCodeGenEnv globalInit =
104128 CodeGenerationEnv $ zipWith (\ (name, _) pos -> (name, pos)) globalInit [0..]
105129
106−mkCompilationUnit :: [(Name, SExp)] -> [(Name, SDecl)] -> FilePath -> CodeGeneration CompilationUnit
107−mkCompilationUnit globalInit defs out =
108− CompilationUnit Nothing [ ImportDecl False idrisRts True
109− , ImportDecl True idrisForeign True
110− , ImportDecl False bigInteger False
111− , ImportDecl False stringBuffer False
112− , ImportDecl False runtimeException False
113− , ImportDecl False scanner False
114− , ImportDecl False arrays False
115− ] <$>
116− mkTypeDecl globalInit defs out
130+mkCompilationUnit :: [(Name, SExp)] -> [(Name, SDecl)] -> [String] -> FilePath -> CodeGeneration CompilationUnit
131+mkCompilationUnit globalInit defs hdrs out =
132+ CompilationUnit Nothing ( [ ImportDecl False idrisRts True
133+ , ImportDecl True idrisForeign True
134+ , ImportDecl False bigInteger False
135+ , ImportDecl False stringBuffer False
136+ , ImportDecl False runtimeException False
137+ , ImportDecl False scanner False
138+ , ImportDecl False arrays False
139+ ] ++ otherHdrs
140+ )
141+ <$> mkTypeDecl globalInit defs out
117142 where
118143 idrisRts = J.Name $ map Ident ["org", "idris", "rts"]
119144 idrisForeign = J.Name $ map Ident ["org", "idris", "rts", "ForeignPrimitives"]
@@ -122,6 +147,12 @@ mkCompilationUnit globalInit defs out =
122147 runtimeException = J.Name $ map Ident ["java", "lang", "RuntimeException"]
123148 scanner = J.Name $ map Ident ["java", "util", "Scanner"]
124149 arrays = J.Name $ map Ident ["java", "util", "Arrays"]
150+ otherHdrs = map ( (\ name -> ImportDecl False name False)
151+ . J.Name
152+ . map (Ident . T.unpack)
153+ . T.splitOn (T.pack ".")
154+ . T.pack)
155+ $ filter (not . isSuffixOf ".h") hdrs
125156
126157flatIndent :: String -> String
127158flatIndent (' ' : ' ' : xs) = flatIndent xs
@@ -337,6 +368,7 @@ mkIdentifier (UN name) =
337368 cleanReserved "volatile" = "_volatile"
338369 cleanReserved "transient" = "_transient"
339370 cleanReserved "native" = "_native"
371+ cleanReserved "const" = "_const"
340372
341373 cleanReserved "import" = "_import"
342374 cleanReserved "package" = "_package"
@@ -410,9 +442,6 @@ idrisClosureType = ClassType [(Ident "Closure", [])]
410442idrisTailCallClosureType :: ClassType
411443idrisTailCallClosureType = ClassType [(Ident "TailCallClosure", [])]
412444
413−--idrisFunctionType :: ClassType
414−--idrisFunctionType = ClassType [(Ident "IdrisFunction", [])]
415−
416445idrisObjectType :: ClassType
417446idrisObjectType = ClassType [(Ident "IdrisObject", [])]
418447
src/IRTS/Compiler.hs+1−1
@@ -71,7 +71,7 @@ compile target f tm
7171 (concatMap mkObj objs)
7272 (concatMap mkLib libs) NONE
7373 ViaJava ->
74− codegenJava [] c f outty
74+ codegenJava [] c f hdrs libs outty
7575 ViaJavaScript ->
7676 codegenJavaScript JavaScript c f outty
7777 ViaNode ->
src/IRTS/LParser.hs+1−1
@@ -61,7 +61,7 @@ fovm tgt outty f
6161 case checked of
6262 OK c -> case tgt of
6363 ViaC -> codegenC c "a.out" outty ["math.h"] "" "" TRACE
64− ViaJava -> codegenJava [] c "a.out" outty
64+ ViaJava -> codegenJava [] c "a.out" [] [] outty
6565 Error e -> fail $ show e
6666
6767parseFOVM :: FilePath -> IO [(Name, LDecl)]