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