summaryrefslogtreecommitdiff
path: root/src/clj/clojure/proxy.clj
diff options
context:
space:
mode:
Diffstat (limited to 'src/clj/clojure/proxy.clj')
-rw-r--r--src/clj/clojure/proxy.clj307
1 files changed, 0 insertions, 307 deletions
diff --git a/src/clj/clojure/proxy.clj b/src/clj/clojure/proxy.clj
deleted file mode 100644
index b1491adb..00000000
--- a/src/clj/clojure/proxy.clj
+++ /dev/null
@@ -1,307 +0,0 @@
-; 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.
-
-(in-ns 'clojure.core)
-
-(import
- '(clojure.asm ClassWriter ClassVisitor Opcodes Type)
- '(java.lang.reflect Modifier Constructor)
- '(clojure.asm.commons Method GeneratorAdapter)
- '(clojure.lang IProxy Reflector DynamicClassLoader IPersistentMap PersistentHashMap RT))
-
-(def *proxy-classes* (ref {}))
-
-(defn method-sig [#^java.lang.reflect.Method meth]
- [(. meth (getName)) (seq (. meth (getParameterTypes))) (. meth getReturnType)])
-
-(defn get-proxy-class
- "Takes an optional single class followed by zero or more
- interfaces. If not supplied class defaults to Object. Creates an
- returns an instance of a proxy class derived from the supplied
- classes. The resulting value is cached and used for any subsequent
- requests for the same class set. Returns a Class object."
- [& 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"
- totype (fn [c] (. Type (getType c)))
- to-types (fn [cs] (if (pos? (count cs))
- (into-array (map totype cs))
- (make-array Type 0)))
- super-type (totype super)
- map-type (totype PersistentHashMap)
- ifn-type (totype clojure.lang.IFn)
- obj-type (totype Object)
- sym-type (totype clojure.lang.Symbol)
- rt-type (totype clojure.lang.RT)
- ex-type (totype java.lang.UnsupportedOperationException)
- gen-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))
- (. gen (loadThis))
- (. gen (getField ctype fmap map-type))
- ;get symbol corresponding to name
- (. gen (push (. meth (getName))))
- (. gen (invokeStatic sym-type (. Method (getMethod "clojure.lang.Symbol create(String)"))))
- ;lookup fn in map
- (. gen (invokeStatic rt-type (. Method (getMethod "Object get(Object, Object)"))))
- (. 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) (. Opcodes ACC_SUPER))
- 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 super-type 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 (totype 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)))
-
- ;calc set of supers' non-private instance methods
- (let [mm (loop [mm {} considered #{} c super]
- (if c
- (let [[mm considered]
- (loop [mm mm
- considered considered
- meths (concat
- (seq (. c (getDeclaredMethods)))
- (seq (. c (getMethods))))]
- (if meths
- (let [#^java.lang.reflect.Method meth (first meths)
- mods (. meth (getModifiers))
- mk (method-sig meth)]
- (if (or (considered mk)
- (. Modifier (isPrivate mods))
- (. Modifier (isStatic mods))
- (. Modifier (isFinal mods))
- (= "finalize" (.getName meth)))
- (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))]
- ;add methods matching supers', if no mapping -> call super
- (doseq [#^java.lang.reflect.Method meth (vals mm)]
- (gen-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 mapping -> throw
- (doseq [#^Class iface interfaces]
- (doseq [#^java.lang.reflect.Method meth (. iface (getMethods))]
- (when-not (contains? mm (method-sig meth))
- (gen-method meth
- (fn [gen m]
- (. gen (throwException ex-type (. m (getName))))))))))
-
- ;finish class def
- (. cv (visitEnd))
- ;generate, cache and return class object
- (let [loader (. RT ROOT_CLASSLOADER)
- c (. loader (defineClass (. cname (replace "/" "."))
- (. cv (toByteArray))))]
- (sync nil (commute *proxy-classes* assoc bases c))
- c)))))
-
-(defn construct-proxy
- "Takes a proxy class and any arguments for its superclass ctor and
- creates and returns an instance of the proxy."
- [c & ctor-args]
- (. Reflector (invokeConstructor c (to-array ctor-args))))
-
-(defn update-proxy
- "Takes a proxy instance and a map of symbols (whose names must
- correspond to methods of the proxy superclass/superinterfaces) to
- fns (which must take arguments matching the corresponding method,
- plus an additional (explicit) first arg corresponding to this, and
- updates (via assoc) the proxy's fn map. nil can be passed instead of
- a fn, in which case the corresponding method will revert to the
- default behavior. Note that this function can be used to update the
- behavior of an existing instance without changing its identity."
- [#^IProxy proxy mappings]
- (. proxy (__updateClojureFnMappings mappings)))
-
-(defn proxy-mappings
- "Takes a proxy instance and returns the proxy's fn map."
- [#^IProxy proxy]
- (. proxy (__getClojureFnMappings)))
-
-(defmacro proxy
- "class-and-interfaces - a vector of class names
-
- args - a (possibly empty) vector of arguments to the superclass
- constructor.
-
- f => (name [params*] body) or
- (name ([params*] body) ([params+] body) ...)
-
- Expands to code which creates a instance of a proxy class that
- implements the named class/interface(s) by calling the supplied
- fns. A single class, if provided, must be first. If not provided it
- defaults to Object.
-
- The interfaces names must be valid interface types. If a method fn
- is not provided for a class method, the superclass methd will be
- called. If a method fn is not provided for an interface method, an
- UnsupportedOperationException will be thrown should it be
- called. Method fns are closures and can capture the environment in
- which proxy is called. Each method fn takes an additional implicit
- first arg, which is bound to 'this. Note that while method fns can
- be provided to override protected methods, they have no other access
- to protected members, nor to super, as these capabilities cannot be
- proxied."
- [class-and-interfaces args & fs]
- `(let [pc# (get-proxy-class ~@class-and-interfaces)
- p# (construct-proxy pc# ~@args)]
- (update-proxy p#
- ~(loop [fmap {} fs fs]
- (if fs
- (let [[sym & meths] (first fs)
- meths (if (vector? (first meths))
- (list meths)
- meths)
- meths (map (fn [[params & body]]
- (cons (apply vector 'this params) body))
- meths)]
- (recur (assoc fmap (list `quote (symbol (name sym))) (cons `fn meths)) (rest fs)))
- fmap)))
- p#))
-
-(defn proxy-call-with-super [call this meth]
- (let [m (proxy-mappings this)]
- (update-proxy this (assoc m meth nil))
- (let [ret (call)]
- (update-proxy this m)
- ret)))
-
-(defmacro proxy-super
- "Use to call a superclass method in the body of a proxy method.
- Note, expansion captures 'this"
- [meth & args]
- `(proxy-call-with-super (fn [] (. ~'this ~meth ~@args)) ~'this '~(symbol (name meth))))
-
-(defn bean
- "Takes a Java object and returns a read-only implementation of the
- map abstraction based upon its JavaBean properties."
- [#^Object x]
- (let [c (. x (getClass))
- pmap (reduce (fn [m #^java.beans.PropertyDescriptor pd]
- (let [name (. pd (getName))
- method (. pd (getReadMethod))]
- (if (and method (zero? (alength (. method (getParameterTypes)))))
- (assoc m (keyword name) (fn [] (. method (invoke x nil))))
- m)))
- {}
- (seq (.. java.beans.Introspector
- (getBeanInfo c)
- (getPropertyDescriptors))))
- v (fn [k] ((pmap k)))
- snapshot (fn []
- (reduce (fn [m e]
- (assoc m (key e) ((val e))))
- {} (seq pmap)))]
- (proxy [clojure.lang.APersistentMap]
- []
- (containsKey [k] (contains? pmap k))
- (entryAt [k] (when (contains? pmap k) (new clojure.lang.MapEntry k (v k))))
- (valAt ([k] (v k))
- ([k default] (if (contains? pmap k) (v k) default)))
- (cons [m] (conj (snapshot) m))
- (count [] (count pmap))
- (assoc [k v] (assoc (snapshot) k v))
- (without [k] (dissoc (snapshot) k))
- (seq [] ((fn thisfn [pseq]
- (when pseq
- (lazy-cons (new clojure.lang.MapEntry (first pseq) (v (first pseq)))
- (thisfn (rest pseq))))) (keys pmap))))))