https://github.com/jgpc42/insn
Functional JVM bytecode generation for Clojure.
https://github.com/jgpc42/insn
asm bytecode-instructions clojure jvm-bytecode
Last synced: 26 days ago
JSON representation
Functional JVM bytecode generation for Clojure.
- Host: GitHub
- URL: https://github.com/jgpc42/insn
- Owner: jgpc42
- License: epl-1.0
- Created: 2017-08-18T02:31:45.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2025-01-11T13:56:22.000Z (4 months ago)
- Last Synced: 2025-04-19T23:34:53.917Z (26 days ago)
- Topics: asm, bytecode-instructions, clojure, jvm-bytecode
- Language: Clojure
- Homepage:
- Size: 279 KB
- Stars: 201
- Watchers: 12
- Forks: 5
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
[](https://clojars.org/insn)
[][ci]### Dependency and version information
Click to show
> :warning: This library uses a recent version of [`asm`][asm-jar] which can cause dependency issues. [See here][asm-ver] for more.
[Leiningen][lein]
``` clojure
[insn "0.5.4"]
```[tools.deps][deps]
```clojure
{insn/insn {:mvn/version "0.5.4"}}
```[Maven][maven]
``` xml
insn
insn
0.5.4```
LTS JDK versions 8, 11, 17 and 21 and Clojure versions 1.7 to 1.12 are currently [tested against][ci].
### What is it?
This library provides a functional abstraction over [ASM][asm] for generating JVM bytecode. ASM is the library that Clojure itself uses to dynamically compile Clojure code into code that can be run on the JVM.
### Quick start
Let's begin by creating a simple class, equivalent to the following Java code.
```java
package my.pkg;public class Adder {
public static int VALUE = 42;
public long add (int n) {
return (long) (VALUE + n);
}
}
```The class is specified as a map. The class fields and methods are sequences of maps giving the members of said class.
```clojure
(def class-data
{:name 'my.pkg.Adder
:fields [{:flags #{:public :static}, :name "VALUE", :type :int, :value 42}]
:methods [{:flags #{:public}, :name "add", :desc [:int :long]
:emit [[:getstatic :this "VALUE" :int]
[:iload 1]
[:iadd]
[:i2l]
[:lreturn]]}]})
```Above, we described in data the same information expressed by the Java code, except the method body was given as a sequence of bytecode instructions. (Note: unlike Java, the method return value is specified via the `:desc` key as the last element). If you aren't fluent in JVM bytecode instruction syntax, I would suggest reading chapter 3 of the excellent tutorial pdf from the ASM [site][pdf].
`:emit` can also be a fn that is passed the ASM `MethodVisitor` object to write the method bytecode as shown in [this example][emitfn].
Now to write the bytecode.
```clojure
(require '[insn.core :as insn])(def result (insn/visit class-data))
```The `result` is a map containing the generated class's packaged-prefixed `:name` and `:bytes`, the latter being a byte array. This information is all you need to give to a ClassLoader to define your class.
For convenience, we can use `insn.core/define` to define the class for us.
```clojure
(def class-object (insn/define class-data)) ;; => my.pkg.Adder
(-> class-object .newInstance (.add 17)) ;; => 59
```Note that you can also pass `result` to `define`, the class will not be regenerated. Also note, like Java, since we did not define any constructors, a public no-argument constructor that simply calls the superclass constructor was generated for us.
If you are evaluating the code snippets above in the REPL, you can also just do:
```clojure
(.add (my.pkg.Adder.) 17) ;; => 59
```Since, by default, `define` loads the class using Clojure's own `DynamicClassLoader`, meaning the class will be *first class* to subsequent evaluations in the running Clojure environment.
### More information
For additional usage examples and topics, see the [wiki][wiki]. For a complete reference, see the [docs][doc]. The fairly comprehensive [test suite][tests] is also demonstrative and should be easy to follow.
### Running the tests
```bash
lein test
```Or, `lein test-all` for all supported Clojure versions.
The tests can also be run against all supported Java versions (via [`docker`][docker]) with:
``` bash
./test-all-jdk.sh
```### Similar libraries
- [tools.emitter.jvm](https://github.com/clojure/tools.emitter.jvm)
* Does not provide a general ASM API.
- [mage](https://github.com/nasser/mage) and [magic](https://github.com/nasser/magic)
* Clojure-CLR only.### Projects using insn
- [tech.datatype](https://github.com/techascent/tech.datatype)
* Efficient N-dimensional numerics across a range of primitive datatypes and containers.
* Also used by this libraries' successor, [dtype-next](https://github.com/cnuernber/dtype-next).
- [jmh-clojure](https://github.com/jgpc42/jmh-clojure)
* Clojure bridge to JMH benchmarking via bytecode generation.### License
Copyright © 2017-2025 Justin Conklin
Distributed under the Eclipse Public License, the same as Clojure.
[asm]: http://asm.ow2.org
[asm-jar]: https://mvnrepository.com/artifact/org.ow2.asm/asm
[asm-ver]: https://github.com/jgpc42/insn/wiki/Dependency-Problems
[ci]: https://github.com/jgpc42/insn/blob/master/.github/workflows/test.yml
[deps]: https://github.com/clojure/tools.deps.alpha
[doc]: https://jgpc42.github.io/insn/doc
[docker]: https://www.docker.com
[emitfn]: https://github.com/jgpc42/insn/wiki/Interface-Implementation
[lein]: http://github.com/technomancy/leiningen
[maven]: http://maven.apache.org
[pdf]: https://asm.ow2.io/asm4-guide.pdf
[tests]: https://github.com/jgpc42/insn/blob/master/test/insn/core_test.clj
[wiki]: https://github.com/jgpc42/insn/wiki