OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "src/inspector/wasm-translation.h" |
| 6 |
| 7 #include <algorithm> |
| 8 |
| 9 #include "src/debug/debug-interface.h" |
| 10 #include "src/inspector/protocol/Debugger.h" |
| 11 #include "src/inspector/script-breakpoint.h" |
| 12 #include "src/inspector/string-util.h" |
| 13 #include "src/inspector/v8-debugger-agent-impl.h" |
| 14 #include "src/inspector/v8-debugger-script.h" |
| 15 |
| 16 using namespace v8_inspector; |
| 17 using namespace v8; |
| 18 |
| 19 namespace { |
| 20 int GetScriptId(Isolate *isolate, Local<Object> script_wrapper) { |
| 21 Local<Value> script_id = script_wrapper |
| 22 ->Get(isolate->GetCurrentContext(), |
| 23 toV8StringInternalized(isolate, "id")) |
| 24 .ToLocalChecked(); |
| 25 DCHECK(script_id->IsInt32()); |
| 26 return script_id->Int32Value(isolate->GetCurrentContext()).FromJust(); |
| 27 } |
| 28 |
| 29 String16 GetScriptName(Isolate *isolate, Local<Object> script_wrapper) { |
| 30 Local<Value> script_name = script_wrapper |
| 31 ->Get(isolate->GetCurrentContext(), |
| 32 toV8StringInternalized(isolate, "name")) |
| 33 .ToLocalChecked(); |
| 34 DCHECK(script_name->IsString()); |
| 35 return toProtocolString(script_name.As<String>()); |
| 36 } |
| 37 |
| 38 } // namespace |
| 39 |
| 40 class WasmTranslation::TranslatorImpl { |
| 41 public: |
| 42 struct TransLocation { |
| 43 v8::Isolate *isolate; |
| 44 WasmTranslation *translation; |
| 45 String16 script_id; |
| 46 int line; |
| 47 int column; |
| 48 TransLocation(v8::Isolate *isolate, WasmTranslation *translation, |
| 49 String16 script_id, int line, int column) |
| 50 : isolate(isolate), |
| 51 translation(translation), |
| 52 script_id(script_id), |
| 53 line(line), |
| 54 column(column) {} |
| 55 }; |
| 56 |
| 57 virtual void Translate(TransLocation *loc) = 0; |
| 58 virtual void TranslateBack(TransLocation *loc) = 0; |
| 59 |
| 60 class RawTranslator; |
| 61 class DisassemblingTranslator; |
| 62 }; |
| 63 |
| 64 class WasmTranslation::TranslatorImpl::RawTranslator |
| 65 : public WasmTranslation::TranslatorImpl { |
| 66 public: |
| 67 void Translate(TransLocation *loc) {} |
| 68 void TranslateBack(TransLocation *loc) {} |
| 69 }; |
| 70 |
| 71 class WasmTranslation::TranslatorImpl::DisassemblingTranslator |
| 72 : public WasmTranslation::TranslatorImpl { |
| 73 using OffsetTable = std::vector<std::tuple<uint32_t, int, int>>; |
| 74 |
| 75 public: |
| 76 DisassemblingTranslator(Isolate *isolate, Local<Object> script) |
| 77 : script_(isolate, script) {} |
| 78 |
| 79 void Translate(TransLocation *loc) { |
| 80 const OffsetTable &offset_table = GetOffsetTable(loc); |
| 81 DCHECK(!offset_table.empty()); |
| 82 uint32_t byte_offset = static_cast<uint32_t>(loc->column); |
| 83 |
| 84 // Binary search for the given offset. |
| 85 unsigned left = 0; // inclusive |
| 86 unsigned right = static_cast<unsigned>(offset_table.size()); // exclusive |
| 87 while (right - left > 1) { |
| 88 unsigned mid = (left + right) / 2; |
| 89 if (std::get<0>(offset_table[mid]) <= byte_offset) { |
| 90 left = mid; |
| 91 } else { |
| 92 right = mid; |
| 93 } |
| 94 } |
| 95 |
| 96 loc->script_id = GetFakeScriptId(loc); |
| 97 if (std::get<0>(offset_table[left]) == byte_offset) { |
| 98 loc->line = std::get<1>(offset_table[left]); |
| 99 loc->column = std::get<2>(offset_table[left]); |
| 100 } else { |
| 101 loc->line = 0; |
| 102 loc->column = 0; |
| 103 } |
| 104 } |
| 105 |
| 106 void TranslateBack(TransLocation *loc) { |
| 107 int func_index = GetFunctionIndexFromFakeScriptId(loc->script_id); |
| 108 const OffsetTable *reverse_table = GetReverseTable(func_index); |
| 109 if (!reverse_table) return; |
| 110 DCHECK(!reverse_table->empty()); |
| 111 |
| 112 // Binary search for the given line and column. |
| 113 unsigned left = 0; // inclusive |
| 114 unsigned right = static_cast<unsigned>(reverse_table->size()); // exclusive |
| 115 while (right - left > 1) { |
| 116 unsigned mid = (left + right) / 2; |
| 117 auto &entry = (*reverse_table)[mid]; |
| 118 if (std::get<1>(entry) < loc->line || |
| 119 (std::get<1>(entry) == loc->line && |
| 120 std::get<2>(entry) <= loc->column)) { |
| 121 left = mid; |
| 122 } else { |
| 123 right = mid; |
| 124 } |
| 125 } |
| 126 |
| 127 int found_byte_offset = 0; |
| 128 // If we found an exact match, use it. Otherwise check whether the next |
| 129 // bigger entry is still in the same line. Report that one then. |
| 130 if (std::get<1>((*reverse_table)[left]) == loc->line && |
| 131 std::get<2>((*reverse_table)[left]) == loc->column) { |
| 132 found_byte_offset = std::get<0>((*reverse_table)[left]); |
| 133 } else if (left + 1 < reverse_table->size() && |
| 134 std::get<1>((*reverse_table)[left + 1]) == loc->line) { |
| 135 found_byte_offset = std::get<0>((*reverse_table)[left + 1]); |
| 136 } |
| 137 |
| 138 loc->script_id = String16::fromInteger( |
| 139 GetScriptId(loc->isolate, script_.Get(loc->isolate))); |
| 140 loc->line = func_index; |
| 141 loc->column = found_byte_offset; |
| 142 } |
| 143 |
| 144 private: |
| 145 String16 GetFakeScriptUrl(const TransLocation *loc) { |
| 146 String16 script_name = |
| 147 GetScriptName(loc->isolate, script_.Get(loc->isolate)); |
| 148 return String16::concat("wasm://wasm/", script_name, '/', script_name, '-', |
| 149 String16::fromInteger(loc->line)); |
| 150 } |
| 151 |
| 152 String16 GetFakeScriptId(const TransLocation *loc) { |
| 153 return String16::concat(loc->script_id, '-', |
| 154 String16::fromInteger(loc->line)); |
| 155 } |
| 156 |
| 157 int GetFunctionIndexFromFakeScriptId(const String16 &fake_script_id) { |
| 158 size_t last_dash_pos = fake_script_id.reverseFind('-'); |
| 159 DCHECK_GT(fake_script_id.length(), last_dash_pos); |
| 160 bool ok = true; |
| 161 int func_index = fake_script_id.substring(last_dash_pos + 1).toInteger(&ok); |
| 162 DCHECK(ok); |
| 163 return func_index; |
| 164 } |
| 165 |
| 166 const OffsetTable &GetOffsetTable(const TransLocation *loc) { |
| 167 int func_index = loc->line; |
| 168 auto it = offset_tables_.find(func_index); |
| 169 if (it != offset_tables_.end()) return it->second; |
| 170 |
| 171 std::pair<std::string, OffsetTable> disassembly = |
| 172 DebugInterface::DisassembleWasmFunction( |
| 173 loc->isolate, script_.Get(loc->isolate), func_index); |
| 174 |
| 175 it = offset_tables_ |
| 176 .insert(std::make_pair(func_index, std::move(disassembly.second))) |
| 177 .first; |
| 178 |
| 179 String16 fake_script_id = GetFakeScriptId(loc); |
| 180 String16 fake_script_url = GetFakeScriptUrl(loc); |
| 181 String16 source(disassembly.first.data(), disassembly.first.length()); |
| 182 std::unique_ptr<V8DebuggerScript> fake_script(new V8DebuggerScript( |
| 183 fake_script_id, std::move(fake_script_url), source)); |
| 184 |
| 185 loc->translation->AddFakeScript(std::move(fake_script), this); |
| 186 |
| 187 return it->second; |
| 188 } |
| 189 |
| 190 const OffsetTable *GetReverseTable(int func_index) { |
| 191 auto it = reverse_tables_.find(func_index); |
| 192 if (it != reverse_tables_.end()) return &it->second; |
| 193 |
| 194 // Find offset table, copy and sort it to get reverse table. |
| 195 it = offset_tables_.find(func_index); |
| 196 if (it == offset_tables_.end()) return nullptr; |
| 197 |
| 198 OffsetTable reverse_table = it->second; |
| 199 // Order by line, column, then byte offset. |
| 200 auto cmp = [](std::tuple<uint32_t, int, int> el1, |
| 201 std::tuple<uint32_t, int, int> el2) { |
| 202 if (std::get<1>(el1) != std::get<1>(el2)) |
| 203 return std::get<1>(el1) < std::get<1>(el2); |
| 204 if (std::get<2>(el1) != std::get<2>(el2)) |
| 205 return std::get<2>(el1) < std::get<2>(el2); |
| 206 return std::get<0>(el1) < std::get<0>(el2); |
| 207 }; |
| 208 std::sort(reverse_table.begin(), reverse_table.end(), cmp); |
| 209 |
| 210 auto inserted = reverse_tables_.insert( |
| 211 std::make_pair(func_index, std::move(reverse_table))); |
| 212 DCHECK(inserted.second); |
| 213 return &inserted.first->second; |
| 214 } |
| 215 |
| 216 Global<Object> script_; |
| 217 |
| 218 // We assume to only disassemble a subset of the functions, so store them in a |
| 219 // map instead of an array. |
| 220 std::unordered_map<int, const OffsetTable> offset_tables_; |
| 221 std::unordered_map<int, const OffsetTable> reverse_tables_; |
| 222 }; |
| 223 |
| 224 WasmTranslation::WasmTranslation(Isolate *isolate, |
| 225 V8DebuggerAgentImpl *debugger_agent) |
| 226 : isolate_(isolate), debugger_agent_(debugger_agent), mode_(Disassemble) {} |
| 227 |
| 228 WasmTranslation::~WasmTranslation() { Clear(); } |
| 229 |
| 230 void WasmTranslation::AddScript(Local<Object> script_wrapper) { |
| 231 int script_id = GetScriptId(isolate_, script_wrapper); |
| 232 DCHECK_EQ(0U, wasm_translators_.count(script_id)); |
| 233 std::unique_ptr<TranslatorImpl> impl; |
| 234 switch (mode_) { |
| 235 case Raw: |
| 236 impl.reset(new TranslatorImpl::RawTranslator()); |
| 237 break; |
| 238 case Disassemble: |
| 239 impl.reset(new TranslatorImpl::DisassemblingTranslator(isolate_, |
| 240 script_wrapper)); |
| 241 break; |
| 242 } |
| 243 DCHECK(impl); |
| 244 wasm_translators_.insert(std::make_pair(script_id, std::move(impl))); |
| 245 } |
| 246 |
| 247 void WasmTranslation::Clear() { |
| 248 wasm_translators_.clear(); |
| 249 fake_scripts_.clear(); |
| 250 } |
| 251 |
| 252 bool WasmTranslation::Translate(protocol::Debugger::Location *location) { |
| 253 // Line and column in protocol Location are 0-based. |
| 254 String16 script_id = location->getScriptId(); |
| 255 int line_number = location->getLineNumber(); |
| 256 int column_number = location->getColumnNumber(-1); |
| 257 |
| 258 if (column_number < 0 || !Translate(&script_id, &line_number, &column_number)) |
| 259 return false; |
| 260 |
| 261 location->setScriptId(std::move(script_id)); |
| 262 location->setLineNumber(line_number); |
| 263 location->setColumnNumber(column_number); |
| 264 |
| 265 return true; |
| 266 } |
| 267 |
| 268 bool WasmTranslation::Translate(String16 *script_id, int *line_number, |
| 269 int *column_number) { |
| 270 DCHECK(script_id && line_number && column_number); |
| 271 bool ok = true; |
| 272 int script_id_int = script_id->toInteger(&ok); |
| 273 if (!ok) return false; |
| 274 |
| 275 auto it = wasm_translators_.find(script_id_int); |
| 276 if (it == wasm_translators_.end()) return false; |
| 277 TranslatorImpl *translator = it->second.get(); |
| 278 |
| 279 TranslatorImpl::TransLocation trans_loc(isolate_, this, std::move(*script_id), |
| 280 *line_number, *column_number); |
| 281 translator->Translate(&trans_loc); |
| 282 |
| 283 *script_id = std::move(trans_loc.script_id); |
| 284 *line_number = trans_loc.line; |
| 285 *column_number = trans_loc.column; |
| 286 |
| 287 return true; |
| 288 } |
| 289 |
| 290 bool WasmTranslation::TranslateBack(ScriptBreakpoint *breakpoint) { |
| 291 auto it = fake_scripts_.find(breakpoint->script_id); |
| 292 if (it == fake_scripts_.end()) return false; |
| 293 TranslatorImpl *translator = it->second; |
| 294 |
| 295 TranslatorImpl::TransLocation trans_loc( |
| 296 isolate_, this, std::move(breakpoint->script_id), breakpoint->line_number, |
| 297 breakpoint->column_number); |
| 298 translator->TranslateBack(&trans_loc); |
| 299 |
| 300 breakpoint->script_id = std::move(trans_loc.script_id); |
| 301 breakpoint->line_number = trans_loc.line; |
| 302 breakpoint->column_number = trans_loc.column; |
| 303 |
| 304 return true; |
| 305 } |
| 306 |
| 307 void WasmTranslation::AddFakeScript( |
| 308 std::unique_ptr<V8DebuggerScript> fake_script, TranslatorImpl *translator) { |
| 309 bool inserted = |
| 310 fake_scripts_.insert(std::make_pair(fake_script->scriptId(), translator)) |
| 311 .second; |
| 312 DCHECK(inserted); |
| 313 USE(inserted); |
| 314 debugger_agent_->didParseSource(std::move(fake_script), true); |
| 315 } |
OLD | NEW |