1 | /* |
2 | * Copyright (C) 2016-2019 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 | DebuggerPausePosition position = { DebuggerPausePositionType::Invalid, JSTextPosition(line, column, 0) }; |
37 | auto it = std::lower_bound(m_positions.begin(), m_positions.end(), position, [] (const DebuggerPausePosition& a, const DebuggerPausePosition& b) { |
38 | if (a.position.line == b.position.line) |
39 | return a.position.column() < b.position.column(); |
40 | return a.position.line < b.position.line; |
41 | }); |
42 | if (it == m_positions.end()) |
43 | return WTF::nullopt; |
44 | |
45 | if (line == it->position.line && column == it->position.column()) { |
46 | // Found an exact position match. Roll forward if this was a function Entry. |
47 | // We are guaranteed to have a Leave for an Entry so we don't need to bounds check. |
48 | while (it->type == DebuggerPausePositionType::Enter) |
49 | ++it; |
50 | return it->position; |
51 | } |
52 | |
53 | // If the next location is a function Entry we will need to decide if we should go into |
54 | // the function or go past the function. We decide to go into the function if the |
55 | // input is on the same line as the function entry. For example: |
56 | // |
57 | // 1. x; |
58 | // 2. |
59 | // 3. function foo() { |
60 | // 4. x; |
61 | // 5. } |
62 | // 6. |
63 | // 7. x; |
64 | // |
65 | // If the input was line 2, skip past functions to pause on line 7. |
66 | // If the input was line 3, go into the function to pause on line 4. |
67 | |
68 | // Valid pause location. Use it. |
69 | auto& firstSlidePosition = *it; |
70 | if (firstSlidePosition.type != DebuggerPausePositionType::Enter) |
71 | return Optional<JSTextPosition>(firstSlidePosition.position); |
72 | |
73 | // Determine if we should enter this function or skip past it. |
74 | // If entryStackSize is > 0 we are skipping functions. |
75 | bool shouldEnterFunction = firstSlidePosition.position.line == line; |
76 | int entryStackSize = shouldEnterFunction ? 0 : 1; |
77 | ++it; |
78 | for (; it != m_positions.end(); ++it) { |
79 | auto& slidePosition = *it; |
80 | ASSERT(entryStackSize >= 0); |
81 | |
82 | // Already skipping functions. |
83 | if (entryStackSize) { |
84 | if (slidePosition.type == DebuggerPausePositionType::Enter) |
85 | entryStackSize++; |
86 | else if (slidePosition.type == DebuggerPausePositionType::Leave) |
87 | entryStackSize--; |
88 | continue; |
89 | } |
90 | |
91 | // Start skipping functions. |
92 | if (slidePosition.type == DebuggerPausePositionType::Enter) { |
93 | entryStackSize++; |
94 | continue; |
95 | } |
96 | |
97 | // Found pause position. |
98 | return Optional<JSTextPosition>(slidePosition.position); |
99 | } |
100 | |
101 | // No pause positions found. |
102 | return WTF::nullopt; |
103 | } |
104 | |
105 | void DebuggerPausePositions::sort() |
106 | { |
107 | std::sort(m_positions.begin(), m_positions.end(), [] (const DebuggerPausePosition& a, const DebuggerPausePosition& b) { |
108 | if (a.position.offset == b.position.offset) |
109 | return a.type < b.type; |
110 | return a.position.offset < b.position.offset; |
111 | }); |
112 | } |
113 | |
114 | typedef enum { Program, Module } DebuggerParseInfoTag; |
115 | template <DebuggerParseInfoTag T> struct DebuggerParseInfo { }; |
116 | |
117 | template <> struct DebuggerParseInfo<Program> { |
118 | typedef JSC::ProgramNode RootNode; |
119 | static constexpr SourceParseMode parseMode = SourceParseMode::ProgramMode; |
120 | static constexpr JSParserStrictMode strictMode = JSParserStrictMode::NotStrict; |
121 | static constexpr JSParserScriptMode scriptMode = JSParserScriptMode::Classic; |
122 | }; |
123 | |
124 | template <> struct DebuggerParseInfo<Module> { |
125 | typedef JSC::ModuleProgramNode RootNode; |
126 | static constexpr SourceParseMode parseMode = SourceParseMode::ModuleEvaluateMode; |
127 | static constexpr JSParserStrictMode strictMode = JSParserStrictMode::Strict; |
128 | static constexpr JSParserScriptMode scriptMode = JSParserScriptMode::Module; |
129 | }; |
130 | |
131 | template <DebuggerParseInfoTag T> |
132 | bool gatherDebuggerParseData(VM& vm, const SourceCode& source, DebuggerParseData& debuggerParseData) |
133 | { |
134 | typedef typename DebuggerParseInfo<T>::RootNode RootNode; |
135 | SourceParseMode parseMode = DebuggerParseInfo<T>::parseMode; |
136 | JSParserStrictMode strictMode = DebuggerParseInfo<T>::strictMode; |
137 | JSParserScriptMode scriptMode = DebuggerParseInfo<T>::scriptMode; |
138 | |
139 | ParserError error; |
140 | std::unique_ptr<RootNode> rootNode = parse<RootNode>(vm, source, Identifier(), |
141 | JSParserBuiltinMode::NotBuiltin, strictMode, scriptMode, parseMode, SuperBinding::NotNeeded, |
142 | error, nullptr, ConstructorKind::None, DerivedContextType::None, EvalContextType::None, |
143 | &debuggerParseData); |
144 | if (!rootNode) |
145 | return false; |
146 | |
147 | debuggerParseData.pausePositions.sort(); |
148 | |
149 | return true; |
150 | } |
151 | |
152 | bool gatherDebuggerParseDataForSource(VM& vm, SourceProvider* provider, DebuggerParseData& debuggerParseData) |
153 | { |
154 | ASSERT(provider); |
155 | int startLine = provider->startPosition().m_line.oneBasedInt(); |
156 | int startColumn = provider->startPosition().m_column.oneBasedInt(); |
157 | SourceCode completeSource(*provider, startLine, startColumn); |
158 | |
159 | switch (provider->sourceType()) { |
160 | case SourceProviderSourceType::Program: |
161 | return gatherDebuggerParseData<Program>(vm, completeSource, debuggerParseData); |
162 | case SourceProviderSourceType::Module: |
163 | return gatherDebuggerParseData<Module>(vm, completeSource, debuggerParseData); |
164 | default: |
165 | return false; |
166 | } |
167 | } |
168 | |
169 | } // namespace JSC |
170 | |