proposed improvement to compiler exceptions

6 views
Skip to first unread message

Allen Rohner

unread,
Jun 7, 2010, 11:35:09 PM6/7/10
to Clojure Dev
I wrote some bad clojure code, and got the following stacktrace. I
dare you to tell me the source of the error. In my code, ui.clj line 1
is a standard ns declaration, with ~30 lines of require statements. In
an ns declaration, all of the action happens on line 1, so it's
impossible to tell which file in the ns declaration caused the
exception.

I'd like add a try / catch block somewhere in the load path, that
wraps and re-throws it with an error message of "Error while loading
foo.clj". Since Compiler.load() takes a reader (and the reader doesn't
know the name of the file it's loading), I propose adding the try /
catch in RT.loadResourceScript()

Thoughts? Patch welcome?


user=> (.printStackTrace *e)
java.lang.ClassCastException: java.lang.Integer cannot be cast to
clojure.lang.IObj (ui.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:5437)
at clojure.lang.Compiler.load(Compiler.java:5854)
at clojure.lang.RT.loadResourceScript(RT.java:339)
at clojure.lang.RT.loadResourceScript(RT.java:330)
at clojure.lang.RT.load(RT.java:408)
at clojure.lang.RT.load(RT.java:380)
at clojure.core$load$fn__4455.invoke(core.clj:4891)
at clojure.core$load.doInvoke(core.clj:4890)
at clojure.lang.RestFn.invoke(RestFn.java:409)
at clojure.core$load_one.invoke(core.clj:4715)
at clojure.core$load_lib.doInvoke(core.clj:4752)
at clojure.lang.RestFn.applyTo(RestFn.java:143)
at clojure.core$apply.invoke(core.clj:542)
at clojure.core$load_libs.doInvoke(core.clj:4786)
at clojure.lang.RestFn.applyTo(RestFn.java:138)
at clojure.core$apply.invoke(core.clj:542)
at clojure.core$require.doInvoke(core.clj:4855)
at clojure.lang.RestFn.invoke(RestFn.java:409)
at winston.init
$eval14439$loading__4354__auto____14440.invoke(init.clj:1)
at winston.init$eval14439.invoke(init.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:5421)
at clojure.lang.Compiler.eval(Compiler.java:5412)
at clojure.lang.Compiler.load(Compiler.java:5854)
at clojure.lang.RT.loadResourceScript(RT.java:339)
at clojure.lang.RT.loadResourceScript(RT.java:330)
at clojure.lang.RT.load(RT.java:408)
at clojure.lang.RT.load(RT.java:380)
at clojure.core$load$fn__4455.invoke(core.clj:4891)
at clojure.core$load.doInvoke(core.clj:4890)
at clojure.lang.RestFn.invoke(RestFn.java:409)
at clojure.core$load_one.invoke(core.clj:4715)
at clojure.core$load_lib.doInvoke(core.clj:4752)
at clojure.lang.RestFn.applyTo(RestFn.java:143)
at clojure.core$apply.invoke(core.clj:542)
at clojure.core$load_libs.doInvoke(core.clj:4786)
at clojure.lang.RestFn.applyTo(RestFn.java:138)
at clojure.core$apply.invoke(core.clj:542)
at clojure.core$require.doInvoke(core.clj:4855)
at clojure.lang.RestFn.invoke(RestFn.java:409)
at user$eval14435.invoke(NO_SOURCE_FILE:3)
at clojure.lang.Compiler.eval(Compiler.java:5421)
at clojure.lang.Compiler.eval(Compiler.java:5388)
at clojure.core$eval.invoke(core.clj:2368)
at clojure.main$repl$read_eval_print__5506.invoke(main.clj:183)
at clojure.main$repl$fn__5511.invoke(main.clj:203)
at clojure.main$repl.doInvoke(main.clj:203)
at clojure.lang.RestFn.invoke(RestFn.java:422)
at clojure.main$repl_opt.invoke(main.clj:261)
at clojure.main$main.doInvoke(main.clj:353)
at clojure.lang.RestFn.invoke(RestFn.java:483)
at clojure.lang.Var.invoke(Var.java:381)
at clojure.lang.AFn.applyToHelper(AFn.java:180)
at clojure.lang.Var.applyTo(Var.java:482)
at clojure.main.main(main.java:37)
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 jline.ConsoleRunner.main(ConsoleRunner.java:69)
Caused by: java.lang.ClassCastException: java.lang.Integer cannot be
cast to clojure.lang.IObj
at clojure.core$with_meta.invoke(core.clj:192)
at clojure.core$defn.doInvoke(core.clj:284)
at clojure.lang.RestFn.invoke(RestFn.java:526)
at clojure.lang.Var.invoke(Var.java:385)
at clojure.lang.AFn.applyToHelper(AFn.java:187)
at clojure.lang.Var.applyTo(Var.java:482)
at clojure.lang.Compiler.macroexpand1(Compiler.java:5283)
at clojure.lang.Compiler.macroexpand(Compiler.java:5338)
at clojure.lang.Compiler.eval(Compiler.java:5406)
... 58 more
nil

Allen Rohner

unread,
Jun 7, 2010, 11:42:28 PM6/7/10
to Clojure Dev
I was looking at the wrong code when I wrote that comment. I meant to
propose adding the try catch in RT.load(). It appears there's already
a try / finally that re-throws the caught exception. All that would
need to be done is wrap the caught exception with a new exception that
has a useful message.

Allen

Richard Newman

unread,
Jun 7, 2010, 11:59:01 PM6/7/10
to cloju...@googlegroups.com
> I dare you to tell me the source of the error.

I daresay it's here, a toplevel form:

Allen Rohner

unread,
Jun 8, 2010, 12:18:29 AM6/8/10
to Clojure Dev
I'll give you a hint. It's a toplevel form, but it's not on line 1,
nor in anywhere near line 1. It's also not in winston.init.

After implementing it, my proposed fix doesn't actually work that
well. I'll come back with a new proposal after I have something that
works.

Allen

Allen Rohner

unread,
Jun 8, 2010, 8:46:40 AM6/8/10
to Clojure Dev
Ok, I found the bug.

In Compiler.java, eval() performs the following operations:

1) push a binding for LOADER
2) enter a try block
3) push a binding for LINE
4) enter another try block
5) do some work ( macro expand, eval, invoke, etc)
6) finally block that pops the bindings created in #3
7) catch block that catches compiler exceptions, and prints LINE
8) finally block that pops the bindings created in #1

The error here is that LINE is printed after its binding is popped.
The catch block should be a part of the the try in #4, rather than the
try created in #2. I've tried this fix, and it works for me.

Patch welcome?

Allen

Rich Hickey

unread,
Jun 9, 2010, 7:46:54 AM6/9/10
to Clojure Dev


On Jun 8, 8:46 am, Allen Rohner <aroh...@gmail.com> wrote:
> Ok, I found the bug.
>
> In Compiler.java, eval() performs the following operations:
>
> 1) push a binding for LOADER
> 2) enter a try block
> 3) push a binding for LINE
> 4) enter another try block
> 5) do some work ( macro expand, eval, invoke, etc)
> 6) finally block that pops the bindings created in #3
> 7) catch block that catches compiler exceptions, and prints LINE
> 8) finally block that pops the bindings created in #1
>
> The error here is that LINE is printed after its binding is popped.
> The catch block should be a part of the the try in #4, rather than the
> try created in #2. I've tried this fix, and it works for me.
>
> Patch welcome?
>

Fixed here - you'll see it when I next push.

Thanks for the report!

Rich
Reply all
Reply to author
Forward
0 new messages