1 | /* |
2 | * Copyright (C) 2016 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "DebuggerParseData.h" |
28 | |
29 | #include "Parser.h" |
30 | #include <wtf/Optional.h> |
31 | |
32 | namespace JSC { |
33 | |
34 | Optional<JSTextPosition> DebuggerPausePositions::breakpointLocationForLineColumn(int line, int column) |
35 | { |
36 | unsigned start = 0; |
37 | unsigned end = m_positions.size(); |
38 | while (start != end) { |
39 | unsigned middle = start + ((end - start) / 2); |
40 | DebuggerPausePosition& pausePosition = m_positions[middle]; |
41 | int pauseLine = pausePosition.position.line; |
42 | int pauseColumn = pausePosition.position.offset - pausePosition.position.lineStartOffset; |
43 | |
44 | if (line < pauseLine) { |
45 | end = middle; |
46 | continue; |
47 | } |
48 | if (line > pauseLine) { |
49 | start = middle + 1; |
50 | continue; |
51 | } |
52 | |
53 | if (column == pauseColumn) { |
54 | // Found an exact position match. Roll forward if this was a function Entry. |
55 | // We are guarenteed to have a Leave for an Entry so we don't need to bounds check. |
56 | while (true) { |
57 | if (pausePosition.type != DebuggerPausePositionType::Enter) |
58 | return Optional<JSTextPosition>(pausePosition.position); |
59 | pausePosition = m_positions[middle++]; |
60 | } |
61 | } |
62 | |
63 | if (column < pauseColumn) |
64 | end = middle; |
65 | else |
66 | start = middle + 1; |
67 | } |
68 | |
69 | // Past the end, no possible pause locations. |
70 | if (start >= m_positions.size()) |
71 | return WTF::nullopt; |
72 | |
73 | // If the next location is a function Entry we will need to decide if we should go into |
74 | // the function or go past the function. We decide to go into the function if the |
75 | // input is on the same line as the function entry. For example: |
76 | // |
77 | // 1. x; |
78 | // 2. |
79 | // 3. function foo() { |
80 | // 4. x; |
81 | // 5. } |
82 | // 6. |
83 | // 7. x; |
84 | // |
85 | // If the input was line 2, skip past functions to pause on line 7. |
86 | // If the input was line 3, go into the function to pause on line 4. |
87 | |
88 | // Valid pause location. Use it. |
89 | DebuggerPausePosition& firstSlidePosition = m_positions[start]; |
90 | if (firstSlidePosition.type != DebuggerPausePositionType::Enter) |
91 | return Optional<JSTextPosition>(firstSlidePosition.position); |
92 | |
93 | // Determine if we should enter this function or skip past it. |
94 | // If entryStackSize is > 0 we are skipping functions. |
95 | bool shouldEnterFunction = firstSlidePosition.position.line == line; |
96 | int entryStackSize = shouldEnterFunction ? 0 : 1; |
97 | for (unsigned i = start + 1; i < m_positions.size(); ++i) { |
98 | DebuggerPausePosition& slidePosition = m_positions[i]; |
99 | ASSERT(entryStackSize >= 0); |
100 | |
101 | // Already skipping functions. |
102 | if (entryStackSize) { |
103 | if (slidePosition.type == DebuggerPausePositionType::Enter) |
104 | entryStackSize++; |
105 | else if (slidePosition.type == DebuggerPausePositionType::Leave) |
106 | entryStackSize--; |
107 | continue; |
108 | } |
109 | |
110 | // Start skipping functions. |
111 | if (slidePosition.type == DebuggerPausePositionType::Enter) { |
112 | entryStackSize++; |
113 | continue; |
114 | } |
115 | |
116 | // Found pause position. |
117 | return Optional<JSTextPosition>(slidePosition.position); |
118 | } |
119 | |
120 | // No pause positions found. |
121 | return WTF::nullopt; |
122 | } |
123 | |
124 | void DebuggerPausePositions::sort() |
125 | { |
126 | std::sort(m_positions.begin(), m_positions.end(), [] (const DebuggerPausePosition& a, const DebuggerPausePosition& b) { |
127 | return a.position.offset < b.position.offset; |
128 | }); |
129 | } |
130 | |
131 | typedef enum { Program, Module } DebuggerParseInfoTag; |
132 | template <DebuggerParseInfoTag T> struct DebuggerParseInfo { }; |
133 | |
134 | template <> struct DebuggerParseInfo<Program> { |
135 | typedef JSC::ProgramNode RootNode; |
136 | static const SourceParseMode parseMode = SourceParseMode::ProgramMode; |
137 | static const JSParserStrictMode strictMode = JSParserStrictMode::NotStrict; |
138 | static const JSParserScriptMode scriptMode = JSParserScriptMode::Classic; |
139 | }; |
140 | |
141 | template <> struct DebuggerParseInfo<Module> { |
142 | typedef JSC::ModuleProgramNode RootNode; |
143 | static const SourceParseMode parseMode = SourceParseMode::ModuleEvaluateMode; |
144 | static const JSParserStrictMode strictMode = JSParserStrictMode::Strict; |
145 | static const JSParserScriptMode scriptMode = JSParserScriptMode::Module; |
146 | }; |
147 | |
148 | template <DebuggerParseInfoTag T> |
149 | bool gatherDebuggerParseData(VM& vm, const SourceCode& source, DebuggerParseData& debuggerParseData) |
150 | { |
151 | typedef typename DebuggerParseInfo<T>::RootNode RootNode; |
152 | SourceParseMode parseMode = DebuggerParseInfo<T>::parseMode; |
153 | JSParserStrictMode strictMode = DebuggerParseInfo<T>::strictMode; |
154 | JSParserScriptMode scriptMode = DebuggerParseInfo<T>::scriptMode; |
155 | |
156 | ParserError error; |
157 | std::unique_ptr<RootNode> rootNode = parse<RootNode>(&vm, source, Identifier(), |
158 | JSParserBuiltinMode::NotBuiltin, strictMode, scriptMode, parseMode, SuperBinding::NotNeeded, |
159 | error, nullptr, ConstructorKind::None, DerivedContextType::None, EvalContextType::None, |
160 | &debuggerParseData); |
161 | if (!rootNode) |
162 | return false; |
163 | |
164 | debuggerParseData.pausePositions.sort(); |
165 | |
166 | return true; |
167 | } |
168 | |
169 | bool gatherDebuggerParseDataForSource(VM& vm, SourceProvider* provider, DebuggerParseData& debuggerParseData) |
170 | { |
171 | ASSERT(provider); |
172 | int startLine = provider->startPosition().m_line.oneBasedInt(); |
173 | int startColumn = provider->startPosition().m_column.oneBasedInt(); |
174 | SourceCode completeSource(*provider, startLine, startColumn); |
175 | |
176 | switch (provider->sourceType()) { |
177 | case SourceProviderSourceType::Program: |
178 | return gatherDebuggerParseData<Program>(vm, completeSource, debuggerParseData); |
179 | case SourceProviderSourceType::Module: |
180 | return gatherDebuggerParseData<Module>(vm, completeSource, debuggerParseData); |
181 | default: |
182 | return false; |
183 | } |
184 | } |
185 | |
186 | } // namespace JSC |
187 | |