diff options
author | Rich Hickey <richhickey@gmail.com> | 2008-05-01 20:04:56 +0000 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2008-05-01 20:04:56 +0000 |
commit | 3c61b622e13aa65a668d4635382dfd35c046054c (patch) | |
tree | 9dfc5166d8146cf66a7bda2f87ebc0bf462b94f1 /src | |
parent | 3c7cd543d6cffe1add3bc534af5a7a819715f4d0 (diff) |
added genclass.clj, in progress
Diffstat (limited to 'src')
-rw-r--r-- | src/genclass.clj | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/src/genclass.clj b/src/genclass.clj new file mode 100644 index 00000000..5453b8e2 --- /dev/null +++ b/src/genclass.clj @@ -0,0 +1,253 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) +; which can be found in the file CPL.TXT at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(import '(java.lang.reflect Modifier Constructor) + '(clojure.asm ClassWriter ClassVisitor Opcodes Type) + '(clojure.asm.commons Method GeneratorAdapter) + '(clojure.lang IPersistentMap)) + +(defn method-sig [#^java.lang.reflect.Method meth] + [(. meth (getName)) (seq (. meth (getParameterTypes)))]) + +(defn non-private-methods [#^Class c] + (loop [mm {} + considered #{} + c c] + (if c + (let [[mm considered] + (loop [mm mm + considered considered + meths (concat + (seq (. c (getDeclaredMethods))) + (seq (. c (getMethods))))] + (if meths + (let [#^Method meth (first meths) + mods (. meth (getModifiers)) + mk (method-sig meth)] + (if (or (considered mk) + (. Modifier (isPrivate mods)) + (. Modifier (isStatic mods)) + (. Modifier (isFinal mods))) + (recur mm (conj considered mk) (rest meths)) + (recur (assoc mm mk meth) (conj considered mk) (rest meths)))) + [mm considered]))] + (recur mm considered (. c (getSuperclass)))) + mm))) + +(defn ctor-sigs [super] + (for [#^Constructor ctor (. super (getDeclaredConstructors)) + :when (not (. Modifier (isPrivate (. ctor (getModifiers)))))] + (apply vector (. ctor (getParameterTypes))))) + +(distinct (map first(keys (mapcat non-private-methods [Object IPersistentMap])))) + +(defn gen-class + ([name [super & interfaces :as supers]] + (gen-class name supers (zipmap (ctor-sigs super) (ctor-sigs super)))) + ([name [super & interfaces :as supers] ctor-sig-map] + (let [cv (new ClassWriter (. ClassWriter COMPUTE_MAXS)) + cname (. name (replace "." "/")) + ctype (. Type (getObjectType cname)) + iname (fn [c] (.. Type (getType c) (getInternalName))) + totype (fn [c] (. Type (getType c))) + to-types (fn [cs] (if (pos? (count cs)) + (into-array (map totype cs)) + (make-array Type 0))) + obj-type (totype Object) + arg-types (fn [n] (if (pos? n) + (into-array (replicate n obj-type)) + (make-array Type 0))) + super-type (totype super) + init-name "__init" + factory-name "__create" + state-name "__state" + main-name "main" + var-name (fn [s] (str s "__var")) + rt-type (totype clojure.lang.RT) + var-type (totype clojure.lang.Var) + ifn-type (totype clojure.lang.IFn) + ex-type (totype java.lang.UnsupportedOperationException) + var-fields (list* init-name factory-name main-name + (distinct (map first (keys (mapcat non-private-methods supers))))) + emit-get-var (fn [gen v] + (let [false-label (. gen newLabel) + end-label (. gen newLabel)] + (. gen getStatic ctype (var-name v) var-type) + (. gen invokeVirtual var-type (. Method (getMethod "boolean isBound()"))) + (. gen ifZCmp (GeneratorAdapter.EQ) false-label) + (. gen getStatic ctype (var-name v) var-type) + (. gen goTo end-label) + (. gen mark false-label) + (. gen visitInsn (Opcodes.ACONST_NULL)) + (. gen mark end-label))) + emit-forwarding-method + (fn [#^java.lang.reflect.Method meth else-gen] + (let [pclasses (. meth (getParameterTypes)) + ptypes (to-types pclasses) + rtype (totype (. meth (getReturnType))) + m (new Method (. meth (getName)) rtype ptypes) + gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv) + else-label (. gen (newLabel)) + end-label (. gen (newLabel)) + decl-type (. Type (getType (. meth (getDeclaringClass))))] + (. gen (visitCode)) + (emit-get-var gen (. meth (getName))) + (. gen (dup)) + (. gen (ifNull else-label)) + ;if found + (. gen (loadThis)) + ;box args + (dotimes i (count ptypes) + (. gen (loadArg i)) + (. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i)))) + ;call fn + (. gen (invokeInterface ifn-type (new Method "invoke" obj-type + (into-array (cons obj-type + (replicate (count ptypes) obj-type)))))) + ;unbox return + (. gen (unbox rtype)) + (when (= (. rtype (getSort)) (. Type VOID)) + (. gen (pop))) + (. gen (goTo end-label)) + + ;else call supplied alternative generator + (. gen (mark else-label)) + (. gen (pop)) + + (else-gen gen m) + + (. gen (mark end-label)) + (. gen (returnValue)) + (. gen (endMethod)))) + ] + ;start class definition + (. cv (visit (. Opcodes V1_5) (. Opcodes ACC_PUBLIC) + cname nil (iname super) + (when interfaces + (into-array (map iname interfaces))))) + + ;static fields for vars + (doseq v var-fields + (. cv (visitField (+ (Opcodes.ACC_PUBLIC) (Opcodes.ACC_FINAL) (Opcodes.ACC_STATIC)) + (var-name v) + (. var-type getDescriptor) + nil nil))) + + ;instance field for state + (. cv (visitField (+ (Opcodes.ACC_PUBLIC) (Opcodes.ACC_FINAL)) + state-name + (. obj-type getDescriptor) + nil nil)) + + ;static init to set up var fields + (let [gen (new GeneratorAdapter (+ (Opcodes.ACC_PUBLIC) (Opcodes.ACC_STATIC)) + (Method.getMethod "void <clinit> ()") + nil nil cv)] + (. gen (visitCode)) + (doseq v var-fields + (. gen push name) + (. gen push v) + (. gen (invokeStatic rt-type (. Method (getMethod "clojure.lang.Var var(String,String)")))) + (. gen putStatic ctype (var-name v) var-type)) + (. gen (returnValue)) + (. gen (endMethod))) + + ;ctors + (doseq [pclasses super-pclasses] ctor-sig-map + (let [ptypes (to-types pclasses) + super-ptypes (to-types super-pclasses) + m (new Method "<init>" (. Type VOID_TYPE) ptypes) + super-m (new Method "<init>" (. Type VOID_TYPE) super-ptypes) + gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv) + no-init-label (. gen newLabel) + end-label (. gen newLabel) + nth-method (. Method (getMethod "Object nth(Object,int)")) + local (. gen newLocal obj-type)] + (. gen (visitCode)) + + (emit-get-var gen init-name) + (. gen dup) + (. gen ifNull no-init-label) + ;box init args + (dotimes i (count pclasses) + (. gen (loadArg i)) + (. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i)))) + ;call init fn + (. gen (invokeInterface ifn-type (new Method "invoke" obj-type + (arg-types (count ptypes))))) + ;expecting [[super-ctor-args] state] returned + (. gen dup) + (. gen push 0) + (. gen (invokeStatic rt-type nth-method)) + (. gen storeLocal local) + + (. gen (loadThis)) + (. gen dupX1) + (dotimes i (count super-pclasses) + (. gen loadLocal local) + (. gen push i) + (. gen (invokeStatic rt-type nth-method)) + (. clojure.lang.Compiler$HostExpr (emitUnboxArg nil gen (nth super-pclasses i)))) + (. gen (invokeConstructor super-type super-m)) + + ;set state + (. gen push 1) + (. gen (invokeStatic rt-type nth-method)) + (. gen (putField ctype state-name obj-type)) + + (. gen goTo end-label) + ;no init + (. gen mark no-init-label) + (if (= pclasses super-pclasses) + (do + (. gen pop) + (. gen (loadThis)) + (. gen (loadArgs)) + (. gen (invokeConstructor super-type super-m))) + (. gen (throwException ex-type (str init-name " not defined, but ctor and super ctor args differ")))) + (. gen mark end-label) + (. gen (returnValue)) + (. gen (endMethod)))) + + ;add methods matching supers', if no fn -> call super + (let [mm (non-private-methods super)] + (doseq #^java.lang.reflect.Method meth (vals mm) + (emit-forwarding-method meth + (fn [gen m] + (. gen (loadThis)) + ;push args + (. gen (loadArgs)) + ;call super + (. gen (visitMethodInsn (. Opcodes INVOKESPECIAL) + (. super-type (getInternalName)) + (. m (getName)) + (. m (getDescriptor))))))) + ;add methods matching interfaces', if no fn -> throw + (doseq #^Class iface interfaces + (doseq #^java.lang.reflect.Method meth (. iface (getMethods)) + (when-not (contains? mm (method-sig meth)) + (emit-forwarding-method meth + (fn [gen m] + (. gen (throwException ex-type (. m (getName)))))))))) + + ;finish class def + (. cv (visitEnd)) + {:name name :bytecode (. cv (toByteArray))}))) + +(comment + +(let [{:keys [name bytecode]} (gen-class (str (gensym "fred.lucy.Ethel__")) [Object IPersistentMap])];{[Object] []})] + (.. clojure.lang.RT ROOT_CLASSLOADER (defineClass name bytecode))) + +(def ethel (new fred.lucy.Ethel__1989)) + +(in-ns 'fred.lucy.Ethel__1881) +(clojure/refer 'clojure :exclude '(assoc seq count cons)) +(defn __init [n] [[] n]) + +)
\ No newline at end of file |