debuging?

Sep 30, 2009 at 6:11 AM

Since multi lines of JS code is inside a "String", it seems impossible to debug line-by-line. Any recomendations? Thanks!

Sep 30, 2009 at 6:52 AM

Actually I have the same concerns during the development to correct bugs. Mine is on the lower side though. I think what I could provide is the ability to enable tracing during the execution of the statements. Also you can redefine the alert() to redirect it on the Console or Trace output.

Do you have any opinion on what should be implemented ? And yes, another way could be to integrate the process with Visual Studio debugger. Don't think it would be difficult to do. I will check this option too.

Oct 23, 2009 at 12:22 PM

This a an excellent project – clean design, clean code – great!

However, the project I have in mind requires interactive debugging. Reading your code it looks like debugging support could be implemented without mayor structural changes.

I looked into the idea of creating a class DebugVisitor, which hosts the existing ExecutionVisitor. Basically this new visitor does not a lot: It just visits the corresponding statements of the ExecutionVisitor, for example:

        public void Visit(Program program)
        {
            _jint.Visit(program);
        }

Statements requiring debug support may look like this:

        public void Visit(DoWhileStatement statement)
       {
            if (!checkForBreak(statement)) return;;
            _jint.Visit(statement);
        }

‘checkForBreak’ just checks for breakpoints or single step and fires an event to process the break if appropriate:

The class DebugVisitor contains the following definitions:

        public delegate bool BreakDelegate(int line, int start, int stop);
        public event BreakDelegate BreakListener;
        public ArrayList BreakPoints { set; get; }
        public bool SingleStep { get; set; }
       private bool checkForBreak(Statement st)
        {
            if (BreakPoints.Contains(st.LineNumber) | SingleStep)            {
               if (!RaiseBreak(st.LineNumber, st.StartColumn, st.EndColumn))
                {
                    // Debug mode terminated
                    exit = true;
                   return false;
                }
            }
            return true;
       }

        protected virtual bool RaiseBreak(int line, int start,int stop)
        {
            // Raise the event by using the () operator.
            return BreakListener(line,start,stop);
        }

The constructor would initialize the properties.

        public DebugVisitor()
        {
            _jint = new ExecutionVisitor();
            BreakPoints=new ArrayList();
            _jint.Visitor = this;
        }

Changes in ExecutionVisit:or

This already seems to work – at least for some statements. Other statements require some formal changes within the ExecutionVisitor. Statements processing other statements will not work because the corresponding Accept will be call with the paramenter ‘this’, i.e. the ExecutionVisitor. It should get executed by the DebugVisitor. This can be handled using a new property Visitor, initialized to ‘this’ within the constructor of the ExecutionVisitor. In addition all Accept(this) need to be changed the Accept(Visitor). Example:

        public void Visit(BlockStatement statement)
       {
            foreach (var s in statement.Statements)
            {
                //CHH s.Accept(this);
                s.Accept(Visitor);
               if (StopStatementFlow)
              {
                    return;
               }
            }
        }

        public IJintVisitor Visitor { get; set; }

 

You might have noticed that some of the code above used a property ‘LineNumber’, which needs to be added to the Statement class:

    public abstract class Statement
    {
        public abstract void Accept(IJintVisitor visitor);
        //CHH
        public int LineNumber { get; set; }
   }

Where do we get the LineNumber from? ANTLR supplies the line number as part of the CommonToken class. The Grammar file needs to be changed to add the line number to all statements requiring debug support, for example:

