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
32namespace JSC {
33
34Optional<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
124void 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
131typedef enum { Program, Module } DebuggerParseInfoTag;
132template <DebuggerParseInfoTag T> struct DebuggerParseInfo { };
133
134template <> 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
141template <> 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
148template <DebuggerParseInfoTag T>
149bool 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
169bool 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