Proxy Bug?

60 views
Skip to first unread message

ntupel

unread,
Oct 29, 2008, 7:31:56 PM10/29/08
to Clojure
Consider the following code which attempts to redefine clojure/*out*:


(ns test
(:refer-clojure))

(def output System/out)

(def output-stream
(let [buffer (new java.io.ByteArrayOutputStream)]
(proxy [java.io.OutputStream] []
(flush []
(.append output (.toString buffer "UTF-8"))
(.reset buffer))
(write [x]
(when (>= (.size buffer) 32)
(.flush this))
(.write buffer x))
(write [x off len]
(.write buffer x off len)))))

(in-ns 'clojure)
(def *out* (new java.io.OutputStreamWriter test/output-stream "UTF-8"))


This works, but according to the the documentation of clojure/proxy it
seems that "If a method fn is not provided for a class method, the
superclass methd will be called." [1] However if I omit the second write
method in the proxy definition, I get the following error:


user=> (load-file "test.clj")
#=(var clojure/*out*)
user=> (prn "hello")
java.lang.IllegalArgumentException: Wrong number of args passed to: fn--2490$fn (NO_SOURCE_FILE:0)
user=> (.printStackTrace *e)
java.lang.IllegalArgumentException: Wrong number of args passed to: fn--2490$fn (NO_SOURCE_FILE:0)
at clojure.lang.Compiler.eval(Compiler.java:4122)
at clojure.lang.Repl.main(Repl.java:91)
Caused by: java.lang.IllegalArgumentException: Wrong number of args passed to: fn--2490$fn
at clojure.lang.AFn.throwArity(AFn.java:460)
at clojure.lang.AFn.invoke(AFn.java:75)
at clojure.lang.Proxy__2499.write(Unknown Source)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:202)
at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:272)
at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:276)
at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:122)
at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:212)
at clojure.flush__906.invoke(boot.clj:1658)
at clojure.prn__909.doInvoke(boot.clj:1667)
at clojure.lang.RestFn.invoke(RestFn.java:413)
at user.eval__2503.invoke(Unknown Source)
at clojure.lang.Compiler.eval(Compiler.java:4111)
... 1 more

--------------------------------
[1] http://clojure.org/java_interop#toc20


mb

unread,
Oct 30, 2008, 2:44:02 AM10/30/08
to Clojure
Hi,

On 30 Okt., 00:31, ntupel <ntu...@googlemail.com> wrote:
> Consider the following code which attempts to redefine clojure/*out*:
> (def output-stream
> (let [buffer (new java.io.ByteArrayOutputStream)]
> (proxy [java.io.OutputStream] []
> (flush []
> (.append output (.toString buffer "UTF-8"))
> (.reset buffer))
> (write [x]
> (when (>= (.size buffer) 32)
> (.flush this))
> (.write buffer x))
> (write [x off len]
> (.write buffer x off len)))))
> ...
> However if I omit the second write method in the proxy definition,
> I get the following error:

I think the problem here the second write. The syntax should be the
same as for defns.

(def output-stream
(let [buffer (new java.io.ByteArrayOutputStream)]
(proxy [java.io.OutputStream] []
(flush
[]
(.append output (.toString buffer "UTF-8"))
(.reset buffer))
(write
([x]
(when (>= (.size buffer) 32)
(.flush this))
(.write buffer x))
([x off len]
(.write buffer x off len))))))

I'm not sure what happens in your case, but my suspicion is, that
the second overwrites the first, or that it may be equivalent to
this form.

However when you new remove the second form, there is still a write
method! However this one takes the wrong number of arguments. Hence
the method of the super class is not called and you get the Exception.

But proxy-super to the rescue: You may try the following snippet
for the write method:

(write
([x]
(when (>= (.size buffer) 32)
(.flush this))
(.write buffer x))
([x off len]
(proxy-super write x off len)))

Since there is a write method, we have to call the super-class'
method explicitely.

Hope this helps.

Sincerely
Meikel

ntupel

unread,
Oct 30, 2008, 3:48:51 AM10/30/08
to clo...@googlegroups.com
On Wed, 2008-10-29 at 23:44 -0700, mb wrote:
> But proxy-super to the rescue: You may try the following snippet
> for the write method:

Nice. Never heard of proxy-super before. Would be nice to mention it on
the Java interop page and not only on the API page.

>
> (write
> ([x]
> (when (>= (.size buffer) 32)
> (.flush this))
> (.write buffer x))
> ([x off len]
> (proxy-super write x off len)))
>
> Since there is a write method, we have to call the super-class'
> method explicitely.

Tried it but it fails with:

java.lang.reflect.InvocationTargetException (NO_SOURCE_FILE:0)


at clojure.lang.Compiler.eval(Compiler.java:4122)
at clojure.lang.Repl.main(Repl.java:91)

Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at
clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:86)
at
clojure.lang.Reflector.invokeInstanceMethod(Reflector.java:28)
at test.fn__2490$fn__2492$fn__2495.invoke(test.clj:18)
at clojure.proxy_call_with_super__2077.invoke(proxy.clj:264)
at test.fn__2490$fn__2492.invoke(test.clj:18)
at clojure.lang.Proxy__2503.write(Unknown Source)


at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:202)
at
sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:272)
at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:276)
at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:122)
at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:212)
at clojure.flush__906.invoke(boot.clj:1658)
at clojure.prn__909.doInvoke(boot.clj:1667)
at clojure.lang.RestFn.invoke(RestFn.java:413)

at user.eval__2507.invoke(Unknown Source)


at clojure.lang.Compiler.eval(Compiler.java:4111)
... 1 more

Caused by: java.lang.AbstractMethodError: java.io.OutputStream.write(I)V
at clojure.lang.Proxy__2503.write(Unknown Source)
at java.io.OutputStream.write(OutputStream.java:99)
at clojure.lang.Proxy__2503.write(Unknown Source)
... 21 more


Interestingly java.io.OutputStream.write(I)V is invoked even though this
should be covered by the proxy write method.

> Hope this helps.

Despite the error it did. Thanks!


mb

unread,
Oct 30, 2008, 4:37:52 AM10/30/08
to Clojure
Hi,

On 30 Okt., 08:48, ntupel <ntu...@googlemail.com> wrote:
> Interestingly java.io.OutputStream.write(I)V is invoked even though this
> should be covered by the proxy write method.

I lookep up the implementation of proxy-super. It replaces the
method temporarily. So proxy-super basically doesn't work for
methods with multiple implementations for different arglists,
since only the actually called method-arglist combination is
then in effect. I think it throws the Exception because now,
the first implementation is missing and it is abstract in the
superclass.

But I might be on the wrong track here. I'm not very familiar
with Java. Let alone with reflection and stuff...

Sincerely
Meikel

ntupel

unread,
Oct 30, 2008, 4:57:01 PM10/30/08
to clo...@googlegroups.com
On Thu, 2008-10-30 at 01:37 -0700, mb wrote:
> I lookep up the implementation of proxy-super. It replaces the
> method temporarily. So proxy-super basically doesn't work for
> methods with multiple implementations for different arglists,
> since only the actually called method-arglist combination is
> then in effect. I think it throws the Exception because now,
> the first implementation is missing and it is abstract in the
> superclass.

Yes, after I looked into proxy-super I came to the the same conclusion.
I wonder though what can be done about this limitation of the proxy
approach, as according to the documentation of proxy, access to super
can not be proxied. What a pity.


Reply all
Reply to author
Forward
0 new messages