Skip to content

Interface Implementation

Justin Conklin edited this page Oct 27, 2017 · 3 revisions

The namespace insn.op defines a corresponding helper fn for every bytecode instruction, all of which take an ASM MethodVisitor as their first argument. Below, instead of a sequence of ops, :emit is a fn that is passed the visitor object when the class bytecode is being written.

(require '[insn.core :as insn]
         '[insn.op :as op])

(insn/define
  {:flags #{:public :interface}
   :name 'my.math.Inc
   :methods [{:flags #{:public :abstract}, :name :inc, :desc [:long :long]}
             {:flags #{:public :abstract}, :name :inc, :desc [:double :double]}]})

(defn make-inc
  "Return an emit fn that will increment the given primitive type."
  [type]
  (let [[load const add ret]
        (if (= type :long)
          [op/lload op/lconst-1 op/ladd op/lreturn]
          [op/dload op/dconst-1 op/dadd op/dreturn])]
    (fn [v]
      (doto v (load 1) const add ret))))

(def ops
  (insn/new-instance
    {:flags #{:public}, :name 'my.math.IncImpl, :interfaces ['my.math.Inc]
     :methods [{:flags #{:public}, :name :inc, :desc [:long :long]
                :emit (make-inc :long)}
               {:flags #{:public}, :name :inc, :desc [:double :double]
                :emit (make-inc :double)}]}))

(.inc ops 42)   ;; => 43
(.inc ops 43.0) ;; => 44.0

We use the new-instance helper to quickly define and instantiate an instance of our class.

Using an :emit fn can be useful when performance is of utmost importance.