OLD | NEW |
1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/inspector/wasm-translation.h" | 5 #include "src/inspector/wasm-translation.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "src/debug/debug-interface.h" | 9 #include "src/debug/debug-interface.h" |
10 #include "src/inspector/protocol/Debugger.h" | 10 #include "src/inspector/protocol/Debugger.h" |
11 #include "src/inspector/script-breakpoint.h" | 11 #include "src/inspector/script-breakpoint.h" |
12 #include "src/inspector/string-util.h" | 12 #include "src/inspector/string-util.h" |
13 #include "src/inspector/v8-debugger-agent-impl.h" | 13 #include "src/inspector/v8-debugger-agent-impl.h" |
14 #include "src/inspector/v8-debugger-script.h" | 14 #include "src/inspector/v8-debugger-script.h" |
15 #include "src/inspector/v8-debugger.h" | 15 #include "src/inspector/v8-debugger.h" |
16 #include "src/inspector/v8-inspector-impl.h" | 16 #include "src/inspector/v8-inspector-impl.h" |
17 | 17 |
18 using namespace v8_inspector; | 18 using namespace v8_inspector; |
19 using namespace v8; | 19 using namespace v8; |
20 | 20 |
21 namespace { | |
22 int GetScriptId(Isolate *isolate, Local<Object> script_wrapper) { | |
23 Local<Value> script_id = script_wrapper | |
24 ->Get(isolate->GetCurrentContext(), | |
25 toV8StringInternalized(isolate, "id")) | |
26 .ToLocalChecked(); | |
27 DCHECK(script_id->IsInt32()); | |
28 return script_id->Int32Value(isolate->GetCurrentContext()).FromJust(); | |
29 } | |
30 | |
31 String16 GetScriptName(Isolate *isolate, Local<Object> script_wrapper) { | |
32 Local<Value> script_name = script_wrapper | |
33 ->Get(isolate->GetCurrentContext(), | |
34 toV8StringInternalized(isolate, "name")) | |
35 .ToLocalChecked(); | |
36 DCHECK(script_name->IsString()); | |
37 return toProtocolString(script_name.As<String>()); | |
38 } | |
39 | |
40 } // namespace | |
41 | |
42 class WasmTranslation::TranslatorImpl { | 21 class WasmTranslation::TranslatorImpl { |
43 public: | 22 public: |
44 struct TransLocation { | 23 struct TransLocation { |
45 WasmTranslation *translation; | 24 WasmTranslation *translation; |
46 String16 script_id; | 25 String16 script_id; |
47 int line; | 26 int line; |
48 int column; | 27 int column; |
49 int context_group_id; | |
50 TransLocation(WasmTranslation *translation, String16 script_id, int line, | 28 TransLocation(WasmTranslation *translation, String16 script_id, int line, |
51 int column, int context_group_id) | 29 int column) |
52 : translation(translation), | 30 : translation(translation), |
53 script_id(script_id), | 31 script_id(script_id), |
54 line(line), | 32 line(line), |
55 column(column), | 33 column(column) {} |
56 context_group_id(context_group_id) {} | |
57 }; | 34 }; |
58 | 35 |
59 virtual void Translate(TransLocation *loc) = 0; | 36 virtual void Translate(TransLocation *loc) = 0; |
60 virtual void TranslateBack(TransLocation *loc) = 0; | 37 virtual void TranslateBack(TransLocation *loc) = 0; |
61 | 38 |
62 class RawTranslator; | 39 class RawTranslator; |
63 class DisassemblingTranslator; | 40 class DisassemblingTranslator; |
64 }; | 41 }; |
65 | 42 |
66 class WasmTranslation::TranslatorImpl::RawTranslator | 43 class WasmTranslation::TranslatorImpl::RawTranslator |
67 : public WasmTranslation::TranslatorImpl { | 44 : public WasmTranslation::TranslatorImpl { |
68 public: | 45 public: |
69 void Translate(TransLocation *loc) {} | 46 void Translate(TransLocation *loc) {} |
70 void TranslateBack(TransLocation *loc) {} | 47 void TranslateBack(TransLocation *loc) {} |
71 }; | 48 }; |
72 | 49 |
73 class WasmTranslation::TranslatorImpl::DisassemblingTranslator | 50 class WasmTranslation::TranslatorImpl::DisassemblingTranslator |
74 : public WasmTranslation::TranslatorImpl { | 51 : public WasmTranslation::TranslatorImpl { |
75 using OffsetTable = debug::WasmDisassembly::OffsetTable; | 52 using OffsetTable = debug::WasmDisassembly::OffsetTable; |
76 | 53 |
77 public: | 54 public: |
78 DisassemblingTranslator(Isolate *isolate, Local<Object> script) | 55 DisassemblingTranslator(Isolate *isolate, Local<debug::WasmScript> script, |
79 : script_(isolate, script) {} | 56 WasmTranslation *translation, |
| 57 V8DebuggerAgentImpl *agent) |
| 58 : script_(isolate, script) { |
| 59 // Register fake scripts for each function in this wasm module/script. |
| 60 int num_functions = script->NumFunctions(); |
| 61 int num_imported_functions = script->NumImportedFunctions(); |
| 62 DCHECK_LE(0, num_imported_functions); |
| 63 DCHECK_LE(0, num_functions); |
| 64 DCHECK_GE(num_functions, num_imported_functions); |
| 65 String16 script_id = String16::fromInteger(script->Id()); |
| 66 for (int func_idx = num_imported_functions; func_idx < num_functions; |
| 67 ++func_idx) { |
| 68 AddFakeScript(isolate, script_id, func_idx, translation, agent); |
| 69 } |
| 70 } |
80 | 71 |
81 void Translate(TransLocation *loc) { | 72 void Translate(TransLocation *loc) { |
82 const OffsetTable &offset_table = GetOffsetTable(loc); | 73 const OffsetTable &offset_table = GetOffsetTable(loc); |
83 DCHECK(!offset_table.empty()); | 74 DCHECK(!offset_table.empty()); |
84 uint32_t byte_offset = static_cast<uint32_t>(loc->column); | 75 uint32_t byte_offset = static_cast<uint32_t>(loc->column); |
85 | 76 |
86 // Binary search for the given offset. | 77 // Binary search for the given offset. |
87 unsigned left = 0; // inclusive | 78 unsigned left = 0; // inclusive |
88 unsigned right = static_cast<unsigned>(offset_table.size()); // exclusive | 79 unsigned right = static_cast<unsigned>(offset_table.size()); // exclusive |
89 while (right - left > 1) { | 80 while (right - left > 1) { |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 // bigger entry is still in the same line. Report that one then. | 121 // bigger entry is still in the same line. Report that one then. |
131 if ((*reverse_table)[left].line == loc->line && | 122 if ((*reverse_table)[left].line == loc->line && |
132 (*reverse_table)[left].column == loc->column) { | 123 (*reverse_table)[left].column == loc->column) { |
133 found_byte_offset = (*reverse_table)[left].byte_offset; | 124 found_byte_offset = (*reverse_table)[left].byte_offset; |
134 } else if (left + 1 < reverse_table->size() && | 125 } else if (left + 1 < reverse_table->size() && |
135 (*reverse_table)[left + 1].line == loc->line) { | 126 (*reverse_table)[left + 1].line == loc->line) { |
136 found_byte_offset = (*reverse_table)[left + 1].byte_offset; | 127 found_byte_offset = (*reverse_table)[left + 1].byte_offset; |
137 } | 128 } |
138 | 129 |
139 v8::Isolate *isolate = loc->translation->isolate_; | 130 v8::Isolate *isolate = loc->translation->isolate_; |
140 loc->script_id = | 131 loc->script_id = String16::fromInteger(script_.Get(isolate)->Id()); |
141 String16::fromInteger(GetScriptId(isolate, script_.Get(isolate))); | |
142 loc->line = func_index; | 132 loc->line = func_index; |
143 loc->column = found_byte_offset; | 133 loc->column = found_byte_offset; |
144 } | 134 } |
145 | 135 |
146 private: | 136 private: |
147 String16 GetFakeScriptUrl(const TransLocation *loc) { | 137 String16 GetScriptName(v8::Isolate *isolate) { |
148 v8::Isolate *isolate = loc->translation->isolate_; | 138 return toProtocolString(script_.Get(isolate)->Name().ToLocalChecked()); |
149 String16 script_name = GetScriptName(isolate, script_.Get(isolate)); | |
150 return String16::concat("wasm://wasm/", script_name, '/', script_name, '-', | |
151 String16::fromInteger(loc->line)); | |
152 } | 139 } |
153 | 140 |
| 141 String16 GetFakeScriptUrl(v8::Isolate *isolate, int func_index) { |
| 142 String16 script_name = GetScriptName(isolate); |
| 143 return String16::concat("wasm://wasm/", script_name, '/', script_name, '-', |
| 144 String16::fromInteger(func_index)); |
| 145 } |
| 146 String16 GetFakeScriptUrl(const TransLocation *loc) { |
| 147 return GetFakeScriptUrl(loc->translation->isolate_, loc->line); |
| 148 } |
| 149 |
| 150 String16 GetFakeScriptId(const String16 script_id, int func_index) { |
| 151 return String16::concat(script_id, '-', String16::fromInteger(func_index)); |
| 152 } |
154 String16 GetFakeScriptId(const TransLocation *loc) { | 153 String16 GetFakeScriptId(const TransLocation *loc) { |
155 return String16::concat(loc->script_id, '-', | 154 return GetFakeScriptId(loc->script_id, loc->line); |
156 String16::fromInteger(loc->line)); | 155 } |
| 156 |
| 157 void AddFakeScript(v8::Isolate *isolate, const String16 &underlyingScriptId, |
| 158 int func_idx, WasmTranslation *translation, |
| 159 V8DebuggerAgentImpl *agent) { |
| 160 String16 fake_script_id = GetFakeScriptId(underlyingScriptId, func_idx); |
| 161 String16 fake_script_url = GetFakeScriptUrl(isolate, func_idx); |
| 162 |
| 163 // TODO(clemensh): Generate disassembly lazily when queried by the frontend. |
| 164 debug::WasmDisassembly disassembly = |
| 165 script_.Get(isolate)->DisassembleFunction(func_idx); |
| 166 |
| 167 DCHECK_EQ(0, offset_tables_.count(func_idx)); |
| 168 offset_tables_.insert( |
| 169 std::make_pair(func_idx, std::move(disassembly.offset_table))); |
| 170 String16 source(disassembly.disassembly.data(), |
| 171 disassembly.disassembly.length()); |
| 172 std::unique_ptr<V8DebuggerScript> fake_script(new V8DebuggerScript( |
| 173 fake_script_id, std::move(fake_script_url), source)); |
| 174 |
| 175 translation->AddFakeScript(fake_script->scriptId(), this); |
| 176 agent->didParseSource(std::move(fake_script), true); |
157 } | 177 } |
158 | 178 |
159 int GetFunctionIndexFromFakeScriptId(const String16 &fake_script_id) { | 179 int GetFunctionIndexFromFakeScriptId(const String16 &fake_script_id) { |
160 size_t last_dash_pos = fake_script_id.reverseFind('-'); | 180 size_t last_dash_pos = fake_script_id.reverseFind('-'); |
161 DCHECK_GT(fake_script_id.length(), last_dash_pos); | 181 DCHECK_GT(fake_script_id.length(), last_dash_pos); |
162 bool ok = true; | 182 bool ok = true; |
163 int func_index = fake_script_id.substring(last_dash_pos + 1).toInteger(&ok); | 183 int func_index = fake_script_id.substring(last_dash_pos + 1).toInteger(&ok); |
164 DCHECK(ok); | 184 DCHECK(ok); |
165 return func_index; | 185 return func_index; |
166 } | 186 } |
167 | 187 |
168 const OffsetTable &GetOffsetTable(const TransLocation *loc) { | 188 const OffsetTable &GetOffsetTable(const TransLocation *loc) { |
169 int func_index = loc->line; | 189 int func_index = loc->line; |
170 auto it = offset_tables_.find(func_index); | 190 auto it = offset_tables_.find(func_index); |
171 if (it != offset_tables_.end()) return it->second; | 191 // TODO(clemensh): Once we load disassembly lazily, the offset table |
172 | 192 // might not be there yet. Load it lazily then. |
173 v8::Isolate *isolate = loc->translation->isolate_; | 193 DCHECK(it != offset_tables_.end()); |
174 debug::WasmDisassembly disassembly_result = debug::DisassembleWasmFunction( | |
175 isolate, script_.Get(isolate), func_index); | |
176 | |
177 it = offset_tables_ | |
178 .insert(std::make_pair(func_index, | |
179 std::move(disassembly_result.offset_table))) | |
180 .first; | |
181 | |
182 String16 fake_script_id = GetFakeScriptId(loc); | |
183 String16 fake_script_url = GetFakeScriptUrl(loc); | |
184 String16 source(disassembly_result.disassembly.data(), | |
185 disassembly_result.disassembly.length()); | |
186 std::unique_ptr<V8DebuggerScript> fake_script(new V8DebuggerScript( | |
187 fake_script_id, std::move(fake_script_url), source)); | |
188 | |
189 loc->translation->AddFakeScript(std::move(fake_script), this, | |
190 loc->context_group_id); | |
191 | |
192 return it->second; | 194 return it->second; |
193 } | 195 } |
194 | 196 |
195 const OffsetTable *GetReverseTable(int func_index) { | 197 const OffsetTable *GetReverseTable(int func_index) { |
196 auto it = reverse_tables_.find(func_index); | 198 auto it = reverse_tables_.find(func_index); |
197 if (it != reverse_tables_.end()) return &it->second; | 199 if (it != reverse_tables_.end()) return &it->second; |
198 | 200 |
199 // Find offset table, copy and sort it to get reverse table. | 201 // Find offset table, copy and sort it to get reverse table. |
200 it = offset_tables_.find(func_index); | 202 it = offset_tables_.find(func_index); |
201 if (it == offset_tables_.end()) return nullptr; | 203 if (it == offset_tables_.end()) return nullptr; |
202 | 204 |
203 OffsetTable reverse_table = it->second; | 205 OffsetTable reverse_table = it->second; |
204 // Order by line, column, then byte offset. | 206 // Order by line, column, then byte offset. |
205 auto cmp = [](OffsetTable::value_type el1, OffsetTable::value_type el2) { | 207 auto cmp = [](OffsetTable::value_type el1, OffsetTable::value_type el2) { |
206 if (el1.line != el2.line) return el1.line < el2.line; | 208 if (el1.line != el2.line) return el1.line < el2.line; |
207 if (el1.column != el2.column) return el1.column < el2.column; | 209 if (el1.column != el2.column) return el1.column < el2.column; |
208 return el1.byte_offset < el2.byte_offset; | 210 return el1.byte_offset < el2.byte_offset; |
209 }; | 211 }; |
210 std::sort(reverse_table.begin(), reverse_table.end(), cmp); | 212 std::sort(reverse_table.begin(), reverse_table.end(), cmp); |
211 | 213 |
212 auto inserted = reverse_tables_.insert( | 214 auto inserted = reverse_tables_.insert( |
213 std::make_pair(func_index, std::move(reverse_table))); | 215 std::make_pair(func_index, std::move(reverse_table))); |
214 DCHECK(inserted.second); | 216 DCHECK(inserted.second); |
215 return &inserted.first->second; | 217 return &inserted.first->second; |
216 } | 218 } |
217 | 219 |
218 Global<Object> script_; | 220 Global<debug::WasmScript> script_; |
219 | 221 |
220 // We assume to only disassemble a subset of the functions, so store them in a | 222 // We assume to only disassemble a subset of the functions, so store them in a |
221 // map instead of an array. | 223 // map instead of an array. |
222 std::unordered_map<int, const OffsetTable> offset_tables_; | 224 std::unordered_map<int, const OffsetTable> offset_tables_; |
223 std::unordered_map<int, const OffsetTable> reverse_tables_; | 225 std::unordered_map<int, const OffsetTable> reverse_tables_; |
224 }; | 226 }; |
225 | 227 |
226 WasmTranslation::WasmTranslation(v8::Isolate *isolate, V8Debugger *debugger) | 228 WasmTranslation::WasmTranslation(v8::Isolate *isolate) |
227 : isolate_(isolate), debugger_(debugger), mode_(Disassemble) {} | 229 : isolate_(isolate), mode_(Disassemble) {} |
228 | 230 |
229 WasmTranslation::~WasmTranslation() { Clear(); } | 231 WasmTranslation::~WasmTranslation() { Clear(); } |
230 | 232 |
231 void WasmTranslation::AddScript(Local<Object> script_wrapper) { | 233 void WasmTranslation::AddScript(Local<debug::WasmScript> script, |
232 int script_id = GetScriptId(isolate_, script_wrapper); | 234 V8DebuggerAgentImpl *agent) { |
233 DCHECK_EQ(0U, wasm_translators_.count(script_id)); | 235 int script_id = script->Id(); |
| 236 DCHECK_EQ(0, wasm_translators_.count(script_id)); |
234 std::unique_ptr<TranslatorImpl> impl; | 237 std::unique_ptr<TranslatorImpl> impl; |
235 switch (mode_) { | 238 switch (mode_) { |
236 case Raw: | 239 case Raw: |
237 impl.reset(new TranslatorImpl::RawTranslator()); | 240 impl.reset(new TranslatorImpl::RawTranslator()); |
238 break; | 241 break; |
239 case Disassemble: | 242 case Disassemble: |
240 impl.reset(new TranslatorImpl::DisassemblingTranslator(isolate_, | 243 impl.reset(new TranslatorImpl::DisassemblingTranslator(isolate_, script, |
241 script_wrapper)); | 244 this, agent)); |
242 break; | 245 break; |
243 } | 246 } |
244 DCHECK(impl); | 247 DCHECK(impl); |
245 wasm_translators_.insert(std::make_pair(script_id, std::move(impl))); | 248 wasm_translators_.insert(std::make_pair(script_id, std::move(impl))); |
246 } | 249 } |
247 | 250 |
248 void WasmTranslation::Clear() { | 251 void WasmTranslation::Clear() { |
249 wasm_translators_.clear(); | 252 wasm_translators_.clear(); |
250 fake_scripts_.clear(); | 253 fake_scripts_.clear(); |
251 } | 254 } |
252 | 255 |
253 // Translation "forward" (to artificial scripts). | 256 // Translation "forward" (to artificial scripts). |
254 bool WasmTranslation::TranslateWasmScriptLocationToProtocolLocation( | 257 bool WasmTranslation::TranslateWasmScriptLocationToProtocolLocation( |
255 String16 *script_id, int *line_number, int *column_number, | 258 String16 *script_id, int *line_number, int *column_number) { |
256 int context_group_id) { | |
257 DCHECK(script_id && line_number && column_number); | 259 DCHECK(script_id && line_number && column_number); |
258 bool ok = true; | 260 bool ok = true; |
259 int script_id_int = script_id->toInteger(&ok); | 261 int script_id_int = script_id->toInteger(&ok); |
260 if (!ok) return false; | 262 if (!ok) return false; |
261 | 263 |
262 auto it = wasm_translators_.find(script_id_int); | 264 auto it = wasm_translators_.find(script_id_int); |
263 if (it == wasm_translators_.end()) return false; | 265 if (it == wasm_translators_.end()) return false; |
264 TranslatorImpl *translator = it->second.get(); | 266 TranslatorImpl *translator = it->second.get(); |
265 | 267 |
266 TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id), | 268 TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id), |
267 *line_number, *column_number, | 269 *line_number, *column_number); |
268 context_group_id); | |
269 translator->Translate(&trans_loc); | 270 translator->Translate(&trans_loc); |
270 | 271 |
271 *script_id = std::move(trans_loc.script_id); | 272 *script_id = std::move(trans_loc.script_id); |
272 *line_number = trans_loc.line; | 273 *line_number = trans_loc.line; |
273 *column_number = trans_loc.column; | 274 *column_number = trans_loc.column; |
274 | 275 |
275 return true; | 276 return true; |
276 } | 277 } |
277 | 278 |
278 // Translation "backward" (from artificial to real scripts). | 279 // Translation "backward" (from artificial to real scripts). |
279 bool WasmTranslation::TranslateProtocolLocationToWasmScriptLocation( | 280 bool WasmTranslation::TranslateProtocolLocationToWasmScriptLocation( |
280 String16 *script_id, int *line_number, int *column_number) { | 281 String16 *script_id, int *line_number, int *column_number) { |
281 auto it = fake_scripts_.find(*script_id); | 282 auto it = fake_scripts_.find(*script_id); |
282 if (it == fake_scripts_.end()) return false; | 283 if (it == fake_scripts_.end()) return false; |
283 TranslatorImpl *translator = it->second; | 284 TranslatorImpl *translator = it->second; |
284 | 285 |
285 TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id), | 286 TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id), |
286 *line_number, *column_number, -1); | 287 *line_number, *column_number); |
287 translator->TranslateBack(&trans_loc); | 288 translator->TranslateBack(&trans_loc); |
288 | 289 |
289 *script_id = std::move(trans_loc.script_id); | 290 *script_id = std::move(trans_loc.script_id); |
290 *line_number = trans_loc.line; | 291 *line_number = trans_loc.line; |
291 *column_number = trans_loc.column; | 292 *column_number = trans_loc.column; |
292 | 293 |
293 return true; | 294 return true; |
294 } | 295 } |
295 | 296 |
296 void WasmTranslation::AddFakeScript( | 297 void WasmTranslation::AddFakeScript(const String16 &scriptId, |
297 std::unique_ptr<V8DebuggerScript> fake_script, TranslatorImpl *translator, | 298 TranslatorImpl *translator) { |
298 int context_group_id) { | 299 DCHECK_EQ(0, fake_scripts_.count(scriptId)); |
299 bool inserted = | 300 fake_scripts_.insert(std::make_pair(scriptId, translator)); |
300 fake_scripts_.insert(std::make_pair(fake_script->scriptId(), translator)) | |
301 .second; | |
302 DCHECK(inserted); | |
303 USE(inserted); | |
304 V8DebuggerAgentImpl *agent = | |
305 debugger_->inspector()->enabledDebuggerAgentForGroup(context_group_id); | |
306 agent->didParseSource(std::move(fake_script), true); | |
307 } | 301 } |
OLD | NEW |