diff src/clojure/genclass.clj @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
line wrap: on
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/clojure/genclass.clj	Sat Aug 21 06:25:44 2010 -0400
     1.3 @@ -0,0 +1,714 @@
     1.4 +;   Copyright (c) Rich Hickey. All rights reserved.
     1.5 +;   The use and distribution terms for this software are covered by the
     1.6 +;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
     1.7 +;   which can be found in the file epl-v10.html at the root of this distribution.
     1.8 +;   By using this software in any fashion, you are agreeing to be bound by
     1.9 +;   the terms of this license.
    1.10 +;   You must not remove this notice, or any other, from this software.
    1.11 +
    1.12 +(in-ns 'clojure.core)
    1.13 +
    1.14 +(import '(java.lang.reflect Modifier Constructor)
    1.15 +        '(clojure.asm ClassWriter ClassVisitor Opcodes Type)
    1.16 +        '(clojure.asm.commons Method GeneratorAdapter)
    1.17 +        '(clojure.lang IPersistentMap))
    1.18 +
    1.19 +;(defn method-sig [^java.lang.reflect.Method meth]
    1.20 +;  [(. meth (getName)) (seq (. meth (getParameterTypes)))])
    1.21 +
    1.22 +(defn- non-private-methods [^Class c]
    1.23 +  (loop [mm {}
    1.24 +         considered #{}
    1.25 +         c c]
    1.26 +    (if c
    1.27 +      (let [[mm considered]
    1.28 +            (loop [mm mm
    1.29 +                   considered considered
    1.30 +                   meths (seq (concat
    1.31 +                                (seq (. c (getDeclaredMethods)))
    1.32 +                                (seq (. c (getMethods)))))]
    1.33 +              (if meths
    1.34 +                (let [^java.lang.reflect.Method meth (first meths)
    1.35 +                      mods (. meth (getModifiers))
    1.36 +                      mk (method-sig meth)]
    1.37 +                  (if (or (considered mk)
    1.38 +                          (not (or (Modifier/isPublic mods) (Modifier/isProtected mods)))
    1.39 +                          ;(. Modifier (isPrivate mods))
    1.40 +                          (. Modifier (isStatic mods))
    1.41 +                          (. Modifier (isFinal mods))
    1.42 +                          (= "finalize" (.getName meth)))
    1.43 +                    (recur mm (conj considered mk) (next meths))
    1.44 +                    (recur (assoc mm mk meth) (conj considered mk) (next meths))))
    1.45 +                [mm considered]))]
    1.46 +        (recur mm considered (. c (getSuperclass))))
    1.47 +      mm)))
    1.48 +
    1.49 +(defn- ctor-sigs [^Class super]
    1.50 +  (for [^Constructor ctor (. super (getDeclaredConstructors))
    1.51 +        :when (not (. Modifier (isPrivate (. ctor (getModifiers)))))]
    1.52 +    (apply vector (. ctor (getParameterTypes)))))
    1.53 +
    1.54 +(defn- escape-class-name [^Class c]
    1.55 +  (.. (.getSimpleName c) 
    1.56 +      (replace "[]" "<>")))
    1.57 +
    1.58 +(defn- overload-name [mname pclasses]
    1.59 +  (if (seq pclasses)
    1.60 +    (apply str mname (interleave (repeat \-) 
    1.61 +                                 (map escape-class-name pclasses)))
    1.62 +    (str mname "-void")))
    1.63 +
    1.64 +(defn- ^java.lang.reflect.Field find-field [^Class c f]
    1.65 +  (let [start-class c]
    1.66 +    (loop [c c]
    1.67 +      (if (= c Object)
    1.68 +        (throw (new Exception (str "field, " f ", not defined in class, " start-class ", or its ancestors")))
    1.69 +        (let [dflds (.getDeclaredFields c)
    1.70 +              rfld (first (filter #(= f (.getName ^java.lang.reflect.Field %)) dflds))]
    1.71 +          (or rfld (recur (.getSuperclass c))))))))
    1.72 +
    1.73 +;(distinct (map first(keys (mapcat non-private-methods [Object IPersistentMap]))))
    1.74 +
    1.75 +(def ^{:private true} prim->class
    1.76 +     {'int Integer/TYPE
    1.77 +      'long Long/TYPE
    1.78 +      'float Float/TYPE
    1.79 +      'double Double/TYPE
    1.80 +      'void Void/TYPE
    1.81 +      'short Short/TYPE
    1.82 +      'boolean Boolean/TYPE
    1.83 +      'byte Byte/TYPE
    1.84 +      'char Character/TYPE})
    1.85 +
    1.86 +(defn- ^Class the-class [x] 
    1.87 +  (cond 
    1.88 +   (class? x) x
    1.89 +   (contains? prim->class x) (prim->class x)
    1.90 +   :else (let [strx (str x)]
    1.91 +           (clojure.lang.RT/classForName 
    1.92 +            (if (some #{\. \[} strx)
    1.93 +              strx
    1.94 +              (str "java.lang." strx))))))
    1.95 +
    1.96 +;; someday this can be made codepoint aware
    1.97 +(defn- valid-java-method-name
    1.98 +  [^String s]
    1.99 +  (= s (clojure.lang.Compiler/munge s)))
   1.100 +
   1.101 +(defn- validate-generate-class-options
   1.102 +  [{:keys [methods]}]
   1.103 +  (let [[mname] (remove valid-java-method-name (map (comp str first) methods))]
   1.104 +    (when mname (throw (IllegalArgumentException. (str "Not a valid method name: " mname))))))
   1.105 +
   1.106 +(defn- generate-class [options-map]
   1.107 +  (validate-generate-class-options options-map)
   1.108 +  (let [default-options {:prefix "-" :load-impl-ns true :impl-ns (ns-name *ns*)}
   1.109 +        {:keys [name extends implements constructors methods main factory state init exposes 
   1.110 +                exposes-methods prefix load-impl-ns impl-ns post-init]} 
   1.111 +          (merge default-options options-map)
   1.112 +        name-meta (meta name)
   1.113 +        name (str name)
   1.114 +        super (if extends (the-class extends) Object)
   1.115 +        interfaces (map the-class implements)
   1.116 +        supers (cons super interfaces)
   1.117 +        ctor-sig-map (or constructors (zipmap (ctor-sigs super) (ctor-sigs super)))
   1.118 +        cv (new ClassWriter (. ClassWriter COMPUTE_MAXS))
   1.119 +        cname (. name (replace "." "/"))
   1.120 +        pkg-name name
   1.121 +        impl-pkg-name (str impl-ns)
   1.122 +        impl-cname (.. impl-pkg-name (replace "." "/") (replace \- \_))
   1.123 +        ctype (. Type (getObjectType cname))
   1.124 +        iname (fn [^Class c] (.. Type (getType c) (getInternalName)))
   1.125 +        totype (fn [^Class c] (. Type (getType c)))
   1.126 +        to-types (fn [cs] (if (pos? (count cs))
   1.127 +                            (into-array (map totype cs))
   1.128 +                            (make-array Type 0)))
   1.129 +        obj-type ^Type (totype Object)
   1.130 +        arg-types (fn [n] (if (pos? n)
   1.131 +                            (into-array (replicate n obj-type))
   1.132 +                            (make-array Type 0)))
   1.133 +        super-type ^Type (totype super)
   1.134 +        init-name (str init)
   1.135 +        post-init-name (str post-init)
   1.136 +        factory-name (str factory)
   1.137 +        state-name (str state)
   1.138 +        main-name "main"
   1.139 +        var-name (fn [s] (clojure.lang.Compiler/munge (str s "__var")))
   1.140 +        class-type  (totype Class)
   1.141 +        rt-type  (totype clojure.lang.RT)
   1.142 +        var-type ^Type (totype clojure.lang.Var)
   1.143 +        ifn-type (totype clojure.lang.IFn)
   1.144 +        iseq-type (totype clojure.lang.ISeq)
   1.145 +        ex-type  (totype java.lang.UnsupportedOperationException)
   1.146 +        all-sigs (distinct (concat (map #(let[[m p] (key %)] {m [p]}) (mapcat non-private-methods supers))
   1.147 +                                   (map (fn [[m p]] {(str m) [p]}) methods)))
   1.148 +        sigs-by-name (apply merge-with concat {} all-sigs)
   1.149 +        overloads (into {} (filter (fn [[m s]] (next s)) sigs-by-name))
   1.150 +        var-fields (concat (when init [init-name]) 
   1.151 +                           (when post-init [post-init-name])
   1.152 +                           (when main [main-name])
   1.153 +                           ;(when exposes-methods (map str (vals exposes-methods)))
   1.154 +                           (distinct (concat (keys sigs-by-name)
   1.155 +                                             (mapcat (fn [[m s]] (map #(overload-name m (map the-class %)) s)) overloads)
   1.156 +                                             (mapcat (comp (partial map str) vals val) exposes))))
   1.157 +        emit-get-var (fn [^GeneratorAdapter gen v]
   1.158 +                       (let [false-label (. gen newLabel)
   1.159 +                             end-label (. gen newLabel)]
   1.160 +                         (. gen getStatic ctype (var-name v) var-type)
   1.161 +                         (. gen dup)
   1.162 +                         (. gen invokeVirtual var-type (. Method (getMethod "boolean isBound()")))
   1.163 +                         (. gen ifZCmp (. GeneratorAdapter EQ) false-label)
   1.164 +                         (. gen invokeVirtual var-type (. Method (getMethod "Object get()")))
   1.165 +                         (. gen goTo end-label)
   1.166 +                         (. gen mark false-label)
   1.167 +                         (. gen pop)
   1.168 +                         (. gen visitInsn (. Opcodes ACONST_NULL))
   1.169 +                         (. gen mark end-label)))
   1.170 +        emit-unsupported (fn [^GeneratorAdapter gen ^Method m]
   1.171 +                           (. gen (throwException ex-type (str (. m (getName)) " ("
   1.172 +                                                               impl-pkg-name "/" prefix (.getName m)
   1.173 +                                                               " not defined?)"))))
   1.174 +        emit-forwarding-method
   1.175 +        (fn [name pclasses rclass as-static else-gen]
   1.176 +          (let [mname (str name)
   1.177 +                pmetas (map meta pclasses)
   1.178 +                pclasses (map the-class pclasses)
   1.179 +                rclass (the-class rclass)
   1.180 +                ptypes (to-types pclasses)
   1.181 +                rtype ^Type (totype rclass)
   1.182 +                m (new Method mname rtype ptypes)
   1.183 +                is-overload (seq (overloads mname))
   1.184 +                gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (if as-static (. Opcodes ACC_STATIC) 0)) 
   1.185 +                         m nil nil cv)
   1.186 +                found-label (. gen (newLabel))
   1.187 +                else-label (. gen (newLabel))
   1.188 +                end-label (. gen (newLabel))]
   1.189 +            (add-annotations gen (meta name))
   1.190 +            (dotimes [i (count pmetas)]
   1.191 +              (add-annotations gen (nth pmetas i) i))
   1.192 +            (. gen (visitCode))
   1.193 +            (if (> (count pclasses) 18)
   1.194 +              (else-gen gen m)
   1.195 +              (do
   1.196 +                (when is-overload
   1.197 +                  (emit-get-var gen (overload-name mname pclasses))
   1.198 +                  (. gen (dup))
   1.199 +                  (. gen (ifNonNull found-label))
   1.200 +                  (. gen (pop)))
   1.201 +                (emit-get-var gen mname)
   1.202 +                (. gen (dup))
   1.203 +                (. gen (ifNull else-label))
   1.204 +                (when is-overload
   1.205 +                  (. gen (mark found-label)))
   1.206 +                                        ;if found
   1.207 +                (.checkCast gen ifn-type)
   1.208 +                (when-not as-static
   1.209 +                  (. gen (loadThis)))
   1.210 +                                        ;box args
   1.211 +                (dotimes [i (count ptypes)]
   1.212 +                  (. gen (loadArg i))
   1.213 +                  (. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i))))
   1.214 +                                        ;call fn
   1.215 +                (. gen (invokeInterface ifn-type (new Method "invoke" obj-type 
   1.216 +                                                      (to-types (replicate (+ (count ptypes)
   1.217 +                                                                              (if as-static 0 1)) 
   1.218 +                                                                           Object)))))
   1.219 +                                        ;(into-array (cons obj-type 
   1.220 +                                        ;                 (replicate (count ptypes) obj-type))))))
   1.221 +                                        ;unbox return
   1.222 +                (. gen (unbox rtype))
   1.223 +                (when (= (. rtype (getSort)) (. Type VOID))
   1.224 +                  (. gen (pop)))
   1.225 +                (. gen (goTo end-label))
   1.226 +                
   1.227 +                                        ;else call supplied alternative generator
   1.228 +                (. gen (mark else-label))
   1.229 +                (. gen (pop))
   1.230 +                
   1.231 +                (else-gen gen m)
   1.232 +            
   1.233 +                (. gen (mark end-label))))
   1.234 +            (. gen (returnValue))
   1.235 +            (. gen (endMethod))))
   1.236 +        ]
   1.237 +                                        ;start class definition
   1.238 +    (. cv (visit (. Opcodes V1_5) (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_SUPER))
   1.239 +                 cname nil (iname super)
   1.240 +                 (when-let [ifc (seq interfaces)]
   1.241 +                   (into-array (map iname ifc)))))
   1.242 +
   1.243 +                                        ; class annotations
   1.244 +    (add-annotations cv name-meta)
   1.245 +    
   1.246 +                                        ;static fields for vars
   1.247 +    (doseq [v var-fields]
   1.248 +      (. cv (visitField (+ (. Opcodes ACC_PRIVATE) (. Opcodes ACC_FINAL) (. Opcodes ACC_STATIC))
   1.249 +                        (var-name v) 
   1.250 +                        (. var-type getDescriptor)
   1.251 +                        nil nil)))
   1.252 +    
   1.253 +                                        ;instance field for state
   1.254 +    (when state
   1.255 +      (. cv (visitField (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_FINAL))
   1.256 +                        state-name 
   1.257 +                        (. obj-type getDescriptor)
   1.258 +                        nil nil)))
   1.259 +    
   1.260 +                                        ;static init to set up var fields and load init
   1.261 +    (let [gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_STATIC)) 
   1.262 +                   (. Method getMethod "void <clinit> ()")
   1.263 +                   nil nil cv)]
   1.264 +      (. gen (visitCode))
   1.265 +      (doseq [v var-fields]
   1.266 +        (. gen push impl-pkg-name)
   1.267 +        (. gen push (str prefix v))
   1.268 +        (. gen (invokeStatic var-type (. Method (getMethod "clojure.lang.Var internPrivate(String,String)"))))
   1.269 +        (. gen putStatic ctype (var-name v) var-type))
   1.270 +      
   1.271 +      (when load-impl-ns
   1.272 +        (. gen push "clojure.core")
   1.273 +        (. gen push "load")
   1.274 +        (. gen (invokeStatic rt-type (. Method (getMethod "clojure.lang.Var var(String,String)"))))
   1.275 +        (. gen push (str "/" impl-cname))
   1.276 +        (. gen (invokeInterface ifn-type (new Method "invoke" obj-type (to-types [Object]))))
   1.277 +;        (. gen push (str (.replace impl-pkg-name \- \_) "__init"))
   1.278 +;        (. gen (invokeStatic class-type (. Method (getMethod "Class forName(String)"))))
   1.279 +        (. gen pop))
   1.280 +
   1.281 +      (. gen (returnValue))
   1.282 +      (. gen (endMethod)))
   1.283 +    
   1.284 +                                        ;ctors
   1.285 +    (doseq [[pclasses super-pclasses] ctor-sig-map]
   1.286 +      (let [pclasses (map the-class pclasses)
   1.287 +            super-pclasses (map the-class super-pclasses)
   1.288 +            ptypes (to-types pclasses)
   1.289 +            super-ptypes (to-types super-pclasses)
   1.290 +            m (new Method "<init>" (. Type VOID_TYPE) ptypes)
   1.291 +            super-m (new Method "<init>" (. Type VOID_TYPE) super-ptypes)
   1.292 +            gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)
   1.293 +            no-init-label (. gen newLabel)
   1.294 +            end-label (. gen newLabel)
   1.295 +            no-post-init-label (. gen newLabel)
   1.296 +            end-post-init-label (. gen newLabel)
   1.297 +            nth-method (. Method (getMethod "Object nth(Object,int)"))
   1.298 +            local (. gen newLocal obj-type)]
   1.299 +        (. gen (visitCode))
   1.300 +        
   1.301 +        (if init
   1.302 +          (do
   1.303 +            (emit-get-var gen init-name)
   1.304 +            (. gen dup)
   1.305 +            (. gen ifNull no-init-label)
   1.306 +            (.checkCast gen ifn-type)
   1.307 +                                        ;box init args
   1.308 +            (dotimes [i (count pclasses)]
   1.309 +              (. gen (loadArg i))
   1.310 +              (. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i))))
   1.311 +                                        ;call init fn
   1.312 +            (. gen (invokeInterface ifn-type (new Method "invoke" obj-type 
   1.313 +                                                  (arg-types (count ptypes)))))
   1.314 +                                        ;expecting [[super-ctor-args] state] returned
   1.315 +            (. gen dup)
   1.316 +            (. gen push 0)
   1.317 +            (. gen (invokeStatic rt-type nth-method))
   1.318 +            (. gen storeLocal local)
   1.319 +            
   1.320 +            (. gen (loadThis))
   1.321 +            (. gen dupX1)
   1.322 +            (dotimes [i (count super-pclasses)]
   1.323 +              (. gen loadLocal local)
   1.324 +              (. gen push i)
   1.325 +              (. gen (invokeStatic rt-type nth-method))
   1.326 +              (. clojure.lang.Compiler$HostExpr (emitUnboxArg nil gen (nth super-pclasses i))))
   1.327 +            (. gen (invokeConstructor super-type super-m))
   1.328 +            
   1.329 +            (if state
   1.330 +              (do
   1.331 +                (. gen push 1)
   1.332 +                (. gen (invokeStatic rt-type nth-method))
   1.333 +                (. gen (putField ctype state-name obj-type)))
   1.334 +              (. gen pop))
   1.335 +            
   1.336 +            (. gen goTo end-label)
   1.337 +                                        ;no init found
   1.338 +            (. gen mark no-init-label)
   1.339 +            (. gen (throwException ex-type (str impl-pkg-name "/" prefix init-name " not defined")))
   1.340 +            (. gen mark end-label))
   1.341 +          (if (= pclasses super-pclasses)
   1.342 +            (do
   1.343 +              (. gen (loadThis))
   1.344 +              (. gen (loadArgs))
   1.345 +              (. gen (invokeConstructor super-type super-m)))
   1.346 +            (throw (new Exception ":init not specified, but ctor and super ctor args differ"))))
   1.347 +
   1.348 +        (when post-init
   1.349 +          (emit-get-var gen post-init-name)
   1.350 +          (. gen dup)
   1.351 +          (. gen ifNull no-post-init-label)
   1.352 +          (.checkCast gen ifn-type)
   1.353 +          (. gen (loadThis))
   1.354 +                                       ;box init args
   1.355 +          (dotimes [i (count pclasses)]
   1.356 +            (. gen (loadArg i))
   1.357 +            (. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i))))
   1.358 +                                       ;call init fn
   1.359 +          (. gen (invokeInterface ifn-type (new Method "invoke" obj-type 
   1.360 +                                                (arg-types (inc (count ptypes))))))
   1.361 +          (. gen pop)
   1.362 +          (. gen goTo end-post-init-label)
   1.363 +                                       ;no init found
   1.364 +          (. gen mark no-post-init-label)
   1.365 +          (. gen (throwException ex-type (str impl-pkg-name "/" prefix post-init-name " not defined")))
   1.366 +          (. gen mark end-post-init-label))
   1.367 +
   1.368 +        (. gen (returnValue))
   1.369 +        (. gen (endMethod))
   1.370 +                                        ;factory
   1.371 +        (when factory
   1.372 +          (let [fm (new Method factory-name ctype ptypes)
   1.373 +                gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_STATIC)) 
   1.374 +                         fm nil nil cv)]
   1.375 +            (. gen (visitCode))
   1.376 +            (. gen newInstance ctype)
   1.377 +            (. gen dup)
   1.378 +            (. gen (loadArgs))
   1.379 +            (. gen (invokeConstructor ctype m))            
   1.380 +            (. gen (returnValue))
   1.381 +            (. gen (endMethod))))))
   1.382 +    
   1.383 +                                        ;add methods matching supers', if no fn -> call super
   1.384 +    (let [mm (non-private-methods super)]
   1.385 +      (doseq [^java.lang.reflect.Method meth (vals mm)]
   1.386 +             (emit-forwarding-method (.getName meth) (.getParameterTypes meth) (.getReturnType meth) false
   1.387 +                                     (fn [^GeneratorAdapter gen ^Method m]
   1.388 +                                       (. gen (loadThis))
   1.389 +                                        ;push args
   1.390 +                                       (. gen (loadArgs))
   1.391 +                                        ;call super
   1.392 +                                       (. gen (visitMethodInsn (. Opcodes INVOKESPECIAL) 
   1.393 +                                                               (. super-type (getInternalName))
   1.394 +                                                               (. m (getName))
   1.395 +                                                               (. m (getDescriptor)))))))
   1.396 +                                        ;add methods matching interfaces', if no fn -> throw
   1.397 +      (reduce (fn [mm ^java.lang.reflect.Method meth]
   1.398 +                (if (contains? mm (method-sig meth))
   1.399 +                  mm
   1.400 +                  (do
   1.401 +                    (emit-forwarding-method (.getName meth) (.getParameterTypes meth) (.getReturnType meth) false
   1.402 +                                            emit-unsupported)
   1.403 +                    (assoc mm (method-sig meth) meth))))
   1.404 +              mm (mapcat #(.getMethods ^Class %) interfaces))
   1.405 +                                        ;extra methods
   1.406 +       (doseq [[mname pclasses rclass :as msig] methods]
   1.407 +         (emit-forwarding-method mname pclasses rclass (:static (meta msig))
   1.408 +                                 emit-unsupported))
   1.409 +                                        ;expose specified overridden superclass methods
   1.410 +       (doseq [[local-mname ^java.lang.reflect.Method m] (reduce (fn [ms [[name _ _] m]]
   1.411 +                              (if (contains? exposes-methods (symbol name))
   1.412 +                                (conj ms [((symbol name) exposes-methods) m])
   1.413 +                                ms)) [] (seq mm))]
   1.414 +         (let [ptypes (to-types (.getParameterTypes m))
   1.415 +               rtype (totype (.getReturnType m))
   1.416 +               exposer-m (new Method (str local-mname) rtype ptypes)
   1.417 +               target-m (new Method (.getName m) rtype ptypes)
   1.418 +               gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) exposer-m nil nil cv)]
   1.419 +           (. gen (loadThis))
   1.420 +           (. gen (loadArgs))
   1.421 +           (. gen (visitMethodInsn (. Opcodes INVOKESPECIAL) 
   1.422 +                                   (. super-type (getInternalName))
   1.423 +                                   (. target-m (getName))
   1.424 +                                   (. target-m (getDescriptor))))
   1.425 +           (. gen (returnValue))
   1.426 +           (. gen (endMethod)))))
   1.427 +                                        ;main
   1.428 +    (when main
   1.429 +      (let [m (. Method getMethod "void main (String[])")
   1.430 +            gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_STATIC)) 
   1.431 +                     m nil nil cv)
   1.432 +            no-main-label (. gen newLabel)
   1.433 +            end-label (. gen newLabel)]
   1.434 +        (. gen (visitCode))
   1.435 +
   1.436 +        (emit-get-var gen main-name)
   1.437 +        (. gen dup)
   1.438 +        (. gen ifNull no-main-label)
   1.439 +        (.checkCast gen ifn-type)
   1.440 +        (. gen loadArgs)
   1.441 +        (. gen (invokeStatic rt-type (. Method (getMethod "clojure.lang.ISeq seq(Object)"))))
   1.442 +        (. gen (invokeInterface ifn-type (new Method "applyTo" obj-type 
   1.443 +                                              (into-array [iseq-type]))))
   1.444 +        (. gen pop)
   1.445 +        (. gen goTo end-label)
   1.446 +                                        ;no main found
   1.447 +        (. gen mark no-main-label)
   1.448 +        (. gen (throwException ex-type (str impl-pkg-name "/" prefix main-name " not defined")))
   1.449 +        (. gen mark end-label)
   1.450 +        (. gen (returnValue))
   1.451 +        (. gen (endMethod))))
   1.452 +                                        ;field exposers
   1.453 +    (doseq [[f {getter :get setter :set}] exposes]
   1.454 +      (let [fld (find-field super (str f))
   1.455 +            ftype (totype (.getType fld))
   1.456 +            static? (Modifier/isStatic (.getModifiers fld))
   1.457 +            acc (+ Opcodes/ACC_PUBLIC (if static? Opcodes/ACC_STATIC 0))]
   1.458 +        (when getter
   1.459 +          (let [m (new Method (str getter) ftype (to-types []))
   1.460 +                gen (new GeneratorAdapter acc m nil nil cv)]
   1.461 +            (. gen (visitCode))
   1.462 +            (if static?
   1.463 +              (. gen getStatic ctype (str f) ftype)
   1.464 +              (do
   1.465 +                (. gen loadThis)
   1.466 +                (. gen getField ctype (str f) ftype)))
   1.467 +            (. gen (returnValue))
   1.468 +            (. gen (endMethod))))
   1.469 +        (when setter
   1.470 +          (let [m (new Method (str setter) Type/VOID_TYPE (into-array [ftype]))
   1.471 +                gen (new GeneratorAdapter acc m nil nil cv)]
   1.472 +            (. gen (visitCode))
   1.473 +            (if static?
   1.474 +              (do
   1.475 +                (. gen loadArgs)
   1.476 +                (. gen putStatic ctype (str f) ftype))
   1.477 +              (do
   1.478 +                (. gen loadThis)
   1.479 +                (. gen loadArgs)
   1.480 +                (. gen putField ctype (str f) ftype)))
   1.481 +            (. gen (returnValue))
   1.482 +            (. gen (endMethod))))))
   1.483 +                                        ;finish class def
   1.484 +    (. cv (visitEnd))
   1.485 +    [cname (. cv (toByteArray))]))
   1.486 +
   1.487 +(defmacro gen-class 
   1.488 +  "When compiling, generates compiled bytecode for a class with the
   1.489 +  given package-qualified :name (which, as all names in these
   1.490 +  parameters, can be a string or symbol), and writes the .class file
   1.491 +  to the *compile-path* directory.  When not compiling, does
   1.492 +  nothing. The gen-class construct contains no implementation, as the
   1.493 +  implementation will be dynamically sought by the generated class in
   1.494 +  functions in an implementing Clojure namespace. Given a generated
   1.495 +  class org.mydomain.MyClass with a method named mymethod, gen-class
   1.496 +  will generate an implementation that looks for a function named by 
   1.497 +  (str prefix mymethod) (default prefix: \"-\") in a
   1.498 +  Clojure namespace specified by :impl-ns
   1.499 +  (defaults to the current namespace). All inherited methods,
   1.500 +  generated methods, and init and main functions (see :methods, :init,
   1.501 +  and :main below) will be found similarly prefixed. By default, the
   1.502 +  static initializer for the generated class will attempt to load the
   1.503 +  Clojure support code for the class as a resource from the classpath,
   1.504 +  e.g. in the example case, ``org/mydomain/MyClass__init.class``. This
   1.505 +  behavior can be controlled by :load-impl-ns
   1.506 +
   1.507 +  Note that methods with a maximum of 18 parameters are supported.
   1.508 +
   1.509 +  In all subsequent sections taking types, the primitive types can be
   1.510 +  referred to by their Java names (int, float etc), and classes in the
   1.511 +  java.lang package can be used without a package qualifier. All other
   1.512 +  classes must be fully qualified.
   1.513 +
   1.514 +  Options should be a set of key/value pairs, all except for :name are optional:
   1.515 +
   1.516 +  :name aname
   1.517 +
   1.518 +  The package-qualified name of the class to be generated
   1.519 +
   1.520 +  :extends aclass
   1.521 +
   1.522 +  Specifies the superclass, the non-private methods of which will be
   1.523 +  overridden by the class. If not provided, defaults to Object.
   1.524 +
   1.525 +  :implements [interface ...]
   1.526 +
   1.527 +  One or more interfaces, the methods of which will be implemented by the class.
   1.528 +
   1.529 +  :init name
   1.530 +
   1.531 +  If supplied, names a function that will be called with the arguments
   1.532 +  to the constructor. Must return [ [superclass-constructor-args] state] 
   1.533 +  If not supplied, the constructor args are passed directly to
   1.534 +  the superclass constructor and the state will be nil
   1.535 +
   1.536 +  :constructors {[param-types] [super-param-types], ...}
   1.537 +
   1.538 +  By default, constructors are created for the generated class which
   1.539 +  match the signature(s) of the constructors for the superclass. This
   1.540 +  parameter may be used to explicitly specify constructors, each entry
   1.541 +  providing a mapping from a constructor signature to a superclass
   1.542 +  constructor signature. When you supply this, you must supply an :init
   1.543 +  specifier. 
   1.544 +
   1.545 +  :post-init name
   1.546 +
   1.547 +  If supplied, names a function that will be called with the object as
   1.548 +  the first argument, followed by the arguments to the constructor.
   1.549 +  It will be called every time an object of this class is created,
   1.550 +  immediately after all the inherited constructors have completed.
   1.551 +  It's return value is ignored.
   1.552 +
   1.553 +  :methods [ [name [param-types] return-type], ...]
   1.554 +
   1.555 +  The generated class automatically defines all of the non-private
   1.556 +  methods of its superclasses/interfaces. This parameter can be used
   1.557 +  to specify the signatures of additional methods of the generated
   1.558 +  class. Static methods can be specified with ^{:static true} in the
   1.559 +  signature's metadata. Do not repeat superclass/interface signatures
   1.560 +  here.
   1.561 +
   1.562 +  :main boolean
   1.563 +
   1.564 +  If supplied and true, a static public main function will be generated. It will
   1.565 +  pass each string of the String[] argument as a separate argument to
   1.566 +  a function called (str prefix main).
   1.567 +
   1.568 +  :factory name
   1.569 +
   1.570 +  If supplied, a (set of) public static factory function(s) will be
   1.571 +  created with the given name, and the same signature(s) as the
   1.572 +  constructor(s).
   1.573 +  
   1.574 +  :state name
   1.575 +
   1.576 +  If supplied, a public final instance field with the given name will be
   1.577 +  created. You must supply an :init function in order to provide a
   1.578 +  value for the state. Note that, though final, the state can be a ref
   1.579 +  or agent, supporting the creation of Java objects with transactional
   1.580 +  or asynchronous mutation semantics.
   1.581 +
   1.582 +  :exposes {protected-field-name {:get name :set name}, ...}
   1.583 +
   1.584 +  Since the implementations of the methods of the generated class
   1.585 +  occur in Clojure functions, they have no access to the inherited
   1.586 +  protected fields of the superclass. This parameter can be used to
   1.587 +  generate public getter/setter methods exposing the protected field(s)
   1.588 +  for use in the implementation.
   1.589 +
   1.590 +  :exposes-methods {super-method-name exposed-name, ...}
   1.591 +
   1.592 +  It is sometimes necessary to call the superclass' implementation of an
   1.593 +  overridden method.  Those methods may be exposed and referred in 
   1.594 +  the new method implementation by a local name.
   1.595 +
   1.596 +  :prefix string
   1.597 +
   1.598 +  Default: \"-\" Methods called e.g. Foo will be looked up in vars called
   1.599 +  prefixFoo in the implementing ns.
   1.600 +
   1.601 +  :impl-ns name
   1.602 +
   1.603 +  Default: the name of the current ns. Implementations of methods will be 
   1.604 +  looked up in this namespace.
   1.605 +
   1.606 +  :load-impl-ns boolean
   1.607 +
   1.608 +  Default: true. Causes the static initializer for the generated class
   1.609 +  to reference the load code for the implementing namespace. Should be
   1.610 +  true when implementing-ns is the default, false if you intend to
   1.611 +  load the code via some other method."
   1.612 +  {:added "1.0"}
   1.613 +  
   1.614 +  [& options]
   1.615 +    (when *compile-files*
   1.616 +      (let [options-map (into {} (map vec (partition 2 options)))
   1.617 +            [cname bytecode] (generate-class options-map)]
   1.618 +        (clojure.lang.Compiler/writeClassFile cname bytecode))))
   1.619 +
   1.620 +;;;;;;;;;;;;;;;;;;;; gen-interface ;;;;;;;;;;;;;;;;;;;;;;
   1.621 +;; based on original contribution by Chris Houser
   1.622 +
   1.623 +(defn- ^Type asm-type
   1.624 +  "Returns an asm Type object for c, which may be a primitive class
   1.625 +  (such as Integer/TYPE), any other class (such as Double), or a
   1.626 +  fully-qualified class name given as a string or symbol
   1.627 +  (such as 'java.lang.String)"
   1.628 +  [c]
   1.629 +  (if (or (instance? Class c) (prim->class c))
   1.630 +    (Type/getType (the-class c))
   1.631 +    (let [strx (str c)]
   1.632 +      (Type/getObjectType 
   1.633 +       (.replace (if (some #{\.} strx)
   1.634 +                   strx
   1.635 +                   (str "java.lang." strx)) 
   1.636 +                 "." "/")))))
   1.637 +
   1.638 +(defn- generate-interface
   1.639 +  [{:keys [name extends methods]}]
   1.640 +  (let [iname (.replace (str name) "." "/")
   1.641 +        cv (ClassWriter. ClassWriter/COMPUTE_MAXS)]
   1.642 +    (. cv visit Opcodes/V1_5 (+ Opcodes/ACC_PUBLIC 
   1.643 +                                Opcodes/ACC_ABSTRACT
   1.644 +                                Opcodes/ACC_INTERFACE)
   1.645 +       iname nil "java/lang/Object"
   1.646 +       (when (seq extends)
   1.647 +         (into-array (map #(.getInternalName (asm-type %)) extends))))
   1.648 +    (add-annotations cv (meta name))
   1.649 +    (doseq [[mname pclasses rclass pmetas] methods]
   1.650 +      (let [mv (. cv visitMethod (+ Opcodes/ACC_PUBLIC Opcodes/ACC_ABSTRACT)
   1.651 +                  (str mname)
   1.652 +                  (Type/getMethodDescriptor (asm-type rclass) 
   1.653 +                                            (if pclasses
   1.654 +                                              (into-array Type (map asm-type pclasses))
   1.655 +                                              (make-array Type 0)))
   1.656 +                  nil nil)]
   1.657 +        (add-annotations mv (meta mname))
   1.658 +        (dotimes [i (count pmetas)]
   1.659 +          (add-annotations mv (nth pmetas i) i))
   1.660 +        (. mv visitEnd)))
   1.661 +    (. cv visitEnd)
   1.662 +    [iname (. cv toByteArray)]))
   1.663 +
   1.664 +(defmacro gen-interface
   1.665 +  "When compiling, generates compiled bytecode for an interface with
   1.666 +  the given package-qualified :name (which, as all names in these
   1.667 +  parameters, can be a string or symbol), and writes the .class file
   1.668 +  to the *compile-path* directory.  When not compiling, does nothing.
   1.669 + 
   1.670 +  In all subsequent sections taking types, the primitive types can be
   1.671 +  referred to by their Java names (int, float etc), and classes in the
   1.672 +  java.lang package can be used without a package qualifier. All other
   1.673 +  classes must be fully qualified.
   1.674 + 
   1.675 +  Options should be a set of key/value pairs, all except for :name are
   1.676 +  optional:
   1.677 +
   1.678 +  :name aname
   1.679 +
   1.680 +  The package-qualified name of the class to be generated
   1.681 +
   1.682 +  :extends [interface ...]
   1.683 +
   1.684 +  One or more interfaces, which will be extended by this interface.
   1.685 +
   1.686 +  :methods [ [name [param-types] return-type], ...]
   1.687 +
   1.688 +  This parameter is used to specify the signatures of the methods of
   1.689 +  the generated interface.  Do not repeat superinterface signatures
   1.690 +  here."
   1.691 +  {:added "1.0"}
   1.692 +
   1.693 +  [& options]
   1.694 +    (let [options-map (apply hash-map options)
   1.695 +          [cname bytecode] (generate-interface options-map)]
   1.696 +      (if *compile-files*
   1.697 +        (clojure.lang.Compiler/writeClassFile cname bytecode)
   1.698 +        (.defineClass ^DynamicClassLoader (deref clojure.lang.Compiler/LOADER) 
   1.699 +                      (str (:name options-map)) bytecode options)))) 
   1.700 +
   1.701 +(comment
   1.702 +
   1.703 +(defn gen-and-load-class 
   1.704 +  "Generates and immediately loads the bytecode for the specified
   1.705 +  class. Note that a class generated this way can be loaded only once
   1.706 +  - the JVM supports only one class with a given name per
   1.707 +  classloader. Subsequent to generation you can import it into any
   1.708 +  desired namespaces just like any other class. See gen-class for a
   1.709 +  description of the options."
   1.710 +  {:added "1.0"}
   1.711 +
   1.712 +  [& options]
   1.713 +  (let [options-map (apply hash-map options)
   1.714 +        [cname bytecode] (generate-class options-map)]
   1.715 +    (.. (clojure.lang.RT/getRootClassLoader) (defineClass cname bytecode options))))
   1.716 +
   1.717 +)