-
-
Notifications
You must be signed in to change notification settings - Fork 5
Java 9 Modules
As of version 0.2.1
, insn
supports generating Java 9 module definitions.
For this demonstration, we recreate the examples from the Project Jigsaw quick start.
First, we'll create our deps.edn
file. Since the ASM version bundled with Clojure (as of version 1.9
) is too old for our purposes, we must add a dependency on a newer version:
{:deps {insn {:mvn/version "0.2.1"}
org.ow2.asm/asm {:mvn/version "6.0"}}}
(Note: since 0.3.0
, the ASM dependency is no longer necessary.)
Next, we start a REPL with our above dependencies on the class path:
$ clj -r
Like with class types, modules are just simple data:
(def greetings-module-data
{:id :com.greetings
:requires [:org.astro]})
(def astro-module-data
{:id :org.astro
:exports [:org.astro]})
We need some types that will reside in our separate modules:
(def world-class-data
{:name 'org.astro.World
:methods [{:flags [:public :static]
:name "name"
:desc [String]
:emit [[:ldc "World"]
[:areturn]]}]})
(def main-class-data
{:name 'com.greetings.Main
:methods [{:flags [:public :static]
:name "main"
:desc [[String] :void]
:emit [[:getstatic System "out"]
[:ldc "Greetings %s!%n"]
[:ldc 1]
[:anewarray Object]
[:dup]
[:ldc 0]
[:invokestatic 'org.astro.World "name" [String]]
[:aastore]
[:invokevirtual java.io.PrintStream "format" 2]
[:return]]}]})
Now we write our two simple modules named com.greetings
and org.astro
to the current directory:
(require '[insn.objectweb-asm] ;; load the external version
'[insn.core :as type]
'[insn.module :as module])
(set! *compile-path* "mods/com.greetings") ;; used by `insn.core/write`
(-> (module/visit greetings-module-data)
type/write) ;; to ./mods/com.greetings/module-info.class
(-> (type/visit main-class-data)
type/write) ;; to ./mods/com.greetings/com/greetings/Main.class
(set! *compile-path* "mods/org.astro")
(-> (module/visit astro-module-data)
type/write) ;; to ./mods/org.astro/module-info.class
(-> (type/visit world-class-data)
type/write) ;; to ./mods/org.astro/org/astro/World.class
We can run our main class from within the com.greetings
module using Java 9:
$ java -version
# => java version "9.0.1"
$ java --module-path mods -m com.greetings/com.greetings.Main
# => Greetings world!
The final Jigsaw example deals with module services. We simplify it slightly here for brevity, but it is essentially the same. Continuing in our REPL session from above; first the module defs:
(def greetings2-module-data
{:id :com.greetings2
:requires [:com.socket]
:uses ['com.socket.spi.NetworkSocketProvider]})
(def socket-module-data
{:id :com.socket
:exports [:com.socket, :com.socket.spi]})
(def fastsocket-module-data
{:id :org.fastsocket
:requires [:com.socket]
:provides {'com.socket.spi.NetworkSocketProvider
'org.fastsocket.FastNetworkSocketProvider}})
Next, the interface and class types:
(def isocket-class-data
{:name 'com.socket.NetworkSocket
:flags [:public :interface]})
(def iprovider-class-data
{:name 'com.socket.spi.NetworkSocketProvider
:flags [:public :interface]
:methods [{:flags [:public :abstract]
:name "openNetworkSocket"
:desc ['com.socket.NetworkSocket]}]})
(def socket-class-data
{:name 'org.fastsocket.FastNetworkSocket
:flags [:public]
:interfaces ['com.socket.NetworkSocket]})
(def provider-class-data
{:name 'org.fastsocket.FastNetworkSocketProvider
:flags [:public]
:interfaces ['com.socket.spi.NetworkSocketProvider]
:methods [{:flags [:public]
:name "openNetworkSocket"
:desc ['com.socket.NetworkSocket]
:emit [[:new 'org.fastsocket.FastNetworkSocket]
[:dup]
[:invokespecial 'org.fastsocket.FastNetworkSocket :init [:void]]
[:areturn]]}]})
(def main-class2-data
{:name 'com.greetings.Main
:methods [{:flags [:public :static]
:name "main"
:desc [[String] :void]
:emit [[:getstatic System "out"]
[:ldc 'com.socket.spi.NetworkSocketProvider]
[:invokestatic java.util.ServiceLoader "load" 1]
[:invokeinterface Iterable "iterator"]
[:invokeinterface java.util.Iterator "next"]
[:checkcast 'com.socket.spi.NetworkSocketProvider]
[:invokeinterface 'com.socket.spi.NetworkSocketProvider
"openNetworkSocket" ['com.socket.NetworkSocket]]
[:invokevirtual Object "getClass"]
[:invokevirtual java.io.PrintStream "println" [Object :void]]
[:return]]}]})
Finally, we emit our .class
files to disk like before:
(set! *compile-path* "mods/com.greetings2")
(run! type/write [(module/visit greetings2-module-data)
(type/visit main-class2-data)])
(set! *compile-path* "mods/com.socket")
(run! type/write [(module/visit socket-module-data)
(type/visit isocket-class-data)
(type/visit iprovider-class-data)])
(set! *compile-path* "mods/org.fastsocket")
(run! type/write [(module/visit fastsocket-module-data)
(type/visit socket-class-data)
(type/visit provider-class-data)])
Now we run our main class from within the com.greetings2
module that uses the socket service:
$ java -p mods -m com.greetings2/com.greetings.Main
# => class org.fastsocket.FastNetworkSocket
For more on ASM's module support see the javadocs.