Breakpoints on blank lines or comments don't break
authorjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 30 Sep 2016 19:22:50 +0000 (19:22 +0000)
committerjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 30 Sep 2016 19:22:50 +0000 (19:22 +0000)
https://bugs.webkit.org/show_bug.cgi?id=9885
<rdar://problem/6134406>

Reviewed by Mark Lam.

Source/JavaScriptCore:

This change introduces a way to perform a Debugger Parse of a script.
This debugger parse gathers a list of breakpoint locations, which
the backend uses to resolve breakpoint locations that came from the
Inspector frontend to the exact location we would actually pause.
We gather this information from the parser so that we can eagerly
get this information without requiring the code to have executed (the
real op_debugs are generated during bytecode generation when code
is actually evaluated).

If an input location was on a line with whitespace or a comment, the
resolved breakpoint location would be before the next statement that
will be executed. That may be the next line, or even later. We also
update our policy when setting breakpoints on and around function
statements to better match user expectations.

For example, when resolving breakpoints in:

    1.  // Comment
    2.  before;
    3.
    4.  function foo() {
    5.      inside;
    6.  }
    7.
    8.  after;

A breakpoint on line 1, a comment, resolves to line 2 the next
statement that will execute.

A breakpoint on line 3 or 7, empty lines, resolves to line 8 the next
statement that will execute. This skips past the definition of foo,
just like stepping would have done. The creation of foo would have
been hoisted, which would have happened before execution of the
other statements.

A breakpoint on line 4, a function signature, resolves to line 5,
inside the function. Users would expect to pause inside of a function
when setting a breakpoint on that function's name or opening brace.

A breakpoint on line 6, a function's closing brace, resolves to
line 6. The debugger will pause whenever execution leaves foo due to
a return and not an exception. This matches stepping behavior. An
explicit or implicit return (the implicit return undefined) will
pause on the closing brace as we leave the function, giving users
an opportunity to inspect the final state before leaving.

--

At this point, op_debug's are still emitted at custom locations during
bytecode generation of other statements / expressions. In order to
ensure the generated op_debugs correspond to locations the Parser
determined were breakpoint locations, the Parser sets a "needs debug
hook" flag on the nodes it will use for breakpoint locations, and
we assert during bytecode generation that op_debugs are only emitted
for nodes that were marked as needing debug hooks.

This still leaves open the possibility that the Parser will mark
some nodes that get missed during bytecode generation, so we might
fail to emit some op_debugs. The next step will be eliminating the
custom emitDebugHooks spread across StatementNode and ExpressionNode
subclasses, and instead always generating op_debugs whenever we
emit a flagged node.

--

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
New DebuggerParseData files.

* API/JSScriptRef.cpp:
(OpaqueJSScript::OpaqueJSScript):
* jsc.cpp:
(functionCheckModuleSyntax):
* parser/SourceCode.h:
(JSC::makeSource):
* parser/SourceProvider.cpp:
(JSC::SourceProvider::SourceProvider):
* parser/SourceProvider.h:
(JSC::SourceProvider::sourceType):
(JSC::StringSourceProvider::create):
(JSC::StringSourceProvider::StringSourceProvider):
(JSC::WebAssemblySourceProvider::WebAssemblySourceProvider):
(JSC::SourceProvider::startPosition): Deleted.
Add a new type on SourceProvider to distinguish if its script was
intended to be a Script, Module, or WebAssembly. This information
will be needed to know how to best parse this file when the
debugger decides to lazily parse.

* runtime/Executable.cpp:
(JSC::EvalExecutable::EvalExecutable):
(JSC::ProgramExecutable::ProgramExecutable):
(JSC::ModuleProgramExecutable::ModuleProgramExecutable):
(JSC::WebAssemblyExecutable::WebAssemblyExecutable):
* runtime/ModuleLoaderPrototype.cpp:
(JSC::moduleLoaderPrototypeParseModule):
ASSERT the SourceProvider type matches the executable type we are
creating for it.

* parser/ASTBuilder.h:
(JSC::ASTBuilder::breakpointLocation):
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::operatorStackPop):
When gathering breakpoint positions, get the position from the
current node. In the SyntaxChecker, return an invalid position.

* parser/Nodes.h:
(JSC::ExpressionNode::needsDebugHook):
(JSC::ExpressionNode::setNeedsDebugHook):
(JSC::StatementNode::needsDebugHook):
(JSC::StatementNode::setNeedsDebugHook):
When gathering breakpoint positions, mark the node as needing
a debug hook. For now we assert op_debugs generated must come
from these nodes. Later we should just generate op_debugs for
these nodes.

* parser/Parser.cpp:
(JSC::Parser<LexerType>::Parser):
(JSC::Parser<LexerType>::parseStatementListItem):
(JSC::Parser<LexerType>::parseDoWhileStatement):
(JSC::Parser<LexerType>::parseWhileStatement):
(JSC::Parser<LexerType>::parseArrowFunctionSingleExpressionBodySourceElements):
(JSC::Parser<LexerType>::parseForStatement):
(JSC::Parser<LexerType>::parseWithStatement):
(JSC::Parser<LexerType>::parseSwitchStatement):
(JSC::Parser<LexerType>::parseStatement):
(JSC::Parser<LexerType>::parseFunctionBody):
(JSC::Parser<LexerType>::parseFunctionInfo):
(JSC::Parser<LexerType>::parseIfStatement):
(JSC::Parser<LexerType>::parseAssignmentExpression):
* parser/Parser.h:
(JSC::parse):
Add an optional DebuggerParseData struct to the Parser. When available
the Parser will gather debugger data, and parse all functions with the
ASTBuilder instead of SyntaxChecking inner functions.

* debugger/DebuggerParseData.cpp: Added.
(JSC::DebuggerPausePositions::breakpointLocationForLineColumn):
(JSC::DebuggerPausePositions::sort):
(JSC::gatherDebuggerParseData):
(JSC::gatherDebuggerParseDataForSource):
* debugger/DebuggerParseData.h: Copied from Source/JavaScriptCore/debugger/DebuggerPrimitives.h.
(JSC::DebuggerPausePositions::DebuggerPausePositions):
(JSC::DebuggerPausePositions::appendPause):
(JSC::DebuggerPausePositions::appendEntry):
(JSC::DebuggerPausePositions::appendLeave):
The DebuggerParseData struct currently only contains a list of pause positions.
Once populated it can resolve an input location to a pause position.

* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitCall):
(JSC::BytecodeGenerator::emitCallVarargs):
(JSC::BytecodeGenerator::emitDebugHook):
(JSC::BytecodeGenerator::emitEnumeration):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::EmptyStatementNode::emitBytecode):
(JSC::DebuggerStatementNode::emitBytecode):
(JSC::ExprStatementNode::emitBytecode):
(JSC::DeclarationStatement::emitBytecode):
(JSC::IfElseNode::emitBytecode):
(JSC::DoWhileNode::emitBytecode):
(JSC::WhileNode::emitBytecode):
(JSC::ForNode::emitBytecode):
(JSC::ForInNode::emitBytecode):
(JSC::ContinueNode::emitBytecode):
(JSC::BreakNode::emitBytecode):
(JSC::ReturnNode::emitBytecode):
(JSC::WithNode::emitBytecode):
(JSC::SwitchNode::emitBytecode):
(JSC::ThrowNode::emitBytecode):
Emit op_debugs for the nodes themselves. Assert when we do that the
Parser had marked them as needing a debug hook.

* debugger/Breakpoint.h:
(JSC::Breakpoint::Breakpoint):
A breakpoint may be resolved or unresolved. Debugger::resolveBreakpoint
must be used to resolve the breakpoint. Most methods now require a
resolved breakpoint.

* debugger/Debugger.h:
* debugger/Debugger.cpp:
(JSC::Debugger::detach):
(JSC::Debugger::toggleBreakpoint):
(JSC::Debugger::debuggerParseData):
(JSC::Debugger::resolveBreakpoint):
(JSC::Debugger::setBreakpoint):
(JSC::Debugger::clearParsedData):
Provide a public method to resolve a breakpoint location in a script.
This will gather debugger parse data for the script if none is available.
Ensure clients have resolved a breakpoint before attempting to set it.
Currently we allow only a single breakpoint at a location. This may
need to change if multiple breakpoints resolve to the same location
but have different actions.

* inspector/ScriptDebugListener.h:
ScriptDebugServer::Script is effectively duplicating most of the data from
a SourceProvider. We should eliminate this and just use SourceProvider.

* inspector/ScriptDebugServer.cpp:
(Inspector::ScriptDebugServer::setBreakpointActions):
(Inspector::ScriptDebugServer::removeBreakpointActions):
(Inspector::ScriptDebugServer::getActionsForBreakpoint):
(Inspector::ScriptDebugServer::clearBreakpointActions):
(Inspector::ScriptDebugServer::evaluateBreakpointAction):
(Inspector::ScriptDebugServer::dispatchDidParseSource):
(Inspector::ScriptDebugServer::handleBreakpointHit):
(Inspector::ScriptDebugServer::setBreakpoint): Deleted.
(Inspector::ScriptDebugServer::removeBreakpoint): Deleted.
(Inspector::ScriptDebugServer::clearBreakpoints): Deleted.
* inspector/ScriptDebugServer.h:
Reduce ScriptDebugServer's involvement in breakpoints to just handling
breakpoint actions. Eventually we should eliminate it alltogether and
fold breakpoint logic into Debugger or DebugAgent.

* inspector/agents/InspectorDebuggerAgent.h:
* inspector/agents/InspectorDebuggerAgent.cpp:
(Inspector::buildDebuggerLocation):
(Inspector::parseLocation):
(Inspector::InspectorDebuggerAgent::setBreakpointByUrl):
(Inspector::InspectorDebuggerAgent::setBreakpoint):
(Inspector::InspectorDebuggerAgent::didSetBreakpoint):
(Inspector::InspectorDebuggerAgent::resolveBreakpoint):
(Inspector::InspectorDebuggerAgent::removeBreakpoint):
(Inspector::InspectorDebuggerAgent::continueToLocation):
(Inspector::InspectorDebuggerAgent::didParseSource):
(Inspector::InspectorDebuggerAgent::clearDebuggerBreakpointState):
The Inspector can set breakpoints in multiple ways.
Ensure that once we have the Script that we always
resolve the breakpoint location before setting the
breakpoint. The different paths are:

- setBreakpoint(scriptId, location)
  - Here we know the SourceProvider by its SourceID
    - resolve and set

- setBreakpointByURL(url, location)
  - Search for existing Scripts that match the URL
    - resolve in each and set
  - When new Scripts are parsed that match the URL
    - resolve and set

Source/WebCore:

Tests: inspector/debugger/breakpoints/resolved-dump-all-pause-locations.html
       inspector/debugger/breakpoints/resolved-dump-each-line.html

* bindings/js/CachedScriptSourceProvider.h:
(WebCore::CachedScriptSourceProvider::CachedScriptSourceProvider):

LayoutTests:

* inspector/debugger/breakpoints/resolved-dump-all-pause-locations-expected.txt: Added.
* inspector/debugger/breakpoints/resolved-dump-all-pause-locations.html: Added.
* inspector/debugger/breakpoints/resolved-dump-each-line-expected.txt: Added.
* inspector/debugger/breakpoints/resolved-dump-each-line.html: Added.
* inspector/debugger/breakpoints/resources/dump-functions.js: Added.
* inspector/debugger/breakpoints/resources/dump-general.js: Added.
Test for resolved breakpoint locations in all kinds of different source code.

* inspector/debugger/breakpoints/resources/dump.js: Added.
(TestPage.registerInitializer):
(TestPage.registerInitializer.window.addDumpAllPauseLocationsTestCase):
(TestPage.registerInitializer.window.addDumpEachLinePauseLocationTestCase):
Shared code to run different generalized tests for logging all resolved
breakpoint locations or the resolved breakpoint location if a breakpoint
is set on each individual line.

* inspector/debugger/resources/log-pause-location.js:
(TestPage.registerInitializer.insertCaretIntoStringAtIndex):
(TestPage.registerInitializer.window.findScript):
(TestPage.registerInitializer.window.loadLinesFromSourceCode):
(TestPage.registerInitializer.window.loadMainPageContent):
(TestPage.registerInitializer.window.logResolvedBreakpointLinesWithContext):
(TestPage.registerInitializer.window.logLinesWithContext):
Make some more code shared and provide a way to log two locations,
used to see where a breakpoint was set and where it resolved to.

* inspector/debugger/setBreakpoint-expected.txt:
Update error message. Should not include a period.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@206653 268f45cc-cd09-0410-ab3c-d52691b4dbfc

41 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/inspector/debugger/breakpoints/resolved-dump-all-pause-locations-expected.txt [new file with mode: 0644]
LayoutTests/inspector/debugger/breakpoints/resolved-dump-all-pause-locations.html [new file with mode: 0644]
LayoutTests/inspector/debugger/breakpoints/resolved-dump-each-line-expected.txt [new file with mode: 0644]
LayoutTests/inspector/debugger/breakpoints/resolved-dump-each-line.html [new file with mode: 0644]
LayoutTests/inspector/debugger/breakpoints/resources/dump-functions.js [new file with mode: 0644]
LayoutTests/inspector/debugger/breakpoints/resources/dump-general.js [new file with mode: 0644]
LayoutTests/inspector/debugger/breakpoints/resources/dump.js [new file with mode: 0644]
LayoutTests/inspector/debugger/resources/log-pause-location.js
LayoutTests/inspector/debugger/setBreakpoint-expected.txt
Source/JavaScriptCore/API/JSScriptRef.cpp
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/debugger/Breakpoint.h
Source/JavaScriptCore/debugger/Debugger.cpp
Source/JavaScriptCore/debugger/Debugger.h
Source/JavaScriptCore/debugger/DebuggerParseData.cpp [new file with mode: 0644]
Source/JavaScriptCore/debugger/DebuggerParseData.h [new file with mode: 0644]
Source/JavaScriptCore/inspector/ScriptDebugListener.h
Source/JavaScriptCore/inspector/ScriptDebugServer.cpp
Source/JavaScriptCore/inspector/ScriptDebugServer.h
Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.cpp
Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.h
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/parser/ASTBuilder.h
Source/JavaScriptCore/parser/Nodes.h
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/Parser.h
Source/JavaScriptCore/parser/SourceCode.h
Source/JavaScriptCore/parser/SourceProvider.cpp
Source/JavaScriptCore/parser/SourceProvider.h
Source/JavaScriptCore/parser/SyntaxChecker.h
Source/JavaScriptCore/runtime/Executable.cpp
Source/JavaScriptCore/runtime/ModuleLoaderPrototype.cpp
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/CachedScriptSourceProvider.h

index c6af56f..eaa65c0 100644 (file)
@@ -1,5 +1,42 @@
 2016-09-30  Joseph Pecoraro  <pecoraro@apple.com>
 
+        Breakpoints on blank lines or comments don't break
+        https://bugs.webkit.org/show_bug.cgi?id=9885
+        <rdar://problem/6134406>
+
+        Reviewed by Mark Lam.
+
+        * inspector/debugger/breakpoints/resolved-dump-all-pause-locations-expected.txt: Added.
+        * inspector/debugger/breakpoints/resolved-dump-all-pause-locations.html: Added.
+        * inspector/debugger/breakpoints/resolved-dump-each-line-expected.txt: Added.
+        * inspector/debugger/breakpoints/resolved-dump-each-line.html: Added.
+        * inspector/debugger/breakpoints/resources/dump-functions.js: Added.
+        * inspector/debugger/breakpoints/resources/dump-general.js: Added.
+        Test for resolved breakpoint locations in all kinds of different source code.
+
+        * inspector/debugger/breakpoints/resources/dump.js: Added.
+        (TestPage.registerInitializer):
+        (TestPage.registerInitializer.window.addDumpAllPauseLocationsTestCase):
+        (TestPage.registerInitializer.window.addDumpEachLinePauseLocationTestCase):
+        Shared code to run different generalized tests for logging all resolved
+        breakpoint locations or the resolved breakpoint location if a breakpoint
+        is set on each individual line.
+
+        * inspector/debugger/resources/log-pause-location.js:
+        (TestPage.registerInitializer.insertCaretIntoStringAtIndex):
+        (TestPage.registerInitializer.window.findScript):
+        (TestPage.registerInitializer.window.loadLinesFromSourceCode):
+        (TestPage.registerInitializer.window.loadMainPageContent):
+        (TestPage.registerInitializer.window.logResolvedBreakpointLinesWithContext):
+        (TestPage.registerInitializer.window.logLinesWithContext):
+        Make some more code shared and provide a way to log two locations,
+        used to see where a breakpoint was set and where it resolved to.
+
+        * inspector/debugger/setBreakpoint-expected.txt:
+        Update error message. Should not include a period.
+
+2016-09-30  Joseph Pecoraro  <pecoraro@apple.com>
+
         Web Inspector: Stepping out of a function finishes the line that called it.
         https://bugs.webkit.org/show_bug.cgi?id=155325
         <rdar://problem/25094578>
index 80ed852..8e42038 100644 (file)
@@ -139,6 +139,8 @@ webkit.org/b/148036 http/tests/inspector/replay/ [ Skip ]
 webkit.org/b/161951 [ Debug ] inspector/debugger/stepping [ Skip ]
 webkit.org/b/161951 [ Release ] inspector/debugger/stepping [ Slow ]
 
+[ Debug ] inspector/debugger/breakpoints [ Slow ]
+
 webkit.org/b/129639 inspector/dom/dom-search-crash.html [ Skip ]
 
 webkit.org/b/128736 inspector/debugger/setBreakpoint-dfg.html [ Failure Pass ]
