On Mon, Feb 28, 2011 at 9:25 AM, Jules <jules.gosn...@gmail.com> wrote: > This is actualy my preferred route - however I've just revisited some > test code I wrote a while back, that I thought I had working... - I > only had it half working :-( - and in having another go at it, I've > realised that there is a big problem with this approach. I can't see a > way to get from a class object to class bytecode for dynamically > created types. So my client's URLClassLoader can request user.Foo from > my server, which can look up the class in the current > ContextClassLoader, but is then stumped :-(
OK, this is what you're going to have to do. First, hack your version of Clojure to alter clojure.lang.DynamicClassLoader. You need to wrap the defineClass method body in one that saves a copy of the method's parameters somewhere where Clojure can get at it. Say in a "public static Atom bytecode" in a PersistentHashMap, with an added last line in defineClass using compareAndSet to update the map with an association of the Class object just created and a PersistentVector of the parameters. You'll have to work with the Atom, the PersistentHashMap, and the PersistentVector from inside Java, but it can be done. Now, in Clojure: (defn class-data [f] (@(clojure.lang.DynamicClassLoader/bytecode) (.getClass f))) (def class-loader (.getClassLoader (.getClass #(+ %1 %2 %3)))) (defn fn-from-class-data [[nm bytecode src]] (.newInstance (.defineClass class-loader nm bytecode obj)))) And now you just use class-data to extract a magic cookie from a fn which, when passed to fn-from-class-data, can recreate the fn. The cookie is a String (the class name), a byte array (the bytecode), and a quoted Clojure form (the source code). This cookie should be Serializable and in fact if you convert back to a byte array from a seq on reception it should survive going through prn and read, too. I'd probably just pass the source code around and use eval, though. :) In particular there's no need to use a modified Clojure runtime in that case -- just use your own macro wrapping fn to make the functions whose source you'l need (the macro can just capture its arguments to somewhere) and a function that takes an argument set like that and evals up the corresponding function, something like (def fn-sources (atom {})) (defmacro mobile-fn [& args] `(let [f# (fn ~@args)] (swap! fn-sources assoc f# (quote ~args)) f#)) (defn source-of [f] (@fn-sources f)) (defn mobile-fn-from-source [src] (eval `(mobile-fn ~@src))) user=> (def q (mobile-fn [x] (+ 3 x))) #'user/q user=> (q 4) 7 user=> (source-of q) ([x] (+ 3 x)) user=> (def r (mobile-fn-from-source (source-of q))) #'user/r user=> (r 4) 7 user=> As you can see, source-of's output can be moved around and even serialized with prn and deserialized with read; mobile-fn functions' source can be extracted with source-of; and mobile-fn-from-source regenerates the function at the receiving end (and in a form that can be passed on). WARNING: obviously you can actually write a computer virus that will infect runtimes that have the above four things defined! Security is going to be a big concern if internet-visible ports are going to be receiving serialized function sources. The same applies to bytecode schlepped if you use the modified DynamicClassLoader scheme instead. > If there is a way to lookup an engineered class' bytecode from within > Clojure, I'd be very grateful to hear about it. There doesn't seem to be without modifying DynamicClassLoader as described. > Yes - creation of a custom constructor based on my extra metadata is > one of the extra things that my wrapper macro does. > > I guess what I would like to be able to do is to look up my metadata > on the Foo symbol after def-record has run - but then, if I had loaded > Foo from a URLClassLoader I would not find the metadata there, so it > is a good thing that I have not come to rely on this :-) If Foo is being moved around remotely you'll need the metadata to go elsewhere -- in an associated hashtable that can be serialized and shipped around, say, keyed by classname String (I wouldn't key on Class objects -- the key may not serialize, or may not match up with the local copy of Foo defined with the local ClassLoader, etc.) -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en