This JVMTI doesn't know Clojure source code from parsnip soup, I expect, so
can't it be fooled into "thinking" that the bytecode it's interpreting came
from a source with many more, shorter lines? So

(defn x [y z]
  (a (b (c))
      ((d) e (f g))))

could be passed off to it as if it were

(defn x [y z] (a (b
                    (c))
                 ((d)
                    e (f g))))

with breakpoints on (defn ...), (a ...), a, (b (c)), or b being passed to
JVMTI as breakpoints on the first line of the latter formatting of the
code; breakpoints on (c) or c the second line; breakpoints on (d) or d the
third line; and breakpoints on e, (f g), f, or g the fourth line. Execution
would halt at the breakpoint with the correct amount of evaluation done.
(The bare letters are presumed to be self-evaluating literals or var
lookups here, and the operators, other than defn, to be functions.)

Breakpoints on a macro sexp would need to halt on the first evaluation of
any of the expansion, and breakpoints on a macro argument would need to be
carried through into the expansion. The special forms are mostly fairly
obvious -- break on an if or its condition, for example, breaks before
evaluating the condition; break on the then clause breaks after evaluating
the condition, before evaluating the then clause, only if the condition was
true; etc. Combined, those rules mean a breakpoint on a particular (cond
...) condition X would only trip if all the previous conditions were false,
before evaluating condition X, and a breakpoint on the corresponding then
clause only if all the preceding conditions were false but condition X was
true, after evaluating X but before evaluating X's then clause, because it
would end up attached to X's corresponding conditional or then clause
(respectively) in the macro-expanded if-tree (>(sexp)< indicates breakpoint
target):

(cond
  (thing1) (dothis)
  (thing2) (dothat)
  >(thing3)< (dosomethingelse)
  :else (default))

should macroexpand to

(if (thing1)
  (dothis)
  (if (thing2)
    (dothat)
      (if >(thing3)<
        (dosomethingelse)
        (if :else
          (default)
          (throw ( ... no matching clause blah blah ... [never actually
executes in this case]))))))

so the "(if >(thing3)<" line would be what JVMTI "thinks" is the
breakpointed line in this case.

The real issue I can foresee here would be if there's no way to make the
"lines" seen by the breakpoint/debugging stuff be different from the
"lines" used for error reporting (e.g. in stacktraces), as then *either*
the "line" granularity for breakpoints is whole lines of your actual source
code *or* stacktrace line numbers won't always correspond to what your
editor shows for the problem line in your source code, for example if
(default) threw a ClassCastException because "default" did not implement
IFn in the above the stacktrace might point to line 8 when it was really
line 5 of your (cond ...) expression (but line 8 of the (if ...) it
macroexpanded to, and which was vertically spread out to put each distinct
(as to what nontrivial evaluation has happened yet) sexp-based breakpoint
position on its own line to please JVMTI).




On Fri, Nov 8, 2013 at 12:53 AM, Colin Fleming
<colin.mailingl...@gmail.com>wrote:

