Sylvain Wallez wrote:

Christopher Oliver wrote:

Sylvain Wallez wrote:

Mmmh... if we push and pop stack frame indications, isn't it enough to build a debugger?

<snip>



Without getting into all the details, I think a proper API for a global debugger would require quite a bit more infrastructure than what you're suggesting.



Since AFAIK, you've written the Rhino debugger, can you enlighten us on the required infrastructure?


I've also written several other debuggers including a Java debugger and a BPEL4WS debugger which are part of my company's IDE.

Besides knowledge of the call stack, a debugger requires support for accessing variables and their properties (and sub-properties - for display in a tree or tree-table), evaluating expressions, accessing the source code, and notification of line changes (to manage breakpoints). You also have to consider handling multiple threads and allowing for an efficient wire-protocol for remote debugging.

Anyway, attached is a first try at an API.

1) First, a component that implements DebuggableComponent will be called with setDebugger() if it is being debugged.
2) When that component begins execution and enters a "call", it calls the enterFrame() method of the debugger passing in a DebuggableScope object that it implements, and saves the return value (a DebugFrame).
3) During execution of that call, it notifies the debugger of line changes by calling the onLineChange() method on the saved DebugFrame object.
4) If the call completes successully, it notifies the debugger by calling the onExit() method on the DebugFrame.
5) If an exception occurs during the call, it notifies the debugger by calling the onException() method on the DebugFrame


When a frame is entered, the debugger will save a reference to the current DebuggableScope. Depending on its needs and on user input the debugger will call methods on the saved DebuggableScope object to evaluate expressions, get the values of variables, and to retrieve source code.

Breakpoints are maintained internally by the debugger. When a breakpoint is hit, it will block the component's call to onLineChange() until the user steps or continues.

Note that all of the methods of DebuggableScope return String or String[] in order to make remoting easy.

Also the mechanism provided to walk the object graph associated with a variable is designed with JXPath in mind. For example, it can be implemented easily lilke this:

public String[] getProperties(String var, String pointer) {
Object value = <get value of var>
JXPathContext varContext = JXPathContext.newContext(value);
Pointer ptr = varContext.getPointer(pointer);
JXPathContext ptrContext = varContext.getRelativeContext(ptr);
Iterator iter = ptrContext.iteratePointers("*");
List list = new LinkedList();
while (iter.hasNext()) {
Pointer p = (Pointer)iter.next();
list.add(p.asPath());
}
String[] ret = new String[list.size()];
list.toArray(ret);
return ret; }


public String getValue(String var, String pointer) {
   Object value = <get value of var>
    JXPathContext varContext = JXPathContext.newContext(value);
    return varContext.getValue(pointer);
}

What do you think?

--
Chris


// Hypothetical Debugging API:

// This interface is implemented by a processing component that can be debugged.
// It represents the current stack frame from the component's point of view
public interface DebuggableScope {

    // Get the name of the component that implements this scope
    public String getComponentName();

    // Get the current "method" being executed
    public String getMethod();

    // Get a list of the names of the variables visible in this scope
    public String[] getVariables() throws Exception;

    // Get a list of "pointers" to the immediate properties of a variable
    public String[] getProperties(String var) throws Exception;

    // Get a list of "pointers" to the subproperties of a property
    public String[] getProperties(String var, String pointer) throws Exception;

    // Get the value of a variable
    public String getValue(String var) throws Exception;

    // Get the value of the sub[-sub,...] property of {var} identified by {pointer}
    public String getValue(String var, String pointer) throws Exception;

    // Evaluate {expression} in the current scope
    public String evaluate(String expression) throws Exception;

    // Return the source code identified by uri
    public String resolveSource(String uri) throws Exception;
}

// This interface is implemented by the debugger itself. It represents the
// current stack frame from the debugger's point of view.
public interface DebugFrame {

    // Get the caller
    public DebugFrame getCaller();

    // Get the scope associated with this frame
    public DebuggableScope getScope();
   
    // Notify the debugger of a line change
    public void onLineChange(String uri, int line);

    // Notify the debugger of the completion of this scope
    public void onExit(String uri, int line);

    // Notify the debugger of an exception in this scope
    public void onException(String uri, int line, String exception,
                            String message);
}

// Interface to the debugger
public interface Debugger {

    DebugFrame enterFrame(DebuggableScope scope);
   
}

// Give a component access to the current debugger
public interface DebuggableComponent {

    public void setDebugger(Debugger debugger);

}


Reply via email to