summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRich Hickey <richhickey@gmail.com>2008-11-29 22:26:26 +0000
committerRich Hickey <richhickey@gmail.com>2008-11-29 22:26:26 +0000
commit3cee77a74c9d00604535d4e39292e6c40330c13b (patch)
treeb0d5e4ccf0c9c80b88af24851b439ac2163214c2 /src
parenta4f923670d89fd7f4d72f39ec85ec8f46ea43bdc (diff)
added gen-interface
Diffstat (limited to 'src')
-rw-r--r--src/clj/clojure/genclass.clj135
1 files changed, 75 insertions, 60 deletions
diff --git a/src/clj/clojure/genclass.clj b/src/clj/clojure/genclass.clj
index d84ab614..482f791e 100644
--- a/src/clj/clojure/genclass.clj
+++ b/src/clj/clojure/genclass.clj
@@ -80,7 +80,7 @@
strx
(str "java.lang." strx))))))
-(defn generate-class [options-map]
+(defn- generate-class [options-map]
(let [default-options {:prefix "-" :load-impl-ns true :impl-ns (ns-name *ns*)}
{:keys [name extends implements constructors methods main factory state init exposes
prefix load-impl-ns impl-ns]}
@@ -413,7 +413,7 @@
and :main below) will be found similarly prefixed. By default, the
static initializer for the generated class will attempt to load the
Clojure support code for the class as a resource from the classpath,
- e.g. in the example case, org/mydomain/MyClass__init.class. This
+ e.g. in the example case, ``org/mydomain/MyClass__init.class``. This
behavior can be controlled by :load-impl-ns
Note that methods with a maximum of 18 parameters are supported.
@@ -513,6 +513,79 @@
[cname bytecode] (generate-class options-map)]
(clojure.lang.Compiler/writeClassFile cname bytecode))))
+;;;;;;;;;;;;;;;;;;;; gen-interface ;;;;;;;;;;;;;;;;;;;;;;
+;; based on original contribution by Chris Houser
+
+(defn- asm-type
+ "Returns an asm Type object for c, which may be a primitive class
+ (such as Integer/TYPE), any other class (such as Double), or a
+ fully-qualified class name given as a string or symbol
+ (such as 'java.lang.String)"
+ [c]
+ (if (or (instance? Class c) (prim->class c))
+ (Type/getType (the-class c))
+ (let [strx (str c)]
+ (Type/getObjectType
+ (.replace (if (some #{\.} strx)
+ strx
+ (str "java.lang." strx))
+ "." "/")))))
+
+(defn- generate-interface
+ [{:keys [name extends methods]}]
+ (let [iname (.replace (str name) "." "/")
+ cv (ClassWriter. ClassWriter/COMPUTE_MAXS)]
+ (. cv visit Opcodes/V1_5 (+ Opcodes/ACC_PUBLIC
+ Opcodes/ACC_ABSTRACT
+ Opcodes/ACC_INTERFACE)
+ iname nil "java/lang/Object"
+ (when (seq extends)
+ (into-array (map #(.getInternalName (asm-type %)) extends))))
+ (doseq [[mname pclasses rclass] methods]
+ (. cv visitMethod (+ Opcodes/ACC_PUBLIC Opcodes/ACC_ABSTRACT)
+ (str mname)
+ (Type/getMethodDescriptor (asm-type rclass)
+ (if pclasses
+ (into-array Type (map asm-type pclasses))
+ (make-array Type 0)))
+ nil nil))
+ (. cv visitEnd)
+ [iname (. cv toByteArray)]))
+
+(defmacro gen-interface
+ "When compiling, generates compiled bytecode for an interface with
+ the given package-qualified :name (which, as all names in these
+ parameters, can be a string or symbol), and writes the .class file
+ to the *compile-path* directory. When not compiling, does nothing.
+
+ In all subsequent sections taking types, the primitive types can be
+ referred to by their Java names (int, float etc), and classes in the
+ java.lang package can be used without a package qualifier. All other
+ classes must be fully qualified.
+
+ Options should be a set of key/value pairs, all except for :name are
+ optional:
+
+ :name aname
+
+ The package-qualified name of the class to be generated
+
+ :extends [interface ...]
+
+ One or more interfaces, which will be extended by this interface.
+
+ :methods [ [name [param-types] return-type], ...]
+
+ This parameter is used to specify the signatures of the methods of
+ the generated interface. Do not repeat superinterface signatures
+ here."
+
+ [& options]
+ (when *compile-files*
+ (let [options-map (apply hash-map options)
+ [cname bytecode] (generate-interface options-map)]
+ (clojure.lang.Compiler/writeClassFile cname bytecode))))
+
(comment
(defn gen-and-load-class
@@ -529,61 +602,3 @@
(.. clojure.lang.RT ROOT_CLASSLOADER (defineClass cname bytecode))))
)
-
-(comment
-;usage
-(gen-class
- package-qualified-name
- ;all below are optional
- :extends aclass
- :implements [interface ...]
- :constructors {[param-types] [super-param-types], }
- :methods [[name [param-types] return-type], ]
- :main boolean
- :factory name
- :state name
- :init name
- :exposes {protected-field {:get name :set name}, })
-
-(gen-and-load-class 'net.n01se.TestObj
- :extends javax.swing.DefaultCellEditor
- :constructors {[Integer] [javax.swing.JCheckBox]}
- :factory 'makeone
- :methods [['mymax [Integer] Integer]]
- :init 'init
- :main true
- :state 'myint
- :exposes '{clickCountToStart {:get get-c-count :set set-c-count}})
-
-;-------------------------------
-(clojure/in-ns 'net.n01se)
-(clojure/refer 'clojure)
-
-(defn TestObj-init [myint]
- [[(javax.swing.JCheckBox.)] myint])
-
-(defn TestObj-mymax [this i]
- (max i (.myint this)))
-
-(defn TestObj-getCellEditorValue [this]
- (prn :getCellEditorValue)
- nil)
-
-(defn TestObj-main [istr]
- (prn :main istr))
-
-;-------------------------------
-(in-ns 'user)
-(prn (.mymax (net.n01se.TestObj. 5) 9))
-(.getCellEditorValue (net.n01se.TestObj. 6))
-(prn (.myint (net.n01se.TestObj. 7)))
-(prn (. net.n01se.TestObj makeone 8))
-(prn (net.n01se.TestObj/makeone 9))
-(. net.n01se.TestObj main (into-array ["howdy"]))
-(prn (net.n01se/TestObj-getCellEditorValue (net.n01se.TestObj. 10)))
-(prn (.get-c-count (net.n01se.TestObj. 11)))
-
-; the following would fail because :exposes only generates class
-; methods, not namespace functions
-;(prn (net.n01se.TestObj/get-c-count (net.n01se.TestObj. 12)))
-)