summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/boot.clj4
-rw-r--r--src/jvm/clojure/lang/IProxy.java20
-rw-r--r--src/proxy.clj100
3 files changed, 123 insertions, 1 deletions
diff --git a/src/boot.clj b/src/boot.clj
index 1a24a72c..966ae840 100644
--- a/src/boot.clj
+++ b/src/boot.clj
@@ -1388,7 +1388,9 @@ make-array
#^{:doc "Returns an array of Objects containing the contents of coll, which can be any Collection.
Maps to java.util.Collection.toArray()."}
to-array [#^java.util.Collection coll]
- (. coll (toArray)))
+ (if (zero? (count coll))
+ (. clojure.lang.RT EMPTY_ARRAY)
+ (. coll (toArray))))
(defn
#^{:doc "Returns a (potentially-ragged) 2-dimensional array of Objects containing the contents of coll,
diff --git a/src/jvm/clojure/lang/IProxy.java b/src/jvm/clojure/lang/IProxy.java
new file mode 100644
index 00000000..7f3630f9
--- /dev/null
+++ b/src/jvm/clojure/lang/IProxy.java
@@ -0,0 +1,20 @@
+/**
+ * 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.
+ **/
+
+/* rich Feb 27, 2008 */
+
+package clojure.lang;
+
+public interface IProxy{
+
+public void __updateClojureFnMappings(IPersistentMap m);
+public IPersistentMap __getClojureFnMappings();
+
+}
diff --git a/src/proxy.clj b/src/proxy.clj
new file mode 100644
index 00000000..5ca04e64
--- /dev/null
+++ b/src/proxy.clj
@@ -0,0 +1,100 @@
+; 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.
+
+(in-ns 'clojure)
+
+(import
+ '(clojure.asm ClassWriter ClassVisitor Opcodes Type)
+ '(java.lang.reflect Modifier Constructor)
+ '(clojure.asm.commons Method GeneratorAdapter)
+ '(clojure.lang IProxy Reflector DynamicClassLoader IPersistentMap PersistentHashMap))
+
+(def *proxy-classes* (ref {}))
+
+(defn get-proxy-class [& bases]
+ (let [bases (if (. (first bases) (isInterface))
+ (cons Object bases)
+ bases)
+ [super & interfaces] bases]
+ (or (get @*proxy-classes* bases)
+ (let [cv (new ClassWriter (. ClassWriter COMPUTE_MAXS))
+ cname (str "clojure/lang/" (gensym "Proxy__"))
+ ctype (. Type (getObjectType cname))
+ iname (fn [c] (.. Type (getType c) (getInternalName)))
+ fmap "__clojureFnMap"
+ to-types (fn [cs] (if (pos? (count cs))
+ (into-array (map #(. Type (getType %)) cs))
+ (make-array Type 0)))
+ map-type (. Type (getType PersistentHashMap))]
+ ;start class definition
+ (. cv (visit (. Opcodes V1_5) (. Opcodes ACC_PUBLIC)
+ cname nil (iname super)
+ (into-array (map iname (cons IProxy interfaces)))))
+ ;add field for fn mappings
+ (. cv (visitField (+ (. Opcodes ACC_PRIVATE) (. Opcodes ACC_VOLATILE))
+ fmap (. map-type (getDescriptor)) nil nil))
+ ;add ctors matching/calling super's
+ (doseq #^Constructor ctor (. super (getDeclaredConstructors))
+ (when-not (. Modifier (isPrivate (. ctor (getModifiers))))
+ (let [ptypes (to-types (. ctor (getParameterTypes)))
+ m (new Method "<init>" (. Type VOID_TYPE) ptypes)
+ gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)]
+ (. gen (visitCode))
+ ;call super ctor
+ (. gen (loadThis))
+ (. gen (dup))
+ (. gen (loadArgs))
+ (. gen (invokeConstructor (. Type (getType super)) m))
+ ;init fmap
+ (. gen (getStatic map-type "EMPTY" map-type))
+ (. gen (putField ctype fmap map-type))
+
+ (. gen (returnValue))
+ (. gen (endMethod)))))
+ ;add IProxy methods
+ (let [m (. Method (getMethod "void __updateClojureFnMappings(clojure.lang.IPersistentMap)"))
+ gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)]
+ (. gen (visitCode))
+ (. gen (loadThis))
+ (. gen (dup))
+ (. gen (getField ctype fmap map-type))
+ (. gen (loadArgs))
+ (. gen (invokeInterface (. Type (getType clojure.lang.IPersistentCollection))
+ (. Method (getMethod "clojure.lang.IPersistentCollection cons(Object)"))))
+ (. gen (checkCast map-type))
+ (. gen (putField ctype fmap map-type))
+
+ (. gen (returnValue))
+ (. gen (endMethod)))
+ (let [m (. Method (getMethod "clojure.lang.IPersistentMap __getClojureFnMappings()"))
+ gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)]
+ (. gen (visitCode))
+ (. gen (loadThis))
+ (. gen (getField ctype fmap map-type))
+ (. gen (returnValue))
+ (. gen (endMethod)))
+
+ ;add methods matching super's, if no mapping -> call super
+ ;add methods matching interfaces', if no mapping -> return void or throw
+
+ ;finish class def
+ (. cv (visitEnd))
+ ;generate, cache and return class object
+ (let [loader (new DynamicClassLoader)
+ c (. loader (defineClass (. cname (replace "/" "."))
+ (. cv (toByteArray))))]
+ (sync nil (commute *proxy-classes* assoc bases c))
+ c)))))
+
+(defn construct-proxy [c & ctor-args]
+ (. Reflector (invokeConstructor c (to-array ctor-args))))
+
+(defn update-proxy [#^IProxy proxy mappings]
+ (. proxy (updateMappings mappings)))
+
+(defn proxy-mappings [#^IProxy proxy]
+ (. proxy (getMappings))) \ No newline at end of file