Skip to content

Instantly share code, notes, and snippets.

@jgpc42
Created November 7, 2022 17:44
Show Gist options
  • Save jgpc42/841c277d465d90241d67999e5073b1d2 to your computer and use it in GitHub Desktop.
Save jgpc42/841c277d465d90241d67999e5073b1d2 to your computer and use it in GitHub Desktop.
Simple mutable data class with clojure.asm
*clojure-version* ;; => {:major 1, :minor 10, :incremental 3, :qualifier nil}
(System/getProperty "java.version") ;; => "14.0.1"
(import [clojure.asm ClassVisitor ClassWriter Opcodes Type]
[clojure.lang DynamicClassLoader RT])
(defn type-to-desc [t]
(case t
:int "I"
;; ... and so on
(-> (Type/getType t) .getDescriptor)))
(defn mutable-acc-type [class-name types]
(let [base-type "java/lang/Object"
cv (doto (ClassWriter. ClassWriter/COMPUTE_FRAMES)
(.visit Opcodes/V1_7
(bit-or Opcodes/ACC_PUBLIC Opcodes/ACC_FINAL)
(.replace class-name \. \/)
nil base-type nil))]
(doto (.visitMethod cv Opcodes/ACC_PUBLIC "<init>" "()V" nil nil)
(.visitCode)
(.visitVarInsn Opcodes/ALOAD 0)
(.visitMethodInsn Opcodes/INVOKESPECIAL base-type "<init>" "()V" false)
(.visitInsn Opcodes/RETURN)
(.visitMaxs -1 -1)
(.visitEnd))
(dorun (for [[i t] (map vector (range) types)]
(doto (.visitField cv Opcodes/ACC_PUBLIC (str "m" i) (type-to-desc t) nil nil)
(.visitEnd))))
(-> (RT/makeClassLoader)
(.defineClass class-name (.toByteArray cv) nil)
(.newInstance))))
(comment
(def obj (mutable-acc-type "my.pkg.Foo" [:int Object String]))
[(.m0 obj) (.m1 obj) (.m2 obj)] ;; => [0 nil nil]
(do (set! (.m0 obj) 42)
(set! (.m1 obj) [1 2 3])
(set! (.m2 obj) "fred"))
[(.m0 obj) (.m1 obj) (.m2 obj)] ;; => [42 [1 2 3] "fred"]
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment