aboutsummaryrefslogtreecommitdiff
path: root/src/clojure/contrib/gen_interface/gen_interface.clj
diff options
context:
space:
mode:
authorChouser <chouser@n01se.net>2008-11-12 22:28:54 +0000
committerChouser <chouser@n01se.net>2008-11-12 22:28:54 +0000
commitdb748f4c8b6f37da894b3c8f7a3bb683eea3f0aa (patch)
tree39f9b07d6dcbb7e241e2b5eaf35be10b8dc3067f /src/clojure/contrib/gen_interface/gen_interface.clj
parent59ba1cf0eb1811d25d8a8ef4b5c7e09fdf56492d (diff)
Update gen-interface for Clojure SVN 1094
Diffstat (limited to 'src/clojure/contrib/gen_interface/gen_interface.clj')
-rw-r--r--src/clojure/contrib/gen_interface/gen_interface.clj177
1 files changed, 0 insertions, 177 deletions
diff --git a/src/clojure/contrib/gen_interface/gen_interface.clj b/src/clojure/contrib/gen_interface/gen_interface.clj
deleted file mode 100644
index c50154eb..00000000
--- a/src/clojure/contrib/gen_interface/gen_interface.clj
+++ /dev/null
@@ -1,177 +0,0 @@
-; Copyright (c) Chris Houser, July 2008. 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.
-
-; Functions for generating interface classes, which can then be loaded
-; or saved to a .class file.
-
-(ns clojure.contrib.gen-interface
- (:import (clojure.asm ClassWriter Opcodes Type)
- (java.io File FileOutputStream IOException)))
-
-(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 (instance? Class c)
- (Type/getType c)
- (Type/getObjectType (.replace (str c) "." "/"))))
-
-(defn- iname
- "Returns the internal name of given class or class name. Cannot be
- used for primitive types."
- [c] (.getInternalName (asm-type c)))
-
-(defstruct #^{:private true} spec-map :cname :iname :extends :methods)
-
-(defn- make-spec
- "Returns an interface spec object based on the given description.
- cname is the fully-qualified classname (string or symbol) of the
- interface to be created.
- extends is a collection of classes this interface will extend (each
- may be a string, symbol, or a class). These are followed by the
- method descriptions, each of which is a vector: [methodName,
- arg types, return type]"
- [cname extends & methods]
- (struct spec-map
- (str cname)
- (iname cname)
- (set (map iname extends))
- (set (map (fn [[mname pclasses rclass]]
- [(str mname)
- (map asm-type pclasses)
- (asm-type rclass)])
- methods))))
-
-(defn- spec-from-class
- "Returns an interface spec object based on the given class."
- [c]
- (struct spec-map
- (.getName c)
- (iname c)
- (set (map iname (.getInterfaces c)))
- (set (map (fn [m]
- [(.getName m)
- (map asm-type (.getParameterTypes m))
- (asm-type (.getReturnType m))])
- (.getDeclaredMethods c)))))
-
-(def #^{:private true} object-iname (iname Object))
-
-(defn- spec-bytecode
- "Uses the given interface spec object (such as created by make-spec)
- to generate a Java interface. Returns a byte array containing the
- Java bytecode for the interface. You'll almost always want to use
- gen-interface instead."
- [{:keys [iname extends methods]}]
- (let [cv (ClassWriter. ClassWriter/COMPUTE_MAXS)]
- (. cv visit Opcodes/V1_5 (+ Opcodes/ACC_PUBLIC Opcodes/ACC_ABSTRACT
- Opcodes/ACC_INTERFACE)
- iname nil object-iname
- (when (seq extends)
- (into-array extends)))
- (doseq [mname pclasses rclass] methods
- (. cv visitMethod (+ Opcodes/ACC_PUBLIC Opcodes/ACC_ABSTRACT)
- mname
- (Type/getMethodDescriptor rclass (if pclasses
- (into-array pclasses)
- (make-array Type 0)))
- nil nil))
- (. cv visitEnd)
- (. cv toByteArray)))
-
-
-(defn- load-interface-bytecode
- [{:keys [cname] :as spec} bytecode]
- (let [old-class (try (Class/forName cname) (catch Throwable t nil))]
- (if old-class
- (when-not (= spec (spec-from-class old-class))
- (throw (Exception. (str "A different class named "
- cname " already loaded"))))
- (.. clojure.lang.RT
- ROOT_CLASSLOADER (defineClass cname bytecode)))))
-
-(defn- save-interface-bytecode
- [path {:keys [cname]} bytecode]
- (let [file (File. path (str (.replace cname \. File/separatorChar)
- ".class"))]
- (try
- (.createNewFile file)
- (catch IOException e
- (throw (Exception. (str "Failed to create " file) e))))
- (with-open f (FileOutputStream. file)
- (.write f bytecode))))
-
-(defn gen-and-load-interface
- "Uses the given interface description to generate a Java interface
- and immediately load it. make-spec-args is the interface
- description as documented in make-spec. This function is not
- generally useful since you'll usually want a .class file in order
- to write Java code that uses the generated interface -- see
- gen-interface instead."
- [& make-spec-args]
- (let [spec (apply make-spec make-spec-args)]
- (load-interface-bytecode spec (spec-bytecode spec))))
-
-(defn gen-and-save-interface
- "Uses the given interface description to generate a Java interface
- and save it to a .class file. make-spec-args is the interface
- description as documented in make-spec. The .class file will be
- written into a sub-directory of the given base path (note that the
- appropriate sub-directories under path must already exist or this
- will throw an exception). If you intend to use this interface
- immediately (for example to refer to it in a later gen-interface or
- gen-class call), you'll want to use gen-interface instead."
- [path & make-spec-args]
- (let [spec (apply make-spec make-spec-args)]
- (save-interface-bytecode path spec (spec-bytecode spec))))
-
-(defn gen-interface
- "Uses the given interface description to generate a Java interface,
- save it to a .class file, and immediately load it so it's ready
- for use by subsequent gen-interface or gen-class calls. The .class
- file will be written into a sub-directory of the given base path.
- make-spec-args is the interface description as documented in
- make-spec."
- [path & make-spec-args]
- (let [spec (apply make-spec make-spec-args)
- bytecode (spec-bytecode spec)]
- (load-interface-bytecode spec bytecode)
- (save-interface-bytecode path spec bytecode)))
-
-(comment
-
-(gen-interface "/tmp" 'net.n01se.Foo [Appendable]
- ['foo [] Integer]
- ['bar [Integer/TYPE String] Double])
-
-; re-genning an identical interface doesn't try to load anything
-(gen-interface "/tmp" 'net.n01se.Foo [Appendable]
- ['foo [] Integer]
- ['bar [Integer/TYPE String] Double])
-
-; re-genning a different interface throws an exception
-;(gen-interface "/tmp" 'net.n01se.Foo [Appendable]
-; ['foo [] Integer])
-
-; gen-and-save-interface is used directly in this example because I
-; want to refer to a class that's not yet defined in this runtime
-; (Other). This is possible because I specify the class as a quoted
-; symbol, and then don't load it -- but this isn't really recommended.
-; Instead, why not make sure Other is defined -- then you can use
-; gen-interface.
-(gen-and-save-interface "/tmp" 'net.n01se.Bar ['net.n01se.Other Iterable]
- ['baz [] net.n01se.Foo])
-
-(prn :isInterface (.isInterface (identity net.n01se.Foo)))
-(prn :interfaces (seq (.getGenericInterfaces (identity net.n01se.Foo))))
-(doseq m (seq (.getMethods (identity net.n01se.Foo)))
- (prn m))
-
-)