ifStatement returns [Statement value]
@init {
IfStatement st = new IfStatement();
st.LineNumber = ((CommonToken)retval.Start).Line;

These very formal changes would add debug support for most statements. There are probably some more issues  that need to be addressed (f.e. methodCall). There might be a problem with several routines expecting  an ExecutionVisitor instead of an IJintVisitor. But anyway,  this seems to be quite usefull.

To play around I did a quick and dirty DebugShell based on your JintShell with the following command set:

Jint Debug Shell

break <lineno>      - Sets a breakpoint at <lineno>
debug               - Enters debug mode
exit                - Quits the application
import(assembly)    - assembly: String containing the partial name of a .NET assembly to load
list                - Lists the current script on the console
print(output)       - Prints the specified output to the console
reset               - Initialize the current script
run <path>          - Initialize the script, load and run the content of the file

Jint Debug Commands

abort               - aborts debugging and terminates program
break <lineno>      - Sets a breakpoint at <lineno>
deletebreak <lineno>- Clears a breakpoint at <lineno>
display <var>       - Print the value of the variable specified
clearbreaks         - clears all breakpoint
exit                - Quits the application
go                  - Terminates single step
                   - Just <enter> continues, single step if enabled

 

Is this an acceptable approach?

Oct 24, 2009 at 8:10 AM

Fantastic !

I will implement those concepts and let you know about that.

Oct 25, 2009 at 8:34 AM

After some reflexions, I have suggestion to make, and would like your opinions.

I think debugging support should allow developers to:
- Define a debug mode optionaly (Release by default)
- Execute step by step (Step event)
- See locals at each step, and also current Statement (object model), call stack, and current position
- Set breakpoints by position(line, char) and also a stop condition (Break event, and BreakPoints list)
- Define watch list (expressions) which would be evaluated at each step (break point or step)
- Add this information to the JintException to be able to understand why a script failed (Current statement, locals, position, call stack)

This would allow any standard application to handle simple debugging, but would allow to create a very simple graphical editor which would display a script, break points, and steps while executing. This application would just need to register to the Step event to get every step executed and display it to the editor, as the locals, watch list and call stack.

What do you think about this spec ? I could use automan32 suggestions to implement this behaviour.

Oct 25, 2009 at 11:13 AM

 

This is what we need!

- Define a debug mode optionally (Release by default)

The proposed concepts (DebugVisitor) would allow this as the existing ExecutionVistor would be used in Release mode, while DebugVisitor would be used in Debug mode.

There is no performance degradation in release mode!

- Execute step by step (Step event)

Yes, absolutely needed

- See locals at each step, and also current Statement (object model), call stack, and current position

Would be great - my prototype allowed for calling specific know variables as this was simple. If possible we should be able to should variables in all scopes

- Set breakpoints by position(line, char) and also a stop condition (Break event, and BreakPoints list

It would be nice to bread by procedure name - you don't have to keep track of the line numbers. Conditional breaks would be excellent.

- Define watch list (expressions) which would be evaluated at each step (break point or step)

Even better!

- Add this information to the JintException to be able to understand why a script failed (Current statement, locals, position, call stack)

Yes, perfect!

There is one issue that might come up in future: Up to now we talk about a complete block of code. A real life application would probably implement some kind of library concept - probably just merging the code in background.
This might add some requirements to assist the user where to find the problem.
Do you see a library concept as a part of the application or are you planning to have some kind of support in Jint?

Anyway, this debugging support turns Jint into a great tool for scriptable applications.

 

Oct 25, 2009 at 2:31 PM

Can you elaborate the library concept ? Do you mean being able to "import" other scripts ? Did you check the latest syntax modification which allow to Run() multiple scripts with the same engine instance ?

JintEngine engine = new JintEngine();
engine.Run(lib1);
engine.Run(lib2);
engine.Run(script);
Oct 25, 2009 at 5:57 PM

Sorry, I did not notice.

I'm not sure if this is it, need to play with it. Multiple Run commands maybe fine.

Without looking into details, I would have expected multiple compiles extending the AST.

For the Debugger – It would be nice if the debugger returns the ‘location’ of a failing function. As Jint does not deal with files (which is OK), there may be something like an optional reference or key passed to the Run (or Compile) command. This should be returned by the debugger along with the line number and function name. Possible?

Oct 26, 2009 at 12:26 PM

I have published a new release (0.8.4) including the debugger we discussed about. For documentation, please refer to http://jint.codeplex.com/wikipage?title=Debugging&referringTitle=Documentation

 

Oct 26, 2009 at 7:28 PM

 

You are really fast :-)

Reading your documentation this looks great. As a test I tried to convert my little interactive line debugger  to the implemented structure.

I have some problems with the separation of Break and Step events. For an interactive debugger both breaks have to be handled the same way. No problem - just connect both events to the same routine. This works, but if you want to stop single stepping and keep the breakpoints alive, it get messy.

In fact your  Step event is an excellent Trace event. Single Step should just be a On/Off property and should fire the same Break event.

Did I get something wrong? Do you see a better way to handle this?

 

Question: Why did you decide against the DebugVisitor structure? Were there any implications that I did not see? My feeling is that you bought into some (may be minimal) performance degradation in release mode by handling the debugging logic within the ExecutionVisitor. Am I wrong?

Oct 26, 2009 at 10:44 PM

With my Step/Break technique, it is up to the debugger to say if stepping should be on/off. Because you can handle this switch from the debugger application, and ignore the step method if off. And I think this is a very rare scenario.

I preferred not to use the DebuggerVisitor as actually there was very little to change in the original code. And moreover, the impact is less than low, as I just need to check th DebugMode boolean before doing anything. So if DebugMode is off, just boolean tests are added, which is really really not impacting the performance of the overall process. Even don't think it's measurable.

I'm up to a deeper discussion if you can challenge it ! Also you made me find a new way to handle line numbers, by using the @after action in ANTLR, and using retval as you suggested. With @after i also have access to the Stop location which is very interesting.

I have a suggestion for you. Why not implemented an editor with a debugger inside. There are multiple text editor solutions for .NET, and a lightweight debugger could use the RichTextBox to highlight current statements or breakpoints. Just look a Rhino Debugger for instance. It would be really simple with the current architecture.