Skip to content

Commit a74dfbe

Browse files
committed
new :seq-emit-fn and map-style ops
1 parent 9abaa2e commit a74dfbe

File tree

8 files changed

+467
-32
lines changed

8 files changed

+467
-32
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
## 0.5.5-SNAPSHOT (20XX-XX-XX)
1+
## 0.6.0-SNAPSHOT (20XX-XX-XX)
22

3-
* Upgrade to [ASM 9.7.1][asm]
3+
* Upgrade to [ASM 9.8][asm]
44
* Explicit support for Clojure 1.12
55
* Allow creating class names with no package (remove auto ns prefixing)
66
* Add support for custom index objects via the `LocalIndex` protocol
77
* Generate default constructor for :abstract classes too if one not provided
88
* Support passing parameter and return types separately when defining methods
9+
* Experimental new map compilation facilitated by `:seq-emit-fn`.
910

1011
## 0.5.4 (2022-04-14)
1112

deps.edn

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
{:deps
2-
{org.ow2.asm/asm {:mvn/version "9.7.1"}}}
2+
{org.ow2.asm/asm {:mvn/version "9.8"}}}

project.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
:deps (mapv #(vector (% 0) (:mvn/version (% 1))))
44
(into '[[org.clojure/clojure "1.8.0"]])))
55

6-
(defproject insn "0.5.5-SNAPSHOT"
6+
(defproject insn "0.6.0-SNAPSHOT"
77
:description "Functional JVM bytecode generation for Clojure."
88
:url "https://github.com/jgpc42/insn"
99
:license {:name "Eclipse Public License"

src/insn/core.clj

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@
6969
7070
:debug string of arbitrary debug information - optional.
7171
72+
:seq-emit-fn fn to call with each bytecode seq. This is an advanced
73+
option and, if present, will be passed both the ASM
74+
MethodVisitor and the raw (possibly nested) :emit op
75+
seq for each method. Can be used to support new op
76+
data types. For example, the `insn.op-map/emit-seq` fn
77+
can be used to enable map-style op sequences.
78+
7279
Each field and method can also be given :annotations and a :signature
7380
as per above.
7481
@@ -192,13 +199,14 @@
192199
{:name :init
193200
:emit [[:aload 0]
194201
[:invokespecial :super :init [:void]]
195-
[:return]]}))]
202+
[:return]]}))
203+
seq-emit (:seq-emit-fn t op/emit-seq)]
196204
(binding [util/*this* this
197205
util/*super* super]
198206
(doseq [f (:fields t)]
199207
(visit-field cv f))
200208
(doseq [m (:methods t)]
201-
(visit-method cv m))
209+
(visit-method cv m seq-emit))
202210
(doto cv
203211
(ann/visit (:annotations t))
204212
.visitEnd))
@@ -217,7 +225,7 @@
217225
(ann/visit fv (:annotations f))
218226
(.visitEnd fv)))
219227

220-
(defn- visit-method [^ClassVisitor cv m]
228+
(defn- visit-method [^ClassVisitor cv m seq-emit]
221229
(let [mname (util/method-name (:name m))
222230
clinit? (= mname "<clinit>")
223231
init? (= mname "<init>")
@@ -245,7 +253,9 @@
245253
(.visitCode))
246254
emit (or (:emit m) (:bytecode m))]
247255
(binding [util/*labels* (atom {})]
248-
(emit mv))
256+
(if (fn? emit)
257+
(emit mv)
258+
(seq-emit mv emit)))
249259
(ann/visit mv (:annotations m))
250260
(doseq [[i anns] (or (:parameter-annotations m) (:param-annotations m))]
251261
(ann/visit mv i anns))

src/insn/op.clj

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
~@args]
6868
(let [~'&op ~field]
6969
~@body)))))
70-
(alter-meta! #'~fname assoc :arglists '~alists)
70+
(alter-meta! #'~fname assoc :arglists '~alists ::op true)
7171
(def-op-method ~fname)))))))
7272

7373
;;;
@@ -236,9 +236,7 @@
236236
bootstrap method."
237237
([v mname desc boot] (&fn v mname desc boot []))
238238
([v mname desc boot args]
239-
(let [boot (if (sequential? boot)
240-
(apply util/handle boot)
241-
boot)]
239+
(let [boot (if (util/handle? boot) boot (apply util/handle boot))]
242240
(.visitInvokeDynamicInsn v (util/method-name mname) (util/method-desc desc)
243241
boot (object-array args)))))
244242

@@ -384,20 +382,19 @@
384382
(def-op-method pop1)
385383
(def-op-method trycatch)
386384

385+
(def op?
386+
"Returns true if the given value is a valid op vector."
387+
(comp keyword? first))
388+
387389
(defn op-seq
388390
"Return a flattened sequence of ops."
389-
[xs]
390-
(let [op? (comp keyword? first)
391-
go (fn go [[x & xs]]
392-
(lazy-seq
393-
(cond
394-
(op? x)
395-
(cons x (go xs))
396-
(seq x)
397-
(concat (go x) (go xs))
398-
(seq xs)
399-
(go xs))))]
400-
(go xs)))
391+
([xs] (op-seq op? xs))
392+
([op? [x & xs]]
393+
(lazy-seq
394+
(cond
395+
(op? x) (cons x (op-seq xs))
396+
(seq x) (concat (op-seq x) (op-seq xs))
397+
(seq xs) (op-seq xs)))))
401398

402399
(defn compile
403400
"Compile a sequence of op seqs to a fn that accepts an ASM
@@ -409,3 +406,24 @@
409406
(fn [v]
410407
(doseq [op ops]
411408
(apply (::fn op) v (::args op))))))
409+
410+
(def ^:private keyword-opcode*
411+
(into {} (for [var (vals (ns-publics *ns*))
412+
:let [m (meta var)]
413+
:when (::op m)]
414+
(let [sym (:name m)
415+
field (-> sym name .toUpperCase (.replace \- \_))]
416+
[(keyword sym)
417+
(.getInt (.getField Opcodes field) nil)]))))
418+
419+
(defn keyword-opcode
420+
"Return the ASM opcode number as a long for the given op keyword. If
421+
the keyword is invalid an error is raised."
422+
^long [k]
423+
(let [v (util/check-valid "op keyword" keyword-opcode* k)]
424+
(.longValue ^Integer v)))
425+
426+
(defn emit-seq
427+
"Emit the op sequence to the given ASM MethodVisitor. See `compile`."
428+
[v ops]
429+
((compile ops) v))

0 commit comments

Comments
 (0)