insn.core

Simple JVM bytecode generation.

*bytecode-version*

dynamic

The bytecode version to use for types if unspecified.

*class-flags*

dynamic

The class/interface flags to use if unspecified.

*field-flags*

dynamic

The field flags to use if unspecified.

*init-flags*

dynamic

The constructor flags to use if unspecified.

*method-flags*

dynamic

The method flags to use if unspecified.

define

(define t)(define cl t)
Return a Class object from the provided type.

generate

(generate t)
Alias of `visit`.

get-bytes

(get-bytes t)
Return a representation of the provided type as an array of bytes.
This array is an in-memory equivalent to a java .class file on disk.

Loader

protocol

members

load-type

(load-type cl t)
Return a class object from the given map containing the class
:bytes and :name.

new-instance

(new-instance t)(new-instance t & args)
Define and return an instance of the generated class. The
non-abstract type must define a public constructor that accepts
either no arguments, or if `args` is provided, the given arguments.

If the given argument count matches a constructor with a distinct
arity, that one is invoked. In this case, constructors that take
primitives are supported via reflective unboxing.

Otherwise, the argument types must exactly match (as per
`clojure.core/class`) a defined constructor, or a constructor that
accepts the same amount of `Object`s.

visit

(visit t)
Generate the class bytecode from the provided type map. Returns a map
of the classes' :name and :bytes. Options:

  :name         of the class. Optional, but see below.

  :flags        seq of class/interface modifier flags (e.g., :final).
                See the `insn.util` namespace.

  :super        defaults to Object.

  :interfaces   sequence of interface types to extend/implement.

  :annotations  map or sequence of tuples, described below.

  :signature    generic type information - usually only ever needed
                for interop with Java libraries (reflection, etc.)

  :fields       sequence of field maps, described below.

  :methods      sequence of method maps, described below.

  :version      bytecode version given as a integer. For backwards
                compatibility, a float of major.minor may be given
                for versions 1.1 through 1.8.

  :source       string denoting the original source file - optional.

  :debug        string of arbitrary debug information - optional.

Each field and method can also be given :annotations and a :signature
as per above.

Some examples:

  (visit {:flags #{:public :interface}
          :name 'my.ns.Foo
          :methods [{:flags #{:public :abstract}
                     :name :my_method
                     :desc [:int]}]})
  (visit {:flags #{:public}
          :name 'my.ns.Bar
          :interfaces ['my.ns.Foo]
          :methods [{:flags #{:public}
                     :name :toString
                     :desc [String]
                     :emit [[:ldc "Bar"]
                            [:areturn]]}
                    {:flags #{:public}
                     :name :my_method
                     :desc [:int]
                     :emit [[:ldc 42]
                            [:ireturn]]}]})

Class instance/static fields are provided as maps. Options:

  :name   field name (required).

  :type   field type (required).

  :flags  seq of field modifier flags.

  :value  initial value. Only for primitive and String static fields,
          and if given, must be a corresponding Integer, Float, Long,
          Double, or String value.

Some example maps:

  {:flags #{:public :final}, :name :my_string, :type String}
  {:flags #{:static}, :name :some_number, :type :long, :value 42}

Class/interface methods, constructors, and static initializers are
provided as maps. Options:

  :name   method name (required). Can be either :init or :clinit,
          designating a constructor or the static initializer,
          respectively.

  :flags  seq of method modifier flags. Ignored for the static
          initializer.

  :desc   method parameter types and return type (specified last).
          Ignored for the static initializer, optional for
          constructors. For constructors, the method return type is
          forced to void if not explicitly specified as such.

  :emit   either a fn taking a MethodVisitor or a sequence of
          instructions to emit for the method (see `insn.op`).
          Optional if method is abstract.

Some example maps, :emit has been omitted for brevity:

  {:name :add_ints, :desc [:int :int :int]}
  {:flags #{:private}, :name :init, :desc [String :boolean :void]}
  {:name :clinit}

Additionally, methods may be given :parameter-annotations provided as
a map of {parameter-index annotations}.

If the class name is not package prefixed, the current namespace is
used as the resulting classes' package. If a name is not given, a
generated (gensym) class name is used, qualified by the current
namespace.

If the type does not define at least one constructor, and is not an
abstract type, a default, zero-argument constructor with default
access will be written that simply invokes the superclass constructor.

All annotations are provided as a map or sequence of tuples. Each key
is the Annotation name and each value is a map of elements. A non-map
value specifies a single element named :value as per java.

Annotation values are processed the same as in clojure.
See: https://clojure.org/reference/datatypes#_java_annotation_support

write

(write t)(write t root)
Takes a map specifying a classes' :name and :bytes. Writes the class
bytes to a .class file. The file's directory is taken from its
package, its filename from its name, rooted at `root`.

If not given, `root` defaults to `*compile-path*`, or if not set, the
"java.io.tmpdir" property. If both of those are not set, a 'classes'
directory in the current directory is used.