Bug 121796

Summary: Move breakpoint (and exception break) functionality into JSC::Debugger
Product: WebKit Reporter: Mark Lam <mark.lam>
Component: JavaScriptCoreAssignee: Mark Lam <mark.lam>
Status: RESOLVED FIXED    
Severity: Normal CC: buildbot, commit-queue, fpizlo, ggaren, gyuyoung.kim, joepeck, mhahnenberg, msaboff, oliver, philn, rakuco, rniwa, timothy, xan.lopez
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: Unspecified   
OS: Unspecified   
Bug Depends on: 121867, 121969, 123945    
Bug Blocks: 122836    
Attachments:
Description Flags
the patch.
mark.lam: review-
Removed JavaScriptCallFrame.cpp from other build system files.
webkit-ews: commit-queue-
Fix build issues.
ggaren: review-
candidate patch with the latest goodness.
mark.lam: review-, buildbot: commit-queue-
patch 2: updated build files, fixed style issue.
none
patch 3: fixed some issue revealed by a Windows build. ggaren: review+

Description Mark Lam 2013-09-23 11:19:56 PDT
We should provide a more realistic Debugger interface that hides the JSC debug hook callbacks from clients.  This entails:

1. Extract the JSC debug hook callbacks into a JSC::DebuggerEventHandler interface.  The JSC::Debugger not privately implements this interface.

2. Move non-front end related functionality from WebCore::ScriptDebugServer into JSC::Debugger.

3. Move WebCore::JavaScriptCallFrame functionality into a Debugger::FrameVisitor class.  The JavaScriptCallFrame is still necessary because it is published as a JS object to the Inspector.  In a later task, I'll consolidate the Debugger::FrameVisitor functionality into DebuggerCallFrame, and remove the need for the Debugger::FrameVisitor (and may require a lot more refactoring work inside JSC).  For now, to keep things simpler for the present task, we're sticking with the Debugger::FrameVisitor.

4. Create a JSC::Breakpoint based on a subset of WebCore::ScriptBreakPoint.

5. Reimplement WebKit/mac's WebScriptDebugger in terms based on the new public JSC::Debugger interface (i.e. no use of the JSC debug hooks).

Some additional steps that are not strictly needed by this refactoring effort, but is a clean up that should be done:

6. Source ID is now always an intptr_t below the InspectorDebugAgent layer.  The InspectorDebugAgent takes care of translating between the Inspector JSON protocol's String sourceId and the intptr_t sourceId when needed.