diff --git a/LayoutTests/inspector/debugger/breakpoints/resolved-dump-all-pause-locations-expected.txt b/LayoutTests/inspector/debugger/breakpoints/resolved-dump-all-pause-locations-expected.txt
new file mode 100644 (file)
index 0000000..722ee51
--- /dev/null
@@ -0,0 +1,2300 @@
+Checking all resolved breakpoint locations in a script.
+
+
+== Running test suite: Debugger.resolvedBreakpoint.dumpAllLocations
+-- Running test case: Debugger.resolvedBreakpoint.dumpAllLocations.General
+
+INSERTING AT: 0:0
+PAUSES AT: 0:0
+-=>   0    |let a=function(){return 0}
+      1    let b=function(){return 0}
+      2    let c=function(){return 0}
+      3    let arr=[];
+
+INSERTING AT: 0:1
+PAUSES AT: 0:17
+-=>   0    l#et a=function(){|return 0}
+      1    let b=function(){return 0}
+      2    let c=function(){return 0}
+      3    let arr=[];
+
+INSERTING AT: 0:18
+PAUSES AT: 0:25
+-=>   0    let a=function(){r#eturn 0|}
+      1    let b=function(){return 0}
+      2    let c=function(){return 0}
+      3    let arr=[];
+
+INSERTING AT: 0:26
+PAUSES AT: 1:0
+ ->   0    let a=function(){return 0}#
+ =>   1    |let b=function(){return 0}
+      2    let c=function(){return 0}
+      3    let arr=[];
+      4    let obj={};
+
+INSERTING AT: 1:1
+PAUSES AT: 1:17
+      0    let a=function(){return 0}
+-=>   1    l#et b=function(){|return 0}
+      2    let c=function(){return 0}
+      3    let arr=[];
+      4    let obj={};
+
+INSERTING AT: 1:18
+PAUSES AT: 1:25
+      0    let a=function(){return 0}
+-=>   1    let b=function(){r#eturn 0|}
+      2    let c=function(){return 0}
+      3    let arr=[];
+      4    let obj={};
+
+INSERTING AT: 1:26
+PAUSES AT: 2:0
+      0    let a=function(){return 0}
+ ->   1    let b=function(){return 0}#
+ =>   2    |let c=function(){return 0}
+      3    let arr=[];
+      4    let obj={};
+      5    
+
+INSERTING AT: 2:1
+PAUSES AT: 2:17
+      0    let a=function(){return 0}
+      1    let b=function(){return 0}
+-=>   2    l#et c=function(){|return 0}
+      3    let arr=[];
+      4    let obj={};
+      5    
+
+INSERTING AT: 2:18
+PAUSES AT: 2:25
+      0    let a=function(){return 0}
+      1    let b=function(){return 0}
+-=>   2    let c=function(){r#eturn 0|}
+      3    let arr=[];
+      4    let obj={};
+      5    
+
+INSERTING AT: 2:26
+PAUSES AT: 3:0
+      0    let a=function(){return 0}
+      1    let b=function(){return 0}
+ ->   2    let c=function(){return 0}#
+ =>   3    |let arr=[];
+      4    let obj={};
+      5    
+      6    // Control flow
+
+INSERTING AT: 3:1
+PAUSES AT: 4:0
+      0    let a=function(){return 0}
+      1    let b=function(){return 0}
+      2    let c=function(){return 0}
+ ->   3    l#et arr=[];
+ =>   4    |let obj={};
+      5    
+      6    // Control flow
+      7    
+
+INSERTING AT: 4:1
+PAUSES AT: 8:4
+      1    let b=function(){return 0}
+      2    let c=function(){return 0}
+      3    let arr=[];
+ ->   4    l#et obj={};
+      5    
+      6    // Control flow
+      7    
+ =>   8    if (|0)
+      9        a();
+     10    
+     11    if (0) {
+
+INSERTING AT: 8:5
+PAUSES AT: 9:4
+      5    
+      6    // Control flow
+      7    
+ ->   8    if (0#)
+ =>   9        |a();
+     10    
+     11    if (0) {
+     12        a();
+
+INSERTING AT: 9:5
+PAUSES AT: 11:4
+      6    // Control flow
+      7    
+      8    if (0)
+ ->   9        a#();
+     10    
+ =>  11    if (|0) {
+     12        a();
+     13    }
+     14    
+
+INSERTING AT: 11:5
+PAUSES AT: 12:4
+      8    if (0)
+      9        a();
+     10    
+ ->  11    if (0#) {
+ =>  12        |a();
+     13    }
+     14    
+     15    if (0)
+
+INSERTING AT: 12:5
+PAUSES AT: 15:4
+      9        a();
+     10    
+     11    if (0) {
+ ->  12        a#();
+     13    }
+     14    
+ =>  15    if (|0)
+     16        a();
+     17    else if (0)
+     18        a();
+
+INSERTING AT: 15:5
+PAUSES AT: 16:4
+     12        a();
+     13    }
+     14    
+ ->  15    if (0#)
+ =>  16        |a();
+     17    else if (0)
+     18        a();
+     19    else
+
+INSERTING AT: 16:5
+PAUSES AT: 17:9
+     13    }
+     14    
+     15    if (0)
+ ->  16        a#();
+ =>  17    else if (|0)
+     18        a();
+     19    else
+     20        a();
+
+INSERTING AT: 17:10
+PAUSES AT: 18:4
+     14    
+     15    if (0)
+     16        a();
+ ->  17    else if (0#)
+ =>  18        |a();
+     19    else
+     20        a();
+     21    
+
+INSERTING AT: 18:5
+PAUSES AT: 20:4
+     15    if (0)
+     16        a();
+     17    else if (0)
+ ->  18        a#();
+     19    else
+ =>  20        |a();
+     21    
+     22    a() ? b() : c()
+     23    
+
+INSERTING AT: 20:5
+PAUSES AT: 22:0
+     17    else if (0)
+     18        a();
+     19    else
+ ->  20        a#();
+     21    
+ =>  22    |a() ? b() : c()
+     23    
+     24    // Loops
+     25    
+
+INSERTING AT: 22:1
+PAUSES AT: 26:7
+     19    else
+     20        a();
+     21    
+ ->  22    a#() ? b() : c()
+     23    
+     24    // Loops
+     25    
+ =>  26    while (|0)
+     27        a();
+     28    
+     29    while (0) {
+
+INSERTING AT: 26:8
+PAUSES AT: 27:4
+     23    
+     24    // Loops
+     25    
+ ->  26    while (0#)
+ =>  27        |a();
+     28    
+     29    while (0) {
+     30        a();
+
+INSERTING AT: 27:5
+PAUSES AT: 29:7
+     24    // Loops
+     25    
+     26    while (0)
+ ->  27        a#();
+     28    
+ =>  29    while (|0) {
+     30        a();
+     31    }
+     32    
+
+INSERTING AT: 29:8
+PAUSES AT: 30:4
+     26    while (0)
+     27        a();
+     28    
+ ->  29    while (0#) {
+ =>  30        |a();
+     31    }
+     32    
+     33    do {
+
+INSERTING AT: 30:5
+PAUSES AT: 34:4
+     27        a();
+     28    
+     29    while (0) {
+ ->  30        a#();
+     31    }
+     32    
+     33    do {
+ =>  34        |a();
+     35    } while (0);
+     36    
+     37    for (a(); b(); c())
+
+INSERTING AT: 34:5
+PAUSES AT: 35:9
+     31    }
+     32    
+     33    do {
+ ->  34        a#();
+ =>  35    } while (|0);
+     36    
+     37    for (a(); b(); c())
+     38        break;
+
+INSERTING AT: 35:10
+PAUSES AT: 37:5
+     32    
+     33    do {
+     34        a();
+ ->  35    } while (0#);
+     36    
+ =>  37    for (|a(); b(); c())
+     38        break;
+     39    
+     40    for (; b(); c())
+
+INSERTING AT: 37:6
+PAUSES AT: 37:10
+     34        a();
+     35    } while (0);
+     36    
+-=>  37    for (a#(); |b(); c())
+     38        break;
+     39    
+     40    for (; b(); c())
+
+INSERTING AT: 37:11
+PAUSES AT: 37:15
+     34        a();
+     35    } while (0);
+     36    
+-=>  37    for (a(); b#(); |c())
+     38        break;
+     39    
+     40    for (; b(); c())
+
+INSERTING AT: 37:16
+PAUSES AT: 38:4
+     34        a();
+     35    } while (0);
+     36    
+ ->  37    for (a(); b(); c#())
+ =>  38        |break;
+     39    
+     40    for (; b(); c())
+     41        break;
+
+INSERTING AT: 38:5
+PAUSES AT: 40:7
+     35    } while (0);
+     36    
+     37    for (a(); b(); c())
+ ->  38        b#reak;
+     39    
+ =>  40    for (; |b(); c())
+     41        break;
+     42    
+     43    for (a(); b();)
+
+INSERTING AT: 40:8
+PAUSES AT: 40:12
+     37    for (a(); b(); c())
+     38        break;
+     39    
+-=>  40    for (; b#(); |c())
+     41        break;
+     42    
+     43    for (a(); b();)
+
+INSERTING AT: 40:13
+PAUSES AT: 41:4
+     37    for (a(); b(); c())
+     38        break;
+     39    
+ ->  40    for (; b(); c#())
+ =>  41        |break;
+     42    
+     43    for (a(); b();)
+     44        break;
+
+INSERTING AT: 41:5
+PAUSES AT: 43:5
+     38        break;
+     39    
+     40    for (; b(); c())
+ ->  41        b#reak;
+     42    
+ =>  43    for (|a(); b();)
+     44        break;
+     45    
+     46    for (a();; c())
+
+INSERTING AT: 43:6
+PAUSES AT: 43:10
+     40    for (; b(); c())
+     41        break;
+     42    
+-=>  43    for (a#(); |b();)
+     44        break;
+     45    
+     46    for (a();; c())
+
+INSERTING AT: 43:11
+PAUSES AT: 44:4
+     40    for (; b(); c())
+     41        break;
+     42    
+ ->  43    for (a(); b#();)
+ =>  44        |break;
+     45    
+     46    for (a();; c())
+     47        break;
+
+INSERTING AT: 44:5
+PAUSES AT: 46:5
+     41        break;
+     42    
+     43    for (a(); b();)
+ ->  44        b#reak;
+     45    
+ =>  46    for (|a();; c())
+     47        break;
+     48    
+     49    for (a();;)
+
+INSERTING AT: 46:6
+PAUSES AT: 46:11
+     43    for (a(); b();)
+     44        break;
+     45    
+-=>  46    for (a#();; |c())
+     47        break;
+     48    
+     49    for (a();;)
+
+INSERTING AT: 46:12
+PAUSES AT: 47:4
+     43    for (a(); b();)
+     44        break;
+     45    
+ ->  46    for (a();; c#())
+ =>  47        |break;
+     48    
+     49    for (a();;)
+     50        break;
+
+INSERTING AT: 47:5
+PAUSES AT: 49:5
+     44        break;
+     45    
+     46    for (a();; c())
+ ->  47        b#reak;
+     48    
+ =>  49    for (|a();;)
+     50        break;
+     51    
+     52    for (; b();)
+
+INSERTING AT: 49:6
+PAUSES AT: 50:4
+     46    for (a();; c())
+     47        break;
+     48    
+ ->  49    for (a#();;)
+ =>  50        |break;
+     51    
+     52    for (; b();)
+     53        break;
+
+INSERTING AT: 50:5
+PAUSES AT: 52:7
+     47        break;
+     48    
+     49    for (a();;)
+ ->  50        b#reak;
+     51    
+ =>  52    for (; |b();)
+     53        break;
+     54    
+     55    for (;; c())
+
+INSERTING AT: 52:8
+PAUSES AT: 53:4
+     49    for (a();;)
+     50        break;
+     51    
+ ->  52    for (; b#();)
+ =>  53        |break;
+     54    
+     55    for (;; c())
+     56        break;
+
+INSERTING AT: 53:5
+PAUSES AT: 55:8
+     50        break;
+     51    
+     52    for (; b();)
+ ->  53        b#reak;
+     54    
+ =>  55    for (;; |c())
+     56        break;
+     57    
+     58    for (;;)
+
+INSERTING AT: 55:9
+PAUSES AT: 56:4
+     52    for (; b();)
+     53        break;
+     54    
+ ->  55    for (;; c#())
+ =>  56        |break;
+     57    
+     58    for (;;)
+     59        break;
+
+INSERTING AT: 56:5
+PAUSES AT: 59:4
+     53        break;
+     54    
+     55    for (;; c())
+ ->  56        b#reak;
+     57    
+     58    for (;;)
+ =>  59        |break;
+     60    
+     61    for (a(); b(); c()) {
+     62        break;
+
+INSERTING AT: 59:5
+PAUSES AT: 61:5
+     56        break;
+     57    
+     58    for (;;)
+ ->  59        b#reak;
+     60    
+ =>  61    for (|a(); b(); c()) {
+     62        break;
+     63    }
+     64    
+
+INSERTING AT: 61:6
+PAUSES AT: 61:10
+     58    for (;;)
+     59        break;
+     60    
+-=>  61    for (a#(); |b(); c()) {
+     62        break;
+     63    }
+     64    
+
+INSERTING AT: 61:11
+PAUSES AT: 61:15
+     58    for (;;)
+     59        break;
+     60    
+-=>  61    for (a(); b#(); |c()) {
+     62        break;
+     63    }
+     64    
+
+INSERTING AT: 61:16
+PAUSES AT: 62:4
+     58    for (;;)
+     59        break;
+     60    
+ ->  61    for (a(); b(); c#()) {
+ =>  62        |break;
+     63    }
+     64    
+     65    for (let x of arr)
+
+INSERTING AT: 62:5
+PAUSES AT: 65:14
+     59        break;
+     60    
+     61    for (a(); b(); c()) {
+ ->  62        b#reak;
+     63    }
+     64    
+ =>  65    for (let x of |arr)
+     66        break;
+     67    
+     68    for (let x in obj)
+
+INSERTING AT: 65:15
+PAUSES AT: 66:4
+     62        break;
+     63    }
+     64    
+ ->  65    for (let x of a#rr)
+ =>  66        |break;
+     67    
+     68    for (let x in obj)
+     69        break;
+
+INSERTING AT: 66:5
+PAUSES AT: 68:14
+     63    }
+     64    
+     65    for (let x of arr)
+ ->  66        b#reak;
+     67    
+ =>  68    for (let x in |obj)
+     69        break;
+     70    
+     71    // Switch
+
+INSERTING AT: 68:15
+PAUSES AT: 69:4
+     65    for (let x of arr)
+     66        break;
+     67    
+ ->  68    for (let x in o#bj)
+ =>  69        |break;
+     70    
+     71    // Switch
+     72    
+
+INSERTING AT: 69:5
+PAUSES AT: 73:8
+     66        break;
+     67    
+     68    for (let x in obj)
+ ->  69        b#reak;
+     70    
+     71    // Switch
+     72    
+ =>  73    switch (|0) {
+     74    case 1:
+     75        a();
+     76        break;
+
+INSERTING AT: 73:9
+PAUSES AT: 75:4
+     70    
+     71    // Switch
+     72    
+ ->  73    switch (0#) {
+     74    case 1:
+ =>  75        |a();
+     76        break;
+     77    case 2:
+     78        a();
+
+INSERTING AT: 75:5
+PAUSES AT: 76:4
+     72    
+     73    switch (0) {
+     74    case 1:
+ ->  75        a#();
+ =>  76        |break;
+     77    case 2:
+     78        a();
+     79        // fallthrough
+
+INSERTING AT: 76:5
+PAUSES AT: 78:4
+     73    switch (0) {
+     74    case 1:
+     75        a();
+ ->  76        b#reak;
+     77    case 2:
+ =>  78        |a();
+     79        // fallthrough
+     80    case 3:
+     81        a();
+
+INSERTING AT: 78:5
+PAUSES AT: 81:4
+     75        a();
+     76        break;
+     77    case 2:
+ ->  78        a#();
+     79        // fallthrough
+     80    case 3:
+ =>  81        |a();
+     82        break;
+     83    default:
+     84        a();
+
+INSERTING AT: 81:5
+PAUSES AT: 82:4
+     78        a();
+     79        // fallthrough
+     80    case 3:
+ ->  81        a#();
+ =>  82        |break;
+     83    default:
+     84        a();
+     85        break;
+
+INSERTING AT: 82:5
+PAUSES AT: 84:4
+     79        // fallthrough
+     80    case 3:
+     81        a();
+ ->  82        b#reak;
+     83    default:
+ =>  84        |a();
+     85        break;
+     86    }
+     87    
+
+INSERTING AT: 84:5
+PAUSES AT: 85:4
+     81        a();
+     82        break;
+     83    default:
+ ->  84        a#();
+ =>  85        |break;
+     86    }
+     87    
+     88    // Try/Catch
+
+INSERTING AT: 85:5
+PAUSES AT: 91:4
+     82        break;
+     83    default:
+     84        a();
+ ->  85        b#reak;
+     86    }
+     87    
+     88    // Try/Catch
+     89    
+     90    try {
+ =>  91        |a();
+     92    } catch (e) {
+     93        shouldNotBeReached();
+     94    } finally {
+
+INSERTING AT: 91:5
+PAUSES AT: 93:4
+     88    // Try/Catch
+     89    
+     90    try {
+ ->  91        a#();
+     92    } catch (e) {
+ =>  93        |shouldNotBeReached();
+     94    } finally {
+     95        b();
+     96    }
+
+INSERTING AT: 93:5
+PAUSES AT: 95:4
+     90    try {
+     91        a();
+     92    } catch (e) {
+ ->  93        s#houldNotBeReached();
+     94    } finally {
+ =>  95        |b();
+     96    }
+     97    
+     98    // Class
+
+INSERTING AT: 95:5
+PAUSES AT: 151:8
+     92    } catch (e) {
+     93        shouldNotBeReached();
+     94    } finally {
+ ->  95        b#();
+     96    }
+     97    
+     98    // Class
+     99    
+    100    class Base {
+    101        constructor()
+    102        {
+    103            this._base = true;
+    104        }
+    105    
+    106        baseMethod()
+    107        {
+    108            a();
+    109        }
+    110    
+    111        method()
+    112        {
+    113            a();
+    114        }
+    115    }
+    116    
+    117    class Child extends Base {
+    118        constructor()
+    119        {
+    120            super();
+    121            this._child = true;
+    122        }
+    123    
+    124        childMethod()
+    125        {
+    126            b();
+    127        }
+    128    
+    129        method()
+    130        {
+    131            super.method();
+    132            b();
+    133        }
+    134    
+    135        get name()
+    136        {
+    137            return this._name;
+    138        }
+    139        
+    140        set name(x)
+    141        {
+    142            this._name = x;
+    143        }
+    144    }
+    145    
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+INSERTING AT: 101:0
+PAUSES AT: 103:8
+     98    // Class
+     99    
+    100    class Base {
+ -> 101    #    constructor()
+    102        {
+ => 103            |this._base = true;
+    104        }
+    105    
+    106        baseMethod()
+
+INSERTING AT: 103:9
+PAUSES AT: 104:4
+    100    class Base {
+    101        constructor()
+    102        {
+ -> 103            t#his._base = true;
+ => 104        |}
+    105    
+    106        baseMethod()
+    107        {
+
+INSERTING AT: 106:0
+PAUSES AT: 108:8
+    103            this._base = true;
+    104        }
+    105    
+ -> 106    #    baseMethod()
+    107        {
+ => 108            |a();
+    109        }
+    110    
+    111        method()
+
+INSERTING AT: 108:9
+PAUSES AT: 109:4
+    105    
+    106        baseMethod()
+    107        {
+ -> 108            a#();
+ => 109        |}
+    110    
+    111        method()
+    112        {
+
+INSERTING AT: 111:0
+PAUSES AT: 113:8
+    108            a();
+    109        }
+    110    
+ -> 111    #    method()
+    112        {
+ => 113            |a();
+    114        }
+    115    }
+    116    
+
+INSERTING AT: 113:9
+PAUSES AT: 114:4
+    110    
+    111        method()
+    112        {
+ -> 113            a#();
+ => 114        |}
+    115    }
+    116    
+    117    class Child extends Base {
+
+INSERTING AT: 118:0
+PAUSES AT: 120:8
+    115    }
+    116    
+    117    class Child extends Base {
+ -> 118    #    constructor()
+    119        {
+ => 120            |super();
+    121            this._child = true;
+    122        }
+    123    
+
+INSERTING AT: 120:9
+PAUSES AT: 121:8
+    117    class Child extends Base {
+    118        constructor()
+    119        {
+ -> 120            s#uper();
+ => 121            |this._child = true;
+    122        }
+    123    
+    124        childMethod()
+
+INSERTING AT: 121:9
+PAUSES AT: 122:4
+    118        constructor()
+    119        {
+    120            super();
+ -> 121            t#his._child = true;
+ => 122        |}
+    123    
+    124        childMethod()
+    125        {
+
+INSERTING AT: 124:0
+PAUSES AT: 126:8
+    121            this._child = true;
+    122        }
+    123    
+ -> 124    #    childMethod()
+    125        {
+ => 126            |b();
+    127        }
+    128    
+    129        method()
+
+INSERTING AT: 126:9
+PAUSES AT: 127:4
+    123    
+    124        childMethod()
+    125        {
+ -> 126            b#();
+ => 127        |}
+    128    
+    129        method()
+    130        {
+
+INSERTING AT: 129:0
+PAUSES AT: 131:8
+    126            b();
+    127        }
+    128    
+ -> 129    #    method()
+    130        {
+ => 131            |super.method();
+    132            b();
+    133        }
+    134    
+
+INSERTING AT: 131:9
+PAUSES AT: 132:8
+    128    
+    129        method()
+    130        {
+ -> 131            s#uper.method();
+ => 132            |b();
+    133        }
+    134    
+    135        get name()
+
+INSERTING AT: 132:9
+PAUSES AT: 133:4
+    129        method()
+    130        {
+    131            super.method();
+ -> 132            b#();
+ => 133        |}
+    134    
+    135        get name()
+    136        {
+
+INSERTING AT: 135:0
+PAUSES AT: 137:8
+    132            b();
+    133        }
+    134    
+ -> 135    #    get name()
+    136        {
+ => 137            |return this._name;
+    138        }
+    139        
+    140        set name(x)
+
+INSERTING AT: 137:9
+PAUSES AT: 138:4
+    134    
+    135        get name()
+    136        {
+ -> 137            r#eturn this._name;
+ => 138        |}
+    139        
+    140        set name(x)
+    141        {
+
+INSERTING AT: 140:0
+PAUSES AT: 142:8
+    137            return this._name;
+    138        }
+    139        
+ -> 140    #    set name(x)
+    141        {
+ => 142            |this._name = x;
+    143        }
+    144    }
+    145    
+
+INSERTING AT: 142:9
+PAUSES AT: 143:4
+    139        
+    140        set name(x)
+    141        {
+ -> 142            t#his._name = x;
+ => 143        |}
+    144    }
+    145    
+    146    // ---------
+
+INSERTING AT: 151:9
+PAUSES AT: 156:8
+    148    // ---------
+    149    
+    150        {
+ -> 151            a#();
+    152        }
+    153    
+    154    label:
+    155        {
+ => 156            |a();
+    157        }
+    158    
+    159    var w1 = {x:1, y:2};
+
+INSERTING AT: 156:9
+PAUSES AT: 159:0
+    153    
+    154    label:
+    155        {
+ -> 156            a#();
+    157        }
+    158    
+ => 159    |var w1 = {x:1, y:2};
+    160    with (w1) {
+    161        a();
+    162    }
+
+INSERTING AT: 159:1
+PAUSES AT: 160:6
+    156            a();
+    157        }
+    158    
+ -> 159    v#ar w1 = {x:1, y:2};
+ => 160    with (|w1) {
+    161        a();
+    162    }
+    163    
+
+INSERTING AT: 160:7
+PAUSES AT: 161:4
+    157        }
+    158    
+    159    var w1 = {x:1, y:2};
+ -> 160    with (w#1) {
+ => 161        |a();
+    162    }
+    163    
+    164    var v1 = 1,
+
+INSERTING AT: 161:5
+PAUSES AT: 164:0
+    158    
+    159    var w1 = {x:1, y:2};
+    160    with (w1) {
+ -> 161        a#();
+    162    }
+    163    
+ => 164    |var v1 = 1,
+    165        v2 = 1;
+    166    let l1 = 2,
+    167        l2 = 2;
+
+INSERTING AT: 164:1
+PAUSES AT: 166:0
+    161        a();
+    162    }
+    163    
+ -> 164    v#ar v1 = 1,
+    165        v2 = 1;
+ => 166    |let l1 = 2,
+    167        l2 = 2;
+    168    const c1 = 3,
+    169        c2 = 3;
+
+INSERTING AT: 166:1
+PAUSES AT: 168:0
+    163    
+    164    var v1 = 1,
+    165        v2 = 1;
+ -> 166    l#et l1 = 2,
+    167        l2 = 2;
+ => 168    |const c1 = 3,
+    169        c2 = 3;
+    170    
+    171    v1 = v2 = v1;
+
+INSERTING AT: 168:1
+PAUSES AT: 171:0
+    165        v2 = 1;
+    166    let l1 = 2,
+    167        l2 = 2;
+ -> 168    c#onst c1 = 3,
+    169        c2 = 3;
+    170    
+ => 171    |v1 = v2 = v1;
+    172    
+    173    var {x, y} = obj;
+    174    var [w, z] = arr;
+
+INSERTING AT: 171:1
+PAUSES AT: 173:0
+    168    const c1 = 3,
+    169        c2 = 3;
+    170    
+ -> 171    v#1 = v2 = v1;
+    172    
+ => 173    |var {x, y} = obj;
+    174    var [w, z] = arr;
+    175    
+    176    var o1 = {
+
+INSERTING AT: 173:1
+PAUSES AT: 174:0
+    170    
+    171    v1 = v2 = v1;
+    172    
+ -> 173    v#ar {x, y} = obj;
+ => 174    |var [w, z] = arr;
+    175    
+    176    var o1 = {
+    177        p1: 1,
+
+INSERTING AT: 174:1
+PAUSES AT: 176:0
+    171    v1 = v2 = v1;
+    172    
+    173    var {x, y} = obj;
+ -> 174    v#ar [w, z] = arr;
+    175    
+ => 176    |var o1 = {
+    177        p1: 1,
+    178        p2: a(),
+    179        p3: 1,
+
+INSERTING AT: 176:1
+PAUSES AT: 184:0
+    173    var {x, y} = obj;
+    174    var [w, z] = arr;
+    175    
+ -> 176    v#ar o1 = {
+    177        p1: 1,
+    178        p2: a(),
+    179        p3: 1,
+    180        ["p4"]: 1,
+    181        [b()]: 1,
+    182    };
+    183    
+ => 184    |var a1 = [
+    185        1,
+    186        a(),
+    187        1,
+
+INSERTING AT: 184:1
+PAUSES AT: 191:0
+    181        [b()]: 1,
+    182    };
+    183    
+ -> 184    v#ar a1 = [
+    185        1,
+    186        a(),
+    187        1,
+    188        b(),
+    189    ];
+    190    
+ => 191    |var i1 = new Base;
+    192    var i2 = new Child;
+    193    i2.name;
+    194    i2.name = 1;
+
+INSERTING AT: 191:1
+PAUSES AT: 192:0
+    188        b(),
+    189    ];
+    190    
+ -> 191    v#ar i1 = new Base;
+ => 192    |var i2 = new Child;
+    193    i2.name;
+    194    i2.name = 1;
+    195    i2.method();
+
+INSERTING AT: 192:1
+PAUSES AT: 193:0
+    189    ];
+    190    
+    191    var i1 = new Base;
+ -> 192    v#ar i2 = new Child;
+ => 193    |i2.name;
+    194    i2.name = 1;
+    195    i2.method();
+    196    
+
+INSERTING AT: 193:1
+PAUSES AT: 194:0
+    190    
+    191    var i1 = new Base;
+    192    var i2 = new Child;
+ -> 193    i#2.name;
+ => 194    |i2.name = 1;
+    195    i2.method();
+    196    
+    197    var t1 = `${1} ${x=1} ${a()}`;
+
+INSERTING AT: 194:1
+PAUSES AT: 195:0
+    191    var i1 = new Base;
+    192    var i2 = new Child;
+    193    i2.name;
+ -> 194    i#2.name = 1;
+ => 195    |i2.method();
+    196    
+    197    var t1 = `${1} ${x=1} ${a()}`;
+    198    var t2 = a`${1} ${x=1} ${a()}`;
+
+INSERTING AT: 195:1
+PAUSES AT: 197:0
+    192    var i2 = new Child;
+    193    i2.name;
+    194    i2.name = 1;
+ -> 195    i#2.method();
+    196    
+ => 197    |var t1 = `${1} ${x=1} ${a()}`;
+    198    var t2 = a`${1} ${x=1} ${a()}`;
+    199    
+    200    a(a(), b());
+
+INSERTING AT: 197:1
+PAUSES AT: 198:0
+    194    i2.name = 1;
+    195    i2.method();
+    196    
+ -> 197    v#ar t1 = `${1} ${x=1} ${a()}`;
+ => 198    |var t2 = a`${1} ${x=1} ${a()}`;
+    199    
+    200    a(a(), b());
+    201    
+
+INSERTING AT: 198:1
+PAUSES AT: 200:0
+    195    i2.method();
+    196    
+    197    var t1 = `${1} ${x=1} ${a()}`;
+ -> 198    v#ar t2 = a`${1} ${x=1} ${a()}`;
+    199    
+ => 200    |a(a(), b());
+    201    
+
+
+-- Running test case: Debugger.resolvedBreakpoint.dumpAllLocations.Functions
+
+INSERTING AT: 0:0
+PAUSES AT: 0:19
+-=>   0    #function inline() {|}
+      1    function named() {
+      2        var x;
+      3    }
+
+INSERTING AT: 0:20
+PAUSES AT: 44:0
+ ->   0    function inline() {}#
+      1    function named() {
+      2        var x;
+      3    }
+      4    function outer1() {
+      5        function inner() {
+      6            var y;
+      7        }
+      8    }
+      9    function outer2()
+     10    {
+     11        function inner()
+     12        {
+     13            var y;
+     14        }
+     15    }
+     16    function outer3() {
+     17        var x;
+     18        function inner() { var y; }
+     19    }
+     20    function outer4() {
+     21        function inner() { var y; }
+     22        var x;
+     23    }
+     24    function outer5() {
+     25        var x;
+     26        function inner() { var y; }
+     27        var x;
+     28    }
+     29    function outer6() {
+     30        function inner1() { var y; }
+     31        var x;
+     32        function inner2() { var z; }
+     33    }
+     34    function outer7() {
+     35        function inner1() { var y; }
+     36        function inner2() { var z; }
+     37        var x;
+     38    }
+     39    function outer7() {
+     40        function inner1() { var y; }
+     41        function inner2() { var z; }
+     42    }
+     43    
+ =>  44    |(function() {
+     45        var x;
+     46    })();
+     47    
+
+INSERTING AT: 1:0
+PAUSES AT: 2:4
+      0    function inline() {}
+ ->   1    #function named() {
+ =>   2        |var x;
+      3    }
+      4    function outer1() {
+      5        function inner() {
+
+INSERTING AT: 2:5
+PAUSES AT: 3:0
+      0    function inline() {}
+      1    function named() {
+ ->   2        v#ar x;
+ =>   3    |}
+      4    function outer1() {
+      5        function inner() {
+      6            var y;
+
+INSERTING AT: 4:0
+PAUSES AT: 8:0
+      1    function named() {
+      2        var x;
+      3    }
+ ->   4    #function outer1() {
+      5        function inner() {
+      6            var y;
+      7        }
+ =>   8    |}
+      9    function outer2()
+     10    {
+     11        function inner()
+
+INSERTING AT: 4:15
+PAUSES AT: 6:8
+      1    function named() {
+      2        var x;
+      3    }
+ ->   4    function outer1#() {
+      5        function inner() {
+ =>   6            |var y;
+      7        }
+      8    }
+      9    function outer2()
+
+INSERTING AT: 6:9
+PAUSES AT: 7:4
+      3    }
+      4    function outer1() {
+      5        function inner() {
+ ->   6            v#ar y;
+ =>   7        |}
+      8    }
+      9    function outer2()
+     10    {
+
+INSERTING AT: 9:0
+PAUSES AT: 15:0
+      6            var y;
+      7        }
+      8    }
+ ->   9    #function outer2()
+     10    {
+     11        function inner()
+     12        {
+     13            var y;
+     14        }
+ =>  15    |}
+     16    function outer3() {
+     17        var x;
+     18        function inner() { var y; }
+
+INSERTING AT: 9:15
+PAUSES AT: 13:8
+      6            var y;
+      7        }
+      8    }
+ ->   9    function outer2#()
+     10    {
+     11        function inner()
+     12        {
+ =>  13            |var y;
+     14        }
+     15    }
+     16    function outer3() {
+
+INSERTING AT: 13:9
+PAUSES AT: 14:4
+     10    {
+     11        function inner()
+     12        {
+ ->  13            v#ar y;
+ =>  14        |}
+     15    }
+     16    function outer3() {
+     17        var x;
+
+INSERTING AT: 16:0
+PAUSES AT: 17:4
+     13            var y;
+     14        }
+     15    }
+ ->  16    #function outer3() {
+ =>  17        |var x;
+     18        function inner() { var y; }
+     19    }
+     20    function outer4() {
+
+INSERTING AT: 17:5
+PAUSES AT: 19:0
+     14        }
+     15    }
+     16    function outer3() {
+ ->  17        v#ar x;
+     18        function inner() { var y; }
+ =>  19    |}
+     20    function outer4() {
+     21        function inner() { var y; }
+     22        var x;
+
+INSERTING AT: 18:0
+PAUSES AT: 18:23
+     15    }
+     16    function outer3() {
+     17        var x;
+-=>  18    #    function inner() { |var y; }
+     19    }
+     20    function outer4() {
+     21        function inner() { var y; }
+
+INSERTING AT: 18:24
+PAUSES AT: 18:30
+     15    }
+     16    function outer3() {
+     17        var x;
+-=>  18        function inner() { v#ar y; |}
+     19    }
+     20    function outer4() {
+     21        function inner() { var y; }
+
+INSERTING AT: 20:0
+PAUSES AT: 22:4
+     17        var x;
+     18        function inner() { var y; }
+     19    }
+ ->  20    #function outer4() {
+     21        function inner() { var y; }
+ =>  22        |var x;
+     23    }
+     24    function outer5() {
+     25        var x;
+
+INSERTING AT: 20:15
+PAUSES AT: 21:23
+     17        var x;
+     18        function inner() { var y; }
+     19    }
+ ->  20    function outer4#() {
+ =>  21        function inner() { |var y; }
+     22        var x;
+     23    }
+     24    function outer5() {
+
+INSERTING AT: 21:24
+PAUSES AT: 21:30
+     18        function inner() { var y; }
+     19    }
+     20    function outer4() {
+-=>  21        function inner() { v#ar y; |}
+     22        var x;
+     23    }
+     24    function outer5() {
+
+INSERTING AT: 22:5
+PAUSES AT: 23:0
+     19    }
+     20    function outer4() {
+     21        function inner() { var y; }
+ ->  22        v#ar x;
+ =>  23    |}
+     24    function outer5() {
+     25        var x;
+     26        function inner() { var y; }
+
+INSERTING AT: 24:0
+PAUSES AT: 25:4
+     21        function inner() { var y; }
+     22        var x;
+     23    }
+ ->  24    #function outer5() {
+ =>  25        |var x;
+     26        function inner() { var y; }
+     27        var x;
+     28    }
+
+INSERTING AT: 25:5
+PAUSES AT: 27:4
+     22        var x;
+     23    }
+     24    function outer5() {
+ ->  25        v#ar x;
+     26        function inner() { var y; }
+ =>  27        |var x;
+     28    }
+     29    function outer6() {
+     30        function inner1() { var y; }
+
+INSERTING AT: 26:0
+PAUSES AT: 26:23
+     23    }
+     24    function outer5() {
+     25        var x;
+-=>  26    #    function inner() { |var y; }
+     27        var x;
+     28    }
+     29    function outer6() {
+
+INSERTING AT: 26:24
+PAUSES AT: 26:30
+     23    }
+     24    function outer5() {
+     25        var x;
+-=>  26        function inner() { v#ar y; |}
+     27        var x;
+     28    }
+     29    function outer6() {
+
+INSERTING AT: 27:5
+PAUSES AT: 28:0
+     24    function outer5() {
+     25        var x;
+     26        function inner() { var y; }
+ ->  27        v#ar x;
+ =>  28    |}
+     29    function outer6() {
+     30        function inner1() { var y; }
+     31        var x;
+
+INSERTING AT: 29:0
+PAUSES AT: 31:4
+     26        function inner() { var y; }
+     27        var x;
+     28    }
+ ->  29    #function outer6() {
+     30        function inner1() { var y; }
+ =>  31        |var x;
+     32        function inner2() { var z; }
+     33    }
+     34    function outer7() {
+
+INSERTING AT: 29:15
+PAUSES AT: 30:24
+     26        function inner() { var y; }
+     27        var x;
+     28    }
+ ->  29    function outer6#() {
+ =>  30        function inner1() { |var y; }
+     31        var x;
+     32        function inner2() { var z; }
+     33    }
+
+INSERTING AT: 30:25
+PAUSES AT: 30:31
+     27        var x;
+     28    }
+     29    function outer6() {
+-=>  30        function inner1() { v#ar y; |}
+     31        var x;
+     32        function inner2() { var z; }
+     33    }
+
+INSERTING AT: 31:5
+PAUSES AT: 33:0
+     28    }
+     29    function outer6() {
+     30        function inner1() { var y; }
+ ->  31        v#ar x;
+     32        function inner2() { var z; }
+ =>  33    |}
+     34    function outer7() {
+     35        function inner1() { var y; }
+     36        function inner2() { var z; }
+
+INSERTING AT: 32:0
+PAUSES AT: 32:24
+     29    function outer6() {
+     30        function inner1() { var y; }
+     31        var x;
+-=>  32    #    function inner2() { |var z; }
+     33    }
+     34    function outer7() {
+     35        function inner1() { var y; }
+
+INSERTING AT: 32:25
+PAUSES AT: 32:31
+     29    function outer6() {
+     30        function inner1() { var y; }
+     31        var x;
+-=>  32        function inner2() { v#ar z; |}
+     33    }
+     34    function outer7() {
+     35        function inner1() { var y; }
+
+INSERTING AT: 34:0
+PAUSES AT: 37:4
+     31        var x;
+     32        function inner2() { var z; }
+     33    }
+ ->  34    #function outer7() {
+     35        function inner1() { var y; }
+     36        function inner2() { var z; }
+ =>  37        |var x;
+     38    }
+     39    function outer7() {
+     40        function inner1() { var y; }
+
+INSERTING AT: 34:15
+PAUSES AT: 35:24
+     31        var x;
+     32        function inner2() { var z; }
+     33    }
+ ->  34    function outer7#() {
+ =>  35        function inner1() { |var y; }
+     36        function inner2() { var z; }
+     37        var x;
+     38    }
+
+INSERTING AT: 35:25
+PAUSES AT: 35:31
+     32        function inner2() { var z; }
+     33    }
+     34    function outer7() {
+-=>  35        function inner1() { v#ar y; |}
+     36        function inner2() { var z; }
+     37        var x;
+     38    }
+
+INSERTING AT: 36:0
+PAUSES AT: 36:24
+     33    }
+     34    function outer7() {
+     35        function inner1() { var y; }
+-=>  36    #    function inner2() { |var z; }
+     37        var x;
+     38    }
+     39    function outer7() {
+
+INSERTING AT: 36:25
+PAUSES AT: 36:31
+     33    }
+     34    function outer7() {
+     35        function inner1() { var y; }
+-=>  36        function inner2() { v#ar z; |}
+     37        var x;
+     38    }
+     39    function outer7() {
+
+INSERTING AT: 37:5
+PAUSES AT: 38:0
+     34    function outer7() {
+     35        function inner1() { var y; }
+     36        function inner2() { var z; }
+ ->  37        v#ar x;
+ =>  38    |}
+     39    function outer7() {
+     40        function inner1() { var y; }
+     41        function inner2() { var z; }
+
+INSERTING AT: 39:0
+PAUSES AT: 42:0
+     36        function inner2() { var z; }
+     37        var x;
+     38    }
+ ->  39    #function outer7() {
+     40        function inner1() { var y; }
+     41        function inner2() { var z; }
+ =>  42    |}
+     43    
+     44    (function() {
+     45        var x;
+
+INSERTING AT: 39:15
+PAUSES AT: 40:24
+     36        function inner2() { var z; }
+     37        var x;
+     38    }
+ ->  39    function outer7#() {
+ =>  40        function inner1() { |var y; }
+     41        function inner2() { var z; }
+     42    }
+     43    
+
+INSERTING AT: 40:25
+PAUSES AT: 40:31
+     37        var x;
+     38    }
+     39    function outer7() {
+-=>  40        function inner1() { v#ar y; |}
+     41        function inner2() { var z; }
+     42    }
+     43    
+
+INSERTING AT: 41:0
+PAUSES AT: 41:24
+     38    }
+     39    function outer7() {
+     40        function inner1() { var y; }
+-=>  41    #    function inner2() { |var z; }
+     42    }
+     43    
+     44    (function() {
+
+INSERTING AT: 41:25
+PAUSES AT: 41:31
+     38    }
+     39    function outer7() {
+     40        function inner1() { var y; }
+-=>  41        function inner2() { v#ar z; |}
+     42    }
+     43    
+     44    (function() {
+
+INSERTING AT: 44:1
+PAUSES AT: 45:4
+     41        function inner2() { var z; }
+     42    }
+     43    
+ ->  44    (#function() {
+ =>  45        |var x;
+     46    })();
+     47    
+     48    (() => {
+
+INSERTING AT: 45:5
+PAUSES AT: 46:0
+     42    }
+     43    
+     44    (function() {
+ ->  45        v#ar x;
+ =>  46    |})();
+     47    
+     48    (() => {
+     49        var x;
+
+INSERTING AT: 46:1
+PAUSES AT: 48:0
+     43    
+     44    (function() {
+     45        var x;
+ ->  46    }#)();
+     47    
+ =>  48    |(() => {
+     49        var x;
+     50    });
+     51    
+
+INSERTING AT: 48:1
+PAUSES AT: 49:4
+     45        var x;
+     46    })();
+     47    
+ ->  48    (#() => {
+ =>  49        |var x;
+     50    });
+     51    
+     52    function* generator() {
+
+INSERTING AT: 49:5
+PAUSES AT: 50:0
+     46    })();
+     47    
+     48    (() => {
+ ->  49        v#ar x;
+ =>  50    |});
+     51    
+     52    function* generator() {
+     53        var x;
+
+INSERTING AT: 50:1
+PAUSES AT: 87:0
+     47    
+     48    (() => {
+     49        var x;
+ ->  50    }#);
+     51    
+     52    function* generator() {
+     53        var x;
+     54    }
+     55    
+     56    class Class {
+     57        static staticMethod1() {
+     58            var x;
+     59        }
+     60        static staticMethod2()
+     61        {
+     62            var x;
+     63        }
+     64        method1() {
+     65            var x;
+     66        }
+     67        method2()
+     68        {
+     69            var x;
+     70        }
+     71        get getter1() {
+     72            var x;
+     73        }
+     74        get getter2()
+     75        {
+     76            var x;
+     77        }
+     78        set setter1(x) {
+     79            var s;
+     80        }
+     81        set setter2(x)
+     82        {
+     83            var s;
+     84        }
+     85    }
+     86    
+ =>  87    |x => x;
+     88    () => 1;
+     89    (x) => x;
+     90    (x) => { x };
+
+INSERTING AT: 52:0
+PAUSES AT: 53:4
+     49        var x;
+     50    });
+     51    
+ ->  52    #function* generator() {
+ =>  53        |var x;
+     54    }
+     55    
+     56    class Class {
+
+INSERTING AT: 53:5
+PAUSES AT: 54:0
+     50    });
+     51    
+     52    function* generator() {
+ ->  53        v#ar x;
+ =>  54    |}
+     55    
+     56    class Class {
+     57        static staticMethod1() {
+
+INSERTING AT: 57:0
+PAUSES AT: 58:8
+     54    }
+     55    
+     56    class Class {
+ ->  57    #    static staticMethod1() {
+ =>  58            |var x;
+     59        }
+     60        static staticMethod2()
+     61        {
+
+INSERTING AT: 58:9
+PAUSES AT: 59:4
+     55    
+     56    class Class {
+     57        static staticMethod1() {
+ ->  58            v#ar x;
+ =>  59        |}
+     60        static staticMethod2()
+     61        {
+     62            var x;
+
+INSERTING AT: 60:0
+PAUSES AT: 62:8
+     57        static staticMethod1() {
+     58            var x;
+     59        }
+ ->  60    #    static staticMethod2()
+     61        {
+ =>  62            |var x;
+     63        }
+     64        method1() {
+     65            var x;
+
+INSERTING AT: 62:9
+PAUSES AT: 63:4
+     59        }
+     60        static staticMethod2()
+     61        {
+ ->  62            v#ar x;
+ =>  63        |}
+     64        method1() {
+     65            var x;
+     66        }
+
+INSERTING AT: 64:0
+PAUSES AT: 65:8
+     61        {
+     62            var x;
+     63        }
+ ->  64    #    method1() {
+ =>  65            |var x;
+     66        }
+     67        method2()
+     68        {
+
+INSERTING AT: 65:9
+PAUSES AT: 66:4
+     62            var x;
+     63        }
+     64        method1() {
+ ->  65            v#ar x;
+ =>  66        |}
+     67        method2()
+     68        {
+     69            var x;
+
+INSERTING AT: 67:0
+PAUSES AT: 69:8
+     64        method1() {
+     65            var x;
+     66        }
+ ->  67    #    method2()
+     68        {
+ =>  69            |var x;
+     70        }
+     71        get getter1() {
+     72            var x;
+
+INSERTING AT: 69:9
+PAUSES AT: 70:4
+     66        }
+     67        method2()
+     68        {
+ ->  69            v#ar x;
+ =>  70        |}
+     71        get getter1() {
+     72            var x;
+     73        }
+
+INSERTING AT: 71:0
+PAUSES AT: 72:8
+     68        {
+     69            var x;
+     70        }
+ ->  71    #    get getter1() {
+ =>  72            |var x;
+     73        }
+     74        get getter2()
+     75        {
+
+INSERTING AT: 72:9
+PAUSES AT: 73:4
+     69            var x;
+     70        }
+     71        get getter1() {
+ ->  72            v#ar x;
+ =>  73        |}
+     74        get getter2()
+     75        {
+     76            var x;
+
+INSERTING AT: 74:0
+PAUSES AT: 76:8
+     71        get getter1() {
+     72            var x;
+     73        }
+ ->  74    #    get getter2()
+     75        {
+ =>  76            |var x;
+     77        }
+     78        set setter1(x) {
+     79            var s;
+
+INSERTING AT: 76:9
+PAUSES AT: 77:4
+     73        }
+     74        get getter2()
+     75        {
+ ->  76            v#ar x;
+ =>  77        |}
+     78        set setter1(x) {
+     79            var s;
+     80        }
+
+INSERTING AT: 78:0
+PAUSES AT: 79:8
+     75        {
+     76            var x;
+     77        }
+ ->  78    #    set setter1(x) {
+ =>  79            |var s;
+     80        }
+     81        set setter2(x)
+     82        {
+
+INSERTING AT: 79:9
+PAUSES AT: 80:4
+     76            var x;
+     77        }
+     78        set setter1(x) {
+ ->  79            v#ar s;
+ =>  80        |}
+     81        set setter2(x)
+     82        {
+     83            var s;
+
+INSERTING AT: 81:0
+PAUSES AT: 83:8
+     78        set setter1(x) {
+     79            var s;
+     80        }
+ ->  81    #    set setter2(x)
+     82        {
+ =>  83            |var s;
+     84        }
+     85    }
+     86    
+
+INSERTING AT: 83:9
+PAUSES AT: 84:4
+     80        }
+     81        set setter2(x)
+     82        {
+ ->  83            v#ar s;
+ =>  84        |}
+     85    }
+     86    
+     87    x => x;
+
+INSERTING AT: 87:1
+PAUSES AT: 87:5
+     84        }
+     85    }
+     86    
+-=>  87    x# => |x;
+     88    () => 1;
+     89    (x) => x;
+     90    (x) => { x };
+
+INSERTING AT: 87:6
+PAUSES AT: 88:0
+     84        }
+     85    }
+     86    
+ ->  87    x => x#;
+ =>  88    |() => 1;
+     89    (x) => x;
+     90    (x) => { x };
+     91    (x) => {
+
+INSERTING AT: 88:1
+PAUSES AT: 88:6
+     85    }
+     86    
+     87    x => x;
+-=>  88    (#) => |1;
+     89    (x) => x;
+     90    (x) => { x };
+     91    (x) => {
+
+INSERTING AT: 88:7
+PAUSES AT: 89:0
+     85    }
+     86    
+     87    x => x;
+ ->  88    () => 1#;
+ =>  89    |(x) => x;
+     90    (x) => { x };
+     91    (x) => {
+     92        x
+
+INSERTING AT: 89:1
+PAUSES AT: 89:7
+     86    
+     87    x => x;
+     88    () => 1;
+-=>  89    (#x) => |x;
+     90    (x) => { x };
+     91    (x) => {
+     92        x
+
+INSERTING AT: 89:8
+PAUSES AT: 94:0
+     86    
+     87    x => x;
+     88    () => 1;
+ ->  89    (x) => x#;
+     90    (x) => { x };
+     91    (x) => {
+     92        x
+     93    };
+ =>  94    |() => {
+     95        var x;
+     96    };
+     97    
+
+INSERTING AT: 90:0
+PAUSES AT: 90:0
+     87    x => x;
+     88    () => 1;
+     89    (x) => x;
+-=>  90    |(x) => { x };
+     91    (x) => {
+     92        x
+     93    };
+
+INSERTING AT: 90:1
+PAUSES AT: 90:9
+     87    x => x;
+     88    () => 1;
+     89    (x) => x;
+-=>  90    (#x) => { |x };
+     91    (x) => {
+     92        x
+     93    };
+
+INSERTING AT: 90:10
+PAUSES AT: 90:11
+     87    x => x;
+     88    () => 1;
+     89    (x) => x;
+-=>  90    (x) => { x# |};
+     91    (x) => {
+     92        x
+     93    };
+
+INSERTING AT: 91:0
+PAUSES AT: 91:0
+     88    () => 1;
+     89    (x) => x;
+     90    (x) => { x };
+-=>  91    |(x) => {
+     92        x
+     93    };
+     94    () => {
+
+INSERTING AT: 91:1
+PAUSES AT: 92:4
+     88    () => 1;
+     89    (x) => x;
+     90    (x) => { x };
+ ->  91    (#x) => {
+ =>  92        |x
+     93    };
+     94    () => {
+     95        var x;
+
+INSERTING AT: 92:5
+PAUSES AT: 93:0
+     89    (x) => x;
+     90    (x) => { x };
+     91    (x) => {
+ ->  92        x#
+ =>  93    |};
+     94    () => {
+     95        var x;
+     96    };
+
+INSERTING AT: 94:1
+PAUSES AT: 95:4
+     91    (x) => {
+     92        x
+     93    };
+ ->  94    (#) => {
+ =>  95        |var x;
+     96    };
+     97    
+     98    var fObj = {
+
+INSERTING AT: 95:5
+PAUSES AT: 96:0
+     92        x
+     93    };
+     94    () => {
+ ->  95        v#ar x;
+ =>  96    |};
+     97    
+     98    var fObj = {
+     99        f1: function() {
+
+INSERTING AT: 96:1
+PAUSES AT: 98:0
+     93    };
+     94    () => {
+     95        var x;
+ ->  96    }#;
+     97    
+ =>  98    |var fObj = {
+     99        f1: function() {
+    100            var x;
+    101        },
+
+INSERTING AT: 99:0
+PAUSES AT: 100:8
+     96    };
+     97    
+     98    var fObj = {
+ ->  99    #    f1: function() {
+ => 100            |var x;
+    101        },
+    102        f2: function()
+    103        {
+
+INSERTING AT: 100:9
+PAUSES AT: 101:4
+     97    
+     98    var fObj = {
+     99        f1: function() {
+ -> 100            v#ar x;
+ => 101        |},
+    102        f2: function()
+    103        {
+    104            var x;
+
+INSERTING AT: 102:0
+PAUSES AT: 104:8
+     99        f1: function() {
+    100            var x;
+    101        },
+ -> 102    #    f2: function()
+    103        {
+ => 104            |var x;
+    105        },
+    106        f3: () => {
+    107            var x;
+
+INSERTING AT: 104:9
+PAUSES AT: 105:4
+    101        },
+    102        f2: function()
+    103        {
+ -> 104            v#ar x;
+ => 105        |},
+    106        f3: () => {
+    107            var x;
+    108        },
+
+INSERTING AT: 106:0
+PAUSES AT: 107:8
+    103        {
+    104            var x;
+    105        },
+ -> 106    #    f3: () => {
+ => 107            |var x;
+    108        },
+    109        f4: () =>
+    110        {
+
+INSERTING AT: 107:9
+PAUSES AT: 108:4
+    104            var x;
+    105        },
+    106        f3: () => {
+ -> 107            v#ar x;
+ => 108        |},
+    109        f4: () =>
+    110        {
+    111            var x;
+
+INSERTING AT: 109:0
+PAUSES AT: 111:8
+    106        f3: () => {
+    107            var x;
+    108        },
+ -> 109    #    f4: () =>
+    110        {
+ => 111            |var x;
+    112        }   
+    113    };
+    114    
+
+INSERTING AT: 111:9
+PAUSES AT: 112:4
+    108        },
+    109        f4: () =>
+    110        {
+ -> 111            v#ar x;
+ => 112        |}   
+    113    };
+    114    
+
+
diff --git a/LayoutTests/inspector/debugger/breakpoints/resolved-dump-all-pause-locations.html b/LayoutTests/inspector/debugger/breakpoints/resolved-dump-all-pause-locations.html
new file mode 100644 (file)
index 0000000..1203a59
--- /dev/null
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../../http/tests/inspector/resources/inspector-test.js"></script>
+<script src="../resources/log-pause-location.js"></script>
+<script src="resources/dump.js"></script>
+<script src="resources/dump-general.js"></script>
+<script src="resources/dump-functions.js"></script>
+<script>
+function test()
+{
+    let suite = InspectorTest.createAsyncSuite("Debugger.resolvedBreakpoint.dumpAllLocations");
+
+    window.addDumpAllPauseLocationsTestCase(suite, {
+        name: "Debugger.resolvedBreakpoint.dumpAllLocations.General",
+        scriptRegex: /dump-general\.js$/,
+    });
+
+    window.addDumpAllPauseLocationsTestCase(suite, {
+        name: "Debugger.resolvedBreakpoint.dumpAllLocations.Functions",
+        scriptRegex: /dump-functions\.js$/,
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+<p>Checking all resolved breakpoint locations in a script.</p>
+</body>
+</html>
diff --git a/LayoutTests/inspector/debugger/breakpoints/resolved-dump-each-line-expected.txt b/LayoutTests/inspector/debugger/breakpoints/resolved-dump-each-line-expected.txt
new file mode 100644 (file)
index 0000000..ab8355e
--- /dev/null
@@ -0,0 +1,4348 @@
+Checking resolved breakpoint locations for each line in a script.
+
+
+== Running test suite: Debugger.resolvedBreakpoint.dumpEachLine
+-- Running test case: Debugger.resolvedBreakpoint.dumpEachLine.General
+
+INSERTING AT: 0:0
+PAUSES AT: 0:0
+-=>   0    |let a=function(){return 0}
+      1    let b=function(){return 0}
+      2    let c=function(){return 0}
+      3    let arr=[];
+
+
+INSERTING AT: 1:0
+PAUSES AT: 1:0
+      0    let a=function(){return 0}
+-=>   1    |let b=function(){return 0}
+      2    let c=function(){return 0}
+      3    let arr=[];
+      4    let obj={};
+
+
+INSERTING AT: 2:0
+PAUSES AT: 2:0
+      0    let a=function(){return 0}
+      1    let b=function(){return 0}
+-=>   2    |let c=function(){return 0}
+      3    let arr=[];
+      4    let obj={};
+      5    
+
+
+INSERTING AT: 3:0
+PAUSES AT: 3:0
+      0    let a=function(){return 0}
+      1    let b=function(){return 0}
+      2    let c=function(){return 0}
+-=>   3    |let arr=[];
+      4    let obj={};
+      5    
+      6    // Control flow
+
+
+INSERTING AT: 4:0
+PAUSES AT: 4:0
+      1    let b=function(){return 0}
+      2    let c=function(){return 0}
+      3    let arr=[];
+-=>   4    |let obj={};
+      5    
+      6    // Control flow
+      7    
+
+
+INSERTING AT: 5:0
+PAUSES AT: 8:4
+      2    let c=function(){return 0}
+      3    let arr=[];
+      4    let obj={};
+ ->   5    #
+      6    // Control flow
+      7    
+ =>   8    if (|0)
+      9        a();
+     10    
+     11    if (0) {
+
+
+INSERTING AT: 6:0
+PAUSES AT: 8:4
+      3    let arr=[];
+      4    let obj={};
+      5    
+ ->   6    #// Control flow
+      7    
+ =>   8    if (|0)
+      9        a();
+     10    
+     11    if (0) {
+
+
+INSERTING AT: 7:0
+PAUSES AT: 8:4
+      4    let obj={};
+      5    
+      6    // Control flow
+ ->   7    #
+ =>   8    if (|0)
+      9        a();
+     10    
+     11    if (0) {
+
+
+INSERTING AT: 8:0
+PAUSES AT: 8:4
+      5    
+      6    // Control flow
+      7    
+-=>   8    #if (|0)
+      9        a();
+     10    
+     11    if (0) {
+
+
+INSERTING AT: 9:0
+PAUSES AT: 9:4
+      6    // Control flow
+      7    
+      8    if (0)
+-=>   9    #    |a();
+     10    
+     11    if (0) {
+     12        a();
+
+
+INSERTING AT: 10:0
+PAUSES AT: 11:4
+      7    
+      8    if (0)
+      9        a();
+ ->  10    #
+ =>  11    if (|0) {
+     12        a();
+     13    }
+     14    
+
+
+INSERTING AT: 11:0
+PAUSES AT: 11:4
+      8    if (0)
+      9        a();
+     10    
+-=>  11    #if (|0) {
+     12        a();
+     13    }
+     14    
+
+
+INSERTING AT: 12:0
+PAUSES AT: 12:4
+      9        a();
+     10    
+     11    if (0) {
+-=>  12    #    |a();
+     13    }
+     14    
+     15    if (0)
+
+
+INSERTING AT: 13:0
+PAUSES AT: 15:4
+     10    
+     11    if (0) {
+     12        a();
+ ->  13    #}
+     14    
+ =>  15    if (|0)
+     16        a();
+     17    else if (0)
+     18        a();
+
+
+INSERTING AT: 14:0
+PAUSES AT: 15:4
+     11    if (0) {
+     12        a();
+     13    }
+ ->  14    #
+ =>  15    if (|0)
+     16        a();
+     17    else if (0)
+     18        a();
+
+
+INSERTING AT: 15:0
+PAUSES AT: 15:4
+     12        a();
+     13    }
+     14    
+-=>  15    #if (|0)
+     16        a();
+     17    else if (0)
+     18        a();
+
+
+INSERTING AT: 16:0
+PAUSES AT: 16:4
+     13    }
+     14    
+     15    if (0)
+-=>  16    #    |a();
+     17    else if (0)
+     18        a();
+     19    else
+
+
+INSERTING AT: 17:0
+PAUSES AT: 17:9
+     14    
+     15    if (0)
+     16        a();
+-=>  17    #else if (|0)
+     18        a();
+     19    else
+     20        a();
+
+
+INSERTING AT: 18:0
+PAUSES AT: 18:4
+     15    if (0)
+     16        a();
+     17    else if (0)
+-=>  18    #    |a();
+     19    else
+     20        a();
+     21    
+
+
+INSERTING AT: 19:0
+PAUSES AT: 20:4
+     16        a();
+     17    else if (0)
+     18        a();
+ ->  19    #else
+ =>  20        |a();
+     21    
+     22    a() ? b() : c()
+     23    
+
+
+INSERTING AT: 20:0
+PAUSES AT: 20:4
+     17    else if (0)
+     18        a();
+     19    else
+-=>  20    #    |a();
+     21    
+     22    a() ? b() : c()
+     23    
+
+
+INSERTING AT: 21:0
+PAUSES AT: 22:0
+     18        a();
+     19    else
+     20        a();
+ ->  21    #
+ =>  22    |a() ? b() : c()
+     23    
+     24    // Loops
+     25    
+
+
+INSERTING AT: 22:0
+PAUSES AT: 22:0
+     19    else
+     20        a();
+     21    
+-=>  22    |a() ? b() : c()
+     23    
+     24    // Loops
+     25    
+
+
+INSERTING AT: 23:0
+PAUSES AT: 26:7
+     20        a();
+     21    
+     22    a() ? b() : c()
+ ->  23    #
+     24    // Loops
+     25    
+ =>  26    while (|0)
+     27        a();
+     28    
+     29    while (0) {
+
+
+INSERTING AT: 24:0
+PAUSES AT: 26:7
+     21    
+     22    a() ? b() : c()
+     23    
+ ->  24    #// Loops
+     25    
+ =>  26    while (|0)
+     27        a();
+     28    
+     29    while (0) {
+
+
+INSERTING AT: 25:0
+PAUSES AT: 26:7
+     22    a() ? b() : c()
+     23    
+     24    // Loops
+ ->  25    #
+ =>  26    while (|0)
+     27        a();
+     28    
+     29    while (0) {
+
+
+INSERTING AT: 26:0
+PAUSES AT: 26:7
+     23    
+     24    // Loops
+     25    
+-=>  26    #while (|0)
+     27        a();
+     28    
+     29    while (0) {
+
+
+INSERTING AT: 27:0
+PAUSES AT: 27:4
+     24    // Loops
+     25    
+     26    while (0)
+-=>  27    #    |a();
+     28    
+     29    while (0) {
+     30        a();
+
+
+INSERTING AT: 28:0
+PAUSES AT: 29:7
+     25    
+     26    while (0)
+     27        a();
+ ->  28    #
+ =>  29    while (|0) {
+     30        a();
+     31    }
+     32    
+
+
+INSERTING AT: 29:0
+PAUSES AT: 29:7
+     26    while (0)
+     27        a();
+     28    
+-=>  29    #while (|0) {
+     30        a();
+     31    }
+     32    
+
+
+INSERTING AT: 30:0
+PAUSES AT: 30:4
+     27        a();
+     28    
+     29    while (0) {
+-=>  30    #    |a();
+     31    }
+     32    
+     33    do {
+
+
+INSERTING AT: 31:0
+PAUSES AT: 34:4
+     28    
+     29    while (0) {
+     30        a();
+ ->  31    #}
+     32    
+     33    do {
+ =>  34        |a();
+     35    } while (0);
+     36    
+     37    for (a(); b(); c())
+
+
+INSERTING AT: 32:0
+PAUSES AT: 34:4
+     29    while (0) {
+     30        a();
+     31    }
+ ->  32    #
+     33    do {
+ =>  34        |a();
+     35    } while (0);
+     36    
+     37    for (a(); b(); c())
+
+
+INSERTING AT: 33:0
+PAUSES AT: 34:4
+     30        a();
+     31    }
+     32    
+ ->  33    #do {
+ =>  34        |a();
+     35    } while (0);
+     36    
+     37    for (a(); b(); c())
+
+
+INSERTING AT: 34:0
+PAUSES AT: 34:4
+     31    }
+     32    
+     33    do {
+-=>  34    #    |a();
+     35    } while (0);
+     36    
+     37    for (a(); b(); c())
+
+
+INSERTING AT: 35:0
+PAUSES AT: 35:9
+     32    
+     33    do {
+     34        a();
+-=>  35    #} while (|0);
+     36    
+     37    for (a(); b(); c())
+     38        break;
+
+
+INSERTING AT: 36:0
+PAUSES AT: 37:5
+     33    do {
+     34        a();
+     35    } while (0);
+ ->  36    #
+ =>  37    for (|a(); b(); c())
+     38        break;
+     39    
+     40    for (; b(); c())
+
+
+INSERTING AT: 37:0
+PAUSES AT: 37:5
+     34        a();
+     35    } while (0);
+     36    
+-=>  37    #for (|a(); b(); c())
+     38        break;
+     39    
+     40    for (; b(); c())
+
+
+INSERTING AT: 38:0
+PAUSES AT: 38:4
+     35    } while (0);
+     36    
+     37    for (a(); b(); c())
+-=>  38    #    |break;
+     39    
+     40    for (; b(); c())
+     41        break;
+
+
+INSERTING AT: 39:0
+PAUSES AT: 40:7
+     36    
+     37    for (a(); b(); c())
+     38        break;
+ ->  39    #
+ =>  40    for (; |b(); c())
+     41        break;
+     42    
+     43    for (a(); b();)
+
+
+INSERTING AT: 40:0
+PAUSES AT: 40:7
+     37    for (a(); b(); c())
+     38        break;
+     39    
+-=>  40    #for (; |b(); c())
+     41        break;
+     42    
+     43    for (a(); b();)
+
+
+INSERTING AT: 41:0
+PAUSES AT: 41:4
+     38        break;
+     39    
+     40    for (; b(); c())
+-=>  41    #    |break;
+     42    
+     43    for (a(); b();)
+     44        break;
+
+
+INSERTING AT: 42:0
+PAUSES AT: 43:5
+     39    
+     40    for (; b(); c())
+     41        break;
+ ->  42    #
+ =>  43    for (|a(); b();)
+     44        break;
+     45    
+     46    for (a();; c())
+
+
+INSERTING AT: 43:0
+PAUSES AT: 43:5
+     40    for (; b(); c())
+     41        break;
+     42    
+-=>  43    #for (|a(); b();)
+     44        break;
+     45    
+     46    for (a();; c())
+
+
+INSERTING AT: 44:0
+PAUSES AT: 44:4
+     41        break;
+     42    
+     43    for (a(); b();)
+-=>  44    #    |break;
+     45    
+     46    for (a();; c())
+     47        break;
+
+
+INSERTING AT: 45:0
+PAUSES AT: 46:5
+     42    
+     43    for (a(); b();)
+     44        break;
+ ->  45    #
+ =>  46    for (|a();; c())
+     47        break;
+     48    
+     49    for (a();;)
+
+
+INSERTING AT: 46:0
+PAUSES AT: 46:5
+     43    for (a(); b();)
+     44        break;
+     45    
+-=>  46    #for (|a();; c())
+     47        break;
+     48    
+     49    for (a();;)
+
+
+INSERTING AT: 47:0
+PAUSES AT: 47:4
+     44        break;
+     45    
+     46    for (a();; c())
+-=>  47    #    |break;
+     48    
+     49    for (a();;)
+     50        break;
+
+
+INSERTING AT: 48:0
+PAUSES AT: 49:5
+     45    
+     46    for (a();; c())
+     47        break;
+ ->  48    #
+ =>  49    for (|a();;)
+     50        break;
+     51    
+     52    for (; b();)
+
+
+INSERTING AT: 49:0
+PAUSES AT: 49:5
+     46    for (a();; c())
+     47        break;
+     48    
+-=>  49    #for (|a();;)
+     50        break;
+     51    
+     52    for (; b();)
+
+
+INSERTING AT: 50:0
+PAUSES AT: 50:4
+     47        break;
+     48    
+     49    for (a();;)
+-=>  50    #    |break;
+     51    
+     52    for (; b();)
+     53        break;
+
+
+INSERTING AT: 51:0
+PAUSES AT: 52:7
+     48    
+     49    for (a();;)
+     50        break;
+ ->  51    #
+ =>  52    for (; |b();)
+     53        break;
+     54    
+     55    for (;; c())
+
+
+INSERTING AT: 52:0
+PAUSES AT: 52:7
+     49    for (a();;)
+     50        break;
+     51    
+-=>  52    #for (; |b();)
+     53        break;
+     54    
+     55    for (;; c())
+
+
+INSERTING AT: 53:0
+PAUSES AT: 53:4
+     50        break;
+     51    
+     52    for (; b();)
+-=>  53    #    |break;
+     54    
+     55    for (;; c())
+     56        break;
+
+
+INSERTING AT: 54:0
+PAUSES AT: 55:8
+     51    
+     52    for (; b();)
+     53        break;
+ ->  54    #
+ =>  55    for (;; |c())
+     56        break;
+     57    
+     58    for (;;)
+
+
+INSERTING AT: 55:0
+PAUSES AT: 55:8
+     52    for (; b();)
+     53        break;
+     54    
+-=>  55    #for (;; |c())
+     56        break;
+     57    
+     58    for (;;)
+
+
+INSERTING AT: 56:0
+PAUSES AT: 56:4
+     53        break;
+     54    
+     55    for (;; c())
+-=>  56    #    |break;
+     57    
+     58    for (;;)
+     59        break;
+
+
+INSERTING AT: 57:0
+PAUSES AT: 59:4
+     54    
+     55    for (;; c())
+     56        break;
+ ->  57    #
+     58    for (;;)
+ =>  59        |break;
+     60    
+     61    for (a(); b(); c()) {
+     62        break;
+
+
+INSERTING AT: 58:0
+PAUSES AT: 59:4
+     55    for (;; c())
+     56        break;
+     57    
+ ->  58    #for (;;)
+ =>  59        |break;
+     60    
+     61    for (a(); b(); c()) {
+     62        break;
+
+
+INSERTING AT: 59:0
+PAUSES AT: 59:4
+     56        break;
+     57    
+     58    for (;;)
+-=>  59    #    |break;
+     60    
+     61    for (a(); b(); c()) {
+     62        break;
+
+
+INSERTING AT: 60:0
+PAUSES AT: 61:5
+     57    
+     58    for (;;)
+     59        break;
+ ->  60    #
+ =>  61    for (|a(); b(); c()) {
+     62        break;
+     63    }
+     64    
+
+
+INSERTING AT: 61:0
+PAUSES AT: 61:5
+     58    for (;;)
+     59        break;
+     60    
+-=>  61    #for (|a(); b(); c()) {
+     62        break;
+     63    }
+     64    
+
+
+INSERTING AT: 62:0
+PAUSES AT: 62:4
+     59        break;
+     60    
+     61    for (a(); b(); c()) {
+-=>  62    #    |break;
+     63    }
+     64    
+     65    for (let x of arr)
+
+
+INSERTING AT: 63:0
+PAUSES AT: 65:14
+     60    
+     61    for (a(); b(); c()) {
+     62        break;
+ ->  63    #}
+     64    
+ =>  65    for (let x of |arr)
+     66        break;
+     67    
+     68    for (let x in obj)
+
+
+INSERTING AT: 64:0
+PAUSES AT: 65:14
+     61    for (a(); b(); c()) {
+     62        break;
+     63    }
+ ->  64    #
+ =>  65    for (let x of |arr)
+     66        break;
+     67    
+     68    for (let x in obj)
+
+
+INSERTING AT: 65:0
+PAUSES AT: 65:14
+     62        break;
+     63    }
+     64    
+-=>  65    #for (let x of |arr)
+     66        break;
+     67    
+     68    for (let x in obj)
+
+
+INSERTING AT: 66:0
+PAUSES AT: 66:4
+     63    }
+     64    
+     65    for (let x of arr)
+-=>  66    #    |break;
+     67    
+     68    for (let x in obj)
+     69        break;
+
+
+INSERTING AT: 67:0
+PAUSES AT: 68:14
+     64    
+     65    for (let x of arr)
+     66        break;
+ ->  67    #
+ =>  68    for (let x in |obj)
+     69        break;
+     70    
+     71    // Switch
+
+
+INSERTING AT: 68:0
+PAUSES AT: 68:14
+     65    for (let x of arr)
+     66        break;
+     67    
+-=>  68    #for (let x in |obj)
+     69        break;
+     70    
+     71    // Switch
+
+
+INSERTING AT: 69:0
+PAUSES AT: 69:4
+     66        break;
+     67    
+     68    for (let x in obj)
+-=>  69    #    |break;
+     70    
+     71    // Switch
+     72    
+
+
+INSERTING AT: 70:0
+PAUSES AT: 73:8
+     67    
+     68    for (let x in obj)
+     69        break;
+ ->  70    #
+     71    // Switch
+     72    
+ =>  73    switch (|0) {
+     74    case 1:
+     75        a();
+     76        break;
+
+
+INSERTING AT: 71:0
+PAUSES AT: 73:8
+     68    for (let x in obj)
+     69        break;
+     70    
+ ->  71    #// Switch
+     72    
+ =>  73    switch (|0) {
+     74    case 1:
+     75        a();
+     76        break;
+
+
+INSERTING AT: 72:0
+PAUSES AT: 73:8
+     69        break;
+     70    
+     71    // Switch
+ ->  72    #
+ =>  73    switch (|0) {
+     74    case 1:
+     75        a();
+     76        break;
+
+
+INSERTING AT: 73:0
+PAUSES AT: 73:8
+     70    
+     71    // Switch
+     72    
+-=>  73    #switch (|0) {
+     74    case 1:
+     75        a();
+     76        break;
+
+
+INSERTING AT: 74:0
+PAUSES AT: 75:4
+     71    // Switch
+     72    
+     73    switch (0) {
+ ->  74    #case 1:
+ =>  75        |a();
+     76        break;
+     77    case 2:
+     78        a();
+
+
+INSERTING AT: 75:0
+PAUSES AT: 75:4
+     72    
+     73    switch (0) {
+     74    case 1:
+-=>  75    #    |a();
+     76        break;
+     77    case 2:
+     78        a();
+
+
+INSERTING AT: 76:0
+PAUSES AT: 76:4
+     73    switch (0) {
+     74    case 1:
+     75        a();
+-=>  76    #    |break;
+     77    case 2:
+     78        a();
+     79        // fallthrough
+
+
+INSERTING AT: 77:0
+PAUSES AT: 78:4
+     74    case 1:
+     75        a();
+     76        break;
+ ->  77    #case 2:
+ =>  78        |a();
+     79        // fallthrough
+     80    case 3:
+     81        a();
+
+
+INSERTING AT: 78:0
+PAUSES AT: 78:4
+     75        a();
+     76        break;
+     77    case 2:
+-=>  78    #    |a();
+     79        // fallthrough
+     80    case 3:
+     81        a();
+
+
+INSERTING AT: 79:0
+PAUSES AT: 81:4
+     76        break;
+     77    case 2:
+     78        a();
+ ->  79    #    // fallthrough
+     80    case 3:
+ =>  81        |a();
+     82        break;
+     83    default:
+     84        a();
+
+
+INSERTING AT: 80:0
+PAUSES AT: 81:4
+     77    case 2:
+     78        a();
+     79        // fallthrough
+ ->  80    #case 3:
+ =>  81        |a();
+     82        break;
+     83    default:
+     84        a();
+
+
+INSERTING AT: 81:0
+PAUSES AT: 81:4
+     78        a();
+     79        // fallthrough
+     80    case 3:
+-=>  81    #    |a();
+     82        break;
+     83    default:
+     84        a();
+
+
+INSERTING AT: 82:0
+PAUSES AT: 82:4
+     79        // fallthrough
+     80    case 3:
+     81        a();
+-=>  82    #    |break;
+     83    default:
+     84        a();
+     85        break;
+
+
+INSERTING AT: 83:0
+PAUSES AT: 84:4
+     80    case 3:
+     81        a();
+     82        break;
+ ->  83    #default:
+ =>  84        |a();
+     85        break;
+     86    }
+     87    
+
+
+INSERTING AT: 84:0
+PAUSES AT: 84:4
+     81        a();
+     82        break;
+     83    default:
+-=>  84    #    |a();
+     85        break;
+     86    }
+     87    
+
+
+INSERTING AT: 85:0
+PAUSES AT: 85:4
+     82        break;
+     83    default:
+     84        a();
+-=>  85    #    |break;
+     86    }
+     87    
+     88    // Try/Catch
+
+
+INSERTING AT: 86:0
+PAUSES AT: 91:4
+     83    default:
+     84        a();
+     85        break;
+ ->  86    #}
+     87    
+     88    // Try/Catch
+     89    
+     90    try {
+ =>  91        |a();
+     92    } catch (e) {
+     93        shouldNotBeReached();
+     94    } finally {
+
+
+INSERTING AT: 87:0
+PAUSES AT: 91:4
+     84        a();
+     85        break;
+     86    }
+ ->  87    #
+     88    // Try/Catch
+     89    
+     90    try {
+ =>  91        |a();
+     92    } catch (e) {
+     93        shouldNotBeReached();
+     94    } finally {
+
+
+INSERTING AT: 88:0
+PAUSES AT: 91:4
+     85        break;
+     86    }
+     87    
+ ->  88    #// Try/Catch
+     89    
+     90    try {
+ =>  91        |a();
+     92    } catch (e) {
+     93        shouldNotBeReached();
+     94    } finally {
+
+
+INSERTING AT: 89:0
+PAUSES AT: 91:4
+     86    }
+     87    
+     88    // Try/Catch
+ ->  89    #
+     90    try {
+ =>  91        |a();
+     92    } catch (e) {
+     93        shouldNotBeReached();
+     94    } finally {
+
+
+INSERTING AT: 90:0
+PAUSES AT: 91:4
+     87    
+     88    // Try/Catch
+     89    
+ ->  90    #try {
+ =>  91        |a();
+     92    } catch (e) {
+     93        shouldNotBeReached();
+     94    } finally {
+
+
+INSERTING AT: 91:0
+PAUSES AT: 91:4
+     88    // Try/Catch
+     89    
+     90    try {
+-=>  91    #    |a();
+     92    } catch (e) {
+     93        shouldNotBeReached();
+     94    } finally {
+
+
+INSERTING AT: 92:0
+PAUSES AT: 93:4
+     89    
+     90    try {
+     91        a();
+ ->  92    #} catch (e) {
+ =>  93        |shouldNotBeReached();
+     94    } finally {
+     95        b();
+     96    }
+
+
+INSERTING AT: 93:0
+PAUSES AT: 93:4
+     90    try {
+     91        a();
+     92    } catch (e) {
+-=>  93    #    |shouldNotBeReached();
+     94    } finally {
+     95        b();
+     96    }
+
+
+INSERTING AT: 94:0
+PAUSES AT: 95:4
+     91        a();
+     92    } catch (e) {
+     93        shouldNotBeReached();
+ ->  94    #} finally {
+ =>  95        |b();
+     96    }
+     97    
+     98    // Class
+
+
+INSERTING AT: 95:0
+PAUSES AT: 95:4
+     92    } catch (e) {
+     93        shouldNotBeReached();
+     94    } finally {
+-=>  95    #    |b();
+     96    }
+     97    
+     98    // Class
+
+
+INSERTING AT: 96:0
+PAUSES AT: 151:8
+     93        shouldNotBeReached();
+     94    } finally {
+     95        b();
+ ->  96    #}
+     97    
+     98    // Class
+     99    
+    100    class Base {
+    101        constructor()
+    102        {
+    103            this._base = true;
+    104        }
+    105    
+    106        baseMethod()
+    107        {
+    108            a();
+    109        }
+    110    
+    111        method()
+    112        {
+    113            a();
+    114        }
+    115    }
+    116    
+    117    class Child extends Base {
+    118        constructor()
+    119        {
+    120            super();
+    121            this._child = true;
+    122        }
+    123    
+    124        childMethod()
+    125        {
+    126            b();
+    127        }
+    128    
+    129        method()
+    130        {
+    131            super.method();
+    132            b();
+    133        }
+    134    
+    135        get name()
+    136        {
+    137            return this._name;
+    138        }
+    139        
+    140        set name(x)
+    141        {
+    142            this._name = x;
+    143        }
+    144    }
+    145    
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 97:0
+PAUSES AT: 151:8
+     94    } finally {
+     95        b();
+     96    }
+ ->  97    #
+     98    // Class
+     99    
+    100    class Base {
+    101        constructor()
+    102        {
+    103            this._base = true;
+    104        }
+    105    
+    106        baseMethod()
+    107        {
+    108            a();
+    109        }
+    110    
+    111        method()
+    112        {
+    113            a();
+    114        }
+    115    }
+    116    
+    117    class Child extends Base {
+    118        constructor()
+    119        {
+    120            super();
+    121            this._child = true;
+    122        }
+    123    
+    124        childMethod()
+    125        {
+    126            b();
+    127        }
+    128    
+    129        method()
+    130        {
+    131            super.method();
+    132            b();
+    133        }
+    134    
+    135        get name()
+    136        {
+    137            return this._name;
+    138        }
+    139        
+    140        set name(x)
+    141        {
+    142            this._name = x;
+    143        }
+    144    }
+    145    
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 98:0
+PAUSES AT: 151:8
+     95        b();
+     96    }
+     97    
+ ->  98    #// Class
+     99    
+    100    class Base {
+    101        constructor()
+    102        {
+    103            this._base = true;
+    104        }
+    105    
+    106        baseMethod()
+    107        {
+    108            a();
+    109        }
+    110    
+    111        method()
+    112        {
+    113            a();
+    114        }
+    115    }
+    116    
+    117    class Child extends Base {
+    118        constructor()
+    119        {
+    120            super();
+    121            this._child = true;
+    122        }
+    123    
+    124        childMethod()
+    125        {
+    126            b();
+    127        }
+    128    
+    129        method()
+    130        {
+    131            super.method();
+    132            b();
+    133        }
+    134    
+    135        get name()
+    136        {
+    137            return this._name;
+    138        }
+    139        
+    140        set name(x)
+    141        {
+    142            this._name = x;
+    143        }
+    144    }
+    145    
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 99:0
+PAUSES AT: 151:8
+     96    }
+     97    
+     98    // Class
+ ->  99    #
+    100    class Base {
+    101        constructor()
+    102        {
+    103            this._base = true;
+    104        }
+    105    
+    106        baseMethod()
+    107        {
+    108            a();
+    109        }
+    110    
+    111        method()
+    112        {
+    113            a();
+    114        }
+    115    }
+    116    
+    117    class Child extends Base {
+    118        constructor()
+    119        {
+    120            super();
+    121            this._child = true;
+    122        }
+    123    
+    124        childMethod()
+    125        {
+    126            b();
+    127        }
+    128    
+    129        method()
+    130        {
+    131            super.method();
+    132            b();
+    133        }
+    134    
+    135        get name()
+    136        {
+    137            return this._name;
+    138        }
+    139        
+    140        set name(x)
+    141        {
+    142            this._name = x;
+    143        }
+    144    }
+    145    
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 100:0
+PAUSES AT: 151:8
+     97    
+     98    // Class
+     99    
+ -> 100    #class Base {
+    101        constructor()
+    102        {
+    103            this._base = true;
+    104        }
+    105    
+    106        baseMethod()
+    107        {
+    108            a();
+    109        }
+    110    
+    111        method()
+    112        {
+    113            a();
+    114        }
+    115    }
+    116    
+    117    class Child extends Base {
+    118        constructor()
+    119        {
+    120            super();
+    121            this._child = true;
+    122        }
+    123    
+    124        childMethod()
+    125        {
+    126            b();
+    127        }
+    128    
+    129        method()
+    130        {
+    131            super.method();
+    132            b();
+    133        }
+    134    
+    135        get name()
+    136        {
+    137            return this._name;
+    138        }
+    139        
+    140        set name(x)
+    141        {
+    142            this._name = x;
+    143        }
+    144    }
+    145    
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 101:0
+PAUSES AT: 103:8
+     98    // Class
+     99    
+    100    class Base {
+ -> 101    #    constructor()
+    102        {
+ => 103            |this._base = true;
+    104        }
+    105    
+    106        baseMethod()
+
+
+INSERTING AT: 102:0
+PAUSES AT: 103:8
+     99    
+    100    class Base {
+    101        constructor()
+ -> 102    #    {
+ => 103            |this._base = true;
+    104        }
+    105    
+    106        baseMethod()
+
+
+INSERTING AT: 103:0
+PAUSES AT: 103:8
+    100    class Base {
+    101        constructor()
+    102        {
+-=> 103    #        |this._base = true;
+    104        }
+    105    
+    106        baseMethod()
+
+
+INSERTING AT: 104:0
+PAUSES AT: 104:4
+    101        constructor()
+    102        {
+    103            this._base = true;
+-=> 104    #    |}
+    105    
+    106        baseMethod()
+    107        {
+
+
+INSERTING AT: 105:0
+PAUSES AT: 151:8
+    102        {
+    103            this._base = true;
+    104        }
+ -> 105    #
+    106        baseMethod()
+    107        {
+    108            a();
+    109        }
+    110    
+    111        method()
+    112        {
+    113            a();
+    114        }
+    115    }
+    116    
+    117    class Child extends Base {
+    118        constructor()
+    119        {
+    120            super();
+    121            this._child = true;
+    122        }
+    123    
+    124        childMethod()
+    125        {
+    126            b();
+    127        }
+    128    
+    129        method()
+    130        {
+    131            super.method();
+    132            b();
+    133        }
+    134    
+    135        get name()
+    136        {
+    137            return this._name;
+    138        }
+    139        
+    140        set name(x)
+    141        {
+    142            this._name = x;
+    143        }
+    144    }
+    145    
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 106:0
+PAUSES AT: 108:8
+    103            this._base = true;
+    104        }
+    105    
+ -> 106    #    baseMethod()
+    107        {
+ => 108            |a();
+    109        }
+    110    
+    111        method()
+
+
+INSERTING AT: 107:0
+PAUSES AT: 108:8
+    104        }
+    105    
+    106        baseMethod()
+ -> 107    #    {
+ => 108            |a();
+    109        }
+    110    
+    111        method()
+
+
+INSERTING AT: 108:0
+PAUSES AT: 108:8
+    105    
+    106        baseMethod()
+    107        {
+-=> 108    #        |a();
+    109        }
+    110    
+    111        method()
+
+
+INSERTING AT: 109:0
+PAUSES AT: 109:4
+    106        baseMethod()
+    107        {
+    108            a();
+-=> 109    #    |}
+    110    
+    111        method()
+    112        {
+
+
+INSERTING AT: 110:0
+PAUSES AT: 151:8
+    107        {
+    108            a();
+    109        }
+ -> 110    #
+    111        method()
+    112        {
+    113            a();
+    114        }
+    115    }
+    116    
+    117    class Child extends Base {
+    118        constructor()
+    119        {
+    120            super();
+    121            this._child = true;
+    122        }
+    123    
+    124        childMethod()
+    125        {
+    126            b();
+    127        }
+    128    
+    129        method()
+    130        {
+    131            super.method();
+    132            b();
+    133        }
+    134    
+    135        get name()
+    136        {
+    137            return this._name;
+    138        }
+    139        
+    140        set name(x)
+    141        {
+    142            this._name = x;
+    143        }
+    144    }
+    145    
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 111:0
+PAUSES AT: 113:8
+    108            a();
+    109        }
+    110    
+ -> 111    #    method()
+    112        {
+ => 113            |a();
+    114        }
+    115    }
+    116    
+
+
+INSERTING AT: 112:0
+PAUSES AT: 113:8
+    109        }
+    110    
+    111        method()
+ -> 112    #    {
+ => 113            |a();
+    114        }
+    115    }
+    116    
+
+
+INSERTING AT: 113:0
+PAUSES AT: 113:8
+    110    
+    111        method()
+    112        {
+-=> 113    #        |a();
+    114        }
+    115    }
+    116    
+
+
+INSERTING AT: 114:0
+PAUSES AT: 114:4
+    111        method()
+    112        {
+    113            a();
+-=> 114    #    |}
+    115    }
+    116    
+    117    class Child extends Base {
+
+
+INSERTING AT: 115:0
+PAUSES AT: 151:8
+    112        {
+    113            a();
+    114        }
+ -> 115    #}
+    116    
+    117    class Child extends Base {
+    118        constructor()
+    119        {
+    120            super();
+    121            this._child = true;
+    122        }
+    123    
+    124        childMethod()
+    125        {
+    126            b();
+    127        }
+    128    
+    129        method()
+    130        {
+    131            super.method();
+    132            b();
+    133        }
+    134    
+    135        get name()
+    136        {
+    137            return this._name;
+    138        }
+    139        
+    140        set name(x)
+    141        {
+    142            this._name = x;
+    143        }
+    144    }
+    145    
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 116:0
+PAUSES AT: 151:8
+    113            a();
+    114        }
+    115    }
+ -> 116    #
+    117    class Child extends Base {
+    118        constructor()
+    119        {
+    120            super();
+    121            this._child = true;
+    122        }
+    123    
+    124        childMethod()
+    125        {
+    126            b();
+    127        }
+    128    
+    129        method()
+    130        {
+    131            super.method();
+    132            b();
+    133        }
+    134    
+    135        get name()
+    136        {
+    137            return this._name;
+    138        }
+    139        
+    140        set name(x)
+    141        {
+    142            this._name = x;
+    143        }
+    144    }
+    145    
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 117:0
+PAUSES AT: 151:8
+    114        }
+    115    }
+    116    
+ -> 117    #class Child extends Base {
+    118        constructor()
+    119        {
+    120            super();
+    121            this._child = true;
+    122        }
+    123    
+    124        childMethod()
+    125        {
+    126            b();
+    127        }
+    128    
+    129        method()
+    130        {
+    131            super.method();
+    132            b();
+    133        }
+    134    
+    135        get name()
+    136        {
+    137            return this._name;
+    138        }
+    139        
+    140        set name(x)
+    141        {
+    142            this._name = x;
+    143        }
+    144    }
+    145    
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 118:0
+PAUSES AT: 120:8
+    115    }
+    116    
+    117    class Child extends Base {
+ -> 118    #    constructor()
+    119        {
+ => 120            |super();
+    121            this._child = true;
+    122        }
+    123    
+
+
+INSERTING AT: 119:0
+PAUSES AT: 120:8
+    116    
+    117    class Child extends Base {
+    118        constructor()
+ -> 119    #    {
+ => 120            |super();
+    121            this._child = true;
+    122        }
+    123    
+
+
+INSERTING AT: 120:0
+PAUSES AT: 120:8
+    117    class Child extends Base {
+    118        constructor()
+    119        {
+-=> 120    #        |super();
+    121            this._child = true;
+    122        }
+    123    
+
+
+INSERTING AT: 121:0
+PAUSES AT: 121:8
+    118        constructor()
+    119        {
+    120            super();
+-=> 121    #        |this._child = true;
+    122        }
+    123    
+    124        childMethod()
+
+
+INSERTING AT: 122:0
+PAUSES AT: 122:4
+    119        {
+    120            super();
+    121            this._child = true;
+-=> 122    #    |}
+    123    
+    124        childMethod()
+    125        {
+
+
+INSERTING AT: 123:0
+PAUSES AT: 151:8
+    120            super();
+    121            this._child = true;
+    122        }
+ -> 123    #
+    124        childMethod()
+    125        {
+    126            b();
+    127        }
+    128    
+    129        method()
+    130        {
+    131            super.method();
+    132            b();
+    133        }
+    134    
+    135        get name()
+    136        {
+    137            return this._name;
+    138        }
+    139        
+    140        set name(x)
+    141        {
+    142            this._name = x;
+    143        }
+    144    }
+    145    
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 124:0
+PAUSES AT: 126:8
+    121            this._child = true;
+    122        }
+    123    
+ -> 124    #    childMethod()
+    125        {
+ => 126            |b();
+    127        }
+    128    
+    129        method()
+
+
+INSERTING AT: 125:0
+PAUSES AT: 126:8
+    122        }
+    123    
+    124        childMethod()
+ -> 125    #    {
+ => 126            |b();
+    127        }
+    128    
+    129        method()
+
+
+INSERTING AT: 126:0
+PAUSES AT: 126:8
+    123    
+    124        childMethod()
+    125        {
+-=> 126    #        |b();
+    127        }
+    128    
+    129        method()
+
+
+INSERTING AT: 127:0
+PAUSES AT: 127:4
+    124        childMethod()
+    125        {
+    126            b();
+-=> 127    #    |}
+    128    
+    129        method()
+    130        {
+
+
+INSERTING AT: 128:0
+PAUSES AT: 151:8
+    125        {
+    126            b();
+    127        }
+ -> 128    #
+    129        method()
+    130        {
+    131            super.method();
+    132            b();
+    133        }
+    134    
+    135        get name()
+    136        {
+    137            return this._name;
+    138        }
+    139        
+    140        set name(x)
+    141        {
+    142            this._name = x;
+    143        }
+    144    }
+    145    
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 129:0
+PAUSES AT: 131:8
+    126            b();
+    127        }
+    128    
+ -> 129    #    method()
+    130        {
+ => 131            |super.method();
+    132            b();
+    133        }
+    134    
+
+
+INSERTING AT: 130:0
+PAUSES AT: 131:8
+    127        }
+    128    
+    129        method()
+ -> 130    #    {
+ => 131            |super.method();
+    132            b();
+    133        }
+    134    
+
+
+INSERTING AT: 131:0
+PAUSES AT: 131:8
+    128    
+    129        method()
+    130        {
+-=> 131    #        |super.method();
+    132            b();
+    133        }
+    134    
+
+
+INSERTING AT: 132:0
+PAUSES AT: 132:8
+    129        method()
+    130        {
+    131            super.method();
+-=> 132    #        |b();
+    133        }
+    134    
+    135        get name()
+
+
+INSERTING AT: 133:0
+PAUSES AT: 133:4
+    130        {
+    131            super.method();
+    132            b();
+-=> 133    #    |}
+    134    
+    135        get name()
+    136        {
+
+
+INSERTING AT: 134:0
+PAUSES AT: 151:8
+    131            super.method();
+    132            b();
+    133        }
+ -> 134    #
+    135        get name()
+    136        {
+    137            return this._name;
+    138        }
+    139        
+    140        set name(x)
+    141        {
+    142            this._name = x;
+    143        }
+    144    }
+    145    
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 135:0
+PAUSES AT: 137:8
+    132            b();
+    133        }
+    134    
+ -> 135    #    get name()
+    136        {
+ => 137            |return this._name;
+    138        }
+    139        
+    140        set name(x)
+
+
+INSERTING AT: 136:0
+PAUSES AT: 137:8
+    133        }
+    134    
+    135        get name()
+ -> 136    #    {
+ => 137            |return this._name;
+    138        }
+    139        
+    140        set name(x)
+
+
+INSERTING AT: 137:0
+PAUSES AT: 137:8
+    134    
+    135        get name()
+    136        {
+-=> 137    #        |return this._name;
+    138        }
+    139        
+    140        set name(x)
+
+
+INSERTING AT: 138:0
+PAUSES AT: 138:4
+    135        get name()
+    136        {
+    137            return this._name;
+-=> 138    #    |}
+    139        
+    140        set name(x)
+    141        {
+
+
+INSERTING AT: 139:0
+PAUSES AT: 151:8
+    136        {
+    137            return this._name;
+    138        }
+ -> 139    #    
+    140        set name(x)
+    141        {
+    142            this._name = x;
+    143        }
+    144    }
+    145    
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 140:0
+PAUSES AT: 142:8
+    137            return this._name;
+    138        }
+    139        
+ -> 140    #    set name(x)
+    141        {
+ => 142            |this._name = x;
+    143        }
+    144    }
+    145    
+
+
+INSERTING AT: 141:0
+PAUSES AT: 142:8
+    138        }
+    139        
+    140        set name(x)
+ -> 141    #    {
+ => 142            |this._name = x;
+    143        }
+    144    }
+    145    
+
+
+INSERTING AT: 142:0
+PAUSES AT: 142:8
+    139        
+    140        set name(x)
+    141        {
+-=> 142    #        |this._name = x;
+    143        }
+    144    }
+    145    
+
+
+INSERTING AT: 143:0
+PAUSES AT: 143:4
+    140        set name(x)
+    141        {
+    142            this._name = x;
+-=> 143    #    |}
+    144    }
+    145    
+    146    // ---------
+
+
+INSERTING AT: 144:0
+PAUSES AT: 151:8
+    141        {
+    142            this._name = x;
+    143        }
+ -> 144    #}
+    145    
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 145:0
+PAUSES AT: 151:8
+    142            this._name = x;
+    143        }
+    144    }
+ -> 145    #
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 146:0
+PAUSES AT: 151:8
+    143        }
+    144    }
+    145    
+ -> 146    #// ---------
+    147    /* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 147:0
+PAUSES AT: 151:8
+    144    }
+    145    
+    146    // ---------
+ -> 147    #/* Misc */
+    148    // ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 148:0
+PAUSES AT: 151:8
+    145    
+    146    // ---------
+    147    /* Misc */
+ -> 148    #// ---------
+    149    
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 149:0
+PAUSES AT: 151:8
+    146    // ---------
+    147    /* Misc */
+    148    // ---------
+ -> 149    #
+    150        {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 150:0
+PAUSES AT: 151:8
+    147    /* Misc */
+    148    // ---------
+    149    
+ -> 150    #    {
+ => 151            |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 151:0
+PAUSES AT: 151:8
+    148    // ---------
+    149    
+    150        {
+-=> 151    #        |a();
+    152        }
+    153    
+    154    label:
+
+
+INSERTING AT: 152:0
+PAUSES AT: 156:8
+    149    
+    150        {
+    151            a();
+ -> 152    #    }
+    153    
+    154    label:
+    155        {
+ => 156            |a();
+    157        }
+    158    
+    159    var w1 = {x:1, y:2};
+
+
+INSERTING AT: 153:0
+PAUSES AT: 156:8
+    150        {
+    151            a();
+    152        }
+ -> 153    #
+    154    label:
+    155        {
+ => 156            |a();
+    157        }
+    158    
+    159    var w1 = {x:1, y:2};
+
+
+INSERTING AT: 154:0
+PAUSES AT: 156:8
+    151            a();
+    152        }
+    153    
+ -> 154    #label:
+    155        {
+ => 156            |a();
+    157        }
+    158    
+    159    var w1 = {x:1, y:2};
+
+
+INSERTING AT: 155:0
+PAUSES AT: 156:8
+    152        }
+    153    
+    154    label:
+ -> 155    #    {
+ => 156            |a();
+    157        }
+    158    
+    159    var w1 = {x:1, y:2};
+
+
+INSERTING AT: 156:0
+PAUSES AT: 156:8
+    153    
+    154    label:
+    155        {
+-=> 156    #        |a();
+    157        }
+    158    
+    159    var w1 = {x:1, y:2};
+
+
+INSERTING AT: 157:0
+PAUSES AT: 159:0
+    154    label:
+    155        {
+    156            a();
+ -> 157    #    }
+    158    
+ => 159    |var w1 = {x:1, y:2};
+    160    with (w1) {
+    161        a();
+    162    }
+
+
+INSERTING AT: 158:0
+PAUSES AT: 159:0
+    155        {
+    156            a();
+    157        }
+ -> 158    #
+ => 159    |var w1 = {x:1, y:2};
+    160    with (w1) {
+    161        a();
+    162    }
+
+
+INSERTING AT: 159:0
+PAUSES AT: 159:0
+    156            a();
+    157        }
+    158    
+-=> 159    |var w1 = {x:1, y:2};
+    160    with (w1) {
+    161        a();
+    162    }
+
+
+INSERTING AT: 160:0
+PAUSES AT: 160:6
+    157        }
+    158    
+    159    var w1 = {x:1, y:2};
+-=> 160    #with (|w1) {
+    161        a();
+    162    }
+    163    
+
+
+INSERTING AT: 161:0
+PAUSES AT: 161:4
+    158    
+    159    var w1 = {x:1, y:2};
+    160    with (w1) {
+-=> 161    #    |a();
+    162    }
+    163    
+    164    var v1 = 1,
+
+
+INSERTING AT: 162:0
+PAUSES AT: 164:0
+    159    var w1 = {x:1, y:2};
+    160    with (w1) {
+    161        a();
+ -> 162    #}
+    163    
+ => 164    |var v1 = 1,
+    165        v2 = 1;
+    166    let l1 = 2,
+    167        l2 = 2;
+
+
+INSERTING AT: 163:0
+PAUSES AT: 164:0
+    160    with (w1) {
+    161        a();
+    162    }
+ -> 163    #
+ => 164    |var v1 = 1,
+    165        v2 = 1;
+    166    let l1 = 2,
+    167        l2 = 2;
+
+
+INSERTING AT: 164:0
+PAUSES AT: 164:0
+    161        a();
+    162    }
+    163    
+-=> 164    |var v1 = 1,
+    165        v2 = 1;
+    166    let l1 = 2,
+    167        l2 = 2;
+
+
+INSERTING AT: 165:0
+PAUSES AT: 166:0
+    162    }
+    163    
+    164    var v1 = 1,
+ -> 165    #    v2 = 1;
+ => 166    |let l1 = 2,
+    167        l2 = 2;
+    168    const c1 = 3,
+    169        c2 = 3;
+
+
+INSERTING AT: 166:0
+PAUSES AT: 166:0
+    163    
+    164    var v1 = 1,
+    165        v2 = 1;
+-=> 166    |let l1 = 2,
+    167        l2 = 2;
+    168    const c1 = 3,
+    169        c2 = 3;
+
+
+INSERTING AT: 167:0
+PAUSES AT: 168:0
+    164    var v1 = 1,
+    165        v2 = 1;
+    166    let l1 = 2,
+ -> 167    #    l2 = 2;
+ => 168    |const c1 = 3,
+    169        c2 = 3;
+    170    
+    171    v1 = v2 = v1;
+
+
+INSERTING AT: 168:0
+PAUSES AT: 168:0
+    165        v2 = 1;
+    166    let l1 = 2,
+    167        l2 = 2;
+-=> 168    |const c1 = 3,
+    169        c2 = 3;
+    170    
+    171    v1 = v2 = v1;
+
+
+INSERTING AT: 169:0
+PAUSES AT: 171:0
+    166    let l1 = 2,
+    167        l2 = 2;
+    168    const c1 = 3,
+ -> 169    #    c2 = 3;
+    170    
+ => 171    |v1 = v2 = v1;
+    172    
+    173    var {x, y} = obj;
+    174    var [w, z] = arr;
+
+
+INSERTING AT: 170:0
+PAUSES AT: 171:0
+    167        l2 = 2;
+    168    const c1 = 3,
+    169        c2 = 3;
+ -> 170    #
+ => 171    |v1 = v2 = v1;
+    172    
+    173    var {x, y} = obj;
+    174    var [w, z] = arr;
+
+
+INSERTING AT: 171:0
+PAUSES AT: 171:0
+    168    const c1 = 3,
+    169        c2 = 3;
+    170    
+-=> 171    |v1 = v2 = v1;
+    172    
+    173    var {x, y} = obj;
+    174    var [w, z] = arr;
+
+
+INSERTING AT: 172:0
+PAUSES AT: 173:0
+    169        c2 = 3;
+    170    
+    171    v1 = v2 = v1;
+ -> 172    #
+ => 173    |var {x, y} = obj;
+    174    var [w, z] = arr;
+    175    
+    176    var o1 = {
+
+
+INSERTING AT: 173:0
+PAUSES AT: 173:0
+    170    
+    171    v1 = v2 = v1;
+    172    
+-=> 173    |var {x, y} = obj;
+    174    var [w, z] = arr;
+    175    
+    176    var o1 = {
+
+
+INSERTING AT: 174:0
+PAUSES AT: 174:0
+    171    v1 = v2 = v1;
+    172    
+    173    var {x, y} = obj;
+-=> 174    |var [w, z] = arr;
+    175    
+    176    var o1 = {
+    177        p1: 1,
+
+
+INSERTING AT: 175:0
+PAUSES AT: 176:0
+    172    
+    173    var {x, y} = obj;
+    174    var [w, z] = arr;
+ -> 175    #
+ => 176    |var o1 = {
+    177        p1: 1,
+    178        p2: a(),
+    179        p3: 1,
+
+
+INSERTING AT: 176:0
+PAUSES AT: 176:0
+    173    var {x, y} = obj;
+    174    var [w, z] = arr;
+    175    
+-=> 176    |var o1 = {
+    177        p1: 1,
+    178        p2: a(),
+    179        p3: 1,
+
+
+INSERTING AT: 177:0
+PAUSES AT: 184:0
+    174    var [w, z] = arr;
+    175    
+    176    var o1 = {
+ -> 177    #    p1: 1,
+    178        p2: a(),
+    179        p3: 1,
+    180        ["p4"]: 1,
+    181        [b()]: 1,
+    182    };
+    183    
+ => 184    |var a1 = [
+    185        1,
+    186        a(),
+    187        1,
+
+
+INSERTING AT: 178:0
+PAUSES AT: 184:0
+    175    
+    176    var o1 = {
+    177        p1: 1,
+ -> 178    #    p2: a(),
+    179        p3: 1,
+    180        ["p4"]: 1,
+    181        [b()]: 1,
+    182    };
+    183    
+ => 184    |var a1 = [
+    185        1,
+    186        a(),
+    187        1,
+
+
+INSERTING AT: 179:0
+PAUSES AT: 184:0
+    176    var o1 = {
+    177        p1: 1,
+    178        p2: a(),
+ -> 179    #    p3: 1,
+    180        ["p4"]: 1,
+    181        [b()]: 1,
+    182    };
+    183    
+ => 184    |var a1 = [
+    185        1,
+    186        a(),
+    187        1,
+
+
+INSERTING AT: 180:0
+PAUSES AT: 184:0
+    177        p1: 1,
+    178        p2: a(),
+    179        p3: 1,
+ -> 180    #    ["p4"]: 1,
+    181        [b()]: 1,
+    182    };
+    183    
+ => 184    |var a1 = [
+    185        1,
+    186        a(),
+    187        1,
+
+
+INSERTING AT: 181:0
+PAUSES AT: 184:0
+    178        p2: a(),
+    179        p3: 1,
+    180        ["p4"]: 1,
+ -> 181    #    [b()]: 1,
+    182    };
+    183    
+ => 184    |var a1 = [
+    185        1,
+    186        a(),
+    187        1,
+
+
+INSERTING AT: 182:0
+PAUSES AT: 184:0
+    179        p3: 1,
+    180        ["p4"]: 1,
+    181        [b()]: 1,
+ -> 182    #};
+    183    
+ => 184    |var a1 = [
+    185        1,
+    186        a(),
+    187        1,
+
+
+INSERTING AT: 183:0
+PAUSES AT: 184:0
+    180        ["p4"]: 1,
+    181        [b()]: 1,
+    182    };
+ -> 183    #
+ => 184    |var a1 = [
+    185        1,
+    186        a(),
+    187        1,
+
+
+INSERTING AT: 184:0
+PAUSES AT: 184:0
+    181        [b()]: 1,
+    182    };
+    183    
+-=> 184    |var a1 = [
+    185        1,
+    186        a(),
+    187        1,
+
+
+INSERTING AT: 185:0
+PAUSES AT: 191:0
+    182    };
+    183    
+    184    var a1 = [
+ -> 185    #    1,
+    186        a(),
+    187        1,
+    188        b(),
+    189    ];
+    190    
+ => 191    |var i1 = new Base;
+    192    var i2 = new Child;
+    193    i2.name;
+    194    i2.name = 1;
+
+
+INSERTING AT: 186:0
+PAUSES AT: 191:0
+    183    
+    184    var a1 = [
+    185        1,
+ -> 186    #    a(),
+    187        1,
+    188        b(),
+    189    ];
+    190    
+ => 191    |var i1 = new Base;
+    192    var i2 = new Child;
+    193    i2.name;
+    194    i2.name = 1;
+
+
+INSERTING AT: 187:0
+PAUSES AT: 191:0
+    184    var a1 = [
+    185        1,
+    186        a(),
+ -> 187    #    1,
+    188        b(),
+    189    ];
+    190    
+ => 191    |var i1 = new Base;
+    192    var i2 = new Child;
+    193    i2.name;
+    194    i2.name = 1;
+
+
+INSERTING AT: 188:0
+PAUSES AT: 191:0
+    185        1,
+    186        a(),
+    187        1,
+ -> 188    #    b(),
+    189    ];
+    190    
+ => 191    |var i1 = new Base;
+    192    var i2 = new Child;
+    193    i2.name;
+    194    i2.name = 1;
+
+
+INSERTING AT: 189:0
+PAUSES AT: 191:0
+    186        a(),
+    187        1,
+    188        b(),
+ -> 189    #];
+    190    
+ => 191    |var i1 = new Base;
+    192    var i2 = new Child;
+    193    i2.name;
+    194    i2.name = 1;
+
+
+INSERTING AT: 190:0
+PAUSES AT: 191:0
+    187        1,
+    188        b(),
+    189    ];
+ -> 190    #
+ => 191    |var i1 = new Base;
+    192    var i2 = new Child;
+    193    i2.name;
+    194    i2.name = 1;
+
+
+INSERTING AT: 191:0
+PAUSES AT: 191:0
+    188        b(),
+    189    ];
+    190    
+-=> 191    |var i1 = new Base;
+    192    var i2 = new Child;
+    193    i2.name;
+    194    i2.name = 1;
+
+
+INSERTING AT: 192:0
+PAUSES AT: 192:0
+    189    ];
+    190    
+    191    var i1 = new Base;
+-=> 192    |var i2 = new Child;
+    193    i2.name;
+    194    i2.name = 1;
+    195    i2.method();
+
+
+INSERTING AT: 193:0
+PAUSES AT: 193:0
+    190    
+    191    var i1 = new Base;
+    192    var i2 = new Child;
+-=> 193    |i2.name;
+    194    i2.name = 1;
+    195    i2.method();
+    196    
+
+
+INSERTING AT: 194:0
+PAUSES AT: 194:0
+    191    var i1 = new Base;
+    192    var i2 = new Child;
+    193    i2.name;
+-=> 194    |i2.name = 1;
+    195    i2.method();
+    196    
+    197    var t1 = `${1} ${x=1} ${a()}`;
+
+
+INSERTING AT: 195:0
+PAUSES AT: 195:0
+    192    var i2 = new Child;
+    193    i2.name;
+    194    i2.name = 1;
+-=> 195    |i2.method();
+    196    
+    197    var t1 = `${1} ${x=1} ${a()}`;
+    198    var t2 = a`${1} ${x=1} ${a()}`;
+
+
+INSERTING AT: 196:0
+PAUSES AT: 197:0
+    193    i2.name;
+    194    i2.name = 1;
+    195    i2.method();
+ -> 196    #
+ => 197    |var t1 = `${1} ${x=1} ${a()}`;
+    198    var t2 = a`${1} ${x=1} ${a()}`;
+    199    
+    200    a(a(), b());
+
+
+INSERTING AT: 197:0
+PAUSES AT: 197:0
+    194    i2.name = 1;
+    195    i2.method();
+    196    
+-=> 197    |var t1 = `${1} ${x=1} ${a()}`;
+    198    var t2 = a`${1} ${x=1} ${a()}`;
+    199    
+    200    a(a(), b());
+
+
+INSERTING AT: 198:0
+PAUSES AT: 198:0
+    195    i2.method();
+    196    
+    197    var t1 = `${1} ${x=1} ${a()}`;
+-=> 198    |var t2 = a`${1} ${x=1} ${a()}`;
+    199    
+    200    a(a(), b());
+    201    
+
+
+INSERTING AT: 199:0
+PAUSES AT: 200:0
+    196    
+    197    var t1 = `${1} ${x=1} ${a()}`;
+    198    var t2 = a`${1} ${x=1} ${a()}`;
+ -> 199    #
+ => 200    |a(a(), b());
+    201    
+
+
+INSERTING AT: 200:0
+PAUSES AT: 200:0
+    197    var t1 = `${1} ${x=1} ${a()}`;
+    198    var t2 = a`${1} ${x=1} ${a()}`;
+    199    
+-=> 200    |a(a(), b());
+    201    
+
+
+INSERTING AT: 201:0
+PRODUCES: Could not resolve breakpoint
+
+-- Running test case: Debugger.resolvedBreakpoint.dumpEachLine.Functions
+
+INSERTING AT: 0:0
+PAUSES AT: 0:19
+-=>   0    #function inline() {|}
+      1    function named() {
+      2        var x;
+      3    }
+
+
+INSERTING AT: 1:0
+PAUSES AT: 2:4
+      0    function inline() {}
+ ->   1    #function named() {
+ =>   2        |var x;
+      3    }
+      4    function outer1() {
+      5        function inner() {
+
+
+INSERTING AT: 2:0
+PAUSES AT: 2:4
+      0    function inline() {}
+      1    function named() {
+-=>   2    #    |var x;
+      3    }
+      4    function outer1() {
+      5        function inner() {
+
+
+INSERTING AT: 3:0
+PAUSES AT: 3:0
+      0    function inline() {}
+      1    function named() {
+      2        var x;
+-=>   3    |}
+      4    function outer1() {
+      5        function inner() {
+      6            var y;
+
+
+INSERTING AT: 4:0
+PAUSES AT: 8:0
+      1    function named() {
+      2        var x;
+      3    }
+ ->   4    #function outer1() {
+      5        function inner() {
+      6            var y;
+      7        }
+ =>   8    |}
+      9    function outer2()
+     10    {
+     11        function inner()
+
+
+INSERTING AT: 5:0
+PAUSES AT: 6:8
+      2        var x;
+      3    }
+      4    function outer1() {
+ ->   5    #    function inner() {
+ =>   6            |var y;
+      7        }
+      8    }
+      9    function outer2()
+
+
+INSERTING AT: 6:0
+PAUSES AT: 6:8
+      3    }
+      4    function outer1() {
+      5        function inner() {
+-=>   6    #        |var y;
+      7        }
+      8    }
+      9    function outer2()
+
+
+INSERTING AT: 7:0
+PAUSES AT: 7:4
+      4    function outer1() {
+      5        function inner() {
+      6            var y;
+-=>   7    #    |}
+      8    }
+      9    function outer2()
+     10    {
+
+
+INSERTING AT: 8:0
+PAUSES AT: 8:0
+      5        function inner() {
+      6            var y;
+      7        }
+-=>   8    |}
+      9    function outer2()
+     10    {
+     11        function inner()
+
+
+INSERTING AT: 9:0
+PAUSES AT: 15:0
+      6            var y;
+      7        }
+      8    }
+ ->   9    #function outer2()
+     10    {
+     11        function inner()
+     12        {
+     13            var y;
+     14        }
+ =>  15    |}
+     16    function outer3() {
+     17        var x;
+     18        function inner() { var y; }
+
+
+INSERTING AT: 10:0
+PAUSES AT: 15:0
+      7        }
+      8    }
+      9    function outer2()
+ ->  10    #{
+     11        function inner()
+     12        {
+     13            var y;
+     14        }
+ =>  15    |}
+     16    function outer3() {
+     17        var x;
+     18        function inner() { var y; }
+
+
+INSERTING AT: 11:0
+PAUSES AT: 13:8
+      8    }
+      9    function outer2()
+     10    {
+ ->  11    #    function inner()
+     12        {
+ =>  13            |var y;
+     14        }
+     15    }
+     16    function outer3() {
+
+
+INSERTING AT: 12:0
+PAUSES AT: 13:8
+      9    function outer2()
+     10    {
+     11        function inner()
+ ->  12    #    {
+ =>  13            |var y;
+     14        }
+     15    }
+     16    function outer3() {
+
+
+INSERTING AT: 13:0
+PAUSES AT: 13:8
+     10    {
+     11        function inner()
+     12        {
+-=>  13    #        |var y;
+     14        }
+     15    }
+     16    function outer3() {
+
+
+INSERTING AT: 14:0
+PAUSES AT: 14:4
+     11        function inner()
+     12        {
+     13            var y;
+-=>  14    #    |}
+     15    }
+     16    function outer3() {
+     17        var x;
+
+
+INSERTING AT: 15:0
+PAUSES AT: 15:0
+     12        {
+     13            var y;
+     14        }
+-=>  15    |}
+     16    function outer3() {
+     17        var x;
+     18        function inner() { var y; }
+
+
+INSERTING AT: 16:0
+PAUSES AT: 17:4
+     13            var y;
+     14        }
+     15    }
+ ->  16    #function outer3() {
+ =>  17        |var x;
+     18        function inner() { var y; }
+     19    }
+     20    function outer4() {
+
+
+INSERTING AT: 17:0
+PAUSES AT: 17:4
+     14        }
+     15    }
+     16    function outer3() {
+-=>  17    #    |var x;
+     18        function inner() { var y; }
+     19    }
+     20    function outer4() {
+
+
+INSERTING AT: 18:0
+PAUSES AT: 18:23
+     15    }
+     16    function outer3() {
+     17        var x;
+-=>  18    #    function inner() { |var y; }
+     19    }
+     20    function outer4() {
+     21        function inner() { var y; }
+
+
+INSERTING AT: 19:0
+PAUSES AT: 19:0
+     16    function outer3() {
+     17        var x;
+     18        function inner() { var y; }
+-=>  19    |}
+     20    function outer4() {
+     21        function inner() { var y; }
+     22        var x;
+
+
+INSERTING AT: 20:0
+PAUSES AT: 22:4
+     17        var x;
+     18        function inner() { var y; }
+     19    }
+ ->  20    #function outer4() {
+     21        function inner() { var y; }
+ =>  22        |var x;
+     23    }
+     24    function outer5() {
+     25        var x;
+
+
+INSERTING AT: 21:0
+PAUSES AT: 21:23
+     18        function inner() { var y; }
+     19    }
+     20    function outer4() {
+-=>  21    #    function inner() { |var y; }
+     22        var x;
+     23    }
+     24    function outer5() {
+
+
+INSERTING AT: 22:0
+PAUSES AT: 22:4
+     19    }
+     20    function outer4() {
+     21        function inner() { var y; }
+-=>  22    #    |var x;
+     23    }
+     24    function outer5() {
+     25        var x;
+
+
+INSERTING AT: 23:0
+PAUSES AT: 23:0
+     20    function outer4() {
+     21        function inner() { var y; }
+     22        var x;
+-=>  23    |}
+     24    function outer5() {
+     25        var x;
+     26        function inner() { var y; }
+
+
+INSERTING AT: 24:0
+PAUSES AT: 25:4
+     21        function inner() { var y; }
+     22        var x;
+     23    }
+ ->  24    #function outer5() {
+ =>  25        |var x;
+     26        function inner() { var y; }
+     27        var x;
+     28    }
+
+
+INSERTING AT: 25:0
+PAUSES AT: 25:4
+     22        var x;
+     23    }
+     24    function outer5() {
+-=>  25    #    |var x;
+     26        function inner() { var y; }
+     27        var x;
+     28    }
+
+
+INSERTING AT: 26:0
+PAUSES AT: 26:23
+     23    }
+     24    function outer5() {
+     25        var x;
+-=>  26    #    function inner() { |var y; }
+     27        var x;
+     28    }
+     29    function outer6() {
+
+
+INSERTING AT: 27:0
+PAUSES AT: 27:4
+     24    function outer5() {
+     25        var x;
+     26        function inner() { var y; }
+-=>  27    #    |var x;
+     28    }
+     29    function outer6() {
+     30        function inner1() { var y; }
+
+
+INSERTING AT: 28:0
+PAUSES AT: 28:0
+     25        var x;
+     26        function inner() { var y; }
+     27        var x;
+-=>  28    |}
+     29    function outer6() {
+     30        function inner1() { var y; }
+     31        var x;
+
+
+INSERTING AT: 29:0
+PAUSES AT: 31:4
+     26        function inner() { var y; }
+     27        var x;
+     28    }
+ ->  29    #function outer6() {
+     30        function inner1() { var y; }
+ =>  31        |var x;
+     32        function inner2() { var z; }
+     33    }
+     34    function outer7() {
+
+
+INSERTING AT: 30:0
+PAUSES AT: 30:24
+     27        var x;
+     28    }
+     29    function outer6() {
+-=>  30    #    function inner1() { |var y; }
+     31        var x;
+     32        function inner2() { var z; }
+     33    }
+
+
+INSERTING AT: 31:0
+PAUSES AT: 31:4
+     28    }
+     29    function outer6() {
+     30        function inner1() { var y; }
+-=>  31    #    |var x;
+     32        function inner2() { var z; }
+     33    }
+     34    function outer7() {
+
+
+INSERTING AT: 32:0
+PAUSES AT: 32:24
+     29    function outer6() {
+     30        function inner1() { var y; }
+     31        var x;
+-=>  32    #    function inner2() { |var z; }
+     33    }
+     34    function outer7() {
+     35        function inner1() { var y; }
+
+
+INSERTING AT: 33:0
+PAUSES AT: 33:0
+     30        function inner1() { var y; }
+     31        var x;
+     32        function inner2() { var z; }
+-=>  33    |}
+     34    function outer7() {
+     35        function inner1() { var y; }
+     36        function inner2() { var z; }
+
+
+INSERTING AT: 34:0
+PAUSES AT: 37:4
+     31        var x;
+     32        function inner2() { var z; }
+     33    }
+ ->  34    #function outer7() {
+     35        function inner1() { var y; }
+     36        function inner2() { var z; }
+ =>  37        |var x;
+     38    }
+     39    function outer7() {
+     40        function inner1() { var y; }
+
+
+INSERTING AT: 35:0
+PAUSES AT: 35:24
+     32        function inner2() { var z; }
+     33    }
+     34    function outer7() {
+-=>  35    #    function inner1() { |var y; }
+     36        function inner2() { var z; }
+     37        var x;
+     38    }
+
+
+INSERTING AT: 36:0
+PAUSES AT: 36:24
+     33    }
+     34    function outer7() {
+     35        function inner1() { var y; }
+-=>  36    #    function inner2() { |var z; }
+     37        var x;
+     38    }
+     39    function outer7() {
+
+
+INSERTING AT: 37:0
+PAUSES AT: 37:4
+     34    function outer7() {
+     35        function inner1() { var y; }
+     36        function inner2() { var z; }
+-=>  37    #    |var x;
+     38    }
+     39    function outer7() {
+     40        function inner1() { var y; }
+
+
+INSERTING AT: 38:0
+PAUSES AT: 38:0
+     35        function inner1() { var y; }
+     36        function inner2() { var z; }
+     37        var x;
+-=>  38    |}
+     39    function outer7() {
+     40        function inner1() { var y; }
+     41        function inner2() { var z; }
+
+
+INSERTING AT: 39:0
+PAUSES AT: 42:0
+     36        function inner2() { var z; }
+     37        var x;
+     38    }
+ ->  39    #function outer7() {
+     40        function inner1() { var y; }
+     41        function inner2() { var z; }
+ =>  42    |}
+     43    
+     44    (function() {
+     45        var x;
+
+
+INSERTING AT: 40:0
+PAUSES AT: 40:24
+     37        var x;
+     38    }
+     39    function outer7() {
+-=>  40    #    function inner1() { |var y; }
+     41        function inner2() { var z; }
+     42    }
+     43    
+
+
+INSERTING AT: 41:0
+PAUSES AT: 41:24
+     38    }
+     39    function outer7() {
+     40        function inner1() { var y; }
+-=>  41    #    function inner2() { |var z; }
+     42    }
+     43    
+     44    (function() {
+
+
+INSERTING AT: 42:0
+PAUSES AT: 42:0
+     39    function outer7() {
+     40        function inner1() { var y; }
+     41        function inner2() { var z; }
+-=>  42    |}
+     43    
+     44    (function() {
+     45        var x;
+
+
+INSERTING AT: 43:0
+PAUSES AT: 44:0
+     40        function inner1() { var y; }
+     41        function inner2() { var z; }
+     42    }
+ ->  43    #
+ =>  44    |(function() {
+     45        var x;
+     46    })();
+     47    
+
+
+INSERTING AT: 44:0
+PAUSES AT: 44:0
+     41        function inner2() { var z; }
+     42    }
+     43    
+-=>  44    |(function() {
+     45        var x;
+     46    })();
+     47    
+
+
+INSERTING AT: 45:0
+PAUSES AT: 45:4
+     42    }
+     43    
+     44    (function() {
+-=>  45    #    |var x;
+     46    })();
+     47    
+     48    (() => {
+
+
+INSERTING AT: 46:0
+PAUSES AT: 46:0
+     43    
+     44    (function() {
+     45        var x;
+-=>  46    |})();
+     47    
+     48    (() => {
+     49        var x;
+
+
+INSERTING AT: 47:0
+PAUSES AT: 48:0
+     44    (function() {
+     45        var x;
+     46    })();
+ ->  47    #
+ =>  48    |(() => {
+     49        var x;
+     50    });
+     51    
+
+
+INSERTING AT: 48:0
+PAUSES AT: 48:0
+     45        var x;
+     46    })();
+     47    
+-=>  48    |(() => {
+     49        var x;
+     50    });
+     51    
+
+
+INSERTING AT: 49:0
+PAUSES AT: 49:4
+     46    })();
+     47    
+     48    (() => {
+-=>  49    #    |var x;
+     50    });
+     51    
+     52    function* generator() {
+
+
+INSERTING AT: 50:0
+PAUSES AT: 50:0
+     47    
+     48    (() => {
+     49        var x;
+-=>  50    |});
+     51    
+     52    function* generator() {
+     53        var x;
+
+
+INSERTING AT: 51:0
+PAUSES AT: 87:0
+     48    (() => {
+     49        var x;
+     50    });
+ ->  51    #
+     52    function* generator() {
+     53        var x;
+     54    }
+     55    
+     56    class Class {
+     57        static staticMethod1() {
+     58            var x;
+     59        }
+     60        static staticMethod2()
+     61        {
+     62            var x;
+     63        }
+     64        method1() {
+     65            var x;
+     66        }
+     67        method2()
+     68        {
+     69            var x;
+     70        }
+     71        get getter1() {
+     72            var x;
+     73        }
+     74        get getter2()
+     75        {
+     76            var x;
+     77        }
+     78        set setter1(x) {
+     79            var s;
+     80        }
+     81        set setter2(x)
+     82        {
+     83            var s;
+     84        }
+     85    }
+     86    
+ =>  87    |x => x;
+     88    () => 1;
+     89    (x) => x;
+     90    (x) => { x };
+
+
+INSERTING AT: 52:0
+PAUSES AT: 53:4
+     49        var x;
+     50    });
+     51    
+ ->  52    #function* generator() {
+ =>  53        |var x;
+     54    }
+     55    
+     56    class Class {
+
+
+INSERTING AT: 53:0
+PAUSES AT: 53:4
+     50    });
+     51    
+     52    function* generator() {
+-=>  53    #    |var x;
+     54    }
+     55    
+     56    class Class {
+
+
+INSERTING AT: 54:0
+PAUSES AT: 54:0
+     51    
+     52    function* generator() {
+     53        var x;
+-=>  54    |}
+     55    
+     56    class Class {
+     57        static staticMethod1() {
+
+
+INSERTING AT: 55:0
+PAUSES AT: 87:0
+     52    function* generator() {
+     53        var x;
+     54    }
+ ->  55    #
+     56    class Class {
+     57        static staticMethod1() {
+     58            var x;
+     59        }
+     60        static staticMethod2()
+     61        {
+     62            var x;
+     63        }
+     64        method1() {
+     65            var x;
+     66        }
+     67        method2()
+     68        {
+     69            var x;
+     70        }
+     71        get getter1() {
+     72            var x;
+     73        }
+     74        get getter2()
+     75        {
+     76            var x;
+     77        }
+     78        set setter1(x) {
+     79            var s;
+     80        }
+     81        set setter2(x)
+     82        {
+     83            var s;
+     84        }
+     85    }
+     86    
+ =>  87    |x => x;
+     88    () => 1;
+     89    (x) => x;
+     90    (x) => { x };
+
+
+INSERTING AT: 56:0
+PAUSES AT: 87:0
+     53        var x;
+     54    }
+     55    
+ ->  56    #class Class {
+     57        static staticMethod1() {
+     58            var x;
+     59        }
+     60        static staticMethod2()
+     61        {
+     62            var x;
+     63        }
+     64        method1() {
+     65            var x;
+     66        }
+     67        method2()
+     68        {
+     69            var x;
+     70        }
+     71        get getter1() {
+     72            var x;
+     73        }
+     74        get getter2()
+     75        {
+     76            var x;
+     77        }
+     78        set setter1(x) {
+     79            var s;
+     80        }
+     81        set setter2(x)
+     82        {
+     83            var s;
+     84        }
+     85    }
+     86    
+ =>  87    |x => x;
+     88    () => 1;
+     89    (x) => x;
+     90    (x) => { x };
+
+
+INSERTING AT: 57:0
+PAUSES AT: 58:8
+     54    }
+     55    
+     56    class Class {
+ ->  57    #    static staticMethod1() {
+ =>  58            |var x;
+     59        }
+     60        static staticMethod2()
+     61        {
+
+
+INSERTING AT: 58:0
+PAUSES AT: 58:8
+     55    
+     56    class Class {
+     57        static staticMethod1() {
+-=>  58    #        |var x;
+     59        }
+     60        static staticMethod2()
+     61        {
+
+
+INSERTING AT: 59:0
+PAUSES AT: 59:4
+     56    class Class {
+     57        static staticMethod1() {
+     58            var x;
+-=>  59    #    |}
+     60        static staticMethod2()
+     61        {
+     62            var x;
+
+
+INSERTING AT: 60:0
+PAUSES AT: 62:8
+     57        static staticMethod1() {
+     58            var x;
+     59        }
+ ->  60    #    static staticMethod2()
+     61        {
+ =>  62            |var x;
+     63        }
+     64        method1() {
+     65            var x;
+
+
+INSERTING AT: 61:0
+PAUSES AT: 62:8
+     58            var x;
+     59        }
+     60        static staticMethod2()
+ ->  61    #    {
+ =>  62            |var x;
+     63        }
+     64        method1() {
+     65            var x;
+
+
+INSERTING AT: 62:0
+PAUSES AT: 62:8
+     59        }
+     60        static staticMethod2()
+     61        {
+-=>  62    #        |var x;
+     63        }
+     64        method1() {
+     65            var x;
+
+
+INSERTING AT: 63:0
+PAUSES AT: 63:4
+     60        static staticMethod2()
+     61        {
+     62            var x;
+-=>  63    #    |}
+     64        method1() {
+     65            var x;
+     66        }
+
+
+INSERTING AT: 64:0
+PAUSES AT: 65:8
+     61        {
+     62            var x;
+     63        }
+ ->  64    #    method1() {
+ =>  65            |var x;
+     66        }
+     67        method2()
+     68        {
+
+
+INSERTING AT: 65:0
+PAUSES AT: 65:8
+     62            var x;
+     63        }
+     64        method1() {
+-=>  65    #        |var x;
+     66        }
+     67        method2()
+     68        {
+
+
+INSERTING AT: 66:0
+PAUSES AT: 66:4
+     63        }
+     64        method1() {
+     65            var x;
+-=>  66    #    |}
+     67        method2()
+     68        {
+     69            var x;
+
+
+INSERTING AT: 67:0
+PAUSES AT: 69:8
+     64        method1() {
+     65            var x;
+     66        }
+ ->  67    #    method2()
+     68        {
+ =>  69            |var x;
+     70        }
+     71        get getter1() {
+     72            var x;
+
+
+INSERTING AT: 68:0
+PAUSES AT: 69:8
+     65            var x;
+     66        }
+     67        method2()
+ ->  68    #    {
+ =>  69            |var x;
+     70        }
+     71        get getter1() {
+     72            var x;
+
+
+INSERTING AT: 69:0
+PAUSES AT: 69:8
+     66        }
+     67        method2()
+     68        {
+-=>  69    #        |var x;
+     70        }
+     71        get getter1() {
+     72            var x;
+
+
+INSERTING AT: 70:0
+PAUSES AT: 70:4
+     67        method2()
+     68        {
+     69            var x;
+-=>  70    #    |}
+     71        get getter1() {
+     72            var x;
+     73        }
+
+
+INSERTING AT: 71:0
+PAUSES AT: 72:8
+     68        {
+     69            var x;
+     70        }
+ ->  71    #    get getter1() {
+ =>  72            |var x;
+     73        }
+     74        get getter2()
+     75        {
+
+
+INSERTING AT: 72:0
+PAUSES AT: 72:8
+     69            var x;
+     70        }
+     71        get getter1() {
+-=>  72    #        |var x;
+     73        }
+     74        get getter2()
+     75        {
+
+
+INSERTING AT: 73:0
+PAUSES AT: 73:4
+     70        }
+     71        get getter1() {
+     72            var x;
+-=>  73    #    |}
+     74        get getter2()
+     75        {
+     76            var x;
+
+
+INSERTING AT: 74:0
+PAUSES AT: 76:8
+     71        get getter1() {
+     72            var x;
+     73        }
+ ->  74    #    get getter2()
+     75        {
+ =>  76            |var x;
+     77        }
+     78        set setter1(x) {
+     79            var s;
+
+
+INSERTING AT: 75:0
+PAUSES AT: 76:8
+     72            var x;
+     73        }
+     74        get getter2()
+ ->  75    #    {
+ =>  76            |var x;
+     77        }
+     78        set setter1(x) {
+     79            var s;
+
+
+INSERTING AT: 76:0
+PAUSES AT: 76:8
+     73        }
+     74        get getter2()
+     75        {
+-=>  76    #        |var x;
+     77        }
+     78        set setter1(x) {
+     79            var s;
+
+
+INSERTING AT: 77:0
+PAUSES AT: 77:4
+     74        get getter2()
+     75        {
+     76            var x;
+-=>  77    #    |}
+     78        set setter1(x) {
+     79            var s;
+     80        }
+
+
+INSERTING AT: 78:0
+PAUSES AT: 79:8
+     75        {
+     76            var x;
+     77        }
+ ->  78    #    set setter1(x) {
+ =>  79            |var s;
+     80        }
+     81        set setter2(x)
+     82        {
+
+
+INSERTING AT: 79:0
+PAUSES AT: 79:8
+     76            var x;
+     77        }
+     78        set setter1(x) {
+-=>  79    #        |var s;
+     80        }
+     81        set setter2(x)
+     82        {
+
+
+INSERTING AT: 80:0
+PAUSES AT: 80:4
+     77        }
+     78        set setter1(x) {
+     79            var s;
+-=>  80    #    |}
+     81        set setter2(x)
+     82        {
+     83            var s;
+
+
+INSERTING AT: 81:0
+PAUSES AT: 83:8
+     78        set setter1(x) {
+     79            var s;
+     80        }
+ ->  81    #    set setter2(x)
+     82        {
+ =>  83            |var s;
+     84        }
+     85    }
+     86    
+
+
+INSERTING AT: 82:0
+PAUSES AT: 83:8
+     79            var s;
+     80        }
+     81        set setter2(x)
+ ->  82    #    {
+ =>  83            |var s;
+     84        }
+     85    }
+     86    
+
+
+INSERTING AT: 83:0
+PAUSES AT: 83:8
+     80        }
+     81        set setter2(x)
+     82        {
+-=>  83    #        |var s;
+     84        }
+     85    }
+     86    
+
+
+INSERTING AT: 84:0
+PAUSES AT: 84:4
+     81        set setter2(x)
+     82        {
+     83            var s;
+-=>  84    #    |}
+     85    }
+     86    
+     87    x => x;
+
+
+INSERTING AT: 85:0
+PAUSES AT: 87:0
+     82        {
+     83            var s;
+     84        }
+ ->  85    #}
+     86    
+ =>  87    |x => x;
+     88    () => 1;
+     89    (x) => x;
+     90    (x) => { x };
+
+
+INSERTING AT: 86:0
+PAUSES AT: 87:0
+     83            var s;
+     84        }
+     85    }
+ ->  86    #
+ =>  87    |x => x;
+     88    () => 1;
+     89    (x) => x;
+     90    (x) => { x };
+
+
+INSERTING AT: 87:0
+PAUSES AT: 87:0
+     84        }
+     85    }
+     86    
+-=>  87    |x => x;
+     88    () => 1;
+     89    (x) => x;
+     90    (x) => { x };
+
+
+INSERTING AT: 88:0
+PAUSES AT: 88:0
+     85    }
+     86    
+     87    x => x;
+-=>  88    |() => 1;
+     89    (x) => x;
+     90    (x) => { x };
+     91    (x) => {
+
+
+INSERTING AT: 89:0
+PAUSES AT: 89:0
+     86    
+     87    x => x;
+     88    () => 1;
+-=>  89    |(x) => x;
+     90    (x) => { x };
+     91    (x) => {
+     92        x
+
+
+INSERTING AT: 90:0
+PAUSES AT: 90:0
+     87    x => x;
+     88    () => 1;
+     89    (x) => x;
+-=>  90    |(x) => { x };
+     91    (x) => {
+     92        x
+     93    };
+
+
+INSERTING AT: 91:0
+PAUSES AT: 91:0
+     88    () => 1;
+     89    (x) => x;
+     90    (x) => { x };
+-=>  91    |(x) => {
+     92        x
+     93    };
+     94    () => {
+
+
+INSERTING AT: 92:0
+PAUSES AT: 92:4
+     89    (x) => x;
+     90    (x) => { x };
+     91    (x) => {
+-=>  92    #    |x
+     93    };
+     94    () => {
+     95        var x;
+
+
+INSERTING AT: 93:0
+PAUSES AT: 93:0
+     90    (x) => { x };
+     91    (x) => {
+     92        x
+-=>  93    |};
+     94    () => {
+     95        var x;
+     96    };
+
+
+INSERTING AT: 94:0
+PAUSES AT: 94:0
+     91    (x) => {
+     92        x
+     93    };
+-=>  94    |() => {
+     95        var x;
+     96    };
+     97    
+
+
+INSERTING AT: 95:0
+PAUSES AT: 95:4
+     92        x
+     93    };
+     94    () => {
+-=>  95    #    |var x;
+     96    };
+     97    
+     98    var fObj = {
+
+
+INSERTING AT: 96:0
+PAUSES AT: 96:0
+     93    };
+     94    () => {
+     95        var x;
+-=>  96    |};
+     97    
+     98    var fObj = {
+     99        f1: function() {
+
+
+INSERTING AT: 97:0
+PAUSES AT: 98:0
+     94    () => {
+     95        var x;
+     96    };
+ ->  97    #
+ =>  98    |var fObj = {
+     99        f1: function() {
+    100            var x;
+    101        },
+
+
+INSERTING AT: 98:0
+PAUSES AT: 98:0
+     95        var x;
+     96    };
+     97    
+-=>  98    |var fObj = {
+     99        f1: function() {
+    100            var x;
+    101        },
+
+
+INSERTING AT: 99:0
+PAUSES AT: 100:8
+     96    };
+     97    
+     98    var fObj = {
+ ->  99    #    f1: function() {
+ => 100            |var x;
+    101        },
+    102        f2: function()
+    103        {
+
+
+INSERTING AT: 100:0
+PAUSES AT: 100:8
+     97    
+     98    var fObj = {
+     99        f1: function() {
+-=> 100    #        |var x;
+    101        },
+    102        f2: function()
+    103        {
+
+
+INSERTING AT: 101:0
+PAUSES AT: 101:4
+     98    var fObj = {
+     99        f1: function() {
+    100            var x;
+-=> 101    #    |},
+    102        f2: function()
+    103        {
+    104            var x;
+
+
+INSERTING AT: 102:0
+PAUSES AT: 104:8
+     99        f1: function() {
+    100            var x;
+    101        },
+ -> 102    #    f2: function()
+    103        {
+ => 104            |var x;
+    105        },
+    106        f3: () => {
+    107            var x;
+
+
+INSERTING AT: 103:0
+PAUSES AT: 104:8
+    100            var x;
+    101        },
+    102        f2: function()
+ -> 103    #    {
+ => 104            |var x;
+    105        },
+    106        f3: () => {
+    107            var x;
+
+
+INSERTING AT: 104:0
+PAUSES AT: 104:8
+    101        },
+    102        f2: function()
+    103        {
+-=> 104    #        |var x;
+    105        },
+    106        f3: () => {
+    107            var x;
+
+
+INSERTING AT: 105:0
+PAUSES AT: 105:4
+    102        f2: function()
+    103        {
+    104            var x;
+-=> 105    #    |},
+    106        f3: () => {
+    107            var x;
+    108        },
+
+
+INSERTING AT: 106:0
+PAUSES AT: 107:8
+    103        {
+    104            var x;
+    105        },
+ -> 106    #    f3: () => {
+ => 107            |var x;
+    108        },
+    109        f4: () =>
+    110        {
+
+
+INSERTING AT: 107:0
+PAUSES AT: 107:8
+    104            var x;
+    105        },
+    106        f3: () => {
+-=> 107    #        |var x;
+    108        },
+    109        f4: () =>
+    110        {
+
+
+INSERTING AT: 108:0
+PAUSES AT: 108:4
+    105        },
+    106        f3: () => {
+    107            var x;
+-=> 108    #    |},
+    109        f4: () =>
+    110        {
+    111            var x;
+
+
+INSERTING AT: 109:0
+PAUSES AT: 111:8
+    106        f3: () => {
+    107            var x;
+    108        },
+ -> 109    #    f4: () =>
+    110        {
+ => 111            |var x;
+    112        }   
+    113    };
+    114    
+
+
+INSERTING AT: 110:0
+PAUSES AT: 111:8
+    107            var x;
+    108        },
+    109        f4: () =>
+ -> 110    #    {
+ => 111            |var x;
+    112        }   
+    113    };
+    114    
+
+
+INSERTING AT: 111:0
+PAUSES AT: 111:8
+    108        },
+    109        f4: () =>
+    110        {
+-=> 111    #        |var x;
+    112        }   
+    113    };
+    114    
+
+
+INSERTING AT: 112:0
+PAUSES AT: 112:4
+    109        f4: () =>
+    110        {
+    111            var x;
+-=> 112    #    |}   
+    113    };
+    114    
+
+
+INSERTING AT: 113:0
+PRODUCES: Could not resolve breakpoint
+
+INSERTING AT: 114:0
+PRODUCES: Could not resolve breakpoint
+
diff --git a/LayoutTests/inspector/debugger/breakpoints/resolved-dump-each-line.html b/LayoutTests/inspector/debugger/breakpoints/resolved-dump-each-line.html
new file mode 100644 (file)
index 0000000..2fa196b
--- /dev/null
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../../http/tests/inspector/resources/inspector-test.js"></script>
+<script src="../resources/log-pause-location.js"></script>
+<script src="resources/dump.js"></script>
+<script src="resources/dump-general.js"></script>
+<script src="resources/dump-functions.js"></script>
+<script>
+function test()
+{
+    let suite = InspectorTest.createAsyncSuite("Debugger.resolvedBreakpoint.dumpEachLine");
+
+    window.addDumpEachLinePauseLocationTestCase(suite, {
+        name: "Debugger.resolvedBreakpoint.dumpEachLine.General",
+        scriptRegex: /dump-general\.js$/,
+    });
+
+    window.addDumpEachLinePauseLocationTestCase(suite, {
+        name: "Debugger.resolvedBreakpoint.dumpEachLine.Functions",
+        scriptRegex: /dump-functions\.js$/,
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+<p>Checking resolved breakpoint locations for each line in a script.</p>
+</body>
+</html>
diff --git a/LayoutTests/inspector/debugger/breakpoints/resources/dump-functions.js b/LayoutTests/inspector/debugger/breakpoints/resources/dump-functions.js
new file mode 100644 (file)
index 0000000..ef1646e
--- /dev/null
@@ -0,0 +1,114 @@
+function inline() {}
+function named() {
+    var x;
+}
+function outer1() {
+    function inner() {
+        var y;
+    }
+}
+function outer2()
+{
+    function inner()
+    {
+        var y;
+    }
+}
+function outer3() {
+    var x;
+    function inner() { var y; }
+}
+function outer4() {
+    function inner() { var y; }
+    var x;
+}
+function outer5() {
+    var x;
+    function inner() { var y; }
+    var x;
+}
+function outer6() {
+    function inner1() { var y; }
+    var x;
+    function inner2() { var z; }
+}
+function outer7() {
+    function inner1() { var y; }
+    function inner2() { var z; }
+    var x;
+}
+function outer7() {
+    function inner1() { var y; }
+    function inner2() { var z; }
+}
+
+(function() {
+    var x;
+})();
+
+(() => {
+    var x;
+});
+
+function* generator() {
+    var x;
+}
+
+class Class {
+    static staticMethod1() {
+        var x;
+    }
+    static staticMethod2()
+    {
+        var x;
+    }
+    method1() {
+        var x;
+    }
+    method2()
+    {
+        var x;
+    }
+    get getter1() {
+        var x;
+    }
+    get getter2()
+    {
+        var x;
+    }
+    set setter1(x) {
+        var s;
+    }
+    set setter2(x)
+    {
+        var s;
+    }
+}
+
+x => x;
+() => 1;
+(x) => x;
+(x) => { x };
+(x) => {
+    x
+};
+() => {
+    var x;
+};
+
+var fObj = {
+    f1: function() {
+        var x;
+    },
+    f2: function()
+    {
+        var x;
+    },
+    f3: () => {
+        var x;
+    },
+    f4: () =>
+    {
+        var x;
+    }   
+};
diff --git a/LayoutTests/inspector/debugger/breakpoints/resources/dump-general.js b/LayoutTests/inspector/debugger/breakpoints/resources/dump-general.js
new file mode 100644 (file)
index 0000000..e5d1db6
--- /dev/null
@@ -0,0 +1,201 @@
+let a=function(){return 0}
+let b=function(){return 0}
+let c=function(){return 0}
+let arr=[];
+let obj={};
+
+// Control flow
+
+if (0)
+    a();
+
+if (0) {
+    a();
+}
+
+if (0)
+    a();
+else if (0)
+    a();
+else
+    a();
+
+a() ? b() : c()
+
+// Loops
+
+while (0)
+    a();
+
+while (0) {
+    a();
+}
+
+do {
+    a();
+} while (0);
+
+for (a(); b(); c())
+    break;
+
+for (; b(); c())
+    break;
+
+for (a(); b();)
+    break;
+
+for (a();; c())
+    break;
+
+for (a();;)
+    break;
+
+for (; b();)
+    break;
+
+for (;; c())
+    break;
+
+for (;;)
+    break;
+
+for (a(); b(); c()) {
+    break;
+}
+
+for (let x of arr)
+    break;
+
+for (let x in obj)
+    break;
+
+// Switch
+
+switch (0) {
+case 1:
+    a();
+    break;
+case 2:
+    a();
+    // fallthrough
+case 3:
+    a();
+    break;
+default:
+    a();
+    break;
+}
+
+// Try/Catch
+
+try {
+    a();
+} catch (e) {
+    shouldNotBeReached();
+} finally {
+    b();
+}
+
+// Class
+
+class Base {
+    constructor()
+    {
+        this._base = true;
+    }
+
+    baseMethod()
+    {
+        a();
+    }
+
+    method()
+    {
+        a();
+    }
+}
+
+class Child extends Base {
+    constructor()
+    {
+        super();
+        this._child = true;
+    }
+
+    childMethod()
+    {
+        b();
+    }
+
+    method()
+    {
+        super.method();
+        b();
+    }
+
+    get name()
+    {
+        return this._name;
+    }
+    
+    set name(x)
+    {
+        this._name = x;
+    }
+}
+
+// ---------
+/* Misc */
+// ---------
+
+    {
+        a();
+    }
+
+label:
+    {
+        a();
+    }
+
+var w1 = {x:1, y:2};
+with (w1) {
+    a();
+}
+
+var v1 = 1,
+    v2 = 1;
+let l1 = 2,
+    l2 = 2;
+const c1 = 3,
+    c2 = 3;
+
+v1 = v2 = v1;
+
+var {x, y} = obj;
+var [w, z] = arr;
+
+var o1 = {
+    p1: 1,
+    p2: a(),
+    p3: 1,
+    ["p4"]: 1,
+    [b()]: 1,
+};
+
+var a1 = [
+    1,
+    a(),
+    1,
+    b(),
+];
+
+var i1 = new Base;
+var i2 = new Child;
+i2.name;
+i2.name = 1;
+i2.method();
+
+var t1 = `${1} ${x=1} ${a()}`;
+var t2 = a`${1} ${x=1} ${a()}`;
+
+a(a(), b());
diff --git a/LayoutTests/inspector/debugger/breakpoints/resources/dump.js b/LayoutTests/inspector/debugger/breakpoints/resources/dump.js
new file mode 100644 (file)
index 0000000..554895f
--- /dev/null
@@ -0,0 +1,88 @@
+TestPage.registerInitializer(() => {
+    // Debugger.Location
+    function createLocation(script, lineNumber, columnNumber) {
+        return {scriptId: script.id, lineNumber, columnNumber};
+    }
+
+    // Dump all pause locations test.
+    // Tries to set a breakpoint at every line:column in the file and
+    // logs all unique pause locations with the originator line:column.
+    window.addDumpAllPauseLocationsTestCase = function(suite, {name, scriptRegex}) {
+        suite.addTestCase({
+            name, test(resolve, reject) {
+                let script = window.findScript(scriptRegex);
+                window.loadLinesFromSourceCode(script).then((lines) => {
+                    // Iterate all possible pause locations in this file.
+                    let pauseLocations = new Map;
+                    let seenPauseLocations = new Set;
+
+                    for (let line = script.range.startLine; line <= script.range.endLine; ++line) {
+                        let max = lines[line].length;
+                        for (let column = 0; column <= max; ++column) {
+                            DebuggerAgent.setBreakpoint(createLocation(script, line, column), (error, breakpointId, location) => {
+                                if (error)
+                                    return;
+                                let key = JSON.stringify(location);
+                                if (seenPauseLocations.has(key))
+                                    return;
+                                pauseLocations.set({lineNumber: line, columnNumber: column}, location);
+                            });
+                        }
+                    }
+
+                    // Log the unique locations and the first input that produced it.
+                    InspectorBackend.runAfterPendingDispatches(() => {
+                        InspectorTest.log("");
+                        for (let [inputLocation, payload] of pauseLocations) {
+                            InspectorTest.log(`INSERTING AT: ${inputLocation.lineNumber}:${inputLocation.columnNumber}`);
+                            InspectorTest.log(`PAUSES AT: ${payload.lineNumber}:${payload.columnNumber}`);
+                            let resolvedLocation = script.createSourceCodeLocation(payload.lineNumber, payload.columnNumber);
+                            window.logResolvedBreakpointLinesWithContext(inputLocation, resolvedLocation, 3);
+                            InspectorTest.log("");
+                        }
+                        resolve();
+                    });
+                });
+            }
+        });
+    }
+
+    // Dump each line test.
+    // Tries to set a breakpoint at every line:0 in the file and
+    // logs its pause location. Clears breakpoints before each line.
+    window.addDumpEachLinePauseLocationTestCase = function(suite, {name, scriptRegex}) {
+        suite.addTestCase({
+            name, test(resolve, reject) {
+                let script = window.findScript(scriptRegex);
+                window.loadLinesFromSourceCode(script).then(() => {
+                    // Set one breakpoint per line.
+                    for (let line = script.range.startLine; line <= script.range.endLine; ++line) {
+                        DebuggerAgent.setBreakpoint(createLocation(script, line, 0), (error, breakpointId, payload) => {
+                            InspectorTest.log("");
+                            if (error) {
+                                InspectorTest.log(`INSERTING AT: ${line}:0`);
+                                InspectorTest.log(`PRODUCES: ${error}`);
+                            } else {
+                                let inputLocation = {lineNumber: line, columnNumber: 0};
+                                let resolvedLocation = script.createSourceCodeLocation(payload.lineNumber, payload.columnNumber);
+                                InspectorTest.log(`INSERTING AT: ${inputLocation.lineNumber}:${inputLocation.columnNumber}`);
+                                InspectorTest.log(`PAUSES AT: ${payload.lineNumber}:${payload.columnNumber}`);                                
+                                window.logResolvedBreakpointLinesWithContext(inputLocation, resolvedLocation, 3);
+                                InspectorTest.log("");
+                            }
+                        });
+
+                        // Clear the breakpoint we just set without knowing its breakpoint identifier.
+                        DebuggerAgent.disable();
+                        DebuggerAgent.enable();
+                    }
+
+                    // Resolve after all lines have been tried.
+                    InspectorBackend.runAfterPendingDispatches(() => {
+                        resolve();
+                    });
+                });
+            }
+        });
+    }
+});
index 16fcade..1fe5ae6 100644 (file)
@@ -1,5 +1,6 @@
 TestPage.registerInitializer(() => {
     let lines = [];
+    let linesSourceCode = null;
 
     // Switch back to String.prototype.padStart once this is fixed:
     // FIXME: <https://webkit.org/b/161944> stringProtoFuncRepeatCharacter will return `null` when it should not
@@ -10,12 +11,78 @@ TestPage.registerInitializer(() => {
         return " ".repeat(desired - length) + this;
     };
 
-    function insertCaretIntoStringAtIndex(str, index) {
-        return str.slice(0, index) + "|" + str.slice(index);
+    function insertCaretIntoStringAtIndex(str, index, caret="|") {
+        return str.slice(0, index) + caret + str.slice(index);
     }
 
-    function logLinesWithContext(location, context) {
-        if (!WebInspector.frameResourceManager.mainFrame.mainResource.scripts.includes(location.sourceCode)) {
+    window.findScript = function(regex) {
+        let resources = WebInspector.frameResourceManager.mainFrame.resources;
+        for (let resource of resources) {
+            if (regex.test(resource.url))
+                return resource.scripts[0];
+        }
+    }
+
+    window.loadLinesFromSourceCode = function(sourceCode) {
+        linesSourceCode = sourceCode;
+        return sourceCode.requestContent()
+            .then((content) => {
+                lines = sourceCode.content.split(/\n/);
+                return lines;
+            })
+            .catch(() => {
+                InspectorTest.fail("Failed to load script content.");
+                InspectorTest.completeTest();
+            });
+    }
+
+    window.loadMainPageContent = function() {
+        return loadLinesFromSourceCode(WebInspector.frameResourceManager.mainFrame.mainResource);
+    }
+
+    window.logResolvedBreakpointLinesWithContext = function(inputLocation, resolvedLocation, context) {
+        if (resolvedLocation.sourceCode !== linesSourceCode && !WebInspector.frameResourceManager.mainFrame.mainResource.scripts.includes(resolvedLocation.sourceCode)) {
+            InspectorTest.log("--- Source Unavailable ---");
+            return;
+        }
+
+        InspectorTest.assert(inputLocation.lineNumber <= resolvedLocation.lineNumber, "Input line number should always precede resolve location line number.");
+        InspectorTest.assert(inputLocation.lineNumber !== resolvedLocation.lineNumber || inputLocation.columnNumber <= resolvedLocation.columnNumber, "Input position should always precede resolve position.");
+
+        const inputCaret = "#";
+        const resolvedCaret = "|";
+
+        let startLine = inputLocation.lineNumber - context;
+        let endLine = resolvedLocation.lineNumber + context;
+        for (let lineNumber = startLine; lineNumber <= endLine; ++lineNumber) {
+            let lineContent = lines[lineNumber];
+            if (typeof lineContent !== "string")
+                continue;
+
+            let hasInputLocation = lineNumber === inputLocation.lineNumber;
+            let hasResolvedLocation = lineNumber === resolvedLocation.lineNumber;
+
+            let prefix = "    ";
+            if (hasInputLocation && hasResolvedLocation) {
+                prefix = "-=> ";
+                lineContent = insertCaretIntoStringAtIndex(lineContent, resolvedLocation.columnNumber, resolvedCaret);
+                if (inputLocation.columnNumber !== resolvedLocation.columnNumber)
+                    lineContent = insertCaretIntoStringAtIndex(lineContent, inputLocation.columnNumber, inputCaret);
+            } else if (hasInputLocation) {
+                prefix = " -> ";
+                lineContent = insertCaretIntoStringAtIndex(lineContent, inputLocation.columnNumber, inputCaret);
+            } else if (hasResolvedLocation) {
+                prefix = " => ";
+                lineContent = insertCaretIntoStringAtIndex(lineContent, resolvedLocation.columnNumber, resolvedCaret);
+            }
+
+            let number = lineNumber.toString().myPadStart(3);
+            InspectorTest.log(`${prefix}${number}    ${lineContent}`);
+        }
+    }
+
+    window.logLinesWithContext = function(location, context) {
+        if (location.sourceCode !== linesSourceCode && !WebInspector.frameResourceManager.mainFrame.mainResource.scripts.includes(location.sourceCode)) {
             InspectorTest.log("--- Source Unavailable ---");
             return;
         }
@@ -108,17 +175,6 @@ TestPage.registerInitializer(() => {
             }
         });
     }
-
-    window.loadMainPageContent = function() {
-        return WebInspector.frameResourceManager.mainFrame.mainResource.requestContent()
-            .then((content) => {
-                lines = WebInspector.frameResourceManager.mainFrame.mainResource.content.split(/\n/);
-            })
-            .catch(() => {
-                InspectorTest.fail("Failed to load page content.");
-                InspectorTest.completeTest();
-            });
-    }
 });
 
 if (!window.testRunner) {
index 943dbc5..8397148 100644 (file)
@@ -1,7 +1,7 @@
 Debugger.setBreakpoint on line:0 in <script src="...">
 
 Found breakpoint.js
-PASS: Received error setting duplicate breakpoint: Breakpoint at specified location already exists.
+PASS: Received error setting duplicate breakpoint: Breakpoint at specified location already exists
 Running breakpointBasic
 inside breakpointBasic
 Hit Breakpoint!
index e236f51..05e5a58 100644 (file)
@@ -60,7 +60,7 @@ public:
 
 private:
     OpaqueJSScript(VM* vm, const String& url, int startingLineNumber, const String& source)
-        : SourceProvider(url, TextPosition(OrdinalNumber::fromOneBasedInt(startingLineNumber), OrdinalNumber::first()))
+        : SourceProvider(url, TextPosition(OrdinalNumber::fromOneBasedInt(startingLineNumber), OrdinalNumber::first()), SourceProviderSourceType::Program)
         , m_vm(vm)
         , m_source(source.isNull() ? *StringImpl::empty() : *source.impl())
     {
index ac473be..fb29147 100644 (file)
@@ -248,6 +248,7 @@ set(JavaScriptCore_SOURCES
     debugger/Debugger.cpp
     debugger/DebuggerCallFrame.cpp
     debugger/DebuggerLocation.cpp
+    debugger/DebuggerParseData.cpp
     debugger/DebuggerScope.cpp
 
     dfg/DFGAbstractHeap.cpp
index 221cd50..11d1221 100644 (file)
@@ -1,5 +1,255 @@
 2016-09-30  Joseph Pecoraro  <pecoraro@apple.com>
 
+        Breakpoints on blank lines or comments don't break
+        https://bugs.webkit.org/show_bug.cgi?id=9885
+        <rdar://problem/6134406>
+
+        Reviewed by Mark Lam.
+
+        This change introduces a way to perform a Debugger Parse of a script.
+        This debugger parse gathers a list of breakpoint locations, which
+        the backend uses to resolve breakpoint locations that came from the
+        Inspector frontend to the exact location we would actually pause.
+        We gather this information from the parser so that we can eagerly
+        get this information without requiring the code to have executed (the
+        real op_debugs are generated during bytecode generation when code
+        is actually evaluated).
+
+        If an input location was on a line with whitespace or a comment, the
+        resolved breakpoint location would be before the next statement that
+        will be executed. That may be the next line, or even later. We also
+        update our policy when setting breakpoints on and around function
+        statements to better match user expectations.
+
+        For example, when resolving breakpoints in:
+
+            1.  // Comment
+            2.  before;
+            3.
+            4.  function foo() {
+            5.      inside;
+            6.  }
+            7.
+            8.  after;
+
+        A breakpoint on line 1, a comment, resolves to line 2 the next
+        statement that will execute.
+
+        A breakpoint on line 3 or 7, empty lines, resolves to line 8 the next
+        statement that will execute. This skips past the definition of foo,
+        just like stepping would have done. The creation of foo would have
+        been hoisted, which would have happened before execution of the
+        other statements.
+
+        A breakpoint on line 4, a function signature, resolves to line 5,
+        inside the function. Users would expect to pause inside of a function
+        when setting a breakpoint on that function's name or opening brace.
+
+        A breakpoint on line 6, a function's closing brace, resolves to
+        line 6. The debugger will pause whenever execution leaves foo due to
+        a return and not an exception. This matches stepping behavior. An
+        explicit or implicit return (the implicit return undefined) will
+        pause on the closing brace as we leave the function, giving users
+        an opportunity to inspect the final state before leaving.
+
+        --
+
+        At this point, op_debug's are still emitted at custom locations during
+        bytecode generation of other statements / expressions. In order to
+        ensure the generated op_debugs correspond to locations the Parser
+        determined were breakpoint locations, the Parser sets a "needs debug
+        hook" flag on the nodes it will use for breakpoint locations, and
+        we assert during bytecode generation that op_debugs are only emitted
+        for nodes that were marked as needing debug hooks.
+
+        This still leaves open the possibility that the Parser will mark
+        some nodes that get missed during bytecode generation, so we might
+        fail to emit some op_debugs. The next step will be eliminating the
+        custom emitDebugHooks spread across StatementNode and ExpressionNode
+        subclasses, and instead always generating op_debugs whenever we
+        emit a flagged node.
+
+        --
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        New DebuggerParseData files.
+
+        * API/JSScriptRef.cpp:
+        (OpaqueJSScript::OpaqueJSScript):
+        * jsc.cpp:
+        (functionCheckModuleSyntax):
+        * parser/SourceCode.h:
+        (JSC::makeSource):
+        * parser/SourceProvider.cpp:
+        (JSC::SourceProvider::SourceProvider):
+        * parser/SourceProvider.h:
+        (JSC::SourceProvider::sourceType):
+        (JSC::StringSourceProvider::create):
+        (JSC::StringSourceProvider::StringSourceProvider):
+        (JSC::WebAssemblySourceProvider::WebAssemblySourceProvider):
+        (JSC::SourceProvider::startPosition): Deleted.
+        Add a new type on SourceProvider to distinguish if its script was
+        intended to be a Script, Module, or WebAssembly. This information
+        will be needed to know how to best parse this file when the
+        debugger decides to lazily parse.
+
+        * runtime/Executable.cpp:
+        (JSC::EvalExecutable::EvalExecutable):
+        (JSC::ProgramExecutable::ProgramExecutable):
+        (JSC::ModuleProgramExecutable::ModuleProgramExecutable):
+        (JSC::WebAssemblyExecutable::WebAssemblyExecutable):
+        * runtime/ModuleLoaderPrototype.cpp:
+        (JSC::moduleLoaderPrototypeParseModule):
+        ASSERT the SourceProvider type matches the executable type we are
+        creating for it.
+
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::breakpointLocation):
+        * parser/SyntaxChecker.h:
+        (JSC::SyntaxChecker::operatorStackPop):
+        When gathering breakpoint positions, get the position from the
+        current node. In the SyntaxChecker, return an invalid position.
+
+        * parser/Nodes.h:
+        (JSC::ExpressionNode::needsDebugHook):
+        (JSC::ExpressionNode::setNeedsDebugHook):
+        (JSC::StatementNode::needsDebugHook):
+        (JSC::StatementNode::setNeedsDebugHook):
+        When gathering breakpoint positions, mark the node as needing
+        a debug hook. For now we assert op_debugs generated must come
+        from these nodes. Later we should just generate op_debugs for
+        these nodes.
+
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::Parser):
+        (JSC::Parser<LexerType>::parseStatementListItem):
+        (JSC::Parser<LexerType>::parseDoWhileStatement):
+        (JSC::Parser<LexerType>::parseWhileStatement):
+        (JSC::Parser<LexerType>::parseArrowFunctionSingleExpressionBodySourceElements):
+        (JSC::Parser<LexerType>::parseForStatement):
+        (JSC::Parser<LexerType>::parseWithStatement):
+        (JSC::Parser<LexerType>::parseSwitchStatement):
+        (JSC::Parser<LexerType>::parseStatement):
+        (JSC::Parser<LexerType>::parseFunctionBody):
+        (JSC::Parser<LexerType>::parseFunctionInfo):
+        (JSC::Parser<LexerType>::parseIfStatement):
+        (JSC::Parser<LexerType>::parseAssignmentExpression):
+        * parser/Parser.h:
+        (JSC::parse):
+        Add an optional DebuggerParseData struct to the Parser. When available
+        the Parser will gather debugger data, and parse all functions with the
+        ASTBuilder instead of SyntaxChecking inner functions.
+
+        * debugger/DebuggerParseData.cpp: Added.
+        (JSC::DebuggerPausePositions::breakpointLocationForLineColumn):
+        (JSC::DebuggerPausePositions::sort):
+        (JSC::gatherDebuggerParseData):
+        (JSC::gatherDebuggerParseDataForSource):
+        * debugger/DebuggerParseData.h: Copied from Source/JavaScriptCore/debugger/DebuggerPrimitives.h.
+        (JSC::DebuggerPausePositions::DebuggerPausePositions):
+        (JSC::DebuggerPausePositions::appendPause):
+        (JSC::DebuggerPausePositions::appendEntry):
+        (JSC::DebuggerPausePositions::appendLeave):
+        The DebuggerParseData struct currently only contains a list of pause positions.
+        Once populated it can resolve an input location to a pause position.
+
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitCall):
+        (JSC::BytecodeGenerator::emitCallVarargs):
+        (JSC::BytecodeGenerator::emitDebugHook):
+        (JSC::BytecodeGenerator::emitEnumeration):
+        * bytecompiler/BytecodeGenerator.h:
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::EmptyStatementNode::emitBytecode):
+        (JSC::DebuggerStatementNode::emitBytecode):
+        (JSC::ExprStatementNode::emitBytecode):
+        (JSC::DeclarationStatement::emitBytecode):
+        (JSC::IfElseNode::emitBytecode):
+        (JSC::DoWhileNode::emitBytecode):
+        (JSC::WhileNode::emitBytecode):
+        (JSC::ForNode::emitBytecode):
+        (JSC::ForInNode::emitBytecode):
+        (JSC::ContinueNode::emitBytecode):
+        (JSC::BreakNode::emitBytecode):
+        (JSC::ReturnNode::emitBytecode):
+        (JSC::WithNode::emitBytecode):
+        (JSC::SwitchNode::emitBytecode):
+        (JSC::ThrowNode::emitBytecode):
+        Emit op_debugs for the nodes themselves. Assert when we do that the
+        Parser had marked them as needing a debug hook.
+
+        * debugger/Breakpoint.h:
+        (JSC::Breakpoint::Breakpoint):
+        A breakpoint may be resolved or unresolved. Debugger::resolveBreakpoint
+        must be used to resolve the breakpoint. Most methods now require a
+        resolved breakpoint.
+
+        * debugger/Debugger.h:
+        * debugger/Debugger.cpp:
+        (JSC::Debugger::detach):
+        (JSC::Debugger::toggleBreakpoint):
+        (JSC::Debugger::debuggerParseData):
+        (JSC::Debugger::resolveBreakpoint):
+        (JSC::Debugger::setBreakpoint):
+        (JSC::Debugger::clearParsedData):
+        Provide a public method to resolve a breakpoint location in a script.
+        This will gather debugger parse data for the script if none is available.
+        Ensure clients have resolved a breakpoint before attempting to set it.
+        Currently we allow only a single breakpoint at a location. This may
+        need to change if multiple breakpoints resolve to the same location
+        but have different actions.
+
+        * inspector/ScriptDebugListener.h:
+        ScriptDebugServer::Script is effectively duplicating most of the data from
+        a SourceProvider. We should eliminate this and just use SourceProvider.
+
+        * inspector/ScriptDebugServer.cpp:
+        (Inspector::ScriptDebugServer::setBreakpointActions):
+        (Inspector::ScriptDebugServer::removeBreakpointActions):
+        (Inspector::ScriptDebugServer::getActionsForBreakpoint):
+        (Inspector::ScriptDebugServer::clearBreakpointActions):
+        (Inspector::ScriptDebugServer::evaluateBreakpointAction):
+        (Inspector::ScriptDebugServer::dispatchDidParseSource):
+        (Inspector::ScriptDebugServer::handleBreakpointHit):
+        (Inspector::ScriptDebugServer::setBreakpoint): Deleted.
+        (Inspector::ScriptDebugServer::removeBreakpoint): Deleted.
+        (Inspector::ScriptDebugServer::clearBreakpoints): Deleted.
+        * inspector/ScriptDebugServer.h:
+        Reduce ScriptDebugServer's involvement in breakpoints to just handling
+        breakpoint actions. Eventually we should eliminate it alltogether and
+        fold breakpoint logic into Debugger or DebugAgent.
+
+        * inspector/agents/InspectorDebuggerAgent.h:
+        * inspector/agents/InspectorDebuggerAgent.cpp:
+        (Inspector::buildDebuggerLocation):
+        (Inspector::parseLocation):
+        (Inspector::InspectorDebuggerAgent::setBreakpointByUrl):
+        (Inspector::InspectorDebuggerAgent::setBreakpoint):
+        (Inspector::InspectorDebuggerAgent::didSetBreakpoint):
+        (Inspector::InspectorDebuggerAgent::resolveBreakpoint):
+        (Inspector::InspectorDebuggerAgent::removeBreakpoint):
+        (Inspector::InspectorDebuggerAgent::continueToLocation):
+        (Inspector::InspectorDebuggerAgent::didParseSource):
+        (Inspector::InspectorDebuggerAgent::clearDebuggerBreakpointState):
+        The Inspector can set breakpoints in multiple ways.
+        Ensure that once we have the Script that we always
+        resolve the breakpoint location before setting the
+        breakpoint. The different paths are:
+        
+        - setBreakpoint(scriptId, location)
+          - Here we know the SourceProvider by its SourceID
+            - resolve and set
+        
+        - setBreakpointByURL(url, location)
+          - Search for existing Scripts that match the URL
+            - resolve in each and set
+          - When new Scripts are parsed that match the URL
+            - resolve and set
+            
+
+2016-09-30  Joseph Pecoraro  <pecoraro@apple.com>
+
         Web Inspector: Stepping out of a function finishes the line that called it.
         https://bugs.webkit.org/show_bug.cgi?id=155325
         <rdar://problem/25094578>
index c951ffc..6ee5772 100644 (file)
                A59455921824744700CC3843 /* JSGlobalObjectDebuggable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A59455901824744700CC3843 /* JSGlobalObjectDebuggable.cpp */; };
                A59455931824744700CC3843 /* JSGlobalObjectDebuggable.h in Headers */ = {isa = PBXBuildFile; fileRef = A59455911824744700CC3843 /* JSGlobalObjectDebuggable.h */; };
                A5945595182479EB00CC3843 /* InspectorFrontendChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = A5945594182479EB00CC3843 /* InspectorFrontendChannel.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               A5A1A0941D8CB33E004C2EB8 /* DebuggerParseData.h in Headers */ = {isa = PBXBuildFile; fileRef = A5A1A0921D8CB12F004C2EB8 /* DebuggerParseData.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               A5A1A0951D8CB341004C2EB8 /* DebuggerParseData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A5A1A0931D8CB337004C2EB8 /* DebuggerParseData.cpp */; };
                A5AB49DC1BEC8082007020FB /* PerGlobalObjectWrapperWorld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A5AB49DA1BEC8079007020FB /* PerGlobalObjectWrapperWorld.cpp */; };
                A5AB49DD1BEC8086007020FB /* PerGlobalObjectWrapperWorld.h in Headers */ = {isa = PBXBuildFile; fileRef = A5AB49DB1BEC8079007020FB /* PerGlobalObjectWrapperWorld.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A5B6A74D18C6DBA600F11E91 /* ConsoleClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A5B6A74C18C6DBA600F11E91 /* ConsoleClient.cpp */; };
                A59455901824744700CC3843 /* JSGlobalObjectDebuggable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSGlobalObjectDebuggable.cpp; sourceTree = "<group>"; };
                A59455911824744700CC3843 /* JSGlobalObjectDebuggable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSGlobalObjectDebuggable.h; sourceTree = "<group>"; };
                A5945594182479EB00CC3843 /* InspectorFrontendChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorFrontendChannel.h; sourceTree = "<group>"; };
+               A5A1A0921D8CB12F004C2EB8 /* DebuggerParseData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebuggerParseData.h; sourceTree = "<group>"; };
+               A5A1A0931D8CB337004C2EB8 /* DebuggerParseData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DebuggerParseData.cpp; sourceTree = "<group>"; };
                A5AB49DA1BEC8079007020FB /* PerGlobalObjectWrapperWorld.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PerGlobalObjectWrapperWorld.cpp; sourceTree = "<group>"; };
                A5AB49DB1BEC8079007020FB /* PerGlobalObjectWrapperWorld.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PerGlobalObjectWrapperWorld.h; sourceTree = "<group>"; };
                A5B6A74C18C6DBA600F11E91 /* ConsoleClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConsoleClient.cpp; sourceTree = "<group>"; };
                                6AD2CB4C19B9140100065719 /* DebuggerEvalEnabler.h */,
                                A5FC84B01D1DDAC8006B5C46 /* DebuggerLocation.cpp */,
                                A5FC84B11D1DDAC8006B5C46 /* DebuggerLocation.h */,
+                               A5A1A0931D8CB337004C2EB8 /* DebuggerParseData.cpp */,
+                               A5A1A0921D8CB12F004C2EB8 /* DebuggerParseData.h */,
                                FEA0861F182B7A0400F6D851 /* DebuggerPrimitives.h */,
                                0F2D4DDB19832D34007D4B19 /* DebuggerScope.cpp */,
                                0F2D4DDC19832D34007D4B19 /* DebuggerScope.h */,
                                A7386556118697B400540279 /* ThunkGenerators.h in Headers */,
                                141448CD13A1783700F5BA1A /* TinyBloomFilter.h in Headers */,
                                2684D4381C00161C0081D663 /* AirLiveness.h in Headers */,
+                               A5A1A0941D8CB33E004C2EB8 /* DebuggerParseData.h in Headers */,
                                0F55989817C86C5800A1E543 /* ToNativeFromValue.h in Headers */,
                                DCEE220B1CEB9895000C2396 /* DFGControlEquivalenceAnalysis.h in Headers */,
                                0F2D4DE919832DAC007D4B19 /* ToThisStatus.h in Headers */,
                                148F21B0107EC5410042EC2C /* Lexer.cpp in Sources */,
                                0FF4275715914A20004CB9FF /* LinkBuffer.cpp in Sources */,
                                A7E2EA6C0FB460CF00601F06 /* LiteralParser.cpp in Sources */,
+                               A5A1A0951D8CB341004C2EB8 /* DebuggerParseData.cpp in Sources */,
                                FE3913541B794F6E00EDAF71 /* LiveObjectList.cpp in Sources */,
                                FE20CE9D15F04A9500DF3430 /* LLIntCLoop.cpp in Sources */,
                                0F4680D214BBD16500BFE272 /* LLIntData.cpp in Sources */,
index 844df52..d96d859 100644 (file)
@@ -3180,7 +3180,7 @@ RegisterID* BytecodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, Regi
         callFrame.append(newTemporary());
 
     if (m_shouldEmitDebugHooks && debuggableCall == DebuggableCall::Yes)
-        emitDebugHook(WillExecuteExpression, divotStart.line, divotStart.offset, divotStart.lineStartOffset);
+        emitDebugHook(WillExecuteExpression, divotStart);
 
     emitExpressionInfo(divot, divotStart, divotEnd);
 
@@ -3234,7 +3234,7 @@ RegisterID* BytecodeGenerator::emitCallForwardArgumentsInTailPosition(RegisterID
 RegisterID* BytecodeGenerator::emitCallVarargs(OpcodeID opcode, RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall debuggableCall)
 {
     if (m_shouldEmitDebugHooks && debuggableCall == DebuggableCall::Yes)
-        emitDebugHook(WillExecuteExpression, divotStart.line, divotStart.offset, divotStart.lineStartOffset);
+        emitDebugHook(WillExecuteExpression, divotStart);
 
     emitExpressionInfo(divot, divotStart, divotEnd);
 
@@ -3459,18 +3459,34 @@ void BytecodeGenerator::emitPopWithScope()
     RELEASE_ASSERT(stackEntry.m_isWithScope);
 }
 
-void BytecodeGenerator::emitDebugHook(DebugHookID debugHookID, unsigned line, unsigned charOffset, unsigned lineStart)
+void BytecodeGenerator::emitDebugHook(DebugHookID debugHookID, const JSTextPosition& divot)
 {
     if (!m_shouldEmitDebugHooks)
         return;
 
-    JSTextPosition divot(line, charOffset, lineStart);
     emitExpressionInfo(divot, divot, divot);
     emitOpcode(op_debug);
     instructions().append(debugHookID);
     instructions().append(false);
 }
 
+void BytecodeGenerator::emitDebugHook(DebugHookID debugHookID, unsigned line, unsigned charOffset, unsigned lineStart)
+{
+    emitDebugHook(debugHookID, JSTextPosition(line, charOffset, lineStart));
+}
+
+void BytecodeGenerator::emitDebugHook(StatementNode* statement)
+{
+    RELEASE_ASSERT(statement->needsDebugHook());
+    emitDebugHook(WillExecuteStatement, statement->position());
+}
+
+void BytecodeGenerator::emitDebugHook(ExpressionNode* expr, DebugHookID debugHookID)
+{
+    RELEASE_ASSERT(expr->needsDebugHook());
+    emitDebugHook(debugHookID, expr->position());
+}
+
 void BytecodeGenerator::emitWillLeaveCallFrameDebugHook()
 {
     RELEASE_ASSERT(m_scopeNode->isFunctionNode());
@@ -4134,7 +4150,7 @@ void BytecodeGenerator::emitEnumeration(ThrowableExpressionData* node, Expressio
         if (forLoopNode) {
             RELEASE_ASSERT(forLoopNode->isForOfNode());
             prepareLexicalScopeForNextForLoopIteration(forLoopNode, forLoopSymbolTable);
-            emitDebugHook(WillExecuteStatement, forLoopNode->expr()->firstLine(), forLoopNode->expr()->startOffset(), forLoopNode->expr()->lineStartOffset());
+            emitDebugHook(forLoopNode->expr(), WillExecuteStatement);
         }
 
         {
index a6c6793..a9c37d2 100644 (file)
@@ -676,7 +676,10 @@ namespace JSC {
         void emitPutDerivedConstructorToArrowFunctionContextScope();
         RegisterID* emitLoadDerivedConstructorFromArrowFunctionLexicalEnvironment();
 
+        void emitDebugHook(DebugHookID, const JSTextPosition&);
         void emitDebugHook(DebugHookID, unsigned line, unsigned charOffset, unsigned lineStart);
+        void emitDebugHook(StatementNode*);
+        void emitDebugHook(ExpressionNode*, DebugHookID);
         void emitWillLeaveCallFrameDebugHook();
 
         bool isInFinallyBlock() { return m_finallyDepth > 0; }
index 8481806..df19c1c 100644 (file)
@@ -2341,14 +2341,14 @@ void BlockNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 
 void EmptyStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
 {
-    generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset());
+    generator.emitDebugHook(this);
 }
 
 // ------------------------------ DebuggerStatementNode ---------------------------
 
 void DebuggerStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
 {
-    generator.emitDebugHook(DidReachBreakpoint, lastLine(), startOffset(), lineStartOffset());
+    generator.emitDebugHook(DidReachBreakpoint, position());
 }
 
 // ------------------------------ ExprStatementNode ----------------------------
@@ -2356,7 +2356,7 @@ void DebuggerStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterI
 void ExprStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
     ASSERT(m_expr);
-    generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset());
+    generator.emitDebugHook(this);
     generator.emitNode(dst, m_expr);
 }
 
@@ -2365,7 +2365,7 @@ void ExprStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID* d
 void DeclarationStatement::emitBytecode(BytecodeGenerator& generator, RegisterID*)
 {
     ASSERT(m_expr);
-    generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset());
+    generator.emitDebugHook(this);
     generator.emitNode(m_expr);
 }
 
@@ -2453,7 +2453,7 @@ bool IfElseNode::tryFoldBreakAndContinue(BytecodeGenerator& generator, Statement
 
 void IfElseNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
-    generator.emitDebugHook(WillExecuteStatement, m_condition->firstLine(), m_condition->startOffset(), m_condition->lineStartOffset());
+    generator.emitDebugHook(m_condition, WillExecuteStatement);
     
     RefPtr<Label> beforeThen = generator.newLabel();
     RefPtr<Label> beforeElse = generator.newLabel();
@@ -2499,7 +2499,7 @@ void DoWhileNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
     generator.emitNodeInTailPosition(dst, m_statement);
 
     generator.emitLabel(scope->continueTarget());
-    generator.emitDebugHook(WillExecuteStatement, m_expr->firstLine(), m_expr->startOffset(), m_expr->lineStartOffset());
+    generator.emitDebugHook(m_expr, WillExecuteStatement);
     generator.emitNodeInConditionContext(m_expr, topOfLoop.get(), scope->breakTarget(), FallThroughMeansFalse);
 
     generator.emitLabel(scope->breakTarget());
@@ -2512,7 +2512,7 @@ void WhileNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
     LabelScopePtr scope = generator.newLabelScope(LabelScope::Loop);
     RefPtr<Label> topOfLoop = generator.newLabel();
 
-    generator.emitDebugHook(WillExecuteStatement, m_expr->firstLine(), m_expr->startOffset(), m_expr->lineStartOffset());
+    generator.emitDebugHook(m_expr, WillExecuteStatement);
     generator.emitNodeInConditionContext(m_expr, topOfLoop.get(), scope->breakTarget(), FallThroughMeansTrue);
 
     generator.emitLabel(topOfLoop.get());
@@ -2522,7 +2522,7 @@ void WhileNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
     generator.emitNodeInTailPosition(dst, m_statement);
 
     generator.emitLabel(scope->continueTarget());
-    generator.emitDebugHook(WillExecuteStatement, m_expr->firstLine(), m_expr->startOffset(), m_expr->lineStartOffset());
+    generator.emitDebugHook(m_expr, WillExecuteStatement);
 
     generator.emitNodeInConditionContext(m_expr, topOfLoop.get(), scope->breakTarget(), FallThroughMeansFalse);
 
@@ -2542,17 +2542,17 @@ void ForNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 
     if (m_expr1 || m_expr2) {
         ExpressionNode* firstExpr = m_expr1 ? m_expr1 : m_expr2;
-        generator.emitDebugHook(WillExecuteStatement, firstExpr->firstLine(), firstExpr->startOffset(), firstExpr->lineStartOffset());
+        generator.emitDebugHook(firstExpr, WillExecuteStatement);
     }
 
     if (m_expr1) {
-        generator.emitDebugHook(WillExecuteExpression, m_expr1->firstLine(), m_expr1->startOffset(), m_expr1->lineStartOffset());
+        generator.emitDebugHook(m_expr1, WillExecuteExpression);
         generator.emitNode(generator.ignoredResult(), m_expr1);
     }
 
     RefPtr<Label> topOfLoop = generator.newLabel();
     if (m_expr2) {
-        generator.emitDebugHook(WillExecuteExpression, m_expr2->firstLine(), m_expr2->startOffset(), m_expr2->lineStartOffset());
+        generator.emitDebugHook(m_expr2, WillExecuteExpression);
         generator.emitNodeInConditionContext(m_expr2, topOfLoop.get(), scope->breakTarget(), FallThroughMeansTrue);
     }
 
@@ -2565,12 +2565,12 @@ void ForNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
     generator.emitLabel(scope->continueTarget());
     generator.prepareLexicalScopeForNextForLoopIteration(this, forLoopSymbolTable);
     if (m_expr3) {
-        generator.emitDebugHook(WillExecuteStatement, m_expr3->firstLine(), m_expr3->startOffset(), m_expr3->lineStartOffset());
+        generator.emitDebugHook(m_expr3, WillExecuteStatement);
         generator.emitNode(generator.ignoredResult(), m_expr3);
     }
 
     if (m_expr2) {
-        generator.emitDebugHook(WillExecuteStatement, m_expr2->firstLine(), m_expr2->startOffset(), m_expr2->lineStartOffset());
+        generator.emitDebugHook(m_expr2, WillExecuteStatement);
         generator.emitNodeInConditionContext(m_expr2, topOfLoop.get(), scope->breakTarget(), FallThroughMeansFalse);
     } else
         generator.emitJump(topOfLoop.get());
@@ -2701,7 +2701,7 @@ void ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
     RegisterID* forLoopSymbolTable = nullptr;
     generator.pushLexicalScope(this, BytecodeGenerator::TDZCheckOptimization::Optimize, BytecodeGenerator::NestedScopeType::IsNested, &forLoopSymbolTable);
 
-    generator.emitDebugHook(WillExecuteStatement, m_expr->firstLine(), m_expr->startOffset(), m_expr->lineStartOffset());
+    generator.emitDebugHook(m_expr, WillExecuteStatement);
 
     if (m_lexpr->isAssignResolveNode())
         generator.emitNode(generator.ignoredResult(), m_lexpr);
@@ -2751,7 +2751,7 @@ void ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
         generator.emitLabel(scope->continueTarget());
         generator.prepareLexicalScopeForNextForLoopIteration(this, forLoopSymbolTable);
         generator.emitInc(i.get());
-        generator.emitDebugHook(WillExecuteStatement, m_expr->firstLine(), m_expr->startOffset(), m_expr->lineStartOffset());
+        generator.emitDebugHook(m_expr, WillExecuteStatement);
         generator.emitJump(loopStart.get());
 
         generator.emitLabel(scope->breakTarget());
@@ -2791,7 +2791,7 @@ void ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
         generator.prepareLexicalScopeForNextForLoopIteration(this, forLoopSymbolTable);
         generator.emitInc(enumeratorIndex.get());
         generator.emitEnumeratorStructurePropertyName(propertyName.get(), enumerator.get(), enumeratorIndex.get());
-        generator.emitDebugHook(WillExecuteStatement, m_expr->firstLine(), m_expr->startOffset(), m_expr->lineStartOffset());
+        generator.emitDebugHook(m_expr, WillExecuteStatement);
         generator.emitJump(loopStart.get());
         
         generator.emitLabel(scope->breakTarget());
@@ -2828,7 +2828,7 @@ void ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
         generator.prepareLexicalScopeForNextForLoopIteration(this, forLoopSymbolTable);
         generator.emitInc(enumeratorIndex.get());
         generator.emitEnumeratorGenericPropertyName(propertyName.get(), enumerator.get(), enumeratorIndex.get());
-        generator.emitDebugHook(WillExecuteStatement, m_expr->firstLine(), m_expr->startOffset(), m_expr->lineStartOffset());
+        generator.emitDebugHook(m_expr, WillExecuteStatement);
         generator.emitJump(loopStart.get());
 
         generator.emitLabel(scope->breakTarget());
@@ -2925,7 +2925,7 @@ Label* ContinueNode::trivialTarget(BytecodeGenerator& generator)
 
 void ContinueNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
 {
-    generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset());
+    generator.emitDebugHook(this);
     
     LabelScopePtr scope = generator.continueTarget(m_ident);
     ASSERT(scope);
@@ -2954,7 +2954,7 @@ Label* BreakNode::trivialTarget(BytecodeGenerator& generator)
 
 void BreakNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
 {
-    generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset());
+    generator.emitDebugHook(this);
     
     LabelScopePtr scope = generator.breakTarget(m_ident);
     ASSERT(scope);
@@ -2969,7 +2969,8 @@ void BreakNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
 
 void ReturnNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
-    generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset());
+    generator.emitDebugHook(this);
+
     ASSERT(generator.codeType() == FunctionCode);
 
     if (dst == generator.ignoredResult())
@@ -2996,7 +2997,7 @@ void ReturnNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 
 void WithNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
-    generator.emitDebugHook(WillExecuteStatement, m_expr->firstLine(), m_expr->startOffset(), m_expr->lineStartOffset());
+    generator.emitDebugHook(m_expr, WillExecuteStatement);
 
     RefPtr<RegisterID> scope = generator.emitNode(m_expr);
     generator.emitExpressionInfo(m_divot, m_divot - m_expressionLength, m_divot);
@@ -3171,7 +3172,7 @@ void CaseBlockNode::emitBytecodeForBlock(BytecodeGenerator& generator, RegisterI
 
 void SwitchNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
-    generator.emitDebugHook(WillExecuteStatement, m_expr->firstLine(), m_expr->startOffset(), m_expr->lineStartOffset());
+    generator.emitDebugHook(m_expr, WillExecuteStatement);
     
     LabelScopePtr scope = generator.newLabelScope(LabelScope::Switch);
 
@@ -3201,7 +3202,7 @@ void LabelNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 
 void ThrowNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
-    generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset());
+    generator.emitDebugHook(this);
 
     if (dst == generator.ignoredResult())
         dst = 0;
index e6abf69..c1504a1 100644 (file)
@@ -20,7 +20,7 @@
  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #pragma once
@@ -56,6 +56,7 @@ struct Breakpoint : public DoublyLinkedListNode<Breakpoint> {
         , autoContinue(other.autoContinue)
         , ignoreCount(other.ignoreCount)
         , hitCount(other.hitCount)
+        , resolved(other.resolved)
     {
     }
 
@@ -67,6 +68,7 @@ struct Breakpoint : public DoublyLinkedListNode<Breakpoint> {
     bool autoContinue { false };
     unsigned ignoreCount { 0 };
     unsigned hitCount { 0 };
+    bool resolved { false };
 
     static const unsigned unspecifiedColumn = UINT_MAX;
 
index fe7af25..da2787f 100644 (file)
 #include "Error.h"
 #include "HeapIterationScope.h"
 #include "Interpreter.h"
+#include "JSCInlines.h"
 #include "JSCJSValueInlines.h"
 #include "JSFunction.h"
 #include "JSGlobalObject.h"
-#include "JSCInlines.h"
 #include "MarkedSpaceInlines.h"
 #include "Parser.h"
 #include "Protect.h"
@@ -183,6 +183,9 @@ void Debugger::detach(JSGlobalObject* globalObject, ReasonForDetach reason)
         clearDebuggerRequests(globalObject);
 
     globalObject->setDebugger(nullptr);
+
+    if (m_globalObjects.isEmpty())
+        clearParsedData();
 }
 
 bool Debugger::isAttached(JSGlobalObject* globalObject)
@@ -251,6 +254,8 @@ void Debugger::didEvaluateScript(double startTime, ProfilingReason reason)
 
 void Debugger::toggleBreakpoint(CodeBlock* codeBlock, Breakpoint& breakpoint, BreakpointState enabledOrNot)
 {
+    ASSERT(breakpoint.resolved);
+
     ScriptExecutable* executable = codeBlock->ownerScriptExecutable();
 
     SourceID sourceID = static_cast<SourceID>(executable->sourceID());
@@ -278,6 +283,7 @@ void Debugger::toggleBreakpoint(CodeBlock* codeBlock, Breakpoint& breakpoint, Br
         if (line == endLine && column > endColumn)
             return;
     }
+
     if (!codeBlock->hasOpDebugForLineAndColumn(line, column))
         return;
 
@@ -331,15 +337,55 @@ void Debugger::recompileAllJSFunctions()
     m_vm.deleteAllCode();
 }
 
-BreakpointID Debugger::setBreakpoint(Breakpoint breakpoint, unsigned& actualLine, unsigned& actualColumn)
+DebuggerParseData& Debugger::debuggerParseData(SourceID sourceID, SourceProvider* provider)
+{
+    auto iter = m_parseDataMap.find(sourceID);
+    if (iter != m_parseDataMap.end())
+        return iter->value;
+
+    DebuggerParseData parseData;
+    gatherDebuggerParseDataForSource(m_vm, provider, parseData);
+    auto result = m_parseDataMap.add(sourceID, parseData);
+    return result.iterator->value;
+}
+
+void Debugger::resolveBreakpoint(Breakpoint& breakpoint, SourceProvider* sourceProvider)
+{
+    RELEASE_ASSERT(!breakpoint.resolved);
+    ASSERT(breakpoint.sourceID != noSourceID);
+
+    // FIXME: <https://webkit.org/b/162771> Web Inspector: Adopt TextPosition in Inspector to avoid oneBasedInt/zeroBasedInt ambiguity
+    // Inspector breakpoint line and column values are zero-based but the executable
+    // and CodeBlock line and column values are one-based.
+    unsigned line = breakpoint.line + 1;
+    unsigned column = breakpoint.column ? breakpoint.column : Breakpoint::unspecifiedColumn;
+
+    DebuggerParseData& parseData = debuggerParseData(breakpoint.sourceID, sourceProvider);
+    Optional<JSTextPosition> resolvedPosition = parseData.pausePositions.breakpointLocationForLineColumn((int)line, (int)column);
+    if (!resolvedPosition)
+        return;
+
+    unsigned resolvedLine = resolvedPosition->line;
+    unsigned resolvedColumn = resolvedPosition->offset - resolvedPosition->lineStartOffset + 1;
+
+    breakpoint.line = resolvedLine - 1;
+    breakpoint.column = resolvedColumn - 1;
+    breakpoint.resolved = true;
+}
+
+BreakpointID Debugger::setBreakpoint(Breakpoint& breakpoint, bool& existing)
 {
+    ASSERT(breakpoint.resolved);
+    ASSERT(breakpoint.sourceID != noSourceID);
+
     SourceID sourceID = breakpoint.sourceID;
     unsigned line = breakpoint.line;
     unsigned column = breakpoint.column;
 
-    SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(sourceID);
+    SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(breakpoint.sourceID);
     if (it == m_sourceIDToBreakpoints.end())
         it = m_sourceIDToBreakpoints.set(sourceID, LineToBreakpointsMap()).iterator;
+
     LineToBreakpointsMap::iterator breaksIt = it->value.find(line);
     if (breaksIt == it->value.end())
         breaksIt = it->value.set(line, adoptRef(new BreakpointsList)).iterator;
@@ -347,26 +393,23 @@ BreakpointID Debugger::setBreakpoint(Breakpoint breakpoint, unsigned& actualLine
     BreakpointsList& breakpoints = *breaksIt->value;
     for (Breakpoint* current = breakpoints.head(); current; current = current->next()) {
         if (current->column == column) {
-            // The breakpoint already exists. We're not allowed to create a new
-            // breakpoint at this location. Rather than returning the breakpointID
-            // of the pre-existing breakpoint, we need to return noBreakpointID
-            // to indicate that we're not creating a new one.
-            return noBreakpointID;
+            // Found existing breakpoint. Do not create a duplicate at this location.
+            existing = true;
+            return current->id;
         }
     }
 
+    existing = false;
     BreakpointID id = ++m_topBreakpointID;
     RELEASE_ASSERT(id != noBreakpointID);
 
     breakpoint.id = id;
-    actualLine = line;
-    actualColumn = column;
 
     Breakpoint* newBreakpoint = new Breakpoint(breakpoint);
     breakpoints.append(newBreakpoint);
     m_breakpointIDToBreakpoint.set(id, newBreakpoint);
 
-    toggleBreakpoint(breakpoint, BreakpointEnabled);
+    toggleBreakpoint(*newBreakpoint, BreakpointEnabled);
 
     return id;
 }
@@ -420,7 +463,7 @@ bool Debugger::hasBreakpoint(SourceID sourceID, const TextPosition& position, Br
 
     unsigned line = position.m_line.zeroBasedInt();
     unsigned column = position.m_column.zeroBasedInt();
-
+    
     LineToBreakpointsMap::const_iterator breaksIt = it->value.find(line);
     if (breaksIt == it->value.end())
         return false;
@@ -530,6 +573,11 @@ void Debugger::clearDebuggerRequests(JSGlobalObject* globalObject)
     m_vm.heap.forEachCodeBlock(functor);
 }
 
+void Debugger::clearParsedData()
+{
+    m_parseDataMap.clear();
+}
+
 void Debugger::setBreakpointsActivated(bool activated)
 {
     if (activated == m_breakpointsActivated)
index fde754f..318eca3 100644 (file)
@@ -24,6 +24,7 @@
 #include "Breakpoint.h"
 #include "CallData.h"
 #include "DebuggerCallFrame.h"
+#include "DebuggerParseData.h"
 #include "DebuggerPrimitives.h"
 #include "JSCJSValue.h"
 #include <wtf/HashMap.h>
@@ -72,9 +73,11 @@ public:
     void detach(JSGlobalObject*, ReasonForDetach);
     bool isAttached(JSGlobalObject*);
 
-    BreakpointID setBreakpoint(Breakpoint, unsigned& actualLine, unsigned& actualColumn);
+    void resolveBreakpoint(Breakpoint&, SourceProvider*);
+    BreakpointID setBreakpoint(Breakpoint&, bool& existing);
     void removeBreakpoint(BreakpointID);
     void clearBreakpoints();
+
     void activateBreakpoints() { setBreakpointsActivated(true); }
     void deactivateBreakpoints() { setBreakpointsActivated(false); }
     bool breakpointsActive() const { return m_breakpointsActivated; }
@@ -178,6 +181,8 @@ private:
 
     bool hasBreakpoint(SourceID, const TextPosition&, Breakpoint* hitBreakpoint);
 
+    DebuggerParseData& debuggerParseData(SourceID, SourceProvider*);
+
     void updateNeedForOpDebugCallbacks();
 
     // These update functions are only needed because our current breakpoints are
@@ -207,9 +212,11 @@ private:
     void toggleBreakpoint(Breakpoint&, BreakpointState);
 
     void clearDebuggerRequests(JSGlobalObject*);
+    void clearParsedData();
 
     VM& m_vm;
     HashSet<JSGlobalObject*> m_globalObjects;
+    HashMap<SourceID, DebuggerParseData> m_parseDataMap;
 
     PauseOnExceptionsState m_pauseOnExceptionsState;
     bool m_pauseAtNextOpportunity : 1;
diff --git a/Source/JavaScriptCore/debugger/DebuggerParseData.cpp b/Source/JavaScriptCore/debugger/DebuggerParseData.cpp
new file mode 100644 (file)
index 0000000..e71aac6
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "DebuggerParseData.h"
+
+#include "Parser.h"
+
+namespace JSC {
+
+Optional<JSTextPosition> DebuggerPausePositions::breakpointLocationForLineColumn(int line, int column)
+{
+    unsigned start = 0;
+    unsigned end = m_positions.size();
+    while (start != end) {
+        unsigned middle = start + ((end - start) / 2);
+        DebuggerPausePosition& pausePosition = m_positions[middle];
+        int pauseLine = pausePosition.position.line;
+        int pauseColumn = pausePosition.position.offset - pausePosition.position.lineStartOffset;
+
+        if (line < pauseLine) {
+            end = middle;
+            continue;
+        }
+        if (line > pauseLine) {
+            start = middle + 1;
+            continue;
+        }
+
+        if (column == pauseColumn) {
+            // Found an exact position match. Roll forward if this was a function Entry.
+            // We are guarenteed to have a Leave for an Entry so we don't need to bounds check.
+            while (true) {
+                if (pausePosition.type != DebuggerPausePositionType::Enter)
+                    return Optional<JSTextPosition>(pausePosition.position);
+                pausePosition = m_positions[middle++];
+            }
+        }
+
+        if (column < pauseColumn)
+            end = middle;
+        else
+            start = middle + 1;
+    }
+
+    // Past the end, no possible pause locations.
+    if (start >= m_positions.size())
+        return Nullopt;
+
+    // If the next location is a function Entry we will need to decide if we should go into
+    // the function or go past the function. We decide to go into the function if the
+    // input is on the same line as the function entry. For example:
+    //
+    //     1. x;
+    //     2.
+    //     3. function foo() {
+    //     4.     x;
+    //     5. }
+    //     6.
+    //     7. x;
+    //
+    // If the input was line 2, skip past functions to pause on line 7.
+    // If the input was line 3, go into the function to pause on line 4.
+
+    // Valid pause location. Use it.
+    DebuggerPausePosition& firstSlidePosition = m_positions[start];
+    if (firstSlidePosition.type != DebuggerPausePositionType::Enter)
+        return Optional<JSTextPosition>(firstSlidePosition.position);
+
+    // Determine if we should enter this function or skip past it.
+    // If entryStackSize is > 0 we are skipping functions.
+    bool shouldEnterFunction = firstSlidePosition.position.line == line;
+    int entryStackSize = shouldEnterFunction ? 0 : 1;
+    for (unsigned i = start + 1; i < m_positions.size(); ++i) {
+        DebuggerPausePosition& slidePosition = m_positions[i];
+        ASSERT(entryStackSize >= 0);
+
+        // Already skipping functions.
+        if (entryStackSize) {
+            if (slidePosition.type == DebuggerPausePositionType::Enter)
+                entryStackSize++;
+            else if (slidePosition.type == DebuggerPausePositionType::Leave)
+                entryStackSize--;
+            continue;
+        }
+
+        // Start skipping functions.
+        if (slidePosition.type == DebuggerPausePositionType::Enter) {
+            entryStackSize++;
+            continue;
+        }
+
+        // Found pause position.
+        return Optional<JSTextPosition>(slidePosition.position);
+    }
+
+    // No pause positions found.
+    return Nullopt;
+}
+
+void DebuggerPausePositions::sort()
+{
+    std::sort(m_positions.begin(), m_positions.end(), [] (const DebuggerPausePosition& a, const DebuggerPausePosition& b) {
+        return a.position.offset < b.position.offset;
+    });
+}
+
+typedef enum { Program, Module } DebuggerParseInfoTag;
+template <DebuggerParseInfoTag T> struct DebuggerParseInfo { };
+
+template <> struct DebuggerParseInfo<Program> {
+    typedef JSC::ProgramNode RootNode;
+    static const SourceParseMode parseMode = SourceParseMode::ProgramMode;
+    static const JSParserStrictMode strictMode = JSParserStrictMode::NotStrict;
+    static const JSParserScriptMode scriptMode = JSParserScriptMode::Classic;
+};
+
+template <> struct DebuggerParseInfo<Module> {
+    typedef JSC::ModuleProgramNode RootNode;
+    static const SourceParseMode parseMode = SourceParseMode::ModuleEvaluateMode;
+    static const JSParserStrictMode strictMode = JSParserStrictMode::Strict;
+    static const JSParserScriptMode scriptMode = JSParserScriptMode::Module;
+};
+
+template <DebuggerParseInfoTag T>
+bool gatherDebuggerParseData(VM& vm, const SourceCode& source, DebuggerParseData& debuggerParseData)
+{
+    typedef typename DebuggerParseInfo<T>::RootNode RootNode;
+    SourceParseMode parseMode = DebuggerParseInfo<T>::parseMode;
+    JSParserStrictMode strictMode = DebuggerParseInfo<T>::strictMode;
+    JSParserScriptMode scriptMode = DebuggerParseInfo<T>::scriptMode;
+
+    ParserError error;
+    std::unique_ptr<RootNode> rootNode = parse<RootNode>(&vm, source, Identifier(),
+        JSParserBuiltinMode::NotBuiltin, strictMode, scriptMode, parseMode, SuperBinding::NotNeeded,
+        error, nullptr, ConstructorKind::None, DerivedContextType::None, EvalContextType::None,
+        &debuggerParseData);
+    if (!rootNode)
+        return false;
+
+    debuggerParseData.pausePositions.sort();
+
+    return true;
+}
+
+bool gatherDebuggerParseDataForSource(VM& vm, SourceProvider* provider, DebuggerParseData& debuggerParseData)
+{
+    int startLine = provider->startPosition().m_line.oneBasedInt();
+    int startColumn = provider->startPosition().m_column.oneBasedInt();
+    SourceCode completeSource(provider, startLine, startColumn);
+
+    switch (provider->sourceType()) {
+    case SourceProviderSourceType::Program:
+        return gatherDebuggerParseData<Program>(vm, completeSource, debuggerParseData);        
+    case SourceProviderSourceType::Module:
+        return gatherDebuggerParseData<Module>(vm, completeSource, debuggerParseData);
+    default:
+        return false;
+    }
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/debugger/DebuggerParseData.h b/Source/JavaScriptCore/debugger/DebuggerParseData.h
new file mode 100644 (file)
index 0000000..0622515
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ParserTokens.h"
+#include <wtf/Optional.h>
+#include <wtf/Vector.h>
+
+namespace JSC {
+
+class SourceProvider;
+class VM;
+
+enum class DebuggerPausePositionType { Enter, Leave, Pause };
+struct DebuggerPausePosition {
+    DebuggerPausePositionType type;
+    JSTextPosition position;
+};
+
+class DebuggerPausePositions {
+public:
+    DebuggerPausePositions() { }
+    ~DebuggerPausePositions() { }
+
+    void appendPause(const JSTextPosition& position)
+    {
+        m_positions.append({ DebuggerPausePositionType::Pause, position });
+    }
+
+    void appendEntry(const JSTextPosition& position)
+    {
+        m_positions.append({ DebuggerPausePositionType::Enter, position });
+    }
+
+    void appendLeave(const JSTextPosition& position)
+    {
+        m_positions.append({ DebuggerPausePositionType::Leave, position });
+    }
+
+    Optional<JSTextPosition> breakpointLocationForLineColumn(int line, int column);
+
+    void sort();
+
+private:
+    Vector<DebuggerPausePosition> m_positions;
+};
+
+
+struct DebuggerParseData {
+    DebuggerParseData() { }
+    ~DebuggerParseData() { }
+
+    DebuggerPausePositions pausePositions;
+};
+
+bool gatherDebuggerParseDataForSource(VM&, SourceProvider*, DebuggerParseData&);
+
+} // namespace JSC
index e71b32b..5b0bb9f 100644 (file)
@@ -30,6 +30,7 @@
 #pragma once
 
 #include "debugger/Debugger.h"
+#include "parser/SourceProvider.h"
 #include <wtf/text/WTFString.h>
 
 namespace Inspector {
@@ -43,6 +44,7 @@ public:
         String source;
         String sourceURL;
         String sourceMappingURL;
+        RefPtr<JSC::SourceProvider> sourceProvider;
         int startLine {0};
         int startColumn {0};
         int endLine {0};
index 80261e8..9c5aa77 100644 (file)
@@ -36,7 +36,6 @@
 #include "Exception.h"
 #include "JSCInlines.h"
 #include "JSJavaScriptCallFrame.h"
-#include "JSLock.h"
 #include "JavaScriptCallFrame.h"
 #include "ScriptValue.h"
 #include "SourceProvider.h"
@@ -56,32 +55,36 @@ ScriptDebugServer::~ScriptDebugServer()
 {
 }
 
-JSC::BreakpointID ScriptDebugServer::setBreakpoint(JSC::SourceID sourceID, const ScriptBreakpoint& scriptBreakpoint, unsigned* actualLineNumber, unsigned* actualColumnNumber)
+void ScriptDebugServer::setBreakpointActions(BreakpointID id, const ScriptBreakpoint& scriptBreakpoint)
 {
-    if (!sourceID)
-        return JSC::noBreakpointID;
-
-    JSC::Breakpoint breakpoint(sourceID, scriptBreakpoint.lineNumber, scriptBreakpoint.columnNumber, scriptBreakpoint.condition, scriptBreakpoint.autoContinue, scriptBreakpoint.ignoreCount);
-    JSC::BreakpointID id = Debugger::setBreakpoint(breakpoint, *actualLineNumber, *actualColumnNumber);
-    if (id != JSC::noBreakpointID && !scriptBreakpoint.actions.isEmpty()) {
-#ifndef NDEBUG
-        BreakpointIDToActionsMap::iterator it = m_breakpointIDToActions.find(id);
-        ASSERT(it == m_breakpointIDToActions.end());
-#endif
-        const BreakpointActions& actions = scriptBreakpoint.actions;
-        m_breakpointIDToActions.set(id, actions);
-    }
-    return id;
+    ASSERT(id != noBreakpointID);
+    ASSERT(!m_breakpointIDToActions.contains(id));
+
+    m_breakpointIDToActions.set(id, scriptBreakpoint.actions);
+}
+
+void ScriptDebugServer::removeBreakpointActions(BreakpointID id)
+{
+    ASSERT(id != noBreakpointID);
+
+    m_breakpointIDToActions.remove(id);
 }
 
-void ScriptDebugServer::removeBreakpoint(JSC::BreakpointID id)
+const BreakpointActions& ScriptDebugServer::getActionsForBreakpoint(BreakpointID id)
 {
-    ASSERT(id != JSC::noBreakpointID);
-    BreakpointIDToActionsMap::iterator it = m_breakpointIDToActions.find(id);
-    if (it != m_breakpointIDToActions.end())
-        m_breakpointIDToActions.remove(it);
+    ASSERT(id != noBreakpointID);
 
-    Debugger::removeBreakpoint(id);
+    auto entry = m_breakpointIDToActions.find(id);
+    if (entry != m_breakpointIDToActions.end())
+        return entry->value;
+
+    static NeverDestroyed<BreakpointActions> emptyActionVector = BreakpointActions();
+    return emptyActionVector;
+}
+
+void ScriptDebugServer::clearBreakpointActions()
+{
+    m_breakpointIDToActions.clear();
 }
 
 bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction& breakpointAction)
@@ -111,7 +114,7 @@ bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction& b
         JSC::ExecState* exec = debuggerCallFrame->globalExec();
         if (exception)
             reportException(exec, exception);
-        
+
         dispatchBreakpointActionProbe(exec, breakpointAction, exception ? exception->value() : result);
         break;
     }
@@ -122,12 +125,6 @@ bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction& b
     return true;
 }
 
-void ScriptDebugServer::clearBreakpoints()
-{
-    Debugger::clearBreakpoints();
-    m_breakpointIDToActions.clear();
-}
-
 void ScriptDebugServer::dispatchDidPause(ScriptDebugListener* listener)
 {
     ASSERT(isPaused());
@@ -197,7 +194,9 @@ void ScriptDebugServer::dispatchDidParseSource(const ListenerSet& listeners, Sou
 {
     JSC::SourceID sourceID = sourceProvider->asID();
 
+    // FIXME: <https://webkit.org/b/162773> Web Inspector: Simplify ScriptDebugListener::Script to use SourceProvider
     ScriptDebugListener::Script script;
+    script.sourceProvider = sourceProvider;
     script.url = sourceProvider->url();
     script.source = sourceProvider->source().toString();
     script.startLine = sourceProvider->startPosition().m_line.zeroBasedInt();
@@ -289,9 +288,9 @@ void ScriptDebugServer::handleBreakpointHit(JSC::JSGlobalObject* globalObject, c
 
     m_currentProbeBatchId++;
 
-    BreakpointIDToActionsMap::iterator it = m_breakpointIDToActions.find(breakpoint.id);
-    if (it != m_breakpointIDToActions.end()) {
-        BreakpointActions actions = it->value;
+    auto entry = m_breakpointIDToActions.find(breakpoint.id);
+    if (entry != m_breakpointIDToActions.end()) {
+        BreakpointActions actions = entry->value;
         for (size_t i = 0; i < actions.size(); ++i) {
             if (!evaluateBreakpointAction(actions[i]))
                 return;
@@ -318,17 +317,6 @@ void ScriptDebugServer::handlePause(JSGlobalObject* vmEntryGlobalObject, Debugge
     dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidContinue);
 }
 
-const BreakpointActions& ScriptDebugServer::getActionsForBreakpoint(JSC::BreakpointID breakpointID)
-{
-    ASSERT(breakpointID != JSC::noBreakpointID);
-
-    if (m_breakpointIDToActions.contains(breakpointID))
-        return m_breakpointIDToActions.find(breakpointID)->value;
-    
-    static NeverDestroyed<BreakpointActions> emptyActionVector = BreakpointActions();
-    return emptyActionVector;
-}
-
 void ScriptDebugServer::addListener(ScriptDebugListener* listener)
 {
     ASSERT(listener);
index 8806fc5..7e4c543 100644 (file)
@@ -49,9 +49,11 @@ class JS_EXPORT_PRIVATE ScriptDebugServer : public JSC::Debugger {
     WTF_MAKE_NONCOPYABLE(ScriptDebugServer);
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    JSC::BreakpointID setBreakpoint(JSC::SourceID, const ScriptBreakpoint&, unsigned* actualLineNumber, unsigned* actualColumnNumber);
-    void removeBreakpoint(JSC::BreakpointID);
-    void clearBreakpoints();
+
+    // FIXME: Move BreakpointAction handling into JSC::Debugger or InspectorDebuggerAgent.
+    void setBreakpointActions(JSC::BreakpointID, const ScriptBreakpoint&);
+    void removeBreakpointActions(JSC::BreakpointID);
+    void clearBreakpointActions();
 
     const BreakpointActions& getActionsForBreakpoint(JSC::BreakpointID);
 
@@ -86,7 +88,7 @@ protected:
     void dispatchBreakpointActionSound(JSC::ExecState*, int breakpointActionIdentifier);
     void dispatchBreakpointActionProbe(JSC::ExecState*, const ScriptBreakpointAction&, JSC::JSValue sample);
 
-    bool m_doneProcessingDebuggerEvents {true};
+    bool m_doneProcessingDebuggerEvents { true };
 
 private:
     typedef HashMap<JSC::BreakpointID, BreakpointActions> BreakpointIDToActionsMap;
@@ -100,12 +102,10 @@ private:
     JSC::JSValue exceptionOrCaughtValue(JSC::ExecState*);
 
     BreakpointIDToActionsMap m_breakpointIDToActions;
-
     ListenerSet m_listeners;
-    bool m_callingListeners {false};
-
-    unsigned m_nextProbeSampleId {1};
-    unsigned m_currentProbeBatchId {0};
+    bool m_callingListeners { false };
+    unsigned m_nextProbeSampleId { 1 };
+    unsigned m_currentProbeBatchId { 0 };
 };
 
 } // namespace Inspector
index 21ce4cc..4fc2824 100644 (file)
@@ -289,6 +289,34 @@ bool InspectorDebuggerAgent::breakpointActionsFromProtocol(ErrorString& errorStr
     return true;
 }
 
+static RefPtr<Inspector::Protocol::Debugger::Location> buildDebuggerLocation(const JSC::Breakpoint& breakpoint)
+{
+    ASSERT(breakpoint.resolved);
+
+    auto location = Inspector::Protocol::Debugger::Location::create()
+        .setScriptId(String::number(breakpoint.sourceID))
+        .setLineNumber(breakpoint.line)
+        .release();
+    location->setColumnNumber(breakpoint.column);
+
+    return WTFMove(location);
+}
+
+static bool parseLocation(ErrorString& errorString, const InspectorObject& location, JSC::SourceID& sourceID, unsigned& lineNumber, unsigned& columnNumber)
+{
+    String scriptIDStr;
+    if (!location.getString(ASCIILiteral("scriptId"), scriptIDStr) || !location.getInteger(ASCIILiteral("lineNumber"), lineNumber)) {
+        sourceID = JSC::noSourceID;
+        errorString = ASCIILiteral("scriptId and lineNumber are required.");
+        return false;
+    }
+
+    sourceID = scriptIDStr.toIntPtr();
+    columnNumber = 0;
+    location.getInteger(ASCIILiteral("columnNumber"), columnNumber);
+    return true;
+}
+
 void InspectorDebuggerAgent::setBreakpointByUrl(ErrorString& errorString, int lineNumber, const String* const optionalURL, const String* const optionalURLRegex, const int* const optionalColumnNumber, const InspectorObject* options, Inspector::Protocol::Debugger::BreakpointId* outBreakpointIdentifier, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Debugger::Location>>& locations)
 {
     locations = Inspector::Protocol::Array<Inspector::Protocol::Debugger::Location>::create();
@@ -324,32 +352,30 @@ void InspectorDebuggerAgent::setBreakpointByUrl(ErrorString& errorString, int li
 
     m_javaScriptBreakpoints.set(breakpointIdentifier, buildObjectForBreakpointCookie(url, lineNumber, columnNumber, condition, actions, isRegex, autoContinue, ignoreCount));
 
-    ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition, breakpointActions, autoContinue, ignoreCount);
-    for (ScriptsMap::iterator it = m_scripts.begin(); it != m_scripts.end(); ++it) {
-        String scriptURLForBreakpoints = !it->value.sourceURL.isEmpty() ? it->value.sourceURL : it->value.url;
+    for (auto& entry : m_scripts) {
+        Script& script = entry.value;
+        String scriptURLForBreakpoints = !script.sourceURL.isEmpty() ? script.sourceURL : script.url;
         if (!matches(scriptURLForBreakpoints, url, isRegex))
             continue;
 
-        RefPtr<Inspector::Protocol::Debugger::Location> location = resolveBreakpoint(breakpointIdentifier, it->key, breakpoint);
-        if (location)
-            locations->addItem(WTFMove(location));
-    }
-    *outBreakpointIdentifier = breakpointIdentifier;
-}
+        JSC::SourceID sourceID = entry.key;
+        JSC::Breakpoint breakpoint(sourceID, lineNumber, columnNumber, condition, autoContinue, ignoreCount);
+        resolveBreakpoint(script, breakpoint);
+        if (!breakpoint.resolved)
+            continue;
 
-static bool parseLocation(ErrorString& errorString, const InspectorObject& location, JSC::SourceID& sourceID, unsigned& lineNumber, unsigned& columnNumber)
-{
-    String scriptIDStr;
-    if (!location.getString(ASCIILiteral("scriptId"), scriptIDStr) || !location.getInteger(ASCIILiteral("lineNumber"), lineNumber)) {
-        sourceID = JSC::noSourceID;
-        errorString = ASCIILiteral("scriptId and lineNumber are required.");
-        return false;
+        bool existing;
+        setBreakpoint(breakpoint, existing);
+        if (existing)
+            continue;
+
+        ScriptBreakpoint scriptBreakpoint(breakpoint.line, breakpoint.column, condition, breakpointActions, autoContinue, ignoreCount);
+        didSetBreakpoint(breakpoint, breakpointIdentifier, scriptBreakpoint);
+
+        locations->addItem(buildDebuggerLocation(breakpoint));
     }
 
-    sourceID = scriptIDStr.toIntPtr();
-    columnNumber = 0;
-    location.getInteger(ASCIILiteral("columnNumber"), columnNumber);
-    return true;
+    *outBreakpointIdentifier = breakpointIdentifier;
 }
 
 void InspectorDebuggerAgent::setBreakpoint(ErrorString& errorString, const InspectorObject& location, const InspectorObject* options, Inspector::Protocol::Debugger::BreakpointId* outBreakpointIdentifier, RefPtr<Inspector::Protocol::Debugger::Location>& actualLocation)
@@ -375,22 +401,61 @@ void InspectorDebuggerAgent::setBreakpoint(ErrorString& errorString, const Inspe
     if (!breakpointActionsFromProtocol(errorString, actions, &breakpointActions))
         return;
 
-    String breakpointIdentifier = String::number(sourceID) + ':' + String::number(lineNumber) + ':' + String::number(columnNumber);
-    if (m_breakpointIdentifierToDebugServerBreakpointIDs.find(breakpointIdentifier) != m_breakpointIdentifierToDebugServerBreakpointIDs.end()) {
-        errorString = ASCIILiteral("Breakpoint at specified location already exists.");
+    auto scriptIterator = m_scripts.find(sourceID);
+    if (scriptIterator == m_scripts.end()) {
+        errorString = ASCIILiteral("No script for id: ") + String::number(sourceID);
         return;
     }
 
-    ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition, breakpointActions, autoContinue, ignoreCount);
-    actualLocation = resolveBreakpoint(breakpointIdentifier, sourceID, breakpoint);
-    if (!actualLocation) {
+    Script& script = scriptIterator->value;
+    JSC::Breakpoint breakpoint(sourceID, lineNumber, columnNumber, condition, autoContinue, ignoreCount);
+    resolveBreakpoint(script, breakpoint);
+    if (!breakpoint.resolved) {
         errorString = ASCIILiteral("Could not resolve breakpoint");
         return;
     }
 
+    bool existing;
+    setBreakpoint(breakpoint, existing);
+    if (existing) {
+        errorString = ASCIILiteral("Breakpoint at specified location already exists");
+        return;
+    }
+
+    String breakpointIdentifier = String::number(sourceID) + ':' + String::number(breakpoint.line) + ':' + String::number(breakpoint.column);
+    ScriptBreakpoint scriptBreakpoint(breakpoint.line, breakpoint.column, condition, breakpointActions, autoContinue, ignoreCount);
+    didSetBreakpoint(breakpoint, breakpointIdentifier, scriptBreakpoint);
+
+    actualLocation = buildDebuggerLocation(breakpoint);
     *outBreakpointIdentifier = breakpointIdentifier;
 }
 
+void InspectorDebuggerAgent::didSetBreakpoint(const JSC::Breakpoint& breakpoint, const String& breakpointIdentifier, const ScriptBreakpoint& scriptBreakpoint)
+{
+    JSC::BreakpointID id = breakpoint.id;
+    m_scriptDebugServer.setBreakpointActions(id, scriptBreakpoint);
+
+    auto debugServerBreakpointIDsIterator = m_breakpointIdentifierToDebugServerBreakpointIDs.find(breakpointIdentifier);
+    if (debugServerBreakpointIDsIterator == m_breakpointIdentifierToDebugServerBreakpointIDs.end())
+        debugServerBreakpointIDsIterator = m_breakpointIdentifierToDebugServerBreakpointIDs.set(breakpointIdentifier, Vector<JSC::BreakpointID>()).iterator;
+    debugServerBreakpointIDsIterator->value.append(id);
+
+    m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.set(id, breakpointIdentifier);
+}
+
+void InspectorDebuggerAgent::resolveBreakpoint(const Script& script, JSC::Breakpoint& breakpoint)
+{
+    if (breakpoint.line < static_cast<unsigned>(script.startLine) || static_cast<unsigned>(script.endLine) < breakpoint.line)
+        return;
+
+    m_scriptDebugServer.resolveBreakpoint(breakpoint, script.sourceProvider.get());
+}
+
+void InspectorDebuggerAgent::setBreakpoint(JSC::Breakpoint& breakpoint, bool& existing)
+{
+    m_scriptDebugServer.setBreakpoint(breakpoint, existing);
+}
+
 void InspectorDebuggerAgent::removeBreakpoint(ErrorString&, const String& breakpointIdentifier)
 {
     m_javaScriptBreakpoints.remove(breakpointIdentifier);
@@ -402,6 +467,7 @@ void InspectorDebuggerAgent::removeBreakpoint(ErrorString&, const String& breakp
         for (auto& action : breakpointActions)
             m_injectedScriptManager.releaseObjectGroup(objectGroupForBreakpointAction(action));
 
+        m_scriptDebugServer.removeBreakpointActions(breakpointID);
         m_scriptDebugServer.removeBreakpoint(breakpointID);
     }
 }
@@ -419,39 +485,35 @@ void InspectorDebuggerAgent::continueToLocation(ErrorString& errorString, const
     if (!parseLocation(errorString, location, sourceID, lineNumber, columnNumber))
         return;
 
-    ScriptBreakpoint breakpoint(lineNumber, columnNumber, "", false, 0);
-    m_continueToLocationBreakpointID = m_scriptDebugServer.setBreakpoint(sourceID, breakpoint, &lineNumber, &columnNumber);
-    resume(errorString);
-}
+    auto scriptIterator = m_scripts.find(sourceID);
+    if (scriptIterator == m_scripts.end()) {
+        m_scriptDebugServer.continueProgram();
+        errorString = ASCIILiteral("No script for id: ") + String::number(sourceID);
+        return;
+    }
 
-RefPtr<Inspector::Protocol::Debugger::Location> InspectorDebuggerAgent::resolveBreakpoint(const String& breakpointIdentifier, JSC::SourceID sourceID, const ScriptBreakpoint& breakpoint)
-{
-    ScriptsMap::iterator scriptIterator = m_scripts.find(sourceID);
-    if (scriptIterator == m_scripts.end())
-        return nullptr;
+    String condition;
+    bool autoContinue = false;
+    unsigned ignoreCount = 0;
+    JSC::Breakpoint breakpoint(sourceID, lineNumber, columnNumber, condition, autoContinue, ignoreCount);
     Script& script = scriptIterator->value;
-    if (breakpoint.lineNumber < script.startLine || script.endLine < breakpoint.lineNumber)
-        return nullptr;
+    resolveBreakpoint(script, breakpoint);
+    if (!breakpoint.resolved) {
+        m_scriptDebugServer.continueProgram();
+        errorString = ASCIILiteral("Could not resolve breakpoint");
+        return;
+    }
 
-    unsigned actualLineNumber;
-    unsigned actualColumnNumber;
-    JSC::BreakpointID debugServerBreakpointID = m_scriptDebugServer.setBreakpoint(sourceID, breakpoint, &actualLineNumber, &actualColumnNumber);
-    if (debugServerBreakpointID == JSC::noBreakpointID)
-        return nullptr;
+    bool existing;
+    setBreakpoint(breakpoint, existing);
+    if (existing) {
+        m_scriptDebugServer.continueProgram();
+        return;
+    }
 
-    BreakpointIdentifierToDebugServerBreakpointIDsMap::iterator debugServerBreakpointIDsIterator = m_breakpointIdentifierToDebugServerBreakpointIDs.find(breakpointIdentifier);
-    if (debugServerBreakpointIDsIterator == m_breakpointIdentifierToDebugServerBreakpointIDs.end())
-        debugServerBreakpointIDsIterator = m_breakpointIdentifierToDebugServerBreakpointIDs.set(breakpointIdentifier, Vector<JSC::BreakpointID>()).iterator;
-    debugServerBreakpointIDsIterator->value.append(debugServerBreakpointID);
-    
-    m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.set(debugServerBreakpointID, breakpointIdentifier);
+    m_continueToLocationBreakpointID = breakpoint.id;
 
-    auto location = Inspector::Protocol::Debugger::Location::create()
-        .setScriptId(String::number(sourceID))
-        .setLineNumber(actualLineNumber)
-        .release();
-    location->setColumnNumber(actualColumnNumber);
-    return WTFMove(location);
+    resume(errorString);
 }
 
 void InspectorDebuggerAgent::searchInContent(ErrorString& error, const String& scriptIDStr, const String& query, const bool* optionalCaseSensitive, const bool* optionalIsRegex, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::GenericTypes::SearchMatch>>& results)
@@ -638,10 +700,8 @@ void InspectorDebuggerAgent::didParseSource(JSC::SourceID sourceID, const Script
     if (scriptURLForBreakpoints.isEmpty())
         return;
 
-    for (auto it = m_javaScriptBreakpoints.begin(), end = m_javaScriptBreakpoints.end(); it != end; ++it) {
-        RefPtr<InspectorObject> breakpointObject;
-        if (!it->value->asObject(breakpointObject))
-            return;
+    for (auto& entry : m_javaScriptBreakpoints) {
+        RefPtr<InspectorObject> breakpointObject = entry.value;
 
         bool isRegex;
         String url;
@@ -650,23 +710,34 @@ void InspectorDebuggerAgent::didParseSource(JSC::SourceID sourceID, const Script
         if (!matches(scriptURLForBreakpoints, url, isRegex))
             continue;
 
-        ScriptBreakpoint breakpoint;
-        breakpointObject->getInteger(ASCIILiteral("lineNumber"), breakpoint.lineNumber);
-        breakpointObject->getInteger(ASCIILiteral("columnNumber"), breakpoint.columnNumber);
-        breakpointObject->getString(ASCIILiteral("condition"), breakpoint.condition);
-        breakpointObject->getBoolean(ASCIILiteral("autoContinue"), breakpoint.autoContinue);
-        breakpointObject->getInteger(ASCIILiteral("ignoreCount"), breakpoint.ignoreCount);
+        ScriptBreakpoint scriptBreakpoint;
+        breakpointObject->getInteger(ASCIILiteral("lineNumber"), scriptBreakpoint.lineNumber);
+        breakpointObject->getInteger(ASCIILiteral("columnNumber"), scriptBreakpoint.columnNumber);
+        breakpointObject->getString(ASCIILiteral("condition"), scriptBreakpoint.condition);
+        breakpointObject->getBoolean(ASCIILiteral("autoContinue"), scriptBreakpoint.autoContinue);
+        breakpointObject->getInteger(ASCIILiteral("ignoreCount"), scriptBreakpoint.ignoreCount);
         ErrorString errorString;
         RefPtr<InspectorArray> actions;
         breakpointObject->getArray(ASCIILiteral("actions"), actions);
-        if (!breakpointActionsFromProtocol(errorString, actions, &breakpoint.actions)) {
+        if (!breakpointActionsFromProtocol(errorString, actions, &scriptBreakpoint.actions)) {
             ASSERT_NOT_REACHED();
             continue;
         }
 
-        RefPtr<Inspector::Protocol::Debugger::Location> location = resolveBreakpoint(it->key, sourceID, breakpoint);
-        if (location)
-            m_frontendDispatcher->breakpointResolved(it->key, location);
+        JSC::Breakpoint breakpoint(sourceID, scriptBreakpoint.lineNumber, scriptBreakpoint.columnNumber, scriptBreakpoint.condition, scriptBreakpoint.autoContinue, scriptBreakpoint.ignoreCount);
+        resolveBreakpoint(script, breakpoint);
+        if (!breakpoint.resolved)
+            continue;
+
+        bool existing;
+        setBreakpoint(breakpoint, existing);
+        if (existing)
+            continue;
+
+        String breakpointIdentifier = entry.key;
+        didSetBreakpoint(breakpoint, breakpointIdentifier, scriptBreakpoint);
+
+        m_frontendDispatcher->breakpointResolved(breakpointIdentifier, buildDebuggerLocation(breakpoint));
     }
 }
 
@@ -795,6 +866,7 @@ void InspectorDebuggerAgent::clearInspectorBreakpointState()
 
 void InspectorDebuggerAgent::clearDebuggerBreakpointState()
 {
+    m_scriptDebugServer.clearBreakpointActions();
     m_scriptDebugServer.clearBreakpoints();
 
     m_pausedScriptState = nullptr;
index 615028e..3d19a19 100644 (file)
@@ -47,7 +47,6 @@ class InjectedScript;
 class InjectedScriptManager;
 class InspectorArray;
 class InspectorObject;
-class InspectorValue;
 class ScriptDebugServer;
 typedef String ErrorString;
 
@@ -131,7 +130,10 @@ private:
     void breakpointActionSound(int breakpointActionIdentifier) final;
     void breakpointActionProbe(JSC::ExecState&, const ScriptBreakpointAction&, unsigned batchId, unsigned sampleId, JSC::JSValue sample) final;
 
-    RefPtr<Inspector::Protocol::Debugger::Location> resolveBreakpoint(const String& breakpointIdentifier, JSC::SourceID, const ScriptBreakpoint&);
+    void resolveBreakpoint(const Script&, JSC::Breakpoint&);
+    void setBreakpoint(JSC::Breakpoint&, bool& existing);    
+    void didSetBreakpoint(const JSC::Breakpoint&, const String&, const ScriptBreakpoint&);
+
     bool assertPaused(ErrorString&);
     void clearDebuggerBreakpointState();
     void clearInspectorBreakpointState();
index 8ba4d20..f15000b 100644 (file)
@@ -1977,7 +1977,7 @@ EncodedJSValue JSC_HOST_CALL functionCheckModuleSyntax(ExecState* exec)
     stopWatch.start();
 
     ParserError error;
-    bool validSyntax = checkModuleSyntax(exec, makeSource(source), error);
+    bool validSyntax = checkModuleSyntax(exec, makeSource(source, String(), TextPosition::minimumPosition(), SourceProviderSourceType::Module), error);
     stopWatch.stop();
 
     if (!validSyntax)
index 94754aa..5c03f19 100644 (file)
@@ -612,6 +612,11 @@ public:
         return isObjectLiteral(node) || isArrayLiteral(node);
     }
 
+    bool shouldSkipPauseLocation(StatementNode* statement) const
+    {
+        return !statement || statement->isLabel();
+    }
+
     StatementNode* createEmptyStatement(const JSTokenLocation& location) { return new (m_parserArena) EmptyStatementNode(location); }
 
     StatementNode* createDeclarationStatement(const JSTokenLocation& location, ExpressionNode* expr, int start, int end)
@@ -968,6 +973,17 @@ public:
         node->setStartOffset(offset);
     }
 
+    JSTextPosition breakpointLocation(StatementNode* statement)
+    {
+        statement->setNeedsDebugHook();
+        return statement->position();
+    }
+
+    JSTextPosition breakpointLocation(ExpressionNode* expr)
+    {
+        expr->setNeedsDebugHook();
+        return expr->position();
+    }
 
     void propagateArgumentsUse() { usesArguments(); }
     
index 6314486..7b9f88c 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "BuiltinNames.h"
 #include "Error.h"
+#include "Interpreter.h"
 #include "JITCode.h"
 #include "ParserArena.h"
 #include "ParserTokens.h"
@@ -190,8 +191,12 @@ namespace JSC {
 
         ResultType resultDescriptor() const { return m_resultType; }
 
+        bool needsDebugHook() { return m_needsDebugHook; }
+        void setNeedsDebugHook() { m_needsDebugHook = true; }
+
     private:
         ResultType m_resultType;
+        bool m_needsDebugHook { false };
     };
 
     class StatementNode : public Node {
@@ -213,14 +218,19 @@ namespace JSC {
         virtual bool isExprStatement() const { return false; }
         virtual bool isBreak() const { return false; }
         virtual bool isContinue() const { return false; }
+        virtual bool isLabel() const { return false; }
         virtual bool isBlock() const { return false; }
         virtual bool isFuncDeclNode() const { return false; }
         virtual bool isModuleDeclarationNode() const { return false; }
         virtual bool isForOfNode() const { return false; }
 
+        bool needsDebugHook() { return m_needsDebugHook; }
+        void setNeedsDebugHook() { m_needsDebugHook = true; }
+
     protected:
         StatementNode* m_next;
         int m_lastLine;
+        bool m_needsDebugHook { false };
     };
 
     class VariableEnvironmentNode : public ParserArenaDeletable {
@@ -1546,6 +1556,8 @@ namespace JSC {
     public:
         LabelNode(const JSTokenLocation&, const Identifier& name, StatementNode*);
 
+        bool isLabel() const override { return true; }
+
     private:
         void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
 
index 727a00f..f19c1c2 100644 (file)
 #include "Parser.h"
 
 #include "ASTBuilder.h"
+#include "DebuggerParseData.h"
 #include "JSCInlines.h"
 #include "SetForScope.h"
 #include "VM.h"
 #include <utility>
 #include <wtf/StringPrintStream.h>
 
-
 #define updateErrorMessage(shouldPrintToken, ...) do {\
     propagateError(); \
     logError(shouldPrintToken, __VA_ARGS__); \
@@ -190,7 +190,7 @@ void Parser<LexerType>::logError(bool shouldPrintToken, const A& value1, const B
 }
 
 template <typename LexerType>
-Parser<LexerType>::Parser(VM* vm, const SourceCode& source, JSParserBuiltinMode builtinMode, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, SourceParseMode parseMode, SuperBinding superBinding, ConstructorKind defaultConstructorKind, DerivedContextType derivedContextType, bool isEvalContext, EvalContextType evalContextType)
+Parser<LexerType>::Parser(VM* vm, const SourceCode& source, JSParserBuiltinMode builtinMode, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, SourceParseMode parseMode, SuperBinding superBinding, ConstructorKind defaultConstructorKind, DerivedContextType derivedContextType, bool isEvalContext, EvalContextType evalContextType, DebuggerParseData* debuggerParseData)
     : m_vm(vm)
     , m_source(&source)
     , m_hasStackOverflow(false)
@@ -203,6 +203,7 @@ Parser<LexerType>::Parser(VM* vm, const SourceCode& source, JSParserBuiltinMode
     , m_superBinding(superBinding)
     , m_defaultConstructorKind(defaultConstructorKind)
     , m_immediateParentAllowsFunctionDeclarationInStatement(false)
+    , m_debuggerParseData(debuggerParseData)
 {
     m_lexer = std::make_unique<LexerType>(vm, builtinMode, scriptMode);
     m_lexer->setCode(source, &m_parserArena);
@@ -592,9 +593,12 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementList
     m_statementDepth++;
     TreeStatement result = 0;
     bool shouldSetEndOffset = true;
+    bool shouldSetPauseLocation = false;
+
     switch (m_token.m_type) {
     case CONSTTOKEN:
         result = parseVariableDeclaration(context, DeclarationType::ConstDeclaration);
+        shouldSetPauseLocation = true;
         break;
     case LET: {
         bool shouldParseVariableDeclaration = true;
@@ -616,7 +620,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementList
             bool allowFunctionDeclarationAsStatement = true;
             result = parseExpressionOrLabelStatement(context, allowFunctionDeclarationAsStatement);
         }
-
+        shouldSetPauseLocation = !context.shouldSkipPauseLocation(result);
         break;
     }
     case CLASSTOKEN:
@@ -648,6 +652,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementList
         // ``` function foo() { label: function bar() { } } ```
         bool allowFunctionDeclarationAsStatement = true;
         result = parseExpressionOrLabelStatement(context, allowFunctionDeclarationAsStatement);
+        shouldSetPauseLocation = !context.shouldSkipPauseLocation(result);
         break;
     }
     default:
@@ -657,8 +662,12 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementList
         break;
     }
 
-    if (result && shouldSetEndOffset)
-        context.setEndOffset(result, m_lastTokenEndPosition.offset);
+    if (result) {
+        if (shouldSetEndOffset)
+            context.setEndOffset(result, m_lastTokenEndPosition.offset);
+        if (shouldSetPauseLocation)
+            recordPauseLocation(context.breakpointLocation(result));
+    }
 
     return result;
 }
@@ -700,6 +709,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseDoWhileStatem
     semanticFailIfTrue(match(CLOSEPAREN), "Must provide an expression as a do-while loop condition");
     TreeExpression expr = parseExpression(context);
     failIfFalse(expr, "Unable to parse do-while loop condition");
+    recordPauseLocation(context.breakpointLocation(expr));
     handleProductionOrFail(CLOSEPAREN, ")", "end", "do-while loop condition");
     if (match(SEMICOLON))
         next(); // Always performs automatic semicolon insertion.
@@ -718,9 +728,10 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseWhileStatemen
     semanticFailIfTrue(match(CLOSEPAREN), "Must provide an expression as a while loop condition");
     TreeExpression expr = parseExpression(context);
     failIfFalse(expr, "Unable to parse while loop condition");
+    recordPauseLocation(context.breakpointLocation(expr));
     int endLine = tokenLine();
     handleProductionOrFail(CLOSEPAREN, ")", "end", "while loop condition");
-    
+
     const Identifier* unused = 0;
     startLoop();
     TreeStatement statement = parseStatement(context, unused);
@@ -922,6 +933,7 @@ template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseArrowFun
     TreeSourceElements sourceElements = context.createSourceElements();
     TreeStatement body = context.createReturnStatement(location, expr, start, end);
     context.setEndOffset(body, m_lastTokenEndPosition.offset);
+    recordPauseLocation(context.breakpointLocation(body));
     context.appendStatement(sourceElements, body);
 
     return sourceElements;
@@ -1249,6 +1261,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement(
         }
         TreeExpression expr = parseExpression(context);
         failIfFalse(expr, "Expected expression to enumerate");
+        recordPauseLocation(context.breakpointLocation(expr));
         JSTextPosition exprEnd = lastTokenEndPosition();
         
         int endLine = tokenLine();
@@ -1291,11 +1304,14 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement(
         declsEnd = lastTokenEndPosition();
         m_allowsIn = true;
         failIfFalse(decls, "Cannot parse for loop declarations");
+        recordPauseLocation(context.breakpointLocation(decls));
     }
     
     if (match(SEMICOLON)) {
     standardForLoop:
         // Standard for loop
+        if (decls)
+            recordPauseLocation(context.breakpointLocation(decls));
         next();
         TreeExpression condition = 0;
         failIfTrue(forLoopConstDoesNotHaveInitializer && isConstDeclaration, "const variables in for loops must have initializers");
@@ -1303,6 +1319,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement(
         if (!match(SEMICOLON)) {
             condition = parseExpression(context);
             failIfFalse(condition, "Cannot parse for loop condition expression");
+            recordPauseLocation(context.breakpointLocation(condition));
         }
         consumeOrFail(SEMICOLON, "Expected a ';' after the for loop condition expression");
         
@@ -1310,6 +1327,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement(
         if (!match(CLOSEPAREN)) {
             increment = parseExpression(context);
             failIfFalse(increment, "Cannot parse for loop iteration expression");
+            recordPauseLocation(context.breakpointLocation(increment));
         }
         int endLine = tokenLine();
         handleProductionOrFail(CLOSEPAREN, ")", "end", "for-loop header");
@@ -1335,6 +1353,7 @@ enumerationLoop:
     }
     TreeExpression expr = parseExpression(context);
     failIfFalse(expr, "Cannot parse subject for-", isOfEnumeration ? "of" : "in", " statement");
+    recordPauseLocation(context.breakpointLocation(expr));
     JSTextPosition exprEnd = lastTokenEndPosition();
     int endLine = tokenLine();
     
@@ -1469,6 +1488,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseWithStatement
     int start = tokenStart();
     TreeExpression expr = parseExpression(context);
     failIfFalse(expr, "Cannot parse 'with' subject expression");
+    recordPauseLocation(context.breakpointLocation(expr));
     JSTextPosition end = lastTokenEndPosition();
     int endLine = tokenLine();
     handleProductionOrFail(CLOSEPAREN, ")", "start", "subject of a 'with' statement");
@@ -1489,6 +1509,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseSwitchStateme
     handleProductionOrFail(OPENPAREN, "(", "start", "subject of a 'switch'");
     TreeExpression expr = parseExpression(context);
     failIfFalse(expr, "Cannot parse switch subject expression");
+    recordPauseLocation(context.breakpointLocation(expr));
     int endLine = tokenLine();
     
     handleProductionOrFail(CLOSEPAREN, ")", "end", "subject of a 'switch'");
@@ -1681,6 +1702,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatement(Tre
     failIfStackOverflow();
     TreeStatement result = 0;
     bool shouldSetEndOffset = true;
+    bool shouldSetPauseLocation = false;
     bool parentAllowsFunctionDeclarationAsStatement = m_immediateParentAllowsFunctionDeclarationInStatement;
     m_immediateParentAllowsFunctionDeclarationInStatement = false;
 
@@ -1691,6 +1713,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatement(Tre
         break;
     case VAR:
         result = parseVariableDeclaration(context, DeclarationType::VarDeclaration);
+        shouldSetPauseLocation = true;
         break;
     case FUNCTION: {
         const bool isAsync = false;
@@ -1701,6 +1724,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatement(Tre
         JSTokenLocation location(tokenLocation());
         next();
         result = context.createEmptyStatement(location);
+        shouldSetPauseLocation = true;
         break;
     }
     case IF:
@@ -1717,12 +1741,15 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatement(Tre
         break;
     case CONTINUE:
         result = parseContinueStatement(context);
+        shouldSetPauseLocation = true;
         break;
     case BREAK:
         result = parseBreakStatement(context);
+        shouldSetPauseLocation = true;
         break;
     case RETURN:
         result = parseReturnStatement(context);
+        shouldSetPauseLocation = true;
         break;
     case WITH:
         result = parseWithStatement(context);
@@ -1732,12 +1759,14 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatement(Tre
         break;
     case THROW:
         result = parseThrowStatement(context);
+        shouldSetPauseLocation = true;
         break;
     case TRY:
         result = parseTryStatement(context);
         break;
     case DEBUGGER:
         result = parseDebuggerStatement(context);
+        shouldSetPauseLocation = true;
         break;
     case EOFTOK:
     case CASE:
@@ -1755,6 +1784,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatement(Tre
     case YIELD: {
         bool allowFunctionDeclarationAsStatement = false;
         result = parseExpressionOrLabelStatement(context, allowFunctionDeclarationAsStatement);
+        shouldSetPauseLocation = !context.shouldSkipPauseLocation(result);
         break;
     }
     case STRING:
@@ -1768,11 +1798,17 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatement(Tre
         if (directive && nonTrivialExpressionCount != m_parserState.nonTrivialExpressionCount)
             directive = nullptr;
         result = exprStatement;
+        shouldSetPauseLocation = true;
         break;
     }
 
-    if (result && shouldSetEndOffset)
-        context.setEndOffset(result, m_lastTokenEndPosition.offset);
+    if (result) {
+        if (shouldSetEndOffset)
+            context.setEndOffset(result, m_lastTokenEndPosition.offset);
+        if (shouldSetPauseLocation)
+            recordPauseLocation(context.breakpointLocation(result));
+    }
+
     return result;
 }
 
@@ -1908,10 +1944,17 @@ template <class TreeBuilder> TreeFunctionBody Parser<LexerType>::parseFunctionBo
 
     DepthManager statementDepth(&m_statementDepth);
     m_statementDepth = 0;
-    if (bodyType == ArrowFunctionBodyExpression)
-        failIfFalse(parseArrowFunctionSingleExpressionBodySourceElements(syntaxChecker), "Cannot parse body of this arrow function");
-    else
-        failIfFalse(parseSourceElements(syntaxChecker, CheckForStrictMode), bodyType == StandardFunctionBodyBlock ? "Cannot parse body of this function" : "Cannot parse body of this arrow function");
+    if (bodyType == ArrowFunctionBodyExpression) {
+        if (m_debuggerParseData)
+            failIfFalse(parseArrowFunctionSingleExpressionBodySourceElements(context), "Cannot parse body of this arrow function");
+        else
+            failIfFalse(parseArrowFunctionSingleExpressionBodySourceElements(syntaxChecker), "Cannot parse body of this arrow function");
+    } else {
+        if (m_debuggerParseData)
+            failIfFalse(parseSourceElements(context, CheckForStrictMode), bodyType == StandardFunctionBodyBlock ? "Cannot parse body of this function" : "Cannot parse body of this arrow function");
+        else
+            failIfFalse(parseSourceElements(syntaxChecker, CheckForStrictMode), bodyType == StandardFunctionBodyBlock ? "Cannot parse body of this function" : "Cannot parse body of this arrow function");
+    }
     unsigned endColumn = tokenColumn();
     return context.createFunctionMetadata(startLocation, tokenLocation(), startColumn, endColumn, functionKeywordStart, functionNameStart, parametersStart, strictMode(), constructorKind, superBinding, parameterCount, functionLength, parseMode, isArrowFunctionBodyExpression);
 }
@@ -2082,6 +2125,9 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuild
         if (UNLIKELY(!Options::useSourceProviderCache()))
             return false;
 
+        if (UNLIKELY(m_debuggerParseData))
+            return false;
+
         ASSERT(parametersStart != -1);
         ASSERT(startColumn != -1);
 
@@ -2328,8 +2374,11 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuild
     if (functionBodyType == ArrowFunctionBodyExpression) {
         location = locationBeforeLastToken();
         functionInfo.endOffset = location.endOffset;
+    } else {
+        recordFunctionEntryLocation(JSTextPosition(startLocation.line, startLocation.startOffset, startLocation.lineStartOffset));
+        recordFunctionLeaveLocation(JSTextPosition(location.line, location.startOffset, location.lineStartOffset));
     }
-    
+
     // Cache the tokenizer state and the function scope the first time the function is parsed.
     // Any future reparsing can then skip the function.
     // For arrow function is 8 = x=>x + 4 symbols;
@@ -2833,6 +2882,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseIfStatement(T
 
     TreeExpression condition = parseExpression(context);
     failIfFalse(condition, "Expected a expression as the condition for an if statement");
+    recordPauseLocation(context.breakpointLocation(condition));
     int end = tokenLine();
     handleProductionOrFail2(CLOSEPAREN, ")", "end", "'if' condition");
 
@@ -2868,6 +2918,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseIfStatement(T
 
         TreeExpression innerCondition = parseExpression(context);
         failIfFalse(innerCondition, "Expected a expression as the condition for an if statement");
+        recordPauseLocation(context.breakpointLocation(innerCondition));
         int innerEnd = tokenLine();
         handleProductionOrFail2(CLOSEPAREN, ")", "end", "'if' condition");
         const Identifier* unused = 0;
@@ -3853,6 +3904,36 @@ template <class TreeBuilder> bool Parser<LexerType>::shouldCheckPropertyForUnder
 }
 
 template <typename LexerType>
+void Parser<LexerType>::recordPauseLocation(const JSTextPosition& position)
+{
+    if (LIKELY(!m_debuggerParseData))
+        return;
+
+    if (position.line < 0)
+        return;
+
+    m_debuggerParseData->pausePositions.appendPause(position);
+}
+
+template <typename LexerType>
+void Parser<LexerType>::recordFunctionEntryLocation(const JSTextPosition& position)
+{
+    if (LIKELY(!m_debuggerParseData))
+        return;
+
+    m_debuggerParseData->pausePositions.appendEntry(position);
+}
+
+template <typename LexerType>
+void Parser<LexerType>::recordFunctionLeaveLocation(const JSTextPosition& position)
+{
+    if (LIKELY(!m_debuggerParseData))
+        return;
+
+    m_debuggerParseData->pausePositions.appendLeave(position);
+}
+
+template <typename LexerType>
 template <class TreeBuilder> TreeExpression Parser<LexerType>::parseObjectLiteral(TreeBuilder& context)
 {
     SavePoint savePoint = createSavePoint();
index 825b9da..8c4474b 100644 (file)
@@ -47,6 +47,7 @@ class Identifier;
 class VM;
 class SourceCode;
 class SyntaxChecker;
+struct DebuggerParseData;
 
 // Macros to make the more common TreeBuilder types a little less verbose
 #define TreeStatement typename TreeBuilder::Statement
@@ -847,7 +848,7 @@ class Parser {
     WTF_MAKE_FAST_ALLOCATED;
 
 public:
-    Parser(VM*, const SourceCode&, JSParserBuiltinMode, JSParserStrictMode, JSParserScriptMode, SourceParseMode, SuperBinding, ConstructorKind defaultConstructorKind = ConstructorKind::None, DerivedContextType = DerivedContextType::None, bool isEvalContext = false, EvalContextType = EvalContextType::None);
+    Parser(VM*, const SourceCode&, JSParserBuiltinMode, JSParserStrictMode, JSParserScr