Welcome! Please see the About page for a little more info on how this works.

0 votes
in Macros by

I'm trying to generate code which access object fields but I cannot avoid reflection. Not clear if it is bug or I do anything wrong.

(ns user.macro)

(set! *warn-on-reflection* true)
(set! *print-meta* true)

(deftype MyObj [x])

(def -obj (MyObj. :x))

(defmacro get-x [obj]
  (let [sym (with-meta (gensym) {:tag MyObj})]
    `(let [~sym ~obj]
       (.-x ~sym))))

;; Reflection warning here
(get-x -obj)

(comment
  ;; Macro expanded as:

  (macroexpand-1 '(get-x -obj))
  #_=> (clojure.core/let [^user.macro.MyObj G__17584 -obj] (.-x ^user.macro.MyObj G__17584))

  ;; PROBLEM: Benchmark and decompile shows reflection:

  (get-x -obj)
  #_=> :x
  ;// Decompiling class: user/macro$fn__17604
  ;package user;
  ;
  ;import clojure.lang.*;
  ;
  ;public final class macro$fn__17604 extends AFunction
  ;{
  ;    public static final Var const__0;
  ;
  ;    public static Object invokeStatic() {
  ;        final Object G__17603 = macro$fn__17604.const__0.getRawRoot();
  ;        return Reflector.invokeNoArgInstanceMember(G__17603, "x", true);
  ;    }
  ;
  ;    @Override
  ;    public Object invoke() {
  ;        return invokeStatic();
  ;    }
  ;
  ;    static {
  ;        const__0 = RT.var("user.macro", "-obj");
  ;    }
  ;}
  ;             Execution time mean : 67,705637 ns
  ;    Execution time std-deviation : 7,655054 ns
  ;   Execution time lower quantile : 60,605456 ns ( 2,5%)
  ;   Execution time upper quantile : 79,073366 ns (97,5%)

  ;; Expanded code works as expected:

  (clojure.core/let [^user.macro.MyObj G__17584 -obj] (.-x ^user.macro.MyObj G__17584))
  #_=> :x
  ;// Decompiling class: user/macro$fn__17624
  ;package user;
  ;
  ;import user.macro.*;
  ;import clojure.lang.*;
  ;
  ;public final class macro$fn__17624 extends AFunction
  ;{
  ;    public static final Var const__0;
  ;
  ;    public static Object invokeStatic() {
  ;        final Object G__17584 = macro$fn__17624.const__0.getRawRoot();
  ;        return ((MyObj)G__17584).x;
  ;    }
  ;
  ;    @Override
  ;    public Object invoke() {
  ;        return invokeStatic();
  ;    }
  ;
  ;    static {
  ;        const__0 = RT.var("user.macro", "-obj");
  ;    }
  ;}
  ;             Execution time mean : 5,011374 ns
  ;    Execution time std-deviation : 0,269197 ns
  ;   Execution time lower quantile : 4,714841 ns ( 2,5%)
  ;   Execution time upper quantile : 5,390264 ns (97,5%)

  )

1 Answer

+1 vote
by
selected by
 
Best answer

The ^ reader does not resolve symbols and accepts symbols and strings as tags (impl).
Consequently, the compiler doesn't look at anything but symbols and strings (impl).

With that being said, there's no warning if you preface MyObj in {:tag MyObj} with a syntax quote.

by
I thought that I've tried the variant with syntax quote as well... but it really works.
Thank you.
...