7. Breakpoint ID is now a long instead of a String.
Comment 1 Mark Lam 2013-09-23 11:35:14 PDT
(In reply to comment #0)
Some typos ...

> 1. Extract the JSC debug hook callbacks into a JSC::DebuggerEventHandler interface.  The JSC::Debugger not privately implements this interface.

"not" ==> "now"

> 5. Reimplement WebKit/mac's WebScriptDebugger in terms based on the new public JSC::Debugger interface (i.e. no use of the JSC debug hooks).

Reimplement WebKit/mac's WebScriptDebugger based on the new JSC::Debugger interface (i.e. no use of the JSC debug hooks).
Comment 2 Mark Lam 2013-09-23 11:40:41 PDT
Created attachment 212373 [details]
the patch.
Comment 3 Mark Lam 2013-09-23 11:46:56 PDT
Comment on attachment 212373 [details]
the patch.

Need to remove JavaScriptCallFrame.cpp from other build systems as well.
Comment 4 Mark Lam 2013-09-23 11:54:27 PDT
Created attachment 212375 [details]
Removed JavaScriptCallFrame.cpp from other build system files.
Comment 5 Geoffrey Garen 2013-09-23 11:55:31 PDT
Comment on attachment 212373 [details]
the patch.

View in context: https://bugs.webkit.org/attachment.cgi?id=212373&action=review

> Source/JavaScriptCore/ChangeLog:16
> +        3. Move WebCore::JavaScriptCallFrame functionality into a
> +           Debugger::FrameVisitor class.  The JavaScriptCallFrame is still
> +           necessary because it is published as a JS object to the Inspector. In

If there's an object model published by the Web Inspector, that model belongs in WebCore's Web Inspector code, and not in JavaScriptCore.

> Source/JavaScriptCore/debugger/Breakpoint.h:36
> +namespace WebCore {

The WebCore namespace doesn't belong in JavaScriptCore.

> Source/JavaScriptCore/debugger/Breakpoint.h:42
> +typedef enum {
> +    ScriptBreakpointActionTypeLog,
> +    ScriptBreakpointActionTypeEvaluate,
> +    ScriptBreakpointActionTypeSound
> +} ScriptBreakpointActionType;

Why is everything prefixed with "Script"?

> Source/JavaScriptCore/debugger/Breakpoint.h:55
> +struct ScriptBreakpoint {

Let's not double the number of objects used to represent a breakpoint. One should be enough.

> Source/JavaScriptCore/debugger/Debugger.cpp:48
> +    DebuggerEventHandler* m_debugger;

We call client interfaces like this "*Client". So, "DebuggerClient".

> Source/JavaScriptCore/debugger/Debugger.h:42
> +class DebuggerEventHandler {

We don't need (or want) an abstract interface for something that's implemented by exactly one subclass. Unnecessary abstraction makes the code harder to read.

> Source/JavaScriptCore/debugger/DebuggerFrameVisitor.cpp:28
> +#if ENABLE(JAVASCRIPT_DEBUGGER)

We don't need an enable flag for this.

> Source/JavaScriptCore/debugger/DebuggerFrameVisitor.cpp:30
> +#include "JavaScriptCallFrame.h"

Files should be named after the classes they contain. So, DebuggerFrameVisitor is not a good name for this file.

> Source/JavaScriptCore/debugger/DebuggerFrameVisitor.cpp:46
> +JavaScriptCallFrame::JavaScriptCallFrame(const DebuggerCallFrame& debuggerCallFrame, PassRefPtr<JavaScriptCallFrame> caller)

Let's not double the number of objects used to represent the JavaScript stack.
Comment 6 Early Warning System Bot 2013-09-23 12:01:33 PDT
Comment on attachment 212375 [details]
Removed JavaScriptCallFrame.cpp from other build system files.

Attachment 212375 [details] did not pass qt-ews (qt):
Output: http://webkit-queues.appspot.com/results/1903249
Comment 7 Early Warning System Bot 2013-09-23 12:03:54 PDT
Comment on attachment 212375 [details]
Removed JavaScriptCallFrame.cpp from other build system files.

Attachment 212375 [details] did not pass qt-wk2-ews (qt-wk2):
Output: http://webkit-queues.appspot.com/results/2105068
Comment 8 Build Bot 2013-09-23 12:15:55 PDT
Comment on attachment 212375 [details]
Removed JavaScriptCallFrame.cpp from other build system files.

Attachment 212375 [details] did not pass mac-wk2-ews (mac-wk2):
Output: http://webkit-queues.appspot.com/results/1903250
Comment 9 Mark Lam 2013-09-23 12:19:33 PDT
Comment on attachment 212373 [details]
the patch.

View in context: https://bugs.webkit.org/attachment.cgi?id=212373&action=review

>> Source/JavaScriptCore/ChangeLog:16
>> +           necessary because it is published as a JS object to the Inspector. In
> 
> If there's an object model published by the Web Inspector, that model belongs in WebCore's Web Inspector code, and not in JavaScriptCore.

WebCore::JavaScriptCallFrame still resides in WebCore.  The only differences is that it is now implemented based on JSC::Debugger::FrameVisitor.  When I said "move", I meant that JSC::Debugger::FrameVisitor will now provide the functionality, and WebCore::JavaScriptCallFrame will implement it by inheriting it from JSC::Debugger::FrameVisitor.

>> Source/JavaScriptCore/debugger/Breakpoint.h:36
>> +namespace JSC {
> 
> The WebCore namespace doesn't belong in JavaScriptCore.

It isn't in JavaScriptCore.  Breakpoint is implemented based on ScriptBreakpoint.  So, I "svn cp" the file so that we get to keep the change history.  The change above modifies it so that we're using the JSC name space.  I've not added any reference to WebCore in JavaScriptCore.

>> Source/JavaScriptCore/debugger/Breakpoint.h:42
>> +    Breakpoint()
> 
> Why is everything prefixed with "Script"?

It isn't.  Look again.  I'm removing the prefix, not adding it.

>> Source/JavaScriptCore/debugger/Breakpoint.h:55
>>  
> 
> Let's not double the number of objects used to represent a breakpoint. One should be enough.

Can you please clarify what you mean by this?  In my implementation, there is only one representation of a breakpoint i.e. JSC::Breakpoint.  On WebCore side, they have a ScriptBreakpoint that is used as a collection of paramteters to pass to the debugger.  I could rename WebCore::ScriptBreakpoint to WebCore::ScriptBreakpointParamaters if you prefer that.

>> Source/JavaScriptCore/debugger/Debugger.h:42
>> +class DebuggerEventHandler {
> 
> We don't need (or want) an abstract interface for something that's implemented by exactly one subclass. Unnecessary abstraction makes the code harder to read.

This interface is meant to be obsoleted when we implement the new debugger.  I isolated it into a separate interface like this so that these APIS can be public only to the clients that uses it (i.e. old debug hook code that will eventually get replaced), while keeping it private from that the ScriptDebugServer (and its subclasses) and WebScriptDebugger classes.  This allows me to use the C++ compiler to ensure that no mistakes were made where the old debug hook interfaces get accidentally leaked out and used in clients who aren't meant to use it.  If you object to this approach to encapsulating the old functionality, I can just make them public in JSC::Debugger again.

>> Source/JavaScriptCore/debugger/DebuggerFrameVisitor.cpp:28
>>  
> 
> We don't need an enable flag for this.

Yes.  I removed this enable flag, not added it.  Please look again.

>> Source/JavaScriptCore/debugger/DebuggerFrameVisitor.cpp:30
>>  
> 
> Files should be named after the classes they contain. So, DebuggerFrameVisitor is not a good name for this file.

The class they contain is JSC::Debugger::FrameVisitor.  What would you recommend as a name if not DebuggerFrameVisitor.cpp?
Comment 10 Build Bot 2013-09-23 12:35:52 PDT
Comment on attachment 212375 [details]
Removed JavaScriptCallFrame.cpp from other build system files.

Attachment 212375 [details] did not pass mac-ews (mac):
Output: http://webkit-queues.appspot.com/results/2110046
Comment 11 Mark Lam 2013-09-23 12:37:12 PDT
Comment on attachment 212375 [details]
Removed JavaScriptCallFrame.cpp from other build system files.

Will fix the build problems.  Shouldn't have svn up'ed.
Comment 12 Mark Lam 2013-09-23 13:11:54 PDT
Created attachment 212383 [details]
Fix build issues.

The culprit were inline functions, not the svn up.  This only manifests on release builds (the issue is hidden on debug builds).  It's now fixe.  Uploading a new patch to let the EWS bots give a go at it.  Geoff's feedback has not been addressed yet.
Comment 13 Geoffrey Garen 2013-09-23 17:34:46 PDT
Comment on attachment 212383 [details]
Fix build issues.

View in context: https://bugs.webkit.org/attachment.cgi?id=212383&action=review

Based on our talk in-person, here are the refactoring steps we want:

(1) Change ScriptDebugServer in WebCore to use DebuggerCallFrame instead of JavaScriptCallFrame:
    (1a) Add JSLocking to DebuggerCallFrame
    (1b) Rewrite member functions to use DebuggerCallFrame directly
    (1c) Rewrite ScriptDebugServer::dispatchDidPause to create a new JavaScriptCallFrame for each callback. Test to verify that the front-end doesn't depend on object identity here.

(3) Move some of ScriptDebugServer into JavaScriptCore's JSC::Debugger. Split out ScriptBreakpointAction from the Breakpoint interface JavaScriptCore will expose.

(4) Rewrite WebScriptDebugger in terms of new JavaScript debugger API.

(5) Make VM debugger callbacks non-virtual.

(6) Change DebuggerCallFrame into a ref-counted object that automatically nullifies itself after a callback.

> Source/JavaScriptCore/debugger/Debugger.h:58
> +class JS_EXPORT_PRIVATE Debugger : private DebuggerEventHandler {

Let's make this one class -- Debugger. Our legacy VM callbacks can be non-virtual public functions with a comment above them saying "These are the deprecated callbacks we get from the VM. Clients of Debugger should not call these functions."

Non-virtual is important because it specifies that new clients, with overriding behavior, are not allowed.
Comment 14 Mark Lam 2013-11-06 16:14:47 PST
Status update:

(In reply to comment #13)
> (From update of attachment 212383 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=212383&action=review
> 
> Based on our talk in-person, here are the refactoring steps we want:
> 
> (1) Change ScriptDebugServer in WebCore to use DebuggerCallFrame instead of JavaScriptCallFrame:
>     (1a) Add JSLocking to DebuggerCallFrame
>     (1b) Rewrite member functions to use DebuggerCallFrame directly
>     (1c) Rewrite ScriptDebugServer::dispatchDidPause to create a new JavaScriptCallFrame for each callback. Test to verify that the front-end doesn't depend on object identity here.

This has already been in implemented in r156936: <http://trac.webkit.org/r156936> for https://bugs.webkit.org/show_bug.cgi?id=121969.

> (3) Move some of ScriptDebugServer into JavaScriptCore's JSC::Debugger. Split out ScriptBreakpointAction from the Breakpoint interface JavaScriptCore will expose.
> (4) Rewrite WebScriptDebugger in terms of new JavaScript debugger API.
> (5) Make VM debugger callbacks non-virtual.

Will do these as part of this bug (https://bugs.webkit.org/show_bug.cgi?id=121796).

> (6) Change DebuggerCallFrame into a ref-counted object that automatically nullifies itself after a callback.

This has already been in implemented in r156936: <http://trac.webkit.org/r156936> for https://bugs.webkit.org/show_bug.cgi?id=121969.
Comment 15 Mark Lam 2013-11-07 16:25:40 PST
Created attachment 216342 [details]
candidate patch with the latest goodness.

Not sure this is ready for review yet, but I want to upload it so that I can try it on the EWS bots plus have a prettier view of the diff.
Comment 16 WebKit Commit Bot 2013-11-07 16:28:10 PST
Attachment 216342 [details] did not pass style-queue:

Failed to run "['Tools/Scripts/check-webkit-style', '--diff-files', u'Source/JavaScriptCore/ChangeLog', u'Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj', u'Source/JavaScriptCore/debugger/Breakpoint.h', u'Source/JavaScriptCore/debugger/Debugger.cpp', u'Source/JavaScriptCore/debugger/Debugger.h', u'Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp', u'Source/JavaScriptCore/debugger/DebuggerCallFrame.h', u'Source/JavaScriptCore/debugger/DebuggerPrimitives.h', u'Source/WebCore/ChangeLog', u'Source/WebCore/WebCore.xcodeproj/project.pbxproj', u'Source/WebCore/bindings/js/BreakpointID.h', u'Source/WebCore/bindings/js/ScriptDebugServer.cpp', u'Source/WebCore/bindings/js/ScriptDebugServer.h', u'Source/WebCore/bindings/js/SourceID.h', u'Source/WebCore/bindings/js/WorkerScriptDebugServer.cpp', u'Source/WebCore/bindings/js/WorkerScriptDebugServer.h', u'Source/WebCore/inspector/InspectorDebuggerAgent.cpp', u'Source/WebCore/inspector/InspectorDebuggerAgent.h', u'Source/WebCore/inspector/ScriptDebugListener.h', u'Source/WebKit/mac/ChangeLog', u'Source/WebKit/mac/WebView/WebScriptDebugger.h', u'Source/WebKit/mac/WebView/WebScriptDebugger.mm']" exit_code: 1
Source/JavaScriptCore/ChangeLog:23:  Line contains tab character.  [whitespace/tab] [5]
Total errors found: 1 in 21 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 17 Build Bot 2013-11-07 17:06:42 PST
Comment on attachment 216342 [details]
candidate patch with the latest goodness.

Attachment 216342 [details] did not pass win-ews (win):
Output: http://webkit-queues.appspot.com/results/22718328
Comment 18 Mark Lam 2013-11-07 17:09:50 PST
Comment on attachment 216342 [details]
candidate patch with the latest goodness.

I forgot to update all the other build systems with the new files.  Will upload another patch shortly.
Comment 19 Mark Lam 2013-11-07 17:31:44 PST
Created attachment 216344 [details]
patch 2: updated build files, fixed style issue.
Comment 20 Mark Lam 2013-11-07 19:58:49 PST
Created attachment 216356 [details]
patch 3: fixed some issue revealed by a Windows build.

I think it's ready for a review now.
Comment 21 Geoffrey Garen 2013-11-08 11:03:55 PST
Comment on attachment 216356 [details]
patch 3: fixed some issue revealed by a Windows build.

View in context: https://bugs.webkit.org/attachment.cgi?id=216356&action=review

r=me

This is a reasonable first step. I'm not sure the division of responsibilities is perfect yet. It seems odd for a JSC-level thing to be in charge of defining ReasonForPause, and implementing all the reasons you might pause. That constrains the design of the debugger UI to the set of reasons the VM builds in. A more flexible design would just build into JSC the ability to pause -- and the debugger UI layer could invent as many reasons as it liked.

> Source/JavaScriptCore/debugger/DebuggerPrimitives.h:37
> +typedef intptr_t SourceID;
> +static const SourceID noSourceID = 0;
> +
> +typedef int32_t BreakpointID;
> +static const BreakpointID noBreakpointID = 0;

Why intptr_t for one and int32_t for another? Seems like you actually want size_t for both.
Comment 22 Mark Lam 2013-11-08 11:12:32 PST
Thanks for the review.

(In reply to comment #21)
> This is a reasonable first step. I'm not sure the division of responsibilities is perfect yet. It seems odd for a JSC-level thing to be in charge of defining ReasonForPause, and implementing all the reasons you might pause. That constrains the design of the debugger UI to the set of reasons the VM builds in. A more flexible design would just build into JSC the ability to pause -- and the debugger UI layer could invent as many reasons as it liked.

Currently, the only real need for this is for the debugger UI layer to be able to tell that the pause is for an exception.  I just added the others as a means of providing more info for now, but they may end up being unnecessary.  We can tighten up or replace this design later once we have gained more experience with what this systems needs.

> > Source/JavaScriptCore/debugger/DebuggerPrimitives.h:37
> > +typedef intptr_t SourceID;
> > +static const SourceID noSourceID = 0;
> > +
> > +typedef int32_t BreakpointID;
> > +static const BreakpointID noBreakpointID = 0;
> 
> Why intptr_t for one and int32_t for another? Seems like you actually want size_t for both.

Will fix.
Comment 23 Mark Lam 2013-11-08 12:02:34 PST
Landed in r158937: <http://trac.webkit.org/r158937>.