> Right, sadly I believe this is impossible using JVMTI, although I'm far
> from an expert.
>
>
> On 8 November 2013 18:51, Cedric Greevey <cgree...@gmail.com> wrote:
>
>> Ideally, in a Lisp I'd think the unit of breakpoints would not be a line
>> but instead an s-expression, tripping before evaluating the body of the
>> expression.
>>
>>
>> On Fri, Nov 8, 2013 at 12:24 AM, Colin Fleming <
>> colin.mailingl...@gmail.com> wrote:
>>
>>> I'm slightly late to the discussion, sorry - currently moving cities.
>>> Cursive does indeed have a stepping debugger which works well with Clojure,
>>> although it's quite slow (I haven't been able to figure out why, yet - I
>>> suspect Clojure adds a lot of line information to the bytecode). There have
>>> also been some fairly annoying bugs with it which will be fixed in the next
>>> drop. Here's more or less what it can do:
>>>
>>>    1. Standard stepping - step into, step over. Step out is flaky for
>>>    non-Java languages for some reason, I believe this is fixed in the new
>>>    version of IntelliJ (13, currently in beta, due to be released soon).
>>>    2. Place breakpoints on any line. This generally works well but is
>>>    occasionally frustrating with Clojure since you can pack a lot on a line.
>>>    I've found chained sequence operations to be particularly difficult to
>>>    debug. This is really a JVM limitation since JVMTI is line-oriented.
>>>    3. Breakpoints can be configured in various ways. Instead of
>>>    stopping you can configure them to print the value of an expression, or 
>>> to
>>>    stop only the current thread, or to stop all threads.
>>>    4. Drop frame to go back in time, as long as you're not mutating any
>>>    global state. You're not, right?
>>>    5. Look at locals up and down the stack, as long as you have locals
>>>    clearing disabled.
>>>    6. For the next drop I'm going to add the ability to toggle locals
>>>    clearing in debug REPLs with a button in the REPL tool window.
>>>    7. You can evaluate arbitrary Clojure expressions at any point in
>>>    the call stack.
>>>    8. You can set a breakpoint on an arbitrary exception, and it will
>>>    pause *at the time the exception is thrown* and you can inspect locals 
>>> etc.
>>>
>>> I'm pretty sure all of this works, and I use most of it regularly.
>>> Expression evaluation has been broken until the current dev build so I need
>>> to test that things that depend on it work before promising them, but I'm
>>> pretty sure they will. They include:
>>>
>>>    1. Watches.
>>>    2. Being able to configure breakpoints to only stop when a
>>>    particular expression is true.
>>>
>>> For the next drop I'm planning to work a lot on the debugger and
>>> document it all properly including some pretty animations, so I'll post to
>>> the list when that is done.
>>>
>>>
>>> On 8 November 2013 13:14, intronic <m...@pheasant.co.nz> wrote:
>>>
>>>> Why would a break function in clojure  the language not be considered,
>>>> a-la common-lisp?
>>>>
>>>>
>>>>
>>>> On Friday, 8 November 2013 09:31:55 UTC+10, Lee wrote:
>>>>>
>>>>>
>>>>> On Nov 7, 2013, at 5:48 PM, Alex Miller wrote:
>>>>>
>>>>> > When you say "hit an error", I'm assuming you mean "clojure throws
>>>>> an exception" and not "hit a breakpoint in a debugger" or something else.
>>>>>
>>>>> Yes -- I mean "clojure throws an exception."
>>>>>
>>>>> >
>>>>> > I don't think there is one place where we could generically attach
>>>>> locals info to a thrown exception. The JVM debugging interface (JVMTI -
>>>>> http://docs.oracle.com/javase/7/docs/technotes/guides/jvmti/index.html)
>>>>> can do an awful lot of things (including arbitrary class bytecode
>>>>> transformation) so I would guess it's possible to build a java agent that
>>>>> could do this (or to modify the Compiler to inject the right code at
>>>>> compile time). Sounds like a fun project, but probably non-trivial.
>>>>>
>>>>> Too bad... unless someone wants to have that fun and then share it
>>>>> with the rest of us :-). It still strikes me as really odd that such basic
>>>>> and clearly desirable functionality, available forever in other
>>>>> environments, should be so hard to come by.
>>>>>
>>>>>  -Lee
>>>>
>>>>  --
>>>> --
>>>> 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
>>>> ---
>>>> You received this message because you are subscribed to the Google
>>>> Groups "Clojure" group.
>>>> To unsubscribe from this group and stop receiving emails from it, send
>>>> an email to clojure+unsubscr...@googlegroups.com.
>>>> For more options, visit https://groups.google.com/groups/opt_out.
>>>>
>>>
>>>  --
>>> --
>>> 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
>>> ---
>>> You received this message because you are subscribed to the Google
>>> Groups "Clojure" group.
>>> To unsubscribe from this group and stop receiving emails from it, send
>>> an email to clojure+unsubscr...@googlegroups.com.
>>> For more options, visit https://groups.google.com/groups/opt_out.
>>>
>>
>>  --
>> --
>> 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
>> ---
>> You received this message because you are subscribed to the Google Groups
>> "Clojure" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to clojure+unsubscr...@googlegroups.com.
>> For more options, visit https://groups.google.com/groups/opt_out.
>>
>
>  --
> --
> 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
> ---
> You received this message because you are subscribed to the Google Groups
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to clojure+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>

-- 
-- 
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